diff --git a/packages/compat/src/babel-plugin-adjust-imports.ts b/packages/compat/src/babel-plugin-adjust-imports.ts index 8d217adbe..9fe846aa3 100644 --- a/packages/compat/src/babel-plugin-adjust-imports.ts +++ b/packages/compat/src/babel-plugin-adjust-imports.ts @@ -3,10 +3,9 @@ import type { NodePath } from '@babel/traverse'; import type * as Babel from '@babel/core'; import type { types as t } from '@babel/core'; import { ImportUtil } from 'babel-import-util'; -import { readJSONSync } from 'fs-extra'; import type { CompatResolverOptions } from './resolver-transform'; import type { Package } from '@embroider/core'; -import { cleanUrl, locateEmbroiderWorkingDir, packageName, Resolver, unrelativize } from '@embroider/core'; +import { cleanUrl, packageName, type Resolver, ResolverLoader, unrelativize } from '@embroider/core'; import { snippetToDasherizedName } from './dasherize-component-name'; import type { ActivePackageRules, ComponentRules, ModuleRules, TemplateRules } from './dependency-rules'; import { appTreeRulesDir } from './dependency-rules'; @@ -27,8 +26,7 @@ interface ExtraImports { } type InternalConfig = { - resolverOptions: CompatResolverOptions; - resolver: Resolver; + loader: ResolverLoader; // rule-based extra dependencies, indexed by filename extraImports: ExtraImports; @@ -44,15 +42,12 @@ export default function main(babel: typeof Babel) { if (cached) { return cached; } - let resolverOptions: CompatResolverOptions = readJSONSync( - join(locateEmbroiderWorkingDir(appRoot), 'resolver.json') - ); - let resolver = new Resolver(resolverOptions); + let loader = new ResolverLoader(appRoot); + cached = { - resolverOptions, - resolver, - extraImports: preprocessExtraImports(resolverOptions, resolver), - componentExtraImports: preprocessComponentExtraImports(resolverOptions), + loader, + extraImports: preprocessExtraImports(loader), + componentExtraImports: preprocessComponentExtraImports(loader), }; return cached; } @@ -80,7 +75,7 @@ function addExtraImports(t: BabelTypes, path: NodePath, config: Inter applyRules(t, path, entry, adder, config, filename); } - let componentName = config.resolver.reverseComponentLookup(filename); + let componentName = config.loader.resolver.reverseComponentLookup(filename); if (componentName) { let rules = config.componentExtraImports[componentName]; if (rules) { @@ -138,9 +133,10 @@ function amdDefine(t: BabelTypes, adder: ImportUtil, path: NodePath, ); } -function preprocessExtraImports(config: CompatResolverOptions, resolver: Resolver): ExtraImports { +function preprocessExtraImports(loader: ResolverLoader): ExtraImports { let extraImports: ExtraImports = {}; - for (let rule of config.activePackageRules) { + let config = loader.resolver.options as CompatResolverOptions; + for (let rule of config.activePackageRules ?? []) { if (rule.addonModules) { for (let [filename, moduleRules] of Object.entries(rule.addonModules)) { for (let root of rule.roots) { @@ -156,7 +152,7 @@ function preprocessExtraImports(config: CompatResolverOptions, resolver: Resolve // But this code is only for applying packageRules to auto-upgraded v1 // addons, and those we always organize with their treeForApp output // in _app_. - expandDependsOnRules(appTreeRulesDir(root, resolver), filename, moduleRules, extraImports); + expandDependsOnRules(appTreeRulesDir(root, loader.resolver), filename, moduleRules, extraImports); } } } @@ -170,7 +166,7 @@ function preprocessExtraImports(config: CompatResolverOptions, resolver: Resolve if (rule.appTemplates) { for (let [filename, moduleRules] of Object.entries(rule.appTemplates)) { for (let root of rule.roots) { - expandInvokesRules(appTreeRulesDir(root, resolver), filename, moduleRules, extraImports); + expandInvokesRules(appTreeRulesDir(root, loader.resolver), filename, moduleRules, extraImports); } } } @@ -184,7 +180,7 @@ function lazyPackageLookup(config: InternalConfig, filename: string) { return { get owningPackage() { if (!owningPackage) { - owningPackage = { result: config.resolver.packageCache.ownerOfFile(filename) }; + owningPackage = { result: config.loader.resolver.packageCache.ownerOfFile(filename) }; } return owningPackage.result; }, @@ -193,7 +189,7 @@ function lazyPackageLookup(config: InternalConfig, filename: string) { owningEngine = { result: undefined }; let p = this.owningPackage; if (p) { - owningEngine.result = config.resolver.owningEngine(p); + owningEngine.result = config.loader.resolver.owningEngine(p); } } return owningEngine.result; @@ -201,9 +197,10 @@ function lazyPackageLookup(config: InternalConfig, filename: string) { }; } -function preprocessComponentExtraImports(config: CompatResolverOptions): ExtraImports { +function preprocessComponentExtraImports(loader: ResolverLoader): ExtraImports { let extraImports: ExtraImports = {}; - for (let rule of config.activePackageRules) { + let config = loader.resolver.options as CompatResolverOptions; + for (let rule of config.activePackageRules ?? []) { if (rule.components) { for (let [componentName, rules] of Object.entries(rule.components)) { if (rules.invokes) { diff --git a/packages/compat/src/compat-app-builder.ts b/packages/compat/src/compat-app-builder.ts index 4d143fed4..c905b7d8f 100644 --- a/packages/compat/src/compat-app-builder.ts +++ b/packages/compat/src/compat-app-builder.ts @@ -1,13 +1,10 @@ -import type { AddonPackage, Engine } from '@embroider/core'; -import { explicitRelative, locateEmbroiderWorkingDir } from '@embroider/core'; -import { resolve as resolvePath } from 'path'; +import type { AddonPackage } from '@embroider/core'; +import { locateEmbroiderWorkingDir } from '@embroider/core'; import type Options from './options'; import type { CompatResolverOptions } from './resolver-transform'; import type { PackageRules } from './dependency-rules'; import { activePackageRules } from './dependency-rules'; -import flatMap from 'lodash/flatMap'; -import bind from 'bind-decorator'; -import { outputJSONSync, writeFileSync, realpathSync } from 'fs-extra'; +import { outputJSONSync, writeFileSync } from 'fs-extra'; import type { PortableHint } from '@embroider/core/src/portable'; import { maybeNodeModuleVersion, Portable } from '@embroider/core/src/portable'; import { Memoize } from 'typescript-memoize'; @@ -21,6 +18,7 @@ import { readdirSync } from 'fs-extra'; import type CompatApp from './compat-app'; import type { CompatBabelState } from './babel'; import { MacrosConfig } from '@embroider/macros/src/node'; +import { buildResolverOptions } from '@embroider/core/src/module-resolver-options'; // This exists during the actual broccoli build step. As opposed to CompatApp, // which also exists during pipeline-construction time. @@ -37,65 +35,6 @@ export class CompatAppBuilder { private synthStyles: Package ) {} - private activeAddonChildren(pkg: Package): AddonPackage[] { - let result = (pkg.dependencies.filter(this.isActiveAddon) as AddonPackage[]).filter( - // When looking for child addons, we want to ignore 'peerDependencies' of - // a given package, to align with how ember-cli resolves addons. So here - // we only include dependencies that are definitely active due to one of - // the other sections. - addon => pkg.categorizeDependency(addon.name) !== 'peerDependencies' - ); - if (pkg === this.appPackageWithMovedDeps) { - let extras = [this.synthVendor, this.synthStyles].filter(this.isActiveAddon) as AddonPackage[]; - result = [...result, ...extras]; - } - return result.sort(this.orderAddons); - } - - @Memoize() - private get allActiveAddons(): AddonPackage[] { - let result = this.appPackageWithMovedDeps.findDescendants(this.isActiveAddon) as AddonPackage[]; - let extras = [this.synthVendor, this.synthStyles].filter(this.isActiveAddon) as AddonPackage[]; - let extraDescendants = flatMap(extras, dep => dep.findDescendants(this.isActiveAddon)) as AddonPackage[]; - result = [...result, ...extras, ...extraDescendants]; - return result.sort(this.orderAddons); - } - - @bind - private isActiveAddon(pkg: Package): boolean { - // stage1 already took care of converting everything that's actually active - // into v2 addons. If it's not a v2 addon, we don't want it. - // - // We can encounter v1 addons here when there is inactive stuff floating - // around in the node_modules that accidentally satisfy something like an - // optional peer dep. - return pkg.isV2Addon(); - } - - @bind - private orderAddons(depA: Package, depB: Package): number { - let depAIdx = 0; - let depBIdx = 0; - - if (depA && depA.meta && depA.isV2Addon()) { - depAIdx = depA.meta['order-index'] || 0; - } - if (depB && depB.meta && depB.isV2Addon()) { - depBIdx = depB.meta['order-index'] || 0; - } - - return depAIdx - depBIdx; - } - - private resolvableExtensions(): string[] { - let fromEnv = process.env.EMBROIDER_RESOLVABLE_EXTENSIONS; - if (fromEnv) { - return fromEnv.split(','); - } else { - return ['.mjs', '.gjs', '.js', '.mts', '.gts', '.ts', '.hbs', '.hbs.js', '.json']; - } - } - private modulePrefix(): string { return this.configTree.readConfig().modulePrefix; } @@ -105,141 +44,36 @@ export class CompatAppBuilder { } @Memoize() - private activeRules() { - return activePackageRules(this.options.packageRules.concat(defaultAddonPackageRules()), [ - { name: this.origAppPackage.name, version: this.origAppPackage.version, root: this.origAppPackage.root }, - ...this.allActiveAddons.filter(p => p.meta['auto-upgraded']), - ]); - } - - private resolverConfig(engines: Engine[]): CompatResolverOptions { - let renamePackages = Object.assign({}, ...this.allActiveAddons.map(dep => dep.meta['renamed-packages'])); - let renameModules = Object.assign({}, ...this.allActiveAddons.map(dep => dep.meta['renamed-modules'])); - - let options: CompatResolverOptions['options'] = { - staticHelpers: this.options.staticHelpers, - staticModifiers: this.options.staticModifiers, - staticComponents: this.options.staticComponents, - allowUnsafeDynamicComponents: this.options.allowUnsafeDynamicComponents, - }; - - let config: CompatResolverOptions = { - // this part is the base ModuleResolverOptions as required by @embroider/core - renameModules, - renamePackages, - resolvableExtensions: this.resolvableExtensions(), - appRoot: this.origAppPackage.root, - engines: engines.map(engine => ({ - packageName: engine.package.name, - // we need to use the real path here because webpack requests always use the real path i.e. follow symlinks - root: realpathSync(engine.package.root), - fastbootFiles: {}, - activeAddons: [...engine.addons] - .map(([addon, canResolveFromFile]) => ({ - name: addon.name, - root: addon.root, - canResolveFromFile, - })) - // the traditional order is the order in which addons will run, such - // that the last one wins. Our resolver's order is the order to - // search, so first one wins. - .reverse(), - isLazy: engine.package.isLazyEngine(), - })), - amdCompatibility: this.options.amdCompatibility, - - // this is the additional stufff that @embroider/compat adds on top to do - // global template resolving + private get resolverConfig(): CompatResolverOptions { + return buildResolverOptions({ + appPackage: this.appPackageWithMovedDeps, modulePrefix: this.modulePrefix(), - splitAtRoutes: this.options.splitAtRoutes, podModulePrefix: this.podModulePrefix(), - activePackageRules: this.activeRules(), - options, - autoRun: this.compatApp.autoRun, + splitAtRoutes: this.options.splitAtRoutes, staticAppPaths: this.options.staticAppPaths, - emberVersion: this.emberVersion(), - }; - - return config; - } - - // recurse to find all active addons that don't cross an engine boundary. - // Inner engines themselves will be returned, but not those engines' children. - // The output set's insertion order is the proper ember-cli compatible - // ordering of the addons. - private findActiveAddons(pkg: Package, engine: Engine, isChild = false): void { - for (let child of this.activeAddonChildren(pkg)) { - if (!child.isEngine()) { - this.findActiveAddons(child, engine, true); - } - let canResolveFrom = resolvePath(pkg.root, 'package.json'); - engine.addons.set(child, canResolveFrom); - } - // ensure addons are applied in the correct order, if set (via @embroider/compat/v1-addon) - if (!isChild) { - engine.addons = new Map( - [...engine.addons].sort(([a], [b]) => { - return (a.meta['order-index'] || 0) - (b.meta['order-index'] || 0); - }) - ); - } - } - - private partitionEngines(): Engine[] { - let queue: Engine[] = [ - { - package: this.appPackageWithMovedDeps, - addons: new Map(), - isApp: true, - modulePrefix: this.modulePrefix(), - appRelativePath: '.', + extraDeps: new Map([[this.appPackageWithMovedDeps.root, [this.synthVendor, this.synthStyles] as AddonPackage[]]]), + extend: (options: CompatResolverOptions, allActiveAddons) => { + options.activePackageRules = activePackageRules(this.options.packageRules.concat(defaultAddonPackageRules()), [ + { name: this.origAppPackage.name, version: this.origAppPackage.version, root: this.origAppPackage.root }, + ...allActiveAddons.filter(p => p.meta['auto-upgraded']), + ]); + options.options = { + staticHelpers: this.options.staticHelpers, + staticModifiers: this.options.staticModifiers, + staticComponents: this.options.staticComponents, + allowUnsafeDynamicComponents: this.options.allowUnsafeDynamicComponents, + }; + return options; }, - ]; - let done: Engine[] = []; - let seenEngines: Set = new Set(); - while (true) { - let current = queue.shift(); - if (!current) { - break; - } - this.findActiveAddons(current.package, current); - for (let addon of current.addons.keys()) { - if (addon.isEngine() && !seenEngines.has(addon)) { - seenEngines.add(addon); - queue.push({ - package: addon, - addons: new Map(), - isApp: !current, - modulePrefix: addon.name, - appRelativePath: explicitRelative(this.origAppPackage.root, addon.root), - }); - } - } - done.push(current); - } - return done; - } - - private emberVersion() { - let pkg = this.activeAddonChildren(this.appPackageWithMovedDeps).find(a => a.name === 'ember-source'); - if (!pkg) { - throw new Error('no ember version!'); - } - return pkg.version; + }); } - private engines: Engine[] | undefined; - async build() { // on the first build, we lock down the macros config. on subsequent builds, // this doesn't do anything anyway because it's idempotent. this.compatApp.macrosConfig.finalize(); - if (!this.engines) { - this.engines = this.partitionEngines(); - } - - let resolverConfig = this.resolverConfig(this.engines); + let resolverConfig = this.resolverConfig; let config = this.configTree.readConfig(); let contentForConfig = this.contentForTree.readContents(); diff --git a/packages/compat/src/resolver-transform.ts b/packages/compat/src/resolver-transform.ts index 4e3813ccf..bcd8d6316 100644 --- a/packages/compat/src/resolver-transform.ts +++ b/packages/compat/src/resolver-transform.ts @@ -11,10 +11,9 @@ import { Memoize } from 'typescript-memoize'; import type { WithJSUtils } from 'babel-plugin-ember-template-compilation'; import assertNever from 'assert-never'; import { join, sep } from 'path'; -import { readJSONSync } from 'fs-extra'; import { dasherize, snippetToDasherizedName } from './dasherize-component-name'; import type { ResolverOptions as CoreResolverOptions } from '@embroider/core'; -import { Resolver, cleanUrl, locateEmbroiderWorkingDir } from '@embroider/core'; +import { Resolver, ResolverLoader, cleanUrl } from '@embroider/core'; import type CompatOptions from './options'; import type { AuditMessage, Loc } from './audit'; import { camelCase, mergeWith } from 'lodash'; @@ -976,7 +975,8 @@ class TemplateResolver implements ASTPlugin { // This is the AST transform that resolves components, helpers and modifiers at build time export default function makeResolverTransform({ appRoot, emberVersion }: Options) { - let config: CompatResolverOptions = readJSONSync(join(locateEmbroiderWorkingDir(appRoot), 'resolver.json')); + let loader = new ResolverLoader(appRoot); + let config = loader.resolver.options as CompatResolverOptions; const resolverTransform: ASTPluginBuilder = env => { if (env.strictMode) { return { diff --git a/packages/compat/tests/audit.test.ts b/packages/compat/tests/audit.test.ts index 7ab9ef8f2..656a0b85d 100644 --- a/packages/compat/tests/audit.test.ts +++ b/packages/compat/tests/audit.test.ts @@ -62,7 +62,6 @@ describe('audit', function () { }, ], resolvableExtensions, - autoRun: true, staticAppPaths: [], emberVersion: '4.0.0', }; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 1bc413b3c..4669188b6 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -13,7 +13,8 @@ export { default as toBroccoliPlugin } from './to-broccoli-plugin'; export { default as WaitForTrees, OutputPaths } from './wait-for-trees'; export { compile as jsHandlebarsCompile } from './js-handlebars'; export { todo, unsupported, warn, debug, expectWarning, throwOnWarnings } from './messages'; -export { Resolver, Options as ResolverOptions, ModuleRequest, Resolution } from './module-resolver'; +export { Resolver, ModuleRequest, Resolution } from './module-resolver'; +export type { Options as ResolverOptions } from './module-resolver-options'; export { ResolverLoader } from './resolver-loader'; export { virtualContent } from './virtual-content'; export type { Engine } from './app-files'; diff --git a/packages/core/src/module-resolver-options.ts b/packages/core/src/module-resolver-options.ts new file mode 100644 index 000000000..c118b1d39 --- /dev/null +++ b/packages/core/src/module-resolver-options.ts @@ -0,0 +1,215 @@ +import { explicitRelative, RewrittenPackageCache, type AddonPackage, type Package } from '@embroider/shared-internals'; +import type UserOptions from './options'; +import type { Engine } from './app-files'; +import { resolve as resolvePath } from 'path'; +import { realpathSync } from 'fs-extra'; +import flatMap from 'lodash/flatMap'; + +export interface Options { + renamePackages: { + [fromName: string]: string; + }; + renameModules: { + [fromName: string]: string; + }; + resolvableExtensions: string[]; + appRoot: string; + engines: EngineConfig[]; + modulePrefix: string; + splitAtRoutes?: (RegExp | string)[]; + podModulePrefix?: string; + amdCompatibility: Required; + staticAppPaths: string[]; + emberVersion: string; +} + +export interface EngineConfig { + packageName: string; + activeAddons: { name: string; root: string; canResolveFromFile: string }[]; + fastbootFiles: { [appName: string]: { localFilename: string; shadowedFilename: string | undefined } }; + root: string; + isLazy: boolean; +} + +export function buildResolverOptions(inputs: { + appPackage?: Package; + extraDeps?: Map; + modulePrefix?: string; + podModulePrefix?: string; + splitAtRoutes?: (RegExp | string)[]; + staticAppPaths?: string[]; + extend?: (opts: T, allActiveAddons: AddonPackage[]) => T; +}): T { + let appPackage: Package; + if (inputs.appPackage) { + appPackage = inputs.appPackage; + } else { + let packageCache = RewrittenPackageCache.shared('embroider', process.cwd()); + appPackage = packageCache.get(packageCache.appRoot); + } + + let extraDeps = inputs.extraDeps ?? new Map(); + + let allActiveAddons: AddonPackage[] = findAllActiveAddons(appPackage, extraDeps); + let renamePackages = Object.assign({}, ...allActiveAddons.map(dep => dep.meta['renamed-packages'])); + let renameModules = Object.assign({}, ...allActiveAddons.map(dep => dep.meta['renamed-modules'])); + let modulePrefix = appPackage.name; + let engines = partitionEngines(appPackage, modulePrefix, extraDeps); + + let output: Options = { + renamePackages, + renameModules, + resolvableExtensions: resolvableExtensions(), + appRoot: appPackage.root, + engines, + amdCompatibility: { + es: [], + }, + modulePrefix, + staticAppPaths: inputs.staticAppPaths ?? [], + emberVersion: appPackage.dependencies.find(d => d.name === 'ember-source')!.version, + splitAtRoutes: inputs.splitAtRoutes, + podModulePrefix: inputs.podModulePrefix, + }; + + if (inputs.extend) { + return inputs.extend(output as T, allActiveAddons); + } + return output as T; +} + +function resolvableExtensions(): string[] { + let fromEnv = process.env.EMBROIDER_RESOLVABLE_EXTENSIONS; + if (fromEnv) { + return fromEnv.split(','); + } else { + return ['.mjs', '.gjs', '.js', '.mts', '.gts', '.ts', '.hbs', '.hbs.js', '.json']; + } +} + +function partitionEngines( + appPackage: Package, + modulePrefix: string, + extraDeps: Map +): EngineConfig[] { + let queue: Engine[] = [ + { + package: appPackage, + addons: new Map(), + isApp: true, + modulePrefix, + appRelativePath: '.', + }, + ]; + let done: Engine[] = []; + let seenEngines: Set = new Set(); + while (true) { + let current = queue.shift(); + if (!current) { + break; + } + findActiveAddons(current.package, current, extraDeps); + for (let addon of current.addons.keys()) { + if (addon.isEngine() && !seenEngines.has(addon)) { + seenEngines.add(addon); + queue.push({ + package: addon, + addons: new Map(), + isApp: !current, + modulePrefix: addon.name, + appRelativePath: explicitRelative(appPackage.root, addon.root), + }); + } + } + done.push(current); + } + + return done.map(engine => ({ + packageName: engine.package.name, + // we need to use the real path here because webpack requests always use the real path i.e. follow symlinks + root: realpathSync(engine.package.root), + fastbootFiles: {}, + activeAddons: [...engine.addons] + .map(([addon, canResolveFromFile]) => ({ + name: addon.name, + root: addon.root, + canResolveFromFile, + })) + // the traditional order is the order in which addons will run, such + // that the last one wins. Our resolver's order is the order to + // search, so first one wins. + .reverse(), + isLazy: engine.package.isLazyEngine(), + })); +} + +// recurse to find all active addons that don't cross an engine boundary. +// Inner engines themselves will be returned, but not those engines' children. +// The output set's insertion order is the proper ember-cli compatible +// ordering of the addons. +function findActiveAddons(pkg: Package, engine: Engine, extraDeps: Map, isChild = false): void { + for (let child of activeAddonChildren(pkg, extraDeps)) { + if (!child.isEngine()) { + findActiveAddons(child, engine, extraDeps, true); + } + let canResolveFrom = resolvePath(pkg.root, 'package.json'); + engine.addons.set(child, canResolveFrom); + } + // ensure addons are applied in the correct order, if set (via @embroider/compat/v1-addon) + if (!isChild) { + engine.addons = new Map( + [...engine.addons].sort(([a], [b]) => { + return (a.meta['order-index'] || 0) - (b.meta['order-index'] || 0); + }) + ); + } +} + +function activeAddonChildren(pkg: Package, extraDeps: Map): AddonPackage[] { + let result = (pkg.dependencies.filter(isActiveAddon) as AddonPackage[]).filter( + // When looking for child addons, we want to ignore 'peerDependencies' of + // a given package, to align with how ember-cli resolves addons. So here + // we only include dependencies that are definitely active due to one of + // the other sections. + addon => pkg.categorizeDependency(addon.name) !== 'peerDependencies' + ); + let extras = extraDeps.get(pkg.root); + if (extras) { + result = [...result, ...extras]; + } + return result.sort(orderAddons); +} + +function isActiveAddon(pkg: Package): boolean { + // stage1 already took care of converting everything that's actually active + // into v2 addons. If it's not a v2 addon, we don't want it. + // + // We can encounter v1 addons here when there is inactive stuff floating + // around in the node_modules that accidentally satisfy something like an + // optional peer dep. + return pkg.isV2Addon(); +} + +function orderAddons(depA: Package, depB: Package): number { + let depAIdx = 0; + let depBIdx = 0; + + if (depA && depA.meta && depA.isV2Addon()) { + depAIdx = depA.meta['order-index'] || 0; + } + if (depB && depB.meta && depB.isV2Addon()) { + depBIdx = depB.meta['order-index'] || 0; + } + + return depAIdx - depBIdx; +} + +function findAllActiveAddons(appPackage: Package, extraDeps: Map): AddonPackage[] { + let result = appPackage.findDescendants(isActiveAddon) as AddonPackage[]; + let extras = extraDeps.get(appPackage.root); + if (extras) { + let extraDescendants = flatMap(extras, dep => dep.findDescendants(isActiveAddon)) as AddonPackage[]; + result = [...result, ...extras, ...extraDescendants]; + } + return result.sort(orderAddons); +} diff --git a/packages/core/src/module-resolver.ts b/packages/core/src/module-resolver.ts index eed011d9f..44369c4dc 100644 --- a/packages/core/src/module-resolver.ts +++ b/packages/core/src/module-resolver.ts @@ -24,9 +24,9 @@ import { import { Memoize } from 'typescript-memoize'; import { describeExports } from './describe-exports'; import { readFileSync } from 'fs'; -import type UserOptions from './options'; import { nodeResolve } from './node-resolve'; import { decodePublicRouteEntrypoint, encodeRouteEntrypoint } from './virtual-route-entrypoint'; +import type { Options, EngineConfig } from './module-resolver-options'; const debug = makeDebug('embroider:resolver'); @@ -81,35 +81,6 @@ function isTerminal(request: ModuleRequest): boolean { return request.isVirtual || request.isNotFound || Boolean(request.resolvedTo); } -export interface Options { - renamePackages: { - [fromName: string]: string; - }; - renameModules: { - [fromName: string]: string; - }; - resolvableExtensions: string[]; - appRoot: string; - engines: EngineConfig[]; - modulePrefix: string; - splitAtRoutes?: (RegExp | string)[]; - podModulePrefix?: string; - amdCompatibility: Required; - autoRun: boolean; - staticAppPaths: string[]; - emberVersion: string; -} - -// TODO: once we can remove the stage2 entrypoint this type can get streamlined -// to the parts we actually need -export interface EngineConfig { - packageName: string; - activeAddons: { name: string; root: string; canResolveFromFile: string }[]; - fastbootFiles: { [appName: string]: { localFilename: string; shadowedFilename: string | undefined } }; - root: string; - isLazy: boolean; -} - type MergeEntry = | { type: 'app-only'; diff --git a/packages/core/src/resolver-loader.ts b/packages/core/src/resolver-loader.ts index b7124b5f8..c7d7fe460 100644 --- a/packages/core/src/resolver-loader.ts +++ b/packages/core/src/resolver-loader.ts @@ -1,5 +1,5 @@ -import { readJSONSync } from 'fs-extra'; -import type { Options } from './module-resolver'; +import { existsSync, readJSONSync } from 'fs-extra'; +import { buildResolverOptions, type Options } from './module-resolver-options'; import { Resolver } from './module-resolver'; import { locateEmbroiderWorkingDir } from '@embroider/shared-internals'; import { join } from 'path'; @@ -26,7 +26,12 @@ export class ResolverLoader { get resolver(): Resolver { if (!this.#resolver) { - let config: Options = readJSONSync(join(locateEmbroiderWorkingDir(this.appRoot), 'resolver.json')); + let config: Options; + if (existsSync(this.#configFile)) { + config = readJSONSync(this.#configFile); + } else { + config = buildResolverOptions({}); + } this.#resolver = new Resolver(config); } return this.#resolver; diff --git a/packages/core/src/virtual-entrypoint.ts b/packages/core/src/virtual-entrypoint.ts index a74c5648a..da5d4716a 100644 --- a/packages/core/src/virtual-entrypoint.ts +++ b/packages/core/src/virtual-entrypoint.ts @@ -9,6 +9,7 @@ import walkSync from 'walk-sync'; import type { V2AddonPackage } from '@embroider/shared-internals/src/package'; import { encodePublicRouteEntrypoint } from './virtual-route-entrypoint'; import escapeRegExp from 'escape-string-regexp'; +import { optionsWithDefaults } from './options'; const entrypointPattern = /(?.*)[\\/]-embroider-entrypoint.js/; @@ -68,7 +69,7 @@ export function renderEntrypoint( resolver.options.podModulePrefix ); - let options = (resolver.options as CompatResolverOptions).options; + let options = (resolver.options as CompatResolverOptions).options ?? optionsWithDefaults(); let requiredAppFiles = [appFiles.otherAppFiles]; if (!options.staticComponents) { diff --git a/tests/scenarios/compat-resolver-test.ts b/tests/scenarios/compat-resolver-test.ts index 197db5a1c..f3c0d53f7 100644 --- a/tests/scenarios/compat-resolver-test.ts +++ b/tests/scenarios/compat-resolver-test.ts @@ -97,7 +97,6 @@ Scenarios.fromProject(() => new Project()) ...extraOpts?.appPackageRules, }, ], - autoRun: true, staticAppPaths: [], emberVersion, }; diff --git a/tests/scenarios/core-resolver-test.ts b/tests/scenarios/core-resolver-test.ts index e32c12b04..0279099d1 100644 --- a/tests/scenarios/core-resolver-test.ts +++ b/tests/scenarios/core-resolver-test.ts @@ -135,7 +135,6 @@ Scenarios.fromProject(() => new Project()) roots: [app.dir], }, ], - autoRun: true, staticAppPaths: [], emberVersion: '4.0.0', };