From 87839bc06d4084557d74bf7d103da4ea6df2ce01 Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Thu, 1 Feb 2024 17:40:43 +0000 Subject: [PATCH 01/29] move entrypoint to stage3 --- packages/compat/src/compat-app-builder.ts | 3 +- packages/core/src/module-resolver.ts | 25 ++ packages/core/src/virtual-content.ts | 7 + packages/core/src/virtual-entrypoint.ts | 425 ++++++++++++++++++++++ 4 files changed, 458 insertions(+), 2 deletions(-) create mode 100644 packages/core/src/virtual-entrypoint.ts diff --git a/packages/compat/src/compat-app-builder.ts b/packages/compat/src/compat-app-builder.ts index 86d0357d2..6ca68acad 100644 --- a/packages/compat/src/compat-app-builder.ts +++ b/packages/compat/src/compat-app-builder.ts @@ -385,8 +385,7 @@ export class CompatAppBuilder { // our tests entrypoint already includes a correct module dependency on the // app, so we only insert the app when we're not inserting tests if (!asset.fileAsset.includeTests) { - let appJS = this.topAppJSAsset(appFiles, prepared); - html.insertScriptTag(html.javascript, appJS.relativePath, { type: 'module' }); + html.insertScriptTag(html.javascript, '@embroider/core/entrypoint', { type: 'module' }); } if (this.fastbootConfig) { diff --git a/packages/core/src/module-resolver.ts b/packages/core/src/module-resolver.ts index 86302b22b..825208aa7 100644 --- a/packages/core/src/module-resolver.ts +++ b/packages/core/src/module-resolver.ts @@ -196,6 +196,7 @@ export class Resolver { request = this.handleImplicitTestScripts(request); request = this.handleVendorStyles(request); request = this.handleTestSupportStyles(request); + request = this.handleEntrypoint(request); request = this.handleRenaming(request); request = this.handleVendor(request); // we expect the specifier to be app relative at this point - must be after handleRenaming @@ -425,6 +426,30 @@ export class Resolver { } } + private handleEntrypoint(request: R): R { + if (isTerminal(request)) { + return request; + } + // TODO: also handle targeting from the outside (for engines) like: + // request.specifier === 'my-package-name/-embroider-entrypoint.js' + // just like implicit-modules does. + + //TODO move the extra forwardslash handling out into the vite plugin + const candidates = ['@embroider/core/entrypoint', '/@embroider/core/entrypoint']; + + if (!candidates.includes(request.specifier)) { + return request; + } + + let pkg = this.packageCache.ownerOfFile(request.fromFile); + + if (!pkg?.isV2Ember()) { + throw new Error(`bug: found entrypoint import in non-ember package at ${request.fromFile}`); + } + + return logTransition('entrypoint', request, request.virtualize(resolve(pkg.root, '-embroider-entrypoint.js'))); + } + private handleImplicitTestScripts(request: R): R { //TODO move the extra forwardslash handling out into the vite plugin const candidates = [ diff --git a/packages/core/src/virtual-content.ts b/packages/core/src/virtual-content.ts index 08e910494..a36facdcb 100644 --- a/packages/core/src/virtual-content.ts +++ b/packages/core/src/virtual-content.ts @@ -7,6 +7,8 @@ import { decodeTestSupportStyles, renderTestSupportStyles } from './virtual-test import { decodeVirtualVendor, renderVendor } from './virtual-vendor'; import { decodeVirtualVendorStyles, renderVendorStyles } from './virtual-vendor-styles'; +import { decodeEntrypoint, renderEntrypoint } from './virtual-entrypoint'; + const externalESPrefix = '/@embroider/ext-es/'; const externalCJSPrefix = '/@embroider/ext-cjs/'; @@ -25,6 +27,11 @@ export function virtualContent(filename: string, resolver: Resolver): VirtualCon return renderCJSExternalShim(cjsExtern); } + let entrypoint = decodeEntrypoint(filename); + if (entrypoint) { + return renderEntrypoint(resolver, entrypoint); + } + let extern = decodeVirtualExternalESModule(filename); if (extern) { return renderESExternalShim(extern); diff --git a/packages/core/src/virtual-entrypoint.ts b/packages/core/src/virtual-entrypoint.ts new file mode 100644 index 000000000..0f4fee165 --- /dev/null +++ b/packages/core/src/virtual-entrypoint.ts @@ -0,0 +1,425 @@ +import { AppFiles, type RouteFiles } from './app-files'; +import { compile } from './js-handlebars'; +import type { Resolver } from './module-resolver'; +import type { CompatResolverOptions } from '../../compat/src/resolver-transform'; +import { flatten, partition, sortBy } from 'lodash'; +import { posix, join, dirname } from 'path'; +import { type AddonMeta, explicitRelative, type Package, extensionsPattern } from '@embroider/shared-internals'; +import type { ImplicitAssetPaths } from './asset'; +import { sync as resolveSync } from 'resolve'; +import { type default as Options, optionsWithDefaults } from './options'; +import walkSync from 'walk-sync'; +import type { V2AddonPackage } from '@embroider/shared-internals/src/package'; + +const entrypointPattern = /(?.*)[\\/]-embroider-entrypoint.js/; + +export function decodeEntrypoint(filename: string): { fromFile: string } | undefined { + // Performance: avoid paying regex exec cost unless needed + if (!filename.includes('-embroider-entrypoint')) { + return; + } + let m = entrypointPattern.exec(filename); + if (m) { + return { + fromFile: m.groups!.filename, + }; + } +} + +export function renderEntrypoint( + resolver: Resolver, + { fromFile }: { fromFile: string } +): { src: string; watches: string[] } { + // this is new + const owner = resolver.packageCache.ownerOfFile(fromFile); + + let eagerModules: string[] = []; + + if (!owner) { + throw new Error('Owner expected'); // ToDo: Really bad error, update message + } + + let engine = resolver.owningEngine(owner); + let isApp = owner?.root === resolver.options.engines[0]!.root; + let hasFastboot = Boolean(resolver.options.engines[0]!.activeAddons.find(a => a.name === 'ember-cli-fastboot')); + + let appFiles = new AppFiles( + { + package: owner, + addons: new Map( + engine.activeAddons.map(addon => [ + resolver.packageCache.get(addon.root) as V2AddonPackage, + addon.canResolveFromFile, + ]) + ), + isApp, + modulePrefix: isApp ? resolver.options.modulePrefix : engine.packageName, + appRelativePath: 'NOT_USED_DELETE_ME', + }, + getAppFiles(owner.root), + hasFastboot ? getFastbootFiles(owner.root) : new Set(), + extensionsPattern(resolver.options.resolvableExtensions), + resolver.options.podModulePrefix + ); + + let options = (resolver.options as CompatResolverOptions).options; + + let requiredAppFiles = [appFiles.otherAppFiles]; + if (!options.staticComponents) { + requiredAppFiles.push(appFiles.components); + } + if (!options.staticHelpers) { + requiredAppFiles.push(appFiles.helpers); + } + if (!options.staticModifiers) { + requiredAppFiles.push(appFiles.modifiers); + } + + let styles = []; + // only import styles from engines with a parent (this excludeds the parent application) as their styles + // will be inserted via a direct tag. + if (!appFiles.engine.isApp && appFiles.engine.package.isLazyEngine()) { + let implicitStyles = impliedAddonAssets('implicit-styles', appFiles); + for (let style of implicitStyles) { + styles.push({ + path: explicitRelative(dirname(fromFile), style), + }); + } + + let engineMeta = appFiles.engine.package.meta as AddonMeta; + if (engineMeta && engineMeta['implicit-styles']) { + for (let style of engineMeta['implicit-styles']) { + styles.push({ + path: explicitRelative(dirname(fromFile), style), + }); + } + } + } + + let lazyEngines: { names: string[]; path: string }[] = []; + + if (isApp) { + // deliberately ignoring the app + let [, ...childEngines] = resolver.options.engines; + for (let childEngine of childEngines) { + let target = `${childEngine.packageName}/-embroider-entrypoint.js`; + + if (childEngine.isLazy) { + lazyEngines.push({ + names: [childEngine.packageName], + path: target, + }); + } else { + eagerModules.push(target); + } + } + } + + let lazyRoutes: { names: string[]; path: string }[] = []; + for (let [routeName, routeFiles] of appFiles.routeFiles.children) { + splitRoute( + routeName, + routeFiles, + (_: string, filename: string) => { + requiredAppFiles.push([filename]); + }, + (routeNames: string[], _files: string[]) => { + // TODO: we don't consume files anymore, should we stop generating it? + let routeEntrypoint = `assets/_route_/${encodeURIComponent(routeNames[0])}.js`; + lazyRoutes.push({ + names: routeNames, + path: importPaths(resolver, appFiles, routeEntrypoint).buildtime, + }); + } + ); + } + + let [fastboot, nonFastboot] = partition(excludeDotFiles(flatten(requiredAppFiles)), file => + appFiles.isFastbootOnly.get(file) + ); + + let amdModules = nonFastboot.map(file => importPaths(resolver, appFiles, file)); + let fastbootOnlyAmdModules = fastboot.map(file => importPaths(resolver, appFiles, file)); + + let params = { + amdModules, + fastbootOnlyAmdModules, + lazyRoutes, + lazyEngines, + eagerModules, + styles, + // this is a backward-compatibility feature: addons can force inclusion of modules. + defineModulesFrom: './-embroider-implicit-modules.js', + }; + + // for the top-level entry template we need to pass extra params to the template + // this is new, it used to be passed into the appJS function instead + if (isApp) { + // TODO figure out how to actually translate these + // Object.assign(params, { + // autoRun: this.compatApp.autoRun, + // appBoot: !this.compatApp.autoRun ? this.compatApp.appBoot.readAppBoot() : '', + // mainModule: explicitRelative(dirname(relativePath), 'app'), + // appConfig: this.configTree.readConfig().APP, + // }); + Object.assign(params, { + autoRun: true, + appBoot: '', + mainModule: './app.js', + appConfig: {}, + }); + } + + return { + src: entryTemplate(params), + watches: [], + }; +} + +const entryTemplate = compile(` +import { importSync as i, macroCondition, getGlobalConfig } from '@embroider/macros'; +let w = window; +let d = w.define; + +{{#if styles}} + if (macroCondition(!getGlobalConfig().fastboot?.isRunning)) { + {{#each styles as |stylePath| ~}} + i("{{js-string-escape stylePath.path}}"); + {{/each}} + } +{{/if}} + +{{#if defineModulesFrom ~}} + import implicitModules from "{{js-string-escape defineModulesFrom}}"; + + for(const [name, module] of Object.entries(implicitModules)) { + d(name, function() { return module }); + } +{{/if}} + + +{{#each eagerModules as |eagerModule| ~}} + i("{{js-string-escape eagerModule}}"); +{{/each}} + +{{#each amdModules as |amdModule| ~}} + d("{{js-string-escape amdModule.runtime}}", function(){ return i("{{js-string-escape amdModule.buildtime}}");}); +{{/each}} + +{{#if fastbootOnlyAmdModules}} + if (macroCondition(getGlobalConfig().fastboot?.isRunning)) { + let fastbootModules = {}; + + {{#each fastbootOnlyAmdModules as |amdModule| ~}} + fastbootModules["{{js-string-escape amdModule.runtime}}"] = import("{{js-string-escape amdModule.buildtime}}"); + {{/each}} + + const resolvedValues = await Promise.all(Object.values(fastbootModules)); + + Object.keys(fastbootModules).forEach((k, i) => { + d(k, function(){ return resolvedValues[i];}); + }) + } +{{/if}} + + +{{#if lazyRoutes}} +w._embroiderRouteBundles_ = [ + {{#each lazyRoutes as |route|}} + { + names: {{json-stringify route.names}}, + load: function() { + return import("{{js-string-escape route.path}}"); + } + }, + {{/each}} +] +{{/if}} + +{{#if lazyEngines}} +w._embroiderEngineBundles_ = [ + {{#each lazyEngines as |engine|}} + { + names: {{json-stringify engine.names}}, + load: function() { + return import("{{js-string-escape engine.path}}"); + } + }, + {{/each}} +] +{{/if}} + +{{#if autoRun ~}} +if (!runningTests) { + i("{{js-string-escape mainModule}}").default.create({{json-stringify appConfig}}); +} +{{else if appBoot ~}} + {{ appBoot }} +{{/if}} + +{{#if testSuffix ~}} + {{!- TODO: both of these suffixes should get dynamically generated so they incorporate + any content-for added by addons. -}} + + + {{!- this is the traditional tests-suffix.js -}} + i('../tests/test-helper'); + EmberENV.TESTS_FILE_LOADED = true; +{{/if}} +`) as (params: { + amdModules: { runtime: string; buildtime: string }[]; + fastbootOnlyAmdModules?: { runtime: string; buildtime: string }[]; + defineModulesFrom?: string; + eagerModules?: string[]; + autoRun?: boolean; + appBoot?: string; + mainModule?: string; + appConfig?: unknown; + testSuffix?: boolean; + lazyRoutes?: { names: string[]; path: string }[]; + lazyEngines?: { names: string[]; path: string }[]; + styles?: { path: string }[]; +}) => string; + +function excludeDotFiles(files: string[]) { + return files.filter(file => !file.startsWith('.') && !file.includes('/.')); +} + +function importPaths(resolver: Resolver, { engine }: AppFiles, engineRelativePath: string) { + let resolvableExtensionsPattern = extensionsPattern(resolver.options.resolvableExtensions); + let noHBS = engineRelativePath.replace(resolvableExtensionsPattern, '').replace(/\.hbs$/, ''); + return { + runtime: `${engine.modulePrefix}/${noHBS}`, + buildtime: posix.join(engine.package.name, engineRelativePath), + }; +} + +function impliedAddonAssets(type: keyof ImplicitAssetPaths, { engine }: AppFiles): string[] { + let result: Array = []; + for (let addon of sortBy(Array.from(engine.addons.keys()), scriptPriority)) { + let implicitScripts = addon.meta[type]; + if (implicitScripts) { + let styles = []; + let options = { basedir: addon.root }; + for (let mod of implicitScripts) { + if (type === 'implicit-styles') { + // exclude engines because they will handle their own css importation + if (!addon.isLazyEngine()) { + styles.push(resolveSync(mod, options)); + } + } else { + result.push(resolveSync(mod, options)); + } + } + if (styles.length) { + result = [...styles, ...result]; + } + } + } + return result; +} + +function scriptPriority(pkg: Package) { + switch (pkg.name) { + case 'loader.js': + return 0; + case 'ember-source': + return 10; + default: + return 1000; + } +} + +function splitRoute( + routeName: string, + files: RouteFiles, + addToParent: (routeName: string, filename: string) => void, + addLazyBundle: (routeNames: string[], files: string[]) => void +) { + let shouldSplit = routeName && shouldSplitRoute(routeName); + let ownFiles = []; + let ownNames = new Set() as Set; + + if (files.template) { + if (shouldSplit) { + ownFiles.push(files.template); + ownNames.add(routeName); + } else { + addToParent(routeName, files.template); + } + } + + if (files.controller) { + if (shouldSplit) { + ownFiles.push(files.controller); + ownNames.add(routeName); + } else { + addToParent(routeName, files.controller); + } + } + + if (files.route) { + if (shouldSplit) { + ownFiles.push(files.route); + ownNames.add(routeName); + } else { + addToParent(routeName, files.route); + } + } + + for (let [childName, childFiles] of files.children) { + splitRoute( + `${routeName}.${childName}`, + childFiles, + + (childRouteName: string, childFile: string) => { + // this is our child calling "addToParent" + if (shouldSplit) { + ownFiles.push(childFile); + ownNames.add(childRouteName); + } else { + addToParent(childRouteName, childFile); + } + }, + (routeNames: string[], files: string[]) => { + addLazyBundle(routeNames, files); + } + ); + } + + if (ownFiles.length > 0) { + addLazyBundle([...ownNames], ownFiles); + } +} + +function readEmbroiderConfig(): Required { + // TODO + return optionsWithDefaults({}); +} + +function shouldSplitRoute(routeName: string) { + let config = readEmbroiderConfig(); + return ( + !config.splitAtRoutes || + config.splitAtRoutes.find(pattern => { + if (typeof pattern === 'string') { + return pattern === routeName; + } else { + return pattern.test(routeName); + } + }) + ); +} + +function getAppFiles(appRoot: string): Set { + const files: string[] = walkSync(appRoot, { + ignore: ['_babel_config_.js', '_babel_filter_.js', 'app.js', 'assets', 'testem.js', 'node_modules'], + }); + return new Set(files); +} + +function getFastbootFiles(appRoot: string): Set { + const appDirPath = join(appRoot, '_fastboot_'); + const files: string[] = walkSync(appDirPath); + return new Set(files); +} From 78aafa12e935aa92da2adcc29f28d2aacea24be2 Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Thu, 1 Feb 2024 17:28:25 +0000 Subject: [PATCH 02/29] remove some import sync from entrypoint --- packages/core/src/virtual-entrypoint.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/core/src/virtual-entrypoint.ts b/packages/core/src/virtual-entrypoint.ts index 0f4fee165..d453a83a3 100644 --- a/packages/core/src/virtual-entrypoint.ts +++ b/packages/core/src/virtual-entrypoint.ts @@ -184,7 +184,7 @@ let d = w.define; {{#if styles}} if (macroCondition(!getGlobalConfig().fastboot?.isRunning)) { {{#each styles as |stylePath| ~}} - i("{{js-string-escape stylePath.path}}"); + await import("{{js-string-escape stylePath.path}}"); {{/each}} } {{/if}} @@ -199,11 +199,12 @@ let d = w.define; {{#each eagerModules as |eagerModule| ~}} - i("{{js-string-escape eagerModule}}"); + import "{{js-string-escape eagerModule}}"; {{/each}} -{{#each amdModules as |amdModule| ~}} - d("{{js-string-escape amdModule.runtime}}", function(){ return i("{{js-string-escape amdModule.buildtime}}");}); +{{#each amdModules as |amdModule index| ~}} + import * as amdModule{{index}} from "{{js-string-escape amdModule.buildtime}}" + d("{{js-string-escape amdModule.runtime}}", function(){ return amdModule{{index}}; }); {{/each}} {{#if fastbootOnlyAmdModules}} From 2716d7f27296b541aaf6eda4bc4704f233e3a123 Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Thu, 1 Feb 2024 17:23:30 +0000 Subject: [PATCH 03/29] [wip] add a hack to manage direct links to @embroider-deps --- packages/core/src/module-resolver.ts | 8 ++++++++ packages/core/src/virtual-entrypoint.ts | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/core/src/module-resolver.ts b/packages/core/src/module-resolver.ts index 825208aa7..196f436f5 100644 --- a/packages/core/src/module-resolver.ts +++ b/packages/core/src/module-resolver.ts @@ -190,6 +190,14 @@ export class Resolver { return this.external('early require', request, request.specifier); } + if (request.specifier.startsWith('@embroider-dep')) { + return logTransition( + 'Embroider direct dependency lookup', + request, + request.alias(request.specifier.replace(/^@embroider-dep\//, '')) + ); + } + request = this.handleFastbootSwitch(request); request = await this.handleGlobalsCompat(request); request = this.handleImplicitModules(request); diff --git a/packages/core/src/virtual-entrypoint.ts b/packages/core/src/virtual-entrypoint.ts index d453a83a3..4bfce9ae2 100644 --- a/packages/core/src/virtual-entrypoint.ts +++ b/packages/core/src/virtual-entrypoint.ts @@ -291,7 +291,7 @@ function importPaths(resolver: Resolver, { engine }: AppFiles, engineRelativePat let noHBS = engineRelativePath.replace(resolvableExtensionsPattern, '').replace(/\.hbs$/, ''); return { runtime: `${engine.modulePrefix}/${noHBS}`, - buildtime: posix.join(engine.package.name, engineRelativePath), + buildtime: `@embroider-dep/${posix.join(engine.package.name, engineRelativePath)}`, }; } From 045869045a73a85321ea0c3ffd50d554353f3356 Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Thu, 1 Feb 2024 17:51:04 +0000 Subject: [PATCH 04/29] add new candidate for entrypoint --- packages/core/src/module-resolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/module-resolver.ts b/packages/core/src/module-resolver.ts index 196f436f5..95beb4be5 100644 --- a/packages/core/src/module-resolver.ts +++ b/packages/core/src/module-resolver.ts @@ -443,7 +443,7 @@ export class Resolver { // just like implicit-modules does. //TODO move the extra forwardslash handling out into the vite plugin - const candidates = ['@embroider/core/entrypoint', '/@embroider/core/entrypoint']; + const candidates = ['@embroider/core/entrypoint', '/@embroider/core/entrypoint', './@embroider/core/entrypoint']; if (!candidates.includes(request.specifier)) { return request; From 543d0f6c821c131d319e03dd4ee3c57973d96f6a Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Thu, 1 Feb 2024 17:52:59 +0000 Subject: [PATCH 05/29] use new entrypoint in tests --- packages/compat/src/compat-app-builder.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/compat/src/compat-app-builder.ts b/packages/compat/src/compat-app-builder.ts index 6ca68acad..6e8c3ba02 100644 --- a/packages/compat/src/compat-app-builder.ts +++ b/packages/compat/src/compat-app-builder.ts @@ -1130,10 +1130,7 @@ export class CompatAppBuilder { // script tag in the tests HTML, but that isn't as easy for final stage // packagers to understand. It's better to express it here as a direct // module dependency. - let eagerModules: string[] = [ - 'ember-testing', - explicitRelative(dirname(myName), this.topAppJSAsset(appFiles, prepared).relativePath), - ]; + let eagerModules: string[] = ['ember-testing', explicitRelative(dirname(myName), '-embroider-entrypoint.js')]; let amdModules: { runtime: string; buildtime: string }[] = []; From 723c90aa4262a5f7a8f918d5867a147b924c04ec Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Tue, 12 Mar 2024 14:28:20 +0000 Subject: [PATCH 06/29] delete entrypoint code from compat-app-builder --- packages/compat/src/compat-app-builder.ts | 274 +--------------------- packages/core/src/virtual-entrypoint.ts | 62 +---- 2 files changed, 9 insertions(+), 327 deletions(-) diff --git a/packages/compat/src/compat-app-builder.ts b/packages/compat/src/compat-app-builder.ts index 6e8c3ba02..086413c05 100644 --- a/packages/compat/src/compat-app-builder.ts +++ b/packages/compat/src/compat-app-builder.ts @@ -27,8 +27,6 @@ import type { CompatResolverOptions } from './resolver-transform'; import type { PackageRules } from './dependency-rules'; import { activePackageRules } from './dependency-rules'; import flatMap from 'lodash/flatMap'; -import flatten from 'lodash/flatten'; -import partition from 'lodash/partition'; import mergeWith from 'lodash/mergeWith'; import cloneDeep from 'lodash/cloneDeep'; import { sync as resolveSync } from 'resolve'; @@ -40,7 +38,6 @@ import type { Options as AdjustImportsOptions } from './babel-plugin-adjust-impo import { PreparedEmberHTML } from '@embroider/core/src/ember-html'; import type { InMemoryAsset, OnDiskAsset } from '@embroider/core/src/asset'; import { makePortable } from '@embroider/core/src/portable-babel-config'; -import type { RouteFiles } from '@embroider/core/src/app-files'; import { AppFiles } from '@embroider/core/src/app-files'; import type { PortableHint } from '@embroider/core/src/portable'; import { maybeNodeModuleVersion } from '@embroider/core/src/portable'; @@ -50,12 +47,11 @@ import { join, dirname } from 'path'; import resolve from 'resolve'; import type ContentForConfig from './content-for-config'; import type { V1Config } from './v1-config'; -import type { AddonMeta, Package, PackageInfo } from '@embroider/core'; +import type { Package, PackageInfo } from '@embroider/core'; import { ensureDirSync, copySync, readdirSync, pathExistsSync } from 'fs-extra'; import type { TransformOptions } from '@babel/core'; import { MacrosConfig } from '@embroider/macros/src/node'; import SourceMapConcat from 'fast-sourcemap-concat'; -import escapeRegExp from 'escape-string-regexp'; import type CompatApp from './compat-app'; import { SyncDir } from './sync-dir'; @@ -872,224 +868,6 @@ export class CompatAppBuilder { }); } - private shouldSplitRoute(routeName: string) { - return ( - !this.options.splitAtRoutes || - this.options.splitAtRoutes.find(pattern => { - if (typeof pattern === 'string') { - return pattern === routeName; - } else { - return pattern.test(routeName); - } - }) - ); - } - - private splitRoute( - routeName: string, - files: RouteFiles, - addToParent: (routeName: string, filename: string) => void, - addLazyBundle: (routeNames: string[], files: string[]) => void - ) { - let shouldSplit = routeName && this.shouldSplitRoute(routeName); - let ownFiles = []; - let ownNames = new Set() as Set; - - if (files.template) { - if (shouldSplit) { - ownFiles.push(files.template); - ownNames.add(routeName); - } else { - addToParent(routeName, files.template); - } - } - - if (files.controller) { - if (shouldSplit) { - ownFiles.push(files.controller); - ownNames.add(routeName); - } else { - addToParent(routeName, files.controller); - } - } - - if (files.route) { - if (shouldSplit) { - ownFiles.push(files.route); - ownNames.add(routeName); - } else { - addToParent(routeName, files.route); - } - } - - for (let [childName, childFiles] of files.children) { - this.splitRoute( - `${routeName}.${childName}`, - childFiles, - - (childRouteName: string, childFile: string) => { - // this is our child calling "addToParent" - if (shouldSplit) { - ownFiles.push(childFile); - ownNames.add(childRouteName); - } else { - addToParent(childRouteName, childFile); - } - }, - (routeNames: string[], files: string[]) => { - addLazyBundle(routeNames, files); - } - ); - } - - if (ownFiles.length > 0) { - addLazyBundle([...ownNames], ownFiles); - } - } - - private topAppJSAsset(engines: AppFiles[], prepared: Map): InternalAsset { - let [app, ...childEngines] = engines; - let relativePath = `assets/${this.origAppPackage.name}.js`; - return this.appJSAsset(relativePath, app, childEngines, prepared, { - autoRun: this.compatApp.autoRun, - appBoot: !this.compatApp.autoRun ? this.compatApp.appBoot.readAppBoot() : '', - mainModule: explicitRelative(dirname(relativePath), 'app'), - appConfig: this.configTree.readConfig().APP, - }); - } - - @Memoize() - private get staticAppPathsPattern(): RegExp | undefined { - if (this.options.staticAppPaths.length > 0) { - return new RegExp( - '^(?:' + this.options.staticAppPaths.map(staticAppPath => escapeRegExp(staticAppPath)).join('|') + ')(?:$|/)' - ); - } - } - - private requiredOtherFiles(appFiles: AppFiles): readonly string[] { - let pattern = this.staticAppPathsPattern; - if (pattern) { - return appFiles.otherAppFiles.filter(f => { - return !pattern!.test(f); - }); - } else { - return appFiles.otherAppFiles; - } - } - - private appJSAsset( - relativePath: string, - appFiles: AppFiles, - childEngines: AppFiles[], - prepared: Map, - entryParams?: Partial[0]> - ): InternalAsset { - let cached = prepared.get(relativePath); - if (cached) { - return cached; - } - - let eagerModules: string[] = []; - - let requiredAppFiles = [this.requiredOtherFiles(appFiles)]; - if (!this.options.staticComponents) { - requiredAppFiles.push(appFiles.components); - } - if (!this.options.staticHelpers) { - requiredAppFiles.push(appFiles.helpers); - } - if (!this.options.staticModifiers) { - requiredAppFiles.push(appFiles.modifiers); - } - - let styles = []; - // only import styles from engines with a parent (this excludeds the parent application) as their styles - // will be inserted via a direct tag. - if (!appFiles.engine.isApp && appFiles.engine.package.isLazyEngine()) { - styles.push({ - path: '@embroider/core/vendor.css', - }); - - let engineMeta = appFiles.engine.package.meta as AddonMeta; - if (engineMeta && engineMeta['implicit-styles']) { - for (let style of engineMeta['implicit-styles']) { - styles.push({ - path: explicitRelative(dirname(relativePath), join(appFiles.engine.appRelativePath, style)), - }); - } - } - } - - let lazyEngines: { names: string[]; path: string }[] = []; - for (let childEngine of childEngines) { - let asset = this.appJSAsset( - `assets/_engine_/${encodeURIComponent(childEngine.engine.package.name)}.js`, - childEngine, - [], - prepared - ); - if (childEngine.engine.package.isLazyEngine()) { - lazyEngines.push({ - names: [childEngine.engine.package.name], - path: explicitRelative(dirname(relativePath), asset.relativePath), - }); - } else { - eagerModules.push(explicitRelative(dirname(relativePath), asset.relativePath)); - } - } - let lazyRoutes: { names: string[]; path: string }[] = []; - for (let [routeName, routeFiles] of appFiles.routeFiles.children) { - this.splitRoute( - routeName, - routeFiles, - (_: string, filename: string) => { - requiredAppFiles.push([filename]); - }, - (routeNames: string[], files: string[]) => { - let routeEntrypoint = `assets/_route_/${encodeURIComponent(routeNames[0])}.js`; - if (!prepared.has(routeEntrypoint)) { - prepared.set(routeEntrypoint, this.routeEntrypoint(appFiles, routeEntrypoint, files)); - } - lazyRoutes.push({ - names: routeNames, - path: this.importPaths(appFiles, routeEntrypoint).buildtime, - }); - } - ); - } - - let [fastboot, nonFastboot] = partition(excludeDotFiles(flatten(requiredAppFiles)), file => - appFiles.isFastbootOnly.get(file) - ); - let amdModules = nonFastboot.map(file => this.importPaths(appFiles, file)); - let fastbootOnlyAmdModules = fastboot.map(file => this.importPaths(appFiles, file)); - - let params = { - amdModules, - fastbootOnlyAmdModules, - lazyRoutes, - lazyEngines, - eagerModules, - styles, - // this is a backward-compatibility feature: addons can force inclusion of modules. - defineModulesFrom: './-embroider-implicit-modules.js', - }; - if (entryParams) { - Object.assign(params, entryParams); - } - - let source = entryTemplate(params); - - let asset: InternalAsset = { - kind: 'in-memory', - source, - relativePath, - }; - prepared.set(relativePath, asset); - return asset; - } - private importPaths({ engine }: AppFiles, engineRelativePath: string) { let noHBS = engineRelativePath.replace(this.resolvableExtensionsPattern, '').replace(/\.hbs$/, ''); return { @@ -1098,20 +876,6 @@ export class CompatAppBuilder { }; } - private routeEntrypoint(appFiles: AppFiles, relativePath: string, files: string[]) { - let [fastboot, nonFastboot] = partition(files, file => appFiles.isFastbootOnly.get(file)); - - let asset: InternalAsset = { - kind: 'in-memory', - source: routeEntryTemplate({ - files: nonFastboot.map(f => this.importPaths(appFiles, f)), - fastbootOnlyFiles: fastboot.map(f => this.importPaths(appFiles, f)), - }), - relativePath, - }; - return asset; - } - private testJSEntrypoint(appFiles: AppFiles[], prepared: Map): InternalAsset { let asset = prepared.get(`assets/test.js`); if (asset) { @@ -1125,13 +889,6 @@ export class CompatAppBuilder { const myName = 'assets/test.js'; - // tests necessarily also include the app. This is where we account for - // that. The classic solution was to always include the app's separate - // script tag in the tests HTML, but that isn't as easy for final stage - // packagers to understand. It's better to express it here as a direct - // module dependency. - let eagerModules: string[] = ['ember-testing', explicitRelative(dirname(myName), '-embroider-entrypoint.js')]; - let amdModules: { runtime: string; buildtime: string }[] = []; for (let relativePath of engine.tests) { @@ -1140,7 +897,6 @@ export class CompatAppBuilder { let source = entryTemplate({ amdModules, - eagerModules, testSuffix: true, // this is a backward-compatibility feature: addons can force inclusion of test support modules. defineModulesFrom: './-embroider-implicit-test-modules.js', @@ -1190,9 +946,8 @@ let d = w.define; {{/if}} -{{#each eagerModules as |eagerModule| ~}} - i("{{js-string-escape eagerModule}}"); -{{/each}} +import "ember-testing"; +import "#embroider/core/entrypoint"; {{#each amdModules as |amdModule| ~}} d("{{js-string-escape amdModule.runtime}}", function(){ return i("{{js-string-escape amdModule.buildtime}}");}); @@ -1273,25 +1028,6 @@ if (!runningTests) { styles?: { path: string }[]; }) => string; -const routeEntryTemplate = jsHandlebarsCompile(` -import { importSync as i } from '@embroider/macros'; -let d = window.define; -{{#each files as |amdModule| ~}} -d("{{js-string-escape amdModule.runtime}}", function(){ return i("{{js-string-escape amdModule.buildtime}}");}); -{{/each}} -{{#if fastbootOnlyFiles}} - import { macroCondition, getGlobalConfig } from '@embroider/macros'; - if (macroCondition(getGlobalConfig().fastboot?.isRunning)) { - {{#each fastbootOnlyFiles as |amdModule| ~}} - d("{{js-string-escape amdModule.runtime}}", function(){ return i("{{js-string-escape amdModule.buildtime}}");}); - {{/each}} - } -{{/if}} -`) as (params: { - files: { runtime: string; buildtime: string }[]; - fastbootOnlyFiles: { runtime: string; buildtime: string }[]; -}) => string; - function stringOrBufferEqual(a: string | Buffer, b: string | Buffer): boolean { if (typeof a === 'string' && typeof b === 'string') { return a === b; @@ -1345,10 +1081,6 @@ function addCachablePlugin(babelConfig: TransformOptions) { } } -function excludeDotFiles(files: string[]) { - return files.filter(file => !file.startsWith('.') && !file.includes('/.')); -} - interface TreeNames { appJS: BroccoliNode; htmlTree: BroccoliNode; diff --git a/packages/core/src/virtual-entrypoint.ts b/packages/core/src/virtual-entrypoint.ts index 4bfce9ae2..598ff27b4 100644 --- a/packages/core/src/virtual-entrypoint.ts +++ b/packages/core/src/virtual-entrypoint.ts @@ -2,11 +2,9 @@ import { AppFiles, type RouteFiles } from './app-files'; import { compile } from './js-handlebars'; import type { Resolver } from './module-resolver'; import type { CompatResolverOptions } from '../../compat/src/resolver-transform'; -import { flatten, partition, sortBy } from 'lodash'; -import { posix, join, dirname } from 'path'; -import { type AddonMeta, explicitRelative, type Package, extensionsPattern } from '@embroider/shared-internals'; -import type { ImplicitAssetPaths } from './asset'; -import { sync as resolveSync } from 'resolve'; +import { flatten, partition } from 'lodash'; +import { posix, join } from 'path'; +import { extensionsPattern } from '@embroider/shared-internals'; import { type default as Options, optionsWithDefaults } from './options'; import walkSync from 'walk-sync'; import type { V2AddonPackage } from '@embroider/shared-internals/src/package'; @@ -79,21 +77,9 @@ export function renderEntrypoint( // only import styles from engines with a parent (this excludeds the parent application) as their styles // will be inserted via a direct tag. if (!appFiles.engine.isApp && appFiles.engine.package.isLazyEngine()) { - let implicitStyles = impliedAddonAssets('implicit-styles', appFiles); - for (let style of implicitStyles) { - styles.push({ - path: explicitRelative(dirname(fromFile), style), - }); - } - - let engineMeta = appFiles.engine.package.meta as AddonMeta; - if (engineMeta && engineMeta['implicit-styles']) { - for (let style of engineMeta['implicit-styles']) { - styles.push({ - path: explicitRelative(dirname(fromFile), style), - }); - } - } + styles.push({ + path: '@embroider/core/vendor.css', + }); } let lazyEngines: { names: string[]; path: string }[] = []; @@ -295,42 +281,6 @@ function importPaths(resolver: Resolver, { engine }: AppFiles, engineRelativePat }; } -function impliedAddonAssets(type: keyof ImplicitAssetPaths, { engine }: AppFiles): string[] { - let result: Array = []; - for (let addon of sortBy(Array.from(engine.addons.keys()), scriptPriority)) { - let implicitScripts = addon.meta[type]; - if (implicitScripts) { - let styles = []; - let options = { basedir: addon.root }; - for (let mod of implicitScripts) { - if (type === 'implicit-styles') { - // exclude engines because they will handle their own css importation - if (!addon.isLazyEngine()) { - styles.push(resolveSync(mod, options)); - } - } else { - result.push(resolveSync(mod, options)); - } - } - if (styles.length) { - result = [...styles, ...result]; - } - } - } - return result; -} - -function scriptPriority(pkg: Package) { - switch (pkg.name) { - case 'loader.js': - return 0; - case 'ember-source': - return 10; - default: - return 1000; - } -} - function splitRoute( routeName: string, files: RouteFiles, From 7292024117aa56548ad0c45226ad024d4570ba5e Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Tue, 12 Mar 2024 13:21:41 +0000 Subject: [PATCH 07/29] fix virtual entrypoint extension --- packages/core/src/virtual-entrypoint.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/virtual-entrypoint.ts b/packages/core/src/virtual-entrypoint.ts index 598ff27b4..536846de4 100644 --- a/packages/core/src/virtual-entrypoint.ts +++ b/packages/core/src/virtual-entrypoint.ts @@ -151,7 +151,7 @@ export function renderEntrypoint( Object.assign(params, { autoRun: true, appBoot: '', - mainModule: './app.js', + mainModule: './app', appConfig: {}, }); } From af160eeadafa9815132669d3d5c1c23ad6c8f39c Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Wed, 17 Apr 2024 14:58:30 +0100 Subject: [PATCH 08/29] add splitAtRoutes to resolver.json --- packages/compat/src/compat-app-builder.ts | 1 + packages/core/src/module-resolver.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/compat/src/compat-app-builder.ts b/packages/compat/src/compat-app-builder.ts index 086413c05..61ba69c27 100644 --- a/packages/compat/src/compat-app-builder.ts +++ b/packages/compat/src/compat-app-builder.ts @@ -275,6 +275,7 @@ export class CompatAppBuilder { // this is the additional stufff that @embroider/compat adds on top to do // global template resolving modulePrefix: this.modulePrefix(), + splitAtRoutes: this.options.splitAtRoutes, podModulePrefix: this.podModulePrefix(), activePackageRules: this.activeRules(), options, diff --git a/packages/core/src/module-resolver.ts b/packages/core/src/module-resolver.ts index 95beb4be5..2cc35bc45 100644 --- a/packages/core/src/module-resolver.ts +++ b/packages/core/src/module-resolver.ts @@ -91,6 +91,7 @@ export interface Options { appRoot: string; engines: EngineConfig[]; modulePrefix: string; + splitAtRoutes?: (RegExp | string)[]; podModulePrefix?: string; amdCompatibility: Required; } From 7966bef91c54c604464b556b386ce7ac331cf282 Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Wed, 3 Apr 2024 12:57:04 +0100 Subject: [PATCH 09/29] fix tests --- .../compat-exclude-dot-files-test.ts | 2 +- tests/scenarios/compat-renaming-test.ts | 2 +- tests/scenarios/compat-stage2-test.ts | 73 ++++++---- .../compat-template-colocation-test.ts | 133 +++++++++++++----- 4 files changed, 150 insertions(+), 60 deletions(-) diff --git a/tests/scenarios/compat-exclude-dot-files-test.ts b/tests/scenarios/compat-exclude-dot-files-test.ts index db48e4121..5b0b2ec40 100644 --- a/tests/scenarios/compat-exclude-dot-files-test.ts +++ b/tests/scenarios/compat-exclude-dot-files-test.ts @@ -75,7 +75,7 @@ appScenarios // but not be picked up in the entrypoint expectAudit .module('./node_modules/.embroider/rewritten-app/index.html') - .resolves('/assets/app-template.js') + .resolves('/@embroider/core/entrypoint') .toModule() .withContents(content => { assert.notOk(/app-template\/\.foobar/.test(content), '.foobar is not in the entrypoint'); diff --git a/tests/scenarios/compat-renaming-test.ts b/tests/scenarios/compat-renaming-test.ts index 41945d3f9..dd71ba944 100644 --- a/tests/scenarios/compat-renaming-test.ts +++ b/tests/scenarios/compat-renaming-test.ts @@ -256,7 +256,7 @@ appScenarios test('renamed modules keep their classic runtime name when used as implicit-modules', function () { expectAudit .module('./node_modules/.embroider/rewritten-app/index.html') - .resolves('/assets/app-template.js') + .resolves('/@embroider/core/entrypoint') .toModule() .resolves('./-embroider-implicit-modules.js') .toModule() diff --git a/tests/scenarios/compat-stage2-test.ts b/tests/scenarios/compat-stage2-test.ts index 0124010aa..635b48f57 100644 --- a/tests/scenarios/compat-stage2-test.ts +++ b/tests/scenarios/compat-stage2-test.ts @@ -131,10 +131,9 @@ stage2Scenarios // check that the app trees with in repo addon are combined correctly expectAudit .module('./node_modules/.embroider/rewritten-app/index.html') - .resolves('/assets/my-app.js') + .resolves('/@embroider/core/entrypoint') .toModule() - //TODO investigate removing this @embroider-dep - .resolves('my-app/service/in-repo.js') + .resolves('@embroider-dep/my-app/service/in-repo.js') .to('./node_modules/dep-b/lib/in-repo-c/_app_/service/in-repo.js'); }); @@ -142,10 +141,9 @@ stage2Scenarios // secondary in-repo-addon was correctly detected and activated expectAudit .module('./node_modules/.embroider/rewritten-app/index.html') - .resolves('/assets/my-app.js') + .resolves('/@embroider/core/entrypoint') .toModule() - //TODO investigate removing this @embroider-dep - .resolves('my-app/services/secondary.js') + .resolves('@embroider-dep/my-app/services/secondary.js') .to('./lib/secondary-in-repo-addon/_app_/services/secondary.js'); // secondary is resolvable from primary @@ -209,46 +207,52 @@ stage2Scenarios test('verifies that the correct lexigraphically sorted addons win', function () { let expectModule = expectAudit .module('./node_modules/.embroider/rewritten-app/index.html') - .resolves('/assets/my-app.js') + .resolves('/@embroider/core/entrypoint') .toModule(); - expectModule.resolves('my-app/service/in-repo.js').to('./lib/in-repo-b/_app_/service/in-repo.js'); - expectModule.resolves('my-app/service/addon.js').to('./node_modules/dep-b/_app_/service/addon.js'); - expectModule.resolves('my-app/service/dev-addon.js').to('./node_modules/dev-c/_app_/service/dev-addon.js'); + expectModule + .resolves('@embroider-dep/my-app/service/in-repo.js') + .to('./lib/in-repo-b/_app_/service/in-repo.js'); + expectModule + .resolves('@embroider-dep/my-app/service/addon.js') + .to('./node_modules/dep-b/_app_/service/addon.js'); + expectModule + .resolves('@embroider-dep/my-app/service/dev-addon.js') + .to('./node_modules/dev-c/_app_/service/dev-addon.js'); }); test('addons declared as dependencies should win over devDependencies', function () { expectAudit .module('./node_modules/.embroider/rewritten-app/index.html') - .resolves('/assets/my-app.js') + .resolves('/@embroider/core/entrypoint') .toModule() - .resolves('my-app/service/dep-wins-over-dev.js') + .resolves('@embroider-dep/my-app/service/dep-wins-over-dev.js') .to('./node_modules/dep-b/_app_/service/dep-wins-over-dev.js'); }); test('in repo addons declared win over dependencies', function () { expectAudit .module('./node_modules/.embroider/rewritten-app/index.html') - .resolves('/assets/my-app.js') + .resolves('/@embroider/core/entrypoint') .toModule() - .resolves('my-app/service/in-repo-over-deps.js') + .resolves('@embroider-dep/my-app/service/in-repo-over-deps.js') .to('./lib/in-repo-a/_app_/service/in-repo-over-deps.js'); }); test('ordering with before specified', function () { expectAudit .module('./node_modules/.embroider/rewritten-app/index.html') - .resolves('/assets/my-app.js') + .resolves('/@embroider/core/entrypoint') .toModule() - .resolves('my-app/service/test-before.js') + .resolves('@embroider-dep/my-app/service/test-before.js') .to('./node_modules/dev-d/_app_/service/test-before.js'); }); test('ordering with after specified', function () { expectAudit .module('./node_modules/.embroider/rewritten-app/index.html') - .resolves('/assets/my-app.js') + .resolves('/@embroider/core/entrypoint') .toModule() - .resolves('my-app/service/test-after.js') + .resolves('@embroider-dep/my-app/service/test-after.js') .to('./node_modules/dev-b/_app_/service/test-after.js'); }); }); @@ -672,19 +676,36 @@ stage2Scenarios ); }); - test('non-static other paths are included in the entrypoint', function () { + test('non-static other paths are included in the entrypoint', function (assert) { expectAudit .module('./node_modules/.embroider/rewritten-app/index.html') - .resolves('/assets/my-app.js') - .toModule().codeContains(`d("my-app/non-static-dir/another-library", function () { - return i("my-app/non-static-dir/another-library.js"); - });`); + .resolves('/@embroider/core/entrypoint') + .toModule() + .withContents(contents => { + const result = /import \* as (\w+) from "@embroider-dep\/my-app\/non-static-dir\/another-library.js";/.exec( + contents + ); + + if (!result) { + throw new Error('Could not find import for non-static-dir/another-library'); + } + + const [, amdModule] = result; + + assert.codeContains( + contents, + `d("my-app/non-static-dir/another-library", function () { + return ${amdModule}; + });` + ); + return true; + }); }); test('static other paths are not included in the entrypoint', function () { expectAudit .module('./node_modules/.embroider/rewritten-app/index.html') - .resolves('/assets/my-app.js') + .resolves('/@embroider/core/entrypoint') .toModule() .withContents(content => { return !/my-app\/static-dir\/my-library\.js"/.test(content); @@ -694,7 +715,7 @@ stage2Scenarios test('top-level static other paths are not included in the entrypoint', function () { expectAudit .module('./node_modules/.embroider/rewritten-app/index.html') - .resolves('/assets/my-app.js') + .resolves('/@embroider/core/entrypoint') .toModule() .withContents(content => { return !content.includes('my-app/top-level-static.js'); @@ -704,7 +725,7 @@ stage2Scenarios test('staticAppPaths do not match partial path segments', function () { expectAudit .module('./node_modules/.embroider/rewritten-app/index.html') - .resolves('/assets/my-app.js') + .resolves('/@embroider/core/entrypoint') .toModule() .withContents(content => { return content.includes('my-app/static-dir-not-really/something.js'); diff --git a/tests/scenarios/compat-template-colocation-test.ts b/tests/scenarios/compat-template-colocation-test.ts index 5efba6c18..25ef9295a 100644 --- a/tests/scenarios/compat-template-colocation-test.ts +++ b/tests/scenarios/compat-template-colocation-test.ts @@ -1,10 +1,8 @@ import type { PreparedApp } from 'scenario-tester'; import { appScenarios, baseAddon, renameApp } from './scenarios'; -import { readFileSync } from 'fs'; -import { join } from 'path'; import { Transpiler } from '@embroider/test-support'; import type { ExpectFile } from '@embroider/test-support/file-assertions/qunit'; -import { expectFilesAt, expectRewrittenFilesAt } from '@embroider/test-support/file-assertions/qunit'; +import { expectRewrittenFilesAt } from '@embroider/test-support/file-assertions/qunit'; import { setupAuditTest } from '@embroider/test-support/audit-assertions'; import { throwOnWarnings } from '@embroider/core'; import merge from 'lodash/merge'; @@ -21,7 +19,8 @@ let scenarios = appScenarios.map('compat-template-colocation', app => { templates: { 'index.hbs': ` - + {{!-- TODO why is there a TS component here using an appScenario?? --}} + {{!-- --}} `, }, @@ -130,13 +129,29 @@ scenarios assertFile.doesNotExist('component stub was not created'); }); - test(`app's colocated components are implicitly included correctly`, function () { + test(`app's colocated components are implicitly included correctly`, function (assert) { expectAudit .module('./node_modules/.embroider/rewritten-app/index.html') - .resolves('/assets/my-app.js') - .toModule().codeContains(`d("my-app/components/has-colocated-template", function () { - return i("my-app/components/has-colocated-template.js"); - });`); + .resolves('/@embroider/core/entrypoint') + .toModule() + .withContents(contents => { + const result = + /import \* as (\w+) from "@embroider-dep\/my-app\/components\/has-colocated-template.js";/.exec(contents); + + if (!result) { + throw new Error('Missing import of has-colocated-template'); + } + + const [, amdModule] = result; + + assert.codeContains( + contents, + `d("my-app/components/has-colocated-template", function () { + return ${amdModule}; + });` + ); + return true; + }); }); test(`addon's colocated template is associated with JS`, function () { @@ -189,14 +204,24 @@ scenarios expectFile = expectRewrittenFilesAt(app.dir, { qunit: assert }); }); - test(`app's colocated components are not implicitly included`, function () { - let assertFile = expectFile('assets/my-app.js'); - assertFile.doesNotMatch( - /d\(["']my-app\/components\/has-colocated-template["'], function\(\)\s*\{\s*return i\(["']my-app\/components\/has-colocated-template['"]\);\s*\}/ - ); - assertFile.doesNotMatch( - /d\(["']my-app\/components\/template-only-component["'], function\(\)\s*\{\s*return i\(["']my-app\/components\/template-only-component['"]\);\s*\}/ - ); + let expectAudit = setupAuditTest(hooks, () => ({ app: app.dir })); + + test(`app's colocated components are not implicitly included`, function (assert) { + expectAudit + .module('./node_modules/.embroider/rewritten-app/index.html') + .resolves('/@embroider/core/entrypoint') + .toModule() + .withContents(content => { + assert.notOk( + /import \* as (\w+) from "@embroider-dep\/my-app\/components\/has-colocated-component.js"/.test(content) + ); + + assert.notOk( + /import \* as (\w+) from "@embroider-dep\/my-app\/components\/template-only-component.js"/.test(content) + ); + + return true; + }); }); test(`addon's colocated components are not in implicit-modules`, function () { @@ -269,7 +294,6 @@ appScenarios throwOnWarnings(hooks); let app: PreparedApp; - let expectFile: ExpectFile; hooks.before(async assert => { app = await scenario.prepare(); @@ -277,21 +301,66 @@ appScenarios assert.equal(result.exitCode, 0, result.output); }); - hooks.beforeEach(assert => { - expectFile = expectFilesAt(readFileSync(join(app.dir, 'dist/.stage2-output'), 'utf8'), { qunit: assert }); - }); + let expectAudit = setupAuditTest(hooks, () => ({ app: app.dir })); - test(`app's pod components and templates are implicitly included correctly`, function () { - let assertFile = expectFile('assets/my-app.js'); - assertFile.matches( - /d\(["']my-app\/components\/pod-component\/component["'], function\(\)\s*\{\s*return i\(["']my-app\/components\/pod-component\/component\.js['"]\);\}\)/ - ); - assertFile.matches( - /d\(["']my-app\/components\/pod-component\/template["'], function\(\)\s*\{\s*return i\(["']my-app\/components\/pod-component\/template\.hbs['"]\);\}\)/ - ); - assertFile.matches( - /d\(["']my-app\/components\/template-only\/template["'], function\(\)\s*\{\s*return i\(["']my-app\/components\/template-only\/template\.hbs['"]\);\s*\}/ - ); + test(`app's pod components and templates are implicitly included correctly`, function (assert) { + expectAudit + .module('./node_modules/.embroider/rewritten-app/index.html') + .resolves('/@embroider/core/entrypoint') + .toModule() + .withContents(content => { + let result = + /import \* as (\w+) from "@embroider-dep\/my-app\/components\/pod-component\/component.js"/.exec(content); + + if (!result) { + throw new Error('Could not find pod component'); + } + + const [, podComponentAmd] = result; + + assert.codeContains( + content, + `d("my-app/components/pod-component/component", function () { + return ${podComponentAmd}; + });` + ); + + result = /import \* as (\w+) from "@embroider-dep\/my-app\/components\/pod-component\/template.hbs"/.exec( + content + ); + + if (!result) { + throw new Error('Could not find pod component template'); + } + + const [, podComponentTemplateAmd] = result; + + assert.codeContains( + content, + `d("my-app/components/pod-component/template", function () { + return ${podComponentTemplateAmd}; + });` + ); + + result = /import \* as (\w+) from "@embroider-dep\/my-app\/components\/template-only\/template.hbs"/.exec( + content + ); + + if (!result) { + throw new Error('Could not find template only component'); + } + + const [, templateOnlyAmd] = result; + + assert.codeContains( + content, + `d("my-app/components/template-only/template", function () { + return ${templateOnlyAmd}; + });` + ); + + return true; + }); }); }); }); From 077e30078d5cf946f01cc15e109c4790a99fe689 Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Thu, 2 May 2024 14:28:17 +0100 Subject: [PATCH 10/29] add a patched ember 5.8 scenario --- package.json | 3 ++ patches/ember-source@5.8.0.patch | 55 ++++++++++++++++++++++++++++++++ pnpm-lock.yaml | 18 ++++++++--- tests/scenarios/package.json | 1 + tests/scenarios/scenarios.ts | 9 ++++++ 5 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 patches/ember-source@5.8.0.patch diff --git a/package.json b/package.json index d93c14ba5..5cb29950e 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,9 @@ "graceful-fs": "^4.0.0", "@types/eslint": "^8.37.0", "babel-plugin-module-resolver@5.0.1": "5.0.0" + }, + "patchedDependencies": { + "ember-source@5.8.0": "patches/ember-source@5.8.0.patch" } }, "devDependencies": { diff --git a/patches/ember-source@5.8.0.patch b/patches/ember-source@5.8.0.patch new file mode 100644 index 000000000..6beb6acc3 --- /dev/null +++ b/patches/ember-source@5.8.0.patch @@ -0,0 +1,55 @@ +diff --git a/dist/packages/@ember/debug/index.js b/dist/packages/@ember/debug/index.js +index 27eb2872d475cbff7f80d24c7404627d072bada9..123147f265134c50e32e381b0f976a7f93697485 100644 +--- a/dist/packages/@ember/debug/index.js ++++ b/dist/packages/@ember/debug/index.js +@@ -1,6 +1,6 @@ + import { isChrome, isFirefox } from '@ember/-internals/browser-environment'; + import { DEBUG } from '@glimmer/env'; +-import _deprecate from './lib/deprecate'; ++import defaultDeprecate from './lib/deprecate'; + import { isTesting } from './lib/testing'; + import _warn from './lib/warn'; + export { registerHandler as registerWarnHandler } from './lib/warn'; +@@ -12,11 +12,11 @@ export { default as captureRenderTree } from './lib/capture-render-tree'; + const noop = () => {}; + // SAFETY: these casts are just straight-up lies, but the point is that they do + // not do anything in production builds. + let assert = noop; + let info = noop; + let warn = noop; + let debug = noop; +-let deprecate = noop; ++let currentDeprecate; + let debugSeal = noop; + let debugFreeze = noop; + let runInDebug = noop; +@@ -25,6 +25,12 @@ let getDebugFunction = noop; + let deprecateFunc = function () { + return arguments[arguments.length - 1]; + }; ++ ++function deprecate(...args) { ++ return (currentDeprecate ?? defaultDeprecate)(...args) ++} ++ ++ + if (DEBUG) { + setDebugFunction = function (type, callback) { + switch (type) { +@@ -37,7 +43,7 @@ if (DEBUG) { + case 'debug': + return debug = callback; + case 'deprecate': +- return deprecate = callback; ++ return currentDeprecate = callback; + case 'debugSeal': + return debugSeal = callback; + case 'debugFreeze': +@@ -190,7 +189,6 @@ if (DEBUG) { + Object.freeze(obj); + } + }); +- setDebugFunction('deprecate', _deprecate); + setDebugFunction('warn', _warn); + } + let _warnIfUsingStrippedFeatureFlags; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a06dc8a82..3af97d762 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,6 +10,11 @@ overrides: '@types/eslint': ^8.37.0 babel-plugin-module-resolver@5.0.1: 5.0.0 +patchedDependencies: + ember-source@5.8.0: + hash: qsivfx5huurlb5tuvochap65l4 + path: patches/ember-source@5.8.0.patch + importers: .: @@ -638,7 +643,7 @@ importers: version: 7.6.0 ember-source: specifier: ^5.8.0 - version: 5.8.0(@babel/core@7.24.5) + version: 5.8.0(patch_hash=qsivfx5huurlb5tuvochap65l4)(@babel/core@7.24.5) ember-template-lint: specifier: ^4.0.0 version: 4.18.2 @@ -1782,6 +1787,9 @@ importers: ember-source-4.4: specifier: npm:ember-source@~4.4.0 version: /ember-source@4.4.5(@babel/core@7.24.5)(webpack@5.91.0) + ember-source-5.8: + specifier: npm:ember-source@~5.8.0 + version: /ember-source@5.8.0(patch_hash=qsivfx5huurlb5tuvochap65l4)(@babel/core@7.24.5)(webpack@5.91.0) ember-source-beta: specifier: npm:ember-source@beta version: /ember-source@5.9.0-beta.2(@babel/core@7.24.5)(webpack@5.91.0) @@ -1790,7 +1798,7 @@ importers: version: '@s3.amazonaws.com/builds.emberjs.com/canary/shas/370cf34f9e86df17b880f11fef35a5a0f24ff38a.tgz(@babel/core@7.24.5)(webpack@5.91.0)' ember-source-latest: specifier: npm:ember-source@latest - version: /ember-source@5.8.0(@babel/core@7.24.5)(webpack@5.91.0) + version: /ember-source@5.8.0(patch_hash=qsivfx5huurlb5tuvochap65l4)(@babel/core@7.24.5)(webpack@5.91.0) ember-truth-helpers: specifier: ^3.0.0 version: 3.1.1 @@ -14601,7 +14609,7 @@ packages: - webpack dev: true - /ember-source@5.8.0(@babel/core@7.24.5): + /ember-source@5.8.0(patch_hash=qsivfx5huurlb5tuvochap65l4)(@babel/core@7.24.5): resolution: {integrity: sha512-jRmT5egy7XG2G9pKNdNNwNBZqFxrl7xJwdYrJ3ugreR7zK1FR28lHSR5CMSKtYLmJZxu340cf2EbRohWEtO2Zw==} engines: {node: '>= 16.*'} dependencies: @@ -14659,8 +14667,9 @@ packages: - supports-color - webpack dev: true + patched: true - /ember-source@5.8.0(@babel/core@7.24.5)(webpack@5.91.0): + /ember-source@5.8.0(patch_hash=qsivfx5huurlb5tuvochap65l4)(@babel/core@7.24.5)(webpack@5.91.0): resolution: {integrity: sha512-jRmT5egy7XG2G9pKNdNNwNBZqFxrl7xJwdYrJ3ugreR7zK1FR28lHSR5CMSKtYLmJZxu340cf2EbRohWEtO2Zw==} engines: {node: '>= 16.*'} dependencies: @@ -14718,6 +14727,7 @@ packages: - supports-color - webpack dev: true + patched: true /ember-source@5.9.0-beta.2(@babel/core@7.24.5)(webpack@5.91.0): resolution: {integrity: sha512-3476KQBR7zlq9ITbgeG+P7oYpmtpMF6+/aKH+Sx7iLKjAlw3QRR2UEOboH0joAlbU+1/cLtDdK0txnefK43ViQ==} diff --git a/tests/scenarios/package.json b/tests/scenarios/package.json index 7dfe99322..1f3864a9c 100644 --- a/tests/scenarios/package.json +++ b/tests/scenarios/package.json @@ -79,6 +79,7 @@ "ember-source-4.4": "npm:ember-source@~4.4.0", "ember-source-beta": "npm:ember-source@beta", "ember-source-canary": "https://s3.amazonaws.com/builds.emberjs.com/canary/shas/370cf34f9e86df17b880f11fef35a5a0f24ff38a.tgz", + "ember-source-5.8": "npm:ember-source@~5.8.0", "ember-source-latest": "npm:ember-source@latest", "ember-truth-helpers": "^3.0.0", "execa": "^5.1.1", diff --git a/tests/scenarios/scenarios.ts b/tests/scenarios/scenarios.ts index c21ae6b7a..004b4e5c8 100644 --- a/tests/scenarios/scenarios.ts +++ b/tests/scenarios/scenarios.ts @@ -21,6 +21,14 @@ async function release(project: Project) { project.linkDevDependency('ember-qunit', { baseDir: __dirname, resolveName: 'ember-qunit-7' }); } +async function lts_5_8(project: Project) { + project.linkDevDependency('ember-source', { baseDir: __dirname, resolveName: 'ember-source-5.8' }); + project.linkDevDependency('ember-cli', { baseDir: __dirname, resolveName: 'ember-cli-latest' }); + project.linkDevDependency('ember-data', { baseDir: __dirname, resolveName: 'ember-data-latest' }); + project.linkDevDependency('@ember/test-helpers', { baseDir: __dirname, resolveName: '@ember/test-helpers-3' }); + project.linkDevDependency('ember-qunit', { baseDir: __dirname, resolveName: 'ember-qunit-7' }); +} + async function canary(project: Project) { project.linkDevDependency('ember-source', { baseDir: __dirname, resolveName: 'ember-source-canary' }); project.linkDevDependency('ember-cli', { baseDir: __dirname, resolveName: 'ember-cli-beta' }); @@ -35,6 +43,7 @@ export function supportMatrix(scenarios: Scenarios) { .expand({ lts_3_28, lts_4_4, + lts_5_8, release, canary, }) From 735bfb7cf89681ec871411ee03c0e6c73740d42e Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Mon, 6 May 2024 19:26:12 +0100 Subject: [PATCH 11/29] add basic route entrypoint implementation --- packages/core/src/module-resolver.ts | 22 +++ packages/core/src/virtual-content.ts | 6 + packages/core/src/virtual-entrypoint.ts | 48 +++---- packages/core/src/virtual-route-entrypoint.ts | 131 ++++++++++++++++++ 4 files changed, 180 insertions(+), 27 deletions(-) create mode 100644 packages/core/src/virtual-route-entrypoint.ts diff --git a/packages/core/src/module-resolver.ts b/packages/core/src/module-resolver.ts index 2cc35bc45..b33fb3a4f 100644 --- a/packages/core/src/module-resolver.ts +++ b/packages/core/src/module-resolver.ts @@ -26,6 +26,7 @@ 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'; const debug = makeDebug('embroider:resolver'); @@ -206,6 +207,7 @@ export class Resolver { request = this.handleVendorStyles(request); request = this.handleTestSupportStyles(request); request = this.handleEntrypoint(request); + request = this.handleRouteEntrypoint(request); request = this.handleRenaming(request); request = this.handleVendor(request); // we expect the specifier to be app relative at this point - must be after handleRenaming @@ -459,6 +461,26 @@ export class Resolver { return logTransition('entrypoint', request, request.virtualize(resolve(pkg.root, '-embroider-entrypoint.js'))); } + private handleRouteEntrypoint(request: R): R { + if (isTerminal(request)) { + return request; + } + + let routeName = decodePublicRouteEntrypoint(request.specifier); + + if (!routeName) { + return request; + } + + let pkg = this.packageCache.ownerOfFile(request.fromFile); + + if (!pkg?.isV2Ember()) { + throw new Error(`bug: found entrypoint import in non-ember package at ${request.fromFile}`); + } + + return logTransition('route entrypoint', request, request.virtualize(encodeRouteEntrypoint(pkg.root, routeName))); + } + private handleImplicitTestScripts(request: R): R { //TODO move the extra forwardslash handling out into the vite plugin const candidates = [ diff --git a/packages/core/src/virtual-content.ts b/packages/core/src/virtual-content.ts index a36facdcb..b46f64ab3 100644 --- a/packages/core/src/virtual-content.ts +++ b/packages/core/src/virtual-content.ts @@ -8,6 +8,7 @@ import { decodeVirtualVendor, renderVendor } from './virtual-vendor'; import { decodeVirtualVendorStyles, renderVendorStyles } from './virtual-vendor-styles'; import { decodeEntrypoint, renderEntrypoint } from './virtual-entrypoint'; +import { decodeRouteEntrypoint, renderRouteEntrypoint } from './virtual-route-entrypoint'; const externalESPrefix = '/@embroider/ext-es/'; const externalCJSPrefix = '/@embroider/ext-cjs/'; @@ -32,6 +33,11 @@ export function virtualContent(filename: string, resolver: Resolver): VirtualCon return renderEntrypoint(resolver, entrypoint); } + let routeEntrypoint = decodeRouteEntrypoint(filename); + if (routeEntrypoint) { + return renderRouteEntrypoint(resolver, routeEntrypoint); + } + let extern = decodeVirtualExternalESModule(filename); if (extern) { return renderESExternalShim(extern); diff --git a/packages/core/src/virtual-entrypoint.ts b/packages/core/src/virtual-entrypoint.ts index 536846de4..48bc2a3e0 100644 --- a/packages/core/src/virtual-entrypoint.ts +++ b/packages/core/src/virtual-entrypoint.ts @@ -5,9 +5,9 @@ import type { CompatResolverOptions } from '../../compat/src/resolver-transform' import { flatten, partition } from 'lodash'; import { posix, join } from 'path'; import { extensionsPattern } from '@embroider/shared-internals'; -import { type default as Options, optionsWithDefaults } from './options'; import walkSync from 'walk-sync'; import type { V2AddonPackage } from '@embroider/shared-internals/src/package'; +import { encodePublicRouteEntrypoint } from './virtual-route-entrypoint'; const entrypointPattern = /(?.*)[\\/]-embroider-entrypoint.js/; @@ -106,15 +106,14 @@ export function renderEntrypoint( splitRoute( routeName, routeFiles, + resolver.options.splitAtRoutes, (_: string, filename: string) => { requiredAppFiles.push([filename]); }, (routeNames: string[], _files: string[]) => { - // TODO: we don't consume files anymore, should we stop generating it? - let routeEntrypoint = `assets/_route_/${encodeURIComponent(routeNames[0])}.js`; lazyRoutes.push({ names: routeNames, - path: importPaths(resolver, appFiles, routeEntrypoint).buildtime, + path: encodePublicRouteEntrypoint(routeNames, _files), }); } ); @@ -272,7 +271,7 @@ function excludeDotFiles(files: string[]) { return files.filter(file => !file.startsWith('.') && !file.includes('/.')); } -function importPaths(resolver: Resolver, { engine }: AppFiles, engineRelativePath: string) { +export function importPaths(resolver: Resolver, { engine }: AppFiles, engineRelativePath: string) { let resolvableExtensionsPattern = extensionsPattern(resolver.options.resolvableExtensions); let noHBS = engineRelativePath.replace(resolvableExtensionsPattern, '').replace(/\.hbs$/, ''); return { @@ -281,13 +280,14 @@ function importPaths(resolver: Resolver, { engine }: AppFiles, engineRelativePat }; } -function splitRoute( +export function splitRoute( routeName: string, files: RouteFiles, + splitAtRoutes: (RegExp | string)[] | undefined, addToParent: (routeName: string, filename: string) => void, addLazyBundle: (routeNames: string[], files: string[]) => void ) { - let shouldSplit = routeName && shouldSplitRoute(routeName); + let shouldSplit = routeName && shouldSplitRoute(routeName, splitAtRoutes); let ownFiles = []; let ownNames = new Set() as Set; @@ -322,7 +322,7 @@ function splitRoute( splitRoute( `${routeName}.${childName}`, childFiles, - + splitAtRoutes, (childRouteName: string, childFile: string) => { // this is our child calling "addToParent" if (shouldSplit) { @@ -343,33 +343,27 @@ function splitRoute( } } -function readEmbroiderConfig(): Required { - // TODO - return optionsWithDefaults({}); -} - -function shouldSplitRoute(routeName: string) { - let config = readEmbroiderConfig(); - return ( - !config.splitAtRoutes || - config.splitAtRoutes.find(pattern => { - if (typeof pattern === 'string') { - return pattern === routeName; - } else { - return pattern.test(routeName); - } - }) - ); +function shouldSplitRoute(routeName: string, splitAtRoutes: (RegExp | string)[] | undefined) { + if (!splitAtRoutes) { + return false; + } + return splitAtRoutes.find(pattern => { + if (typeof pattern === 'string') { + return pattern === routeName; + } else { + return pattern.test(routeName); + } + }); } -function getAppFiles(appRoot: string): Set { +export function getAppFiles(appRoot: string): Set { const files: string[] = walkSync(appRoot, { ignore: ['_babel_config_.js', '_babel_filter_.js', 'app.js', 'assets', 'testem.js', 'node_modules'], }); return new Set(files); } -function getFastbootFiles(appRoot: string): Set { +export function getFastbootFiles(appRoot: string): Set { const appDirPath = join(appRoot, '_fastboot_'); const files: string[] = walkSync(appDirPath); return new Set(files); diff --git a/packages/core/src/virtual-route-entrypoint.ts b/packages/core/src/virtual-route-entrypoint.ts new file mode 100644 index 000000000..fcdaf0bed --- /dev/null +++ b/packages/core/src/virtual-route-entrypoint.ts @@ -0,0 +1,131 @@ +import type { V2AddonPackage } from '@embroider/shared-internals/src/package'; +import { AppFiles } from './app-files'; +import type { Resolver } from './module-resolver'; +import { resolve } from 'path/posix'; +import { compile } from './js-handlebars'; +import { extensionsPattern } from '@embroider/shared-internals'; +import { partition } from 'lodash'; +import { getAppFiles, getFastbootFiles, importPaths, splitRoute } from './virtual-entrypoint'; + +const entrypointPattern = /(?.*)[\\/]-embroider-route-entrypoint.js:route=(?.*)/; + +export function encodeRouteEntrypoint(packagePath: string, routeName: string): string { + return resolve(packagePath, `-embroider-route-entrypoint.js:route=${routeName}`); +} + +export function decodeRouteEntrypoint(filename: string): { fromFile: string; route: string } | undefined { + // Performance: avoid paying regex exec cost unless needed + if (!filename.includes('-embroider-route-entrypoint')) { + return; + } + let m = entrypointPattern.exec(filename); + if (m) { + return { + fromFile: m.groups!.filename, + route: m.groups!.route, + }; + } +} + +export function encodePublicRouteEntrypoint(routeNames: string[], _files: string[]) { + return `@embroider/core/route/${encodeURIComponent(routeNames[0])}`; +} + +export function decodePublicRouteEntrypoint(specifier: string): string | null { + const publicPrefix = '@embroider/core/route/'; + if (!specifier.startsWith(publicPrefix)) { + return null; + } + + return specifier.slice(publicPrefix.length); +} + +export function renderRouteEntrypoint( + resolver: Resolver, + { fromFile, route }: { fromFile: string; route: string } +): { src: string; watches: string[] } { + const owner = resolver.packageCache.ownerOfFile(fromFile); + + if (!owner) { + throw new Error('Owner expected'); // ToDo: Really bad error, update message + } + + let engine = resolver.owningEngine(owner); + let isApp = owner?.root === resolver.options.engines[0]!.root; + let hasFastboot = Boolean(resolver.options.engines[0]!.activeAddons.find(a => a.name === 'ember-cli-fastboot')); + + let appFiles = new AppFiles( + { + package: owner, + addons: new Map( + engine.activeAddons.map(addon => [ + resolver.packageCache.get(addon.root) as V2AddonPackage, + addon.canResolveFromFile, + ]) + ), + isApp, + modulePrefix: isApp ? resolver.options.modulePrefix : engine.packageName, + appRelativePath: 'NOT_USED_DELETE_ME', + }, + getAppFiles(owner.root), + hasFastboot ? getFastbootFiles(owner.root) : new Set(), + extensionsPattern(resolver.options.resolvableExtensions), + resolver.options.podModulePrefix + ); + + let src = ''; + + for (let [routeName, routeFiles] of appFiles.routeFiles.children) { + splitRoute( + routeName, + routeFiles, + resolver.options.splitAtRoutes, + (_: string, _filename: string) => { + // noop + }, + (routeNames: string[], routeFiles: string[]) => { + if (routeNames[0] === route) { + let [fastboot, nonFastboot] = partition(routeFiles, file => appFiles.isFastbootOnly.get(file)); + + const amdModules = nonFastboot.map(f => importPaths(resolver, appFiles, f)); + const fastbootOnlyAmdModules = fastboot.map(f => importPaths(resolver, appFiles, f)); + + src = routeEntryTemplate({ + amdModules, + fastbootOnlyAmdModules, + }); + } + } + ); + } + + return { src, watches: [] }; +} + +const routeEntryTemplate = compile(` +let d = window.define; + +{{#each amdModules as |amdModule index| ~}} + import * as amdModule{{index}} from "{{js-string-escape amdModule.buildtime}}" + d("{{js-string-escape amdModule.runtime}}", function(){ return amdModule{{index}}; }); +{{/each}} + +{{#if fastbootOnlyAmdModules}} + if (macroCondition(getGlobalConfig().fastboot?.isRunning)) { + let fastbootModules = {}; + + {{#each fastbootOnlyAmdModules as |amdModule| ~}} + fastbootModules["{{js-string-escape amdModule.runtime}}"] = import("{{js-string-escape amdModule.buildtime}}"); + {{/each}} + + const resolvedValues = await Promise.all(Object.values(fastbootModules)); + + Object.keys(fastbootModules).forEach((k, i) => { + d(k, function(){ return resolvedValues[i];}); + }) + } +{{/if}} +`) as (params: { + amdModules: { runtime: string; buildtime: string }[]; + fastbootOnlyAmdModules: { runtime: string; buildtime: string }[]; +}) => string; From 0d21749e0b77d6462eb14d2628734dee2773f51e Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Mon, 6 May 2024 19:26:27 +0100 Subject: [PATCH 12/29] fix bad split-route tests --- tests/scenarios/compat-route-split-test.ts | 24 ++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/tests/scenarios/compat-route-split-test.ts b/tests/scenarios/compat-route-split-test.ts index 8932f335f..15fdb6a1d 100644 --- a/tests/scenarios/compat-route-split-test.ts +++ b/tests/scenarios/compat-route-split-test.ts @@ -44,6 +44,18 @@ let splitScenarios = appScenarios.map('compat-splitAtRoutes', app => { modifiers: { 'auto-focus.js': 'export default function(){}', }, + 'router.js': `import EmberRouter from '@embroider/router'; + import config from 'my-app/config/environment'; + + export default class Router extends EmberRouter { + location = config.locationType; + rootURL = config.rootURL; + } + + Router.map(function () { + this.route('people'); + }); + `, }, }); app.linkDependency('@ember/string', { baseDir: __dirname }); @@ -63,17 +75,17 @@ splitScenarios }, }, controllers: { - 'index.js': '', - 'people.js': '', + 'index.js': `import Controller from '@ember/controller'; export default class Thingy extends Controller {}`, + 'people.js': `import Controller from '@ember/controller'; export default class Thingy extends Controller {}`, people: { - 'show.js': '', + 'show.js': `import Controller from '@ember/controller'; export default class Thingy extends Controller {}`, }, }, routes: { - 'index.js': '', - 'people.js': '', + 'index.js': `import Route from '@ember/routing/route';export default class ThingyRoute extends Route {}`, + 'people.js': `import Route from '@ember/routing/route';export default class ThingyRoute extends Route {}`, people: { - 'show.js': '', + 'show.js': `import Route from '@ember/routing/route';export default class ThingyRoute extends Route {}`, }, }, }, From 590574590183b57585a0776102ba5ff5f40a2d6a Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Tue, 7 May 2024 14:55:20 +0100 Subject: [PATCH 13/29] convert split tests to check new route entrypoint --- tests/scenarios/compat-route-split-test.ts | 230 ++++++++++++--------- 1 file changed, 130 insertions(+), 100 deletions(-) diff --git a/tests/scenarios/compat-route-split-test.ts b/tests/scenarios/compat-route-split-test.ts index 15fdb6a1d..c5e590222 100644 --- a/tests/scenarios/compat-route-split-test.ts +++ b/tests/scenarios/compat-route-split-test.ts @@ -1,9 +1,5 @@ import type { PreparedApp } from 'scenario-tester'; import { appScenarios, renameApp } from './scenarios'; -import { readFileSync } from 'fs'; -import { join } from 'path'; -import type { ExpectFile } from '@embroider/test-support/file-assertions/qunit'; -import { expectFilesAt } from '@embroider/test-support/file-assertions/qunit'; import { throwOnWarnings } from '@embroider/core'; import merge from 'lodash/merge'; import { setupAuditTest } from '@embroider/test-support/audit-assertions'; @@ -61,6 +57,70 @@ let splitScenarios = appScenarios.map('compat-splitAtRoutes', app => { app.linkDependency('@ember/string', { baseDir: __dirname }); }); +function checkContents( + expectAudit: ReturnType, + fn: (contents: string) => void, + entrypointFile?: string +) { + let resolved = expectAudit + .module('./node_modules/.embroider/rewritten-app/index.html') + .resolves('/@embroider/core/entrypoint'); + + if (entrypointFile) { + resolved = resolved.toModule().resolves(entrypointFile); + } + resolved.toModule().withContents(contents => { + fn(contents); + return true; + }); +} + +function notInEntrypointFunction(expectAudit: ReturnType) { + return function (text: string[] | string, entrypointFile?: string) { + checkContents( + expectAudit, + contents => { + if (Array.isArray(text)) { + text.forEach(t => { + if (contents.includes(t)) { + throw new Error(`${t} should not be found in entrypoint`); + } + }); + } else { + if (contents.includes(text)) { + throw new Error(`${text} should not be found in entrypoint`); + } + } + return true; + }, + entrypointFile + ); + }; +} + +function inEntrypointFunction(expectAudit: ReturnType) { + return function (text: string[] | string, entrypointFile?: string) { + checkContents( + expectAudit, + contents => { + if (Array.isArray(text)) { + text.forEach(t => { + if (!contents.includes(t)) { + throw new Error(`${t} should be found in entrypoint`); + } + }); + } else { + if (!contents.includes(text)) { + console.log(contents); + throw new Error(`${text} should be found in entrypoint`); + } + } + }, + entrypointFile + ); + }; +} + splitScenarios .map('basic', app => { merge(app.files, { @@ -96,7 +156,6 @@ splitScenarios throwOnWarnings(hooks); let app: PreparedApp; - let expectFile: ExpectFile; hooks.before(async assert => { app = await scenario.prepare(); @@ -104,84 +163,75 @@ splitScenarios assert.equal(result.exitCode, 0, result.output); }); - hooks.beforeEach(assert => { - expectFile = expectFilesAt(readFileSync(join(app.dir, 'dist/.stage2-output'), 'utf8'), { qunit: assert }); - }); + let expectAudit = setupAuditTest(hooks, () => ({ app: app.dir })); + let notInEntrypoint = notInEntrypointFunction(expectAudit); + let inEntrypoint = inEntrypointFunction(expectAudit); test('has no components in main entrypoint', function () { - expectFile('./assets/my-app.js').doesNotMatch('all-people'); - expectFile('./assets/my-app.js').doesNotMatch('welcome'); - expectFile('./assets/my-app.js').doesNotMatch('unused'); + notInEntrypoint(['all-people', 'welcome', 'unused']); }); test('has no helpers in main entrypoint', function () { - expectFile('./assets/my-app.js').doesNotMatch('capitalize'); + notInEntrypoint('capitalize'); }); test('has no modifiers in main entrypoint', function () { - expectFile('./assets/my-app.js').doesNotMatch('auto-focus'); + notInEntrypoint('auto-focus'); }); test('has non-split controllers in main entrypoint', function () { - expectFile('./assets/my-app.js').matches('controllers/index'); + inEntrypoint('controllers/index'); }); test('has non-split route templates in main entrypoint', function () { - expectFile('./assets/my-app.js').matches('templates/index'); + inEntrypoint('templates/index'); }); test('has non-split routes in main entrypoint', function () { - expectFile('./assets/my-app.js').matches('routes/index'); + inEntrypoint('routes/index'); }); test('does not have split controllers in main entrypoint', function () { - expectFile('./assets/my-app.js').doesNotMatch('controllers/people'); - expectFile('./assets/my-app.js').doesNotMatch('controllers/people/show'); + notInEntrypoint(['controllers/people', 'controllers/people/show']); }); test('does not have split route templates in main entrypoint', function () { - expectFile('./assets/my-app.js').doesNotMatch('templates/people'); - expectFile('./assets/my-app.js').doesNotMatch('templates/people/index'); - expectFile('./assets/my-app.js').doesNotMatch('templates/people/show'); + notInEntrypoint(['templates/people', 'templates/people/index', 'templates/people/show']); }); test('does not have split routes in main entrypoint', function () { - expectFile('./assets/my-app.js').doesNotMatch('routes/people'); - expectFile('./assets/my-app.js').doesNotMatch('routes/people/show'); + notInEntrypoint(['routes/people', 'routes/people/show']); }); test('dynamically imports the route entrypoint from the main entrypoint', function () { - expectFile('./assets/my-app.js').matches('import("my-app/assets/_route_/people.js")'); + inEntrypoint('import("@embroider/core/route/people");'); }); test('has split controllers in route entrypoint', function () { - expectFile('./assets/_route_/people.js').matches('controllers/people'); - expectFile('./assets/_route_/people.js').matches('controllers/people/show'); + inEntrypoint(['controllers/people', 'controllers/people/show'], '@embroider/core/route/people'); }); test('has split route templates in route entrypoint', function () { - expectFile('./assets/_route_/people.js').matches('templates/people'); - expectFile('./assets/_route_/people.js').matches('templates/people/index'); - expectFile('./assets/_route_/people.js').matches('templates/people/show'); + inEntrypoint( + ['templates/people', 'templates/people/index', 'templates/people/show'], + '@embroider/core/route/people' + ); }); test('has split routes in route entrypoint', function () { - expectFile('./assets/_route_/people.js').matches('routes/people'); - expectFile('./assets/_route_/people.js').matches('routes/people/show'); + inEntrypoint(['routes/people', 'routes/people/show'], '@embroider/core/route/people'); }); test('has no components in route entrypoint', function () { - expectFile('./assets/_route_/people.js').doesNotMatch('all-people'); - expectFile('./assets/_route_/people.js').doesNotMatch('welcome'); - expectFile('./assets/_route_/people.js').doesNotMatch('unused'); + notInEntrypoint(['all-people', 'welcome', 'unused'], '@embroider/core/route/people'); }); test('has no helpers in route entrypoint', function () { - expectFile('./assets/_route_/people.js').doesNotMatch('capitalize'); + notInEntrypoint('capitalize', '@embroider/core/route/people'); }); test('has no helpers in route entrypoint', function () { - expectFile('./assets/_route_/people.js').doesNotMatch('auto-focus'); + notInEntrypoint('auto-focus', '@embroider/core/route/people'); }); Qmodule('audit', function (hooks) { @@ -268,7 +318,6 @@ splitScenarios throwOnWarnings(hooks); let app: PreparedApp; - let expectFile: ExpectFile; hooks.before(async assert => { app = await scenario.prepare(); @@ -276,84 +325,75 @@ splitScenarios assert.equal(result.exitCode, 0, result.output); }); - hooks.beforeEach(assert => { - expectFile = expectFilesAt(readFileSync(join(app.dir, 'dist/.stage2-output'), 'utf8'), { qunit: assert }); - }); + let expectAudit = setupAuditTest(hooks, () => ({ app: app.dir })); + let notInEntrypoint = notInEntrypointFunction(expectAudit); + let inEntrypoint = inEntrypointFunction(expectAudit); test('has no components in main entrypoint', function () { - expectFile('./assets/my-app.js').doesNotMatch('all-people'); - expectFile('./assets/my-app.js').doesNotMatch('welcome'); - expectFile('./assets/my-app.js').doesNotMatch('unused'); + notInEntrypoint(['all-people', 'welcome', 'unused']); }); test('has no helpers in main entrypoint', function () { - expectFile('./assets/my-app.js').doesNotMatch('capitalize'); + notInEntrypoint('capitalize'); }); test('has no modifiers in main entrypoint', function () { - expectFile('./assets/my-app.js').doesNotMatch('auto-focus'); + notInEntrypoint('auto-focus'); }); test('has non-split controllers in main entrypoint', function () { - expectFile('./assets/my-app.js').matches('pods/index/controller'); + inEntrypoint('pods/index/controller'); }); test('has non-split route templates in main entrypoint', function () { - expectFile('./assets/my-app.js').matches('pods/index/template'); + inEntrypoint('pods/index/template'); }); test('has non-split routes in main entrypoint', function () { - expectFile('./assets/my-app.js').matches('pods/index/route'); + inEntrypoint('pods/index/route'); }); test('does not have split controllers in main entrypoint', function () { - expectFile('./assets/my-app.js').doesNotMatch('pods/people/controller'); - expectFile('./assets/my-app.js').doesNotMatch('pods/people/show/controller'); + notInEntrypoint(['pods/people/controller', 'pods/people/show/controller']); }); test('does not have split route templates in main entrypoint', function () { - expectFile('./assets/my-app.js').doesNotMatch('pods/people/template'); - expectFile('./assets/my-app.js').doesNotMatch('pods/people/index/template'); - expectFile('./assets/my-app.js').doesNotMatch('pods/people/show/template'); + notInEntrypoint(['pods/people/template', 'pods/people/index/template', 'pods/people/show/template']); }); test('does not have split routes in main entrypoint', function () { - expectFile('./assets/my-app.js').doesNotMatch('pods/people/route'); - expectFile('./assets/my-app.js').doesNotMatch('pods/people/show/route'); + notInEntrypoint(['pods/people/route', 'pods/people/show/route']); }); test('dynamically imports the route entrypoint from the main entrypoint', function () { - expectFile('./assets/my-app.js').matches('import("my-app/assets/_route_/people.js")'); + inEntrypoint('import("@embroider/core/route/people")'); }); test('has split controllers in route entrypoint', function () { - expectFile('./assets/_route_/people.js').matches('pods/people/controller'); - expectFile('./assets/_route_/people.js').matches('pods/people/show/controller'); + inEntrypoint(['pods/people/controller', 'pods/people/show/controller'], '@embroider/core/route/people'); }); test('has split route templates in route entrypoint', function () { - expectFile('./assets/_route_/people.js').matches('pods/people/template'); - expectFile('./assets/_route_/people.js').matches('pods/people/index/template'); - expectFile('./assets/_route_/people.js').matches('pods/people/show/template'); + inEntrypoint( + ['pods/people/template', 'pods/people/index/template', 'pods/people/show/template'], + '@embroider/core/route/people' + ); }); test('has split routes in route entrypoint', function () { - expectFile('./assets/_route_/people.js').matches('pods/people/route'); - expectFile('./assets/_route_/people.js').matches('pods/people/show/route'); + inEntrypoint(['pods/people/route', 'pods/people/show/route'], '@embroider/core/route/people'); }); test('has no components in route entrypoint', function () { - expectFile('./assets/_route_/people.js').doesNotMatch('all-people'); - expectFile('./assets/_route_/people.js').doesNotMatch('welcome'); - expectFile('./assets/_route_/people.js').doesNotMatch('unused'); + notInEntrypoint(['all-people', 'welcome', 'unused'], '@embroider/core/route/people'); }); test('has no helpers in route entrypoint', function () { - expectFile('./assets/_route_/people.js').doesNotMatch('capitalize'); + notInEntrypoint('capitalize', '@embroider/core/route/people'); }); test('has no modifiers in route entrypoint', function () { - expectFile('./assets/_route_/people.js').doesNotMatch('auto-focus'); + notInEntrypoint('auto-focus', '@embroider/core/route/people'); }); Qmodule('audit', function (hooks) { @@ -440,7 +480,6 @@ splitScenarios throwOnWarnings(hooks); let app: PreparedApp; - let expectFile: ExpectFile; hooks.before(async assert => { app = await scenario.prepare(); @@ -448,84 +487,75 @@ splitScenarios assert.equal(result.exitCode, 0, result.output); }); - hooks.beforeEach(assert => { - expectFile = expectFilesAt(readFileSync(join(app.dir, 'dist/.stage2-output'), 'utf8'), { qunit: assert }); - }); + let expectAudit = setupAuditTest(hooks, () => ({ app: app.dir })); + let notInEntrypoint = notInEntrypointFunction(expectAudit); + let inEntrypoint = inEntrypointFunction(expectAudit); test('has no components in main entrypoint', function () { - expectFile('./assets/my-app.js').doesNotMatch('all-people'); - expectFile('./assets/my-app.js').doesNotMatch('welcome'); - expectFile('./assets/my-app.js').doesNotMatch('unused'); + notInEntrypoint(['all-people', 'welcome', 'unused']); }); test('has no helpers in main entrypoint', function () { - expectFile('./assets/my-app.js').doesNotMatch('capitalize'); + notInEntrypoint('capitalize'); }); test('has no modifiers in main entrypoint', function () { - expectFile('./assets/my-app.js').doesNotMatch('auto-focus'); + notInEntrypoint('auto-focus'); }); test('has non-split controllers in main entrypoint', function () { - expectFile('./assets/my-app.js').matches('routes/index/controller'); + inEntrypoint('routes/index/controller'); }); test('has non-split route templates in main entrypoint', function () { - expectFile('./assets/my-app.js').matches('routes/index/template'); + inEntrypoint('routes/index/template'); }); test('has non-split routes in main entrypoint', function () { - expectFile('./assets/my-app.js').matches('routes/index/route'); + inEntrypoint('routes/index/route'); }); test('does not have split controllers in main entrypoint', function () { - expectFile('./assets/my-app.js').doesNotMatch('routes/people/controller'); - expectFile('./assets/my-app.js').doesNotMatch('routes/people/show/controller'); + notInEntrypoint(['routes/people/controller', 'routes/people/show/controller']); }); test('does not have split route templates in main entrypoint', function () { - expectFile('./assets/my-app.js').doesNotMatch('routes/people/template'); - expectFile('./assets/my-app.js').doesNotMatch('routes/people/index/template'); - expectFile('./assets/my-app.js').doesNotMatch('routes/people/show/template'); + notInEntrypoint(['routes/people/template', 'routes/people/index/template', 'routes/people/show/template']); }); test('does not have split routes in main entrypoint', function () { - expectFile('./assets/my-app.js').doesNotMatch('routes/people/route'); - expectFile('./assets/my-app.js').doesNotMatch('routes/people/show/route'); + notInEntrypoint(['routes/people/route', 'routes/people/show/route']); }); test('dynamically imports the route entrypoint from the main entrypoint', function () { - expectFile('./assets/my-app.js').matches('import("my-app/assets/_route_/people.js")'); + inEntrypoint('import("@embroider/core/route/people")'); }); test('has split controllers in route entrypoint', function () { - expectFile('./assets/_route_/people.js').matches('routes/people/controller'); - expectFile('./assets/_route_/people.js').matches('routes/people/show/controller'); + inEntrypoint(['routes/people/controller', 'routes/people/show/controller'], '@embroider/core/route/people'); }); test('has split route templates in route entrypoint', function () { - expectFile('./assets/_route_/people.js').matches('routes/people/template'); - expectFile('./assets/_route_/people.js').matches('routes/people/index/template'); - expectFile('./assets/_route_/people.js').matches('routes/people/show/template'); + inEntrypoint( + ['routes/people/template', 'routes/people/index/template', 'routes/people/show/template'], + '@embroider/core/route/people' + ); }); test('has split routes in route entrypoint', function () { - expectFile('./assets/_route_/people.js').matches('routes/people/route'); - expectFile('./assets/_route_/people.js').matches('routes/people/show/route'); + inEntrypoint(['routes/people/route', 'routes/people/show/route'], '@embroider/core/route/people'); }); test('has no components in route entrypoint', function () { - expectFile('./assets/_route_/people.js').doesNotMatch('all-people'); - expectFile('./assets/_route_/people.js').doesNotMatch('welcome'); - expectFile('./assets/_route_/people.js').doesNotMatch('unused'); + notInEntrypoint(['all-people', 'welcome', 'unused'], '@embroider/core/route/people'); }); test('has no helpers in route entrypoint', function () { - expectFile('./assets/_route_/people.js').doesNotMatch('capitalize'); + notInEntrypoint('capitalize', '@embroider/core/route/people'); }); test('has no modifiers in route entrypoint', function () { - expectFile('./assets/_route_/people.js').doesNotMatch('auto-focus'); + notInEntrypoint('auto-focus', '@embroider/core/route/people'); }); Qmodule('audit', function (hooks) { From c655bed8b83c603d63b66ac2c7607718966d8512 Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Thu, 9 May 2024 13:22:29 +0100 Subject: [PATCH 14/29] cleanup prepared assets --- packages/compat/src/compat-app-builder.ts | 69 +---------------------- 1 file changed, 3 insertions(+), 66 deletions(-) diff --git a/packages/compat/src/compat-app-builder.ts b/packages/compat/src/compat-app-builder.ts index 61ba69c27..0e5aaa0ab 100644 --- a/packages/compat/src/compat-app-builder.ts +++ b/packages/compat/src/compat-app-builder.ts @@ -51,7 +51,6 @@ import type { Package, PackageInfo } from '@embroider/core'; import { ensureDirSync, copySync, readdirSync, pathExistsSync } from 'fs-extra'; import type { TransformOptions } from '@babel/core'; import { MacrosConfig } from '@embroider/macros/src/node'; -import SourceMapConcat from 'fast-sourcemap-concat'; import type CompatApp from './compat-app'; import { SyncDir } from './sync-dir'; @@ -588,15 +587,6 @@ export class CompatAppBuilder { return prior.kind === 'in-memory' && stringOrBufferEqual(prior.source, asset.source); case 'built-ember': return prior.kind === 'built-ember' && prior.source === asset.source; - case 'concatenated-asset': - return ( - prior.kind === 'concatenated-asset' && - prior.sources.length === asset.sources.length && - prior.sources.every((priorFile, index) => { - let newFile = asset.sources[index]; - return this.assetIsValid(newFile, priorFile); - }) - ); } } @@ -618,35 +608,7 @@ export class CompatAppBuilder { writeFileSync(destination, asset.source, 'utf8'); } - private async updateConcatenatedAsset(asset: ConcatenatedAsset) { - let concat = new SourceMapConcat({ - outputFile: join(this.root, asset.relativePath), - mapCommentType: asset.relativePath.endsWith('.js') ? 'line' : 'block', - baseDir: this.root, - }); - if (process.env.EMBROIDER_CONCAT_STATS) { - let MeasureConcat = (await import('@embroider/core/src/measure-concat')).default; - concat = new MeasureConcat(asset.relativePath, concat, this.root); - } - for (let source of asset.sources) { - switch (source.kind) { - case 'on-disk': - concat.addFile(explicitRelative(this.root, source.sourcePath)); - break; - case 'in-memory': - if (typeof source.source !== 'string') { - throw new Error(`attempted to concatenated a Buffer-backed in-memory asset`); - } - concat.addSpace(source.source); - break; - default: - assertNever(source); - } - } - await concat.end(); - } - - private async updateAssets(requestedAssets: Asset[], appFiles: AppFiles[]) { + private async updateAssets(requestedAssets: Asset[], appFiles: AppFiles[]): Promise { let assets = this.prepareAssets(requestedAssets, appFiles); for (let asset of assets.values()) { if (this.assetIsValid(asset, this.assets.get(asset.relativePath))) { @@ -663,9 +625,6 @@ export class CompatAppBuilder { case 'built-ember': this.updateBuiltEmberAsset(asset); break; - case 'concatenated-asset': - await this.updateConcatenatedAsset(asset); - break; default: assertNever(asset); } @@ -676,7 +635,6 @@ export class CompatAppBuilder { } } this.assets = assets; - return [...assets.values()]; } private gatherAssets(inputPaths: OutputPaths): Asset[] { @@ -719,7 +677,7 @@ export class CompatAppBuilder { let appFiles = this.updateAppJS(inputPaths.appJS); let assets = this.gatherAssets(inputPaths); - let finalAssets = await this.updateAssets(assets, appFiles); + await this.updateAssets(assets, appFiles); let assetPaths = assets.map(asset => asset.relativePath); @@ -728,15 +686,6 @@ export class CompatAppBuilder { assetPaths.push('package.json'); } - for (let asset of finalAssets) { - // our concatenated assets all have map files that ride along. Here we're - // telling the final stage packager to be sure and serve the map files - // too. - if (asset.kind === 'concatenated-asset') { - assetPaths.push(asset.sourcemapPath); - } - } - let meta: AppMeta = { type: 'app', version: 2, @@ -1089,7 +1038,7 @@ interface TreeNames { configTree: BroccoliNode; } -type InternalAsset = OnDiskAsset | InMemoryAsset | BuiltEmberAsset | ConcatenatedAsset; +type InternalAsset = OnDiskAsset | InMemoryAsset | BuiltEmberAsset; class ParsedEmberAsset { kind: 'parsed-ember' = 'parsed-ember'; @@ -1120,15 +1069,3 @@ class BuiltEmberAsset { this.relativePath = asset.relativePath; } } - -class ConcatenatedAsset { - kind: 'concatenated-asset' = 'concatenated-asset'; - constructor( - public relativePath: string, - public sources: (OnDiskAsset | InMemoryAsset)[], - private resolvableExtensions: RegExp - ) {} - get sourcemapPath() { - return this.relativePath.replace(this.resolvableExtensions, '') + '.map'; - } -} From a894ab08c6b7f63cc724489dabfc6eac12c9870f Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Thu, 9 May 2024 13:26:22 +0100 Subject: [PATCH 15/29] add override for ember-legacy-built-in-components --- package.json | 3 ++- pnpm-lock.yaml | 23 ++++++++++++----------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 5cb29950e..dd8b70d48 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,8 @@ "browserslist": "^4.14.0", "graceful-fs": "^4.0.0", "@types/eslint": "^8.37.0", - "babel-plugin-module-resolver@5.0.1": "5.0.0" + "babel-plugin-module-resolver@5.0.1": "5.0.0", + "@ember/legacy-built-in-components": "^0.5.0" }, "patchedDependencies": { "ember-source@5.8.0": "patches/ember-source@5.8.0.patch" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3af97d762..1c947a4e2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,6 +9,7 @@ overrides: graceful-fs: ^4.0.0 '@types/eslint': ^8.37.0 babel-plugin-module-resolver@5.0.1: 5.0.0 + '@ember/legacy-built-in-components': ^0.5.0 patchedDependencies: ember-source@5.8.0: @@ -1680,8 +1681,8 @@ importers: specifier: ^7.18.6 version: 7.24.5 '@ember/legacy-built-in-components': - specifier: ^0.4.1 - version: 0.4.2(ember-source@3.28.12) + specifier: ^0.5.0 + version: 0.5.0(ember-source@3.28.12) '@ember/string': specifier: ^3.0.0 version: 3.1.1 @@ -1771,7 +1772,7 @@ importers: version: /ember-data@5.3.0(@babel/core@7.24.5)(@ember/string@3.1.1)(ember-source@3.28.12) ember-engines: specifier: ^0.8.23 - version: 0.8.23(@ember/legacy-built-in-components@0.4.2)(ember-source@3.28.12) + version: 0.8.23(@ember/legacy-built-in-components@0.5.0)(ember-source@3.28.12) ember-inline-svg: specifier: ^0.2.1 version: 0.2.1(@babel/core@7.24.5) @@ -4519,11 +4520,11 @@ packages: - supports-color dev: true - /@ember/legacy-built-in-components@0.4.2(ember-source@3.28.12): - resolution: {integrity: sha512-rJulbyVQIVe1zEDQDqAQHechHy44DsS2qxO24+NmU/AYxwPFSzWC/OZNCDFSfLU+Y5BVd/00qjxF0pu7Nk+TNA==} - engines: {node: 12.* || 14.* || >= 16} + /@ember/legacy-built-in-components@0.5.0(ember-source@3.28.12): + resolution: {integrity: sha512-hbUCt5rii6CT1L4mheH+aqCDeF1dzp/UjS2g7KFIKYGd9zMqyKU4OEnQGk2/O5tATXkEGPf4Zpj671BddBOrbQ==} + engines: {node: '>= 16'} peerDependencies: - ember-source: '*' + ember-source: '>= 4.8' dependencies: '@embroider/macros': 1.16.1(@glint/template@1.4.0) ember-cli-babel: 7.26.11 @@ -13828,14 +13829,14 @@ packages: - supports-color dev: true - /ember-engines@0.8.23(@ember/legacy-built-in-components@0.4.2)(ember-source@3.28.12): + /ember-engines@0.8.23(@ember/legacy-built-in-components@0.5.0)(ember-source@3.28.12): resolution: {integrity: sha512-rrvHUkZRNrf+9u/sCw7XYrITStjP/9Ypykk1nYQHoo+6Krp11e81QNVsGTXFpXtMHXbNtH5IcRyZvfSXqUOrUQ==} engines: {node: 10.* || >= 12} peerDependencies: - '@ember/legacy-built-in-components': '*' + '@ember/legacy-built-in-components': ^0.5.0 ember-source: ^3.12 || 4 dependencies: - '@ember/legacy-built-in-components': 0.4.2(ember-source@3.28.12) + '@ember/legacy-built-in-components': 0.5.0(ember-source@3.28.12) '@embroider/macros': 1.16.1(@glint/template@1.4.0) amd-name-resolver: 1.3.1 babel-plugin-compact-reexports: 1.1.0 @@ -13864,7 +13865,7 @@ packages: resolution: {integrity: sha512-rrvHUkZRNrf+9u/sCw7XYrITStjP/9Ypykk1nYQHoo+6Krp11e81QNVsGTXFpXtMHXbNtH5IcRyZvfSXqUOrUQ==} engines: {node: 10.* || >= 12} peerDependencies: - '@ember/legacy-built-in-components': '*' + '@ember/legacy-built-in-components': ^0.5.0 ember-source: ^3.12 || 4 dependencies: '@embroider/macros': 1.16.1(@glint/template@1.4.0) From ba00e2b2f2203cc62be64bac0d216dc349fb9025 Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Thu, 9 May 2024 16:15:04 +0100 Subject: [PATCH 16/29] allow entrypoints to be referenced from outside --- packages/compat/src/compat-app-builder.ts | 2 +- packages/core/src/module-resolver.ts | 23 ++++++++++++++++++++--- packages/core/src/virtual-entrypoint.ts | 6 +++--- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/packages/compat/src/compat-app-builder.ts b/packages/compat/src/compat-app-builder.ts index 0e5aaa0ab..bd208337d 100644 --- a/packages/compat/src/compat-app-builder.ts +++ b/packages/compat/src/compat-app-builder.ts @@ -897,7 +897,7 @@ let d = w.define; import "ember-testing"; -import "#embroider/core/entrypoint"; +import "@embroider/core/entrypoint"; {{#each amdModules as |amdModule| ~}} d("{{js-string-escape amdModule.runtime}}", function(){ return i("{{js-string-escape amdModule.buildtime}}");}); diff --git a/packages/core/src/module-resolver.ts b/packages/core/src/module-resolver.ts index b33fb3a4f..6fa8fd9e6 100644 --- a/packages/core/src/module-resolver.ts +++ b/packages/core/src/module-resolver.ts @@ -448,16 +448,33 @@ export class Resolver { //TODO move the extra forwardslash handling out into the vite plugin const candidates = ['@embroider/core/entrypoint', '/@embroider/core/entrypoint', './@embroider/core/entrypoint']; - if (!candidates.includes(request.specifier)) { + if (!candidates.some(c => request.specifier.startsWith(c + '/') || request.specifier === c)) { return request; } - let pkg = this.packageCache.ownerOfFile(request.fromFile); + const result = /\.?\/?@embroider\/core\/entrypoint(?:\/(?.*))?/.exec(request.specifier); - if (!pkg?.isV2Ember()) { + if (!result) { + // TODO make a better error + throw new Error('entrypoint does not match pattern' + request.specifier); + } + + const { packageName } = result.groups!; + + const requestingPkg = this.packageCache.ownerOfFile(request.fromFile); + + if (!requestingPkg?.isV2Ember()) { throw new Error(`bug: found entrypoint import in non-ember package at ${request.fromFile}`); } + let pkg; + + if (packageName) { + pkg = this.packageCache.resolve(packageName, requestingPkg); + } else { + pkg = requestingPkg; + } + return logTransition('entrypoint', request, request.virtualize(resolve(pkg.root, '-embroider-entrypoint.js'))); } diff --git a/packages/core/src/virtual-entrypoint.ts b/packages/core/src/virtual-entrypoint.ts index 48bc2a3e0..1e560e945 100644 --- a/packages/core/src/virtual-entrypoint.ts +++ b/packages/core/src/virtual-entrypoint.ts @@ -85,10 +85,10 @@ export function renderEntrypoint( let lazyEngines: { names: string[]; path: string }[] = []; if (isApp) { - // deliberately ignoring the app + // deliberately ignoring the app (which is the first entry in the engines array) let [, ...childEngines] = resolver.options.engines; for (let childEngine of childEngines) { - let target = `${childEngine.packageName}/-embroider-entrypoint.js`; + let target = `@embroider/core/entrypoint/${childEngine.packageName}`; if (childEngine.isLazy) { lazyEngines.push({ @@ -276,7 +276,7 @@ export function importPaths(resolver: Resolver, { engine }: AppFiles, engineRela let noHBS = engineRelativePath.replace(resolvableExtensionsPattern, '').replace(/\.hbs$/, ''); return { runtime: `${engine.modulePrefix}/${noHBS}`, - buildtime: `@embroider-dep/${posix.join(engine.package.name, engineRelativePath)}`, + buildtime: `./${engineRelativePath}`, }; } From 4426bf34a7eef0f903416772aec6303b016bb273 Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Fri, 10 May 2024 16:01:47 +0100 Subject: [PATCH 17/29] removing the need for @embroider-dep test cleanup --- packages/core/src/module-resolver.ts | 8 ----- tests/scenarios/compat-stage2-test.ts | 30 +++++++------------ .../compat-template-colocation-test.ts | 22 ++++---------- 3 files changed, 17 insertions(+), 43 deletions(-) diff --git a/packages/core/src/module-resolver.ts b/packages/core/src/module-resolver.ts index 6fa8fd9e6..c99f91e2d 100644 --- a/packages/core/src/module-resolver.ts +++ b/packages/core/src/module-resolver.ts @@ -192,14 +192,6 @@ export class Resolver { return this.external('early require', request, request.specifier); } - if (request.specifier.startsWith('@embroider-dep')) { - return logTransition( - 'Embroider direct dependency lookup', - request, - request.alias(request.specifier.replace(/^@embroider-dep\//, '')) - ); - } - request = this.handleFastbootSwitch(request); request = await this.handleGlobalsCompat(request); request = this.handleImplicitModules(request); diff --git a/tests/scenarios/compat-stage2-test.ts b/tests/scenarios/compat-stage2-test.ts index 635b48f57..30fc37b8b 100644 --- a/tests/scenarios/compat-stage2-test.ts +++ b/tests/scenarios/compat-stage2-test.ts @@ -133,7 +133,7 @@ stage2Scenarios .module('./node_modules/.embroider/rewritten-app/index.html') .resolves('/@embroider/core/entrypoint') .toModule() - .resolves('@embroider-dep/my-app/service/in-repo.js') + .resolves('./service/in-repo.js') .to('./node_modules/dep-b/lib/in-repo-c/_app_/service/in-repo.js'); }); @@ -143,7 +143,7 @@ stage2Scenarios .module('./node_modules/.embroider/rewritten-app/index.html') .resolves('/@embroider/core/entrypoint') .toModule() - .resolves('@embroider-dep/my-app/services/secondary.js') + .resolves('./services/secondary.js') .to('./lib/secondary-in-repo-addon/_app_/services/secondary.js'); // secondary is resolvable from primary @@ -209,15 +209,9 @@ stage2Scenarios .module('./node_modules/.embroider/rewritten-app/index.html') .resolves('/@embroider/core/entrypoint') .toModule(); - expectModule - .resolves('@embroider-dep/my-app/service/in-repo.js') - .to('./lib/in-repo-b/_app_/service/in-repo.js'); - expectModule - .resolves('@embroider-dep/my-app/service/addon.js') - .to('./node_modules/dep-b/_app_/service/addon.js'); - expectModule - .resolves('@embroider-dep/my-app/service/dev-addon.js') - .to('./node_modules/dev-c/_app_/service/dev-addon.js'); + expectModule.resolves('./service/in-repo.js').to('./lib/in-repo-b/_app_/service/in-repo.js'); + expectModule.resolves('./service/addon.js').to('./node_modules/dep-b/_app_/service/addon.js'); + expectModule.resolves('./service/dev-addon.js').to('./node_modules/dev-c/_app_/service/dev-addon.js'); }); test('addons declared as dependencies should win over devDependencies', function () { @@ -225,7 +219,7 @@ stage2Scenarios .module('./node_modules/.embroider/rewritten-app/index.html') .resolves('/@embroider/core/entrypoint') .toModule() - .resolves('@embroider-dep/my-app/service/dep-wins-over-dev.js') + .resolves('./service/dep-wins-over-dev.js') .to('./node_modules/dep-b/_app_/service/dep-wins-over-dev.js'); }); @@ -234,7 +228,7 @@ stage2Scenarios .module('./node_modules/.embroider/rewritten-app/index.html') .resolves('/@embroider/core/entrypoint') .toModule() - .resolves('@embroider-dep/my-app/service/in-repo-over-deps.js') + .resolves('./service/in-repo-over-deps.js') .to('./lib/in-repo-a/_app_/service/in-repo-over-deps.js'); }); @@ -243,7 +237,7 @@ stage2Scenarios .module('./node_modules/.embroider/rewritten-app/index.html') .resolves('/@embroider/core/entrypoint') .toModule() - .resolves('@embroider-dep/my-app/service/test-before.js') + .resolves('./service/test-before.js') .to('./node_modules/dev-d/_app_/service/test-before.js'); }); @@ -252,7 +246,7 @@ stage2Scenarios .module('./node_modules/.embroider/rewritten-app/index.html') .resolves('/@embroider/core/entrypoint') .toModule() - .resolves('@embroider-dep/my-app/service/test-after.js') + .resolves('./service/test-after.js') .to('./node_modules/dev-b/_app_/service/test-after.js'); }); }); @@ -682,9 +676,7 @@ stage2Scenarios .resolves('/@embroider/core/entrypoint') .toModule() .withContents(contents => { - const result = /import \* as (\w+) from "@embroider-dep\/my-app\/non-static-dir\/another-library.js";/.exec( - contents - ); + const result = /import \* as (\w+) from "\.\/non-static-dir\/another-library.js";/.exec(contents); if (!result) { throw new Error('Could not find import for non-static-dir/another-library'); @@ -728,7 +720,7 @@ stage2Scenarios .resolves('/@embroider/core/entrypoint') .toModule() .withContents(content => { - return content.includes('my-app/static-dir-not-really/something.js'); + return content.includes('./static-dir-not-really/something.js'); }); }); diff --git a/tests/scenarios/compat-template-colocation-test.ts b/tests/scenarios/compat-template-colocation-test.ts index 25ef9295a..bafc4aed0 100644 --- a/tests/scenarios/compat-template-colocation-test.ts +++ b/tests/scenarios/compat-template-colocation-test.ts @@ -135,8 +135,7 @@ scenarios .resolves('/@embroider/core/entrypoint') .toModule() .withContents(contents => { - const result = - /import \* as (\w+) from "@embroider-dep\/my-app\/components\/has-colocated-template.js";/.exec(contents); + const result = /import \* as (\w+) from "\.\/components\/has-colocated-template.js";/.exec(contents); if (!result) { throw new Error('Missing import of has-colocated-template'); @@ -212,13 +211,9 @@ scenarios .resolves('/@embroider/core/entrypoint') .toModule() .withContents(content => { - assert.notOk( - /import \* as (\w+) from "@embroider-dep\/my-app\/components\/has-colocated-component.js"/.test(content) - ); + assert.notOk(/import \* as (\w+) from "\.\/components\/has-colocated-component.js"/.test(content)); - assert.notOk( - /import \* as (\w+) from "@embroider-dep\/my-app\/components\/template-only-component.js"/.test(content) - ); + assert.notOk(/import \* as (\w+) from "\.\/components\/template-only-component.js"/.test(content)); return true; }); @@ -309,8 +304,7 @@ appScenarios .resolves('/@embroider/core/entrypoint') .toModule() .withContents(content => { - let result = - /import \* as (\w+) from "@embroider-dep\/my-app\/components\/pod-component\/component.js"/.exec(content); + let result = /import \* as (\w+) from "\.\/components\/pod-component\/component.js"/.exec(content); if (!result) { throw new Error('Could not find pod component'); @@ -325,9 +319,7 @@ appScenarios });` ); - result = /import \* as (\w+) from "@embroider-dep\/my-app\/components\/pod-component\/template.hbs"/.exec( - content - ); + result = /import \* as (\w+) from "\.\/components\/pod-component\/template.hbs"/.exec(content); if (!result) { throw new Error('Could not find pod component template'); @@ -342,9 +334,7 @@ appScenarios });` ); - result = /import \* as (\w+) from "@embroider-dep\/my-app\/components\/template-only\/template.hbs"/.exec( - content - ); + result = /import \* as (\w+) from "\.\/components\/template-only\/template.hbs"/.exec(content); if (!result) { throw new Error('Could not find template only component'); From dcfbb61ca460447997d66dd79164a139f298241c Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Mon, 13 May 2024 14:51:21 +0100 Subject: [PATCH 18/29] add better commets to tests --- .../engines-host-app/tests/acceptance/basics-test.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/fixtures/engines-host-app/tests/acceptance/basics-test.js b/tests/fixtures/engines-host-app/tests/acceptance/basics-test.js index 2c6177cbf..13f835ea1 100644 --- a/tests/fixtures/engines-host-app/tests/acceptance/basics-test.js +++ b/tests/fixtures/engines-host-app/tests/acceptance/basics-test.js @@ -56,15 +56,14 @@ function createLazyEngineTest(type) { ); } - // TODO: uncomment once we fix this appearing too eagerly - //assert.notOk(!!window.require.entries['lazy-engine/helpers/duplicated-helper']); + assert.notOk(!!window.require.entries['lazy-engine/helpers/duplicated-helper']); await visit('/use-lazy-engine'); let entriesAfter = Object.entries(window.require.entries).length; if (type === 'safe') { - assert.ok(!!window.require.entries['lazy-engine/helpers/duplicated-helper']); + assert.ok(!!window.require.entries['lazy-engine/helpers/duplicated-helper'], 'in safe mode we expect to see lazy-engine/helpers/duplicated-helper but its not there'); } else { - assert.notOk(!!window.require.entries['lazy-engine/helpers/duplicated-helper']); + assert.notOk(!!window.require.entries['lazy-engine/helpers/duplicated-helper'], 'in optimized mode we expect to *not* see lazy-engine/helpers/duplicated-helper but it is there'); } assert.ok(entriesAfter > entriesBefore); assert.equal(currentURL(), '/use-lazy-engine'); From e387533b1ddc096d96bd2ae7ea4ae452629abc8b Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Tue, 14 May 2024 13:16:29 +0100 Subject: [PATCH 19/29] fix pnpm run test --- tests/scenarios/engines-test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/scenarios/engines-test.ts b/tests/scenarios/engines-test.ts index 95f01612d..1c6026294 100644 --- a/tests/scenarios/engines-test.ts +++ b/tests/scenarios/engines-test.ts @@ -102,7 +102,7 @@ engineScenarios }); test(`pnpm test safe`, async function (assert) { - let result = await app.execute('pnpm test --filter=!@optimized', { + let result = await app.execute("pnpm run test --filter='!@optimized'", { env: { EMBROIDER_TEST_SETUP_OPTIONS: 'safe', EMBROIDER_TEST_SETUP_FORCE: 'embroider', From e356b883b398664c264d7b4d71cc30aa7f95c852 Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Tue, 14 May 2024 13:30:43 +0100 Subject: [PATCH 20/29] add autoRun to the resolver config --- packages/compat/src/compat-app-builder.ts | 1 + packages/compat/tests/audit.test.ts | 1 + packages/core/src/module-resolver.ts | 1 + packages/core/src/virtual-entrypoint.ts | 2 +- tests/scenarios/compat-resolver-test.ts | 1 + tests/scenarios/core-resolver-test.ts | 1 + 6 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/compat/src/compat-app-builder.ts b/packages/compat/src/compat-app-builder.ts index bd208337d..bec7e8696 100644 --- a/packages/compat/src/compat-app-builder.ts +++ b/packages/compat/src/compat-app-builder.ts @@ -278,6 +278,7 @@ export class CompatAppBuilder { podModulePrefix: this.podModulePrefix(), activePackageRules: this.activeRules(), options, + autoRun: this.compatApp.autoRun, }; return config; diff --git a/packages/compat/tests/audit.test.ts b/packages/compat/tests/audit.test.ts index e96d1ebb4..d153d7e52 100644 --- a/packages/compat/tests/audit.test.ts +++ b/packages/compat/tests/audit.test.ts @@ -51,6 +51,7 @@ describe('audit', function () { }, ], resolvableExtensions, + autoRun: true, }; let babel: TransformOptions = { diff --git a/packages/core/src/module-resolver.ts b/packages/core/src/module-resolver.ts index c99f91e2d..e34415144 100644 --- a/packages/core/src/module-resolver.ts +++ b/packages/core/src/module-resolver.ts @@ -95,6 +95,7 @@ export interface Options { splitAtRoutes?: (RegExp | string)[]; podModulePrefix?: string; amdCompatibility: Required; + autoRun: boolean; } // TODO: once we can remove the stage2 entrypoint this type can get streamlined diff --git a/packages/core/src/virtual-entrypoint.ts b/packages/core/src/virtual-entrypoint.ts index 1e560e945..1400a1569 100644 --- a/packages/core/src/virtual-entrypoint.ts +++ b/packages/core/src/virtual-entrypoint.ts @@ -148,7 +148,7 @@ export function renderEntrypoint( // appConfig: this.configTree.readConfig().APP, // }); Object.assign(params, { - autoRun: true, + autoRun: resolver.options.autoRun, appBoot: '', mainModule: './app', appConfig: {}, diff --git a/tests/scenarios/compat-resolver-test.ts b/tests/scenarios/compat-resolver-test.ts index 1141e72b7..f4babf45b 100644 --- a/tests/scenarios/compat-resolver-test.ts +++ b/tests/scenarios/compat-resolver-test.ts @@ -100,6 +100,7 @@ Scenarios.fromProject(() => new Project()) ...extraOpts?.appPackageRules, }, ], + autoRun: true, }; givenFiles({ diff --git a/tests/scenarios/core-resolver-test.ts b/tests/scenarios/core-resolver-test.ts index 96b25a5aa..e6a8664be 100644 --- a/tests/scenarios/core-resolver-test.ts +++ b/tests/scenarios/core-resolver-test.ts @@ -132,6 +132,7 @@ Scenarios.fromProject(() => new Project()) roots: [app.dir], }, ], + autoRun: true, }; givenFiles({ From 85deb6794375a15f51b722418edb2a1a91176b47 Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Tue, 14 May 2024 16:34:30 +0100 Subject: [PATCH 21/29] skip fastboot and engines tests for now --- tests/scenarios/engines-test.ts | 2 ++ tests/scenarios/fastboot-app-test.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/tests/scenarios/engines-test.ts b/tests/scenarios/engines-test.ts index 1c6026294..7d94e373c 100644 --- a/tests/scenarios/engines-test.ts +++ b/tests/scenarios/engines-test.ts @@ -84,6 +84,7 @@ engineScenarios .skip('lts_3_28-engines') // this skip should be removed before https://github.com/embroider-build/embroider/pull/1435 is merged .skip('lts_4_4-engines') // this skip should be removed before https://github.com/embroider-build/embroider/pull/1435 is merged .skip('release-engines') // this skip should be removed before https://github.com/embroider-build/embroider/pull/1435 is merged + .skip('lts_5_8-engines') // this shouldn't be run .skip('canary-engines') // this shouldn't be run .map('without-fastboot', () => {}) .forEachScenario(scenario => { @@ -141,6 +142,7 @@ engineScenarios .skip('lts_3_28-engines') // fails due to https://github.com/emberjs/ember.js/pull/20461 .skip('lts_4_4-engines') // fails due to https://github.com/emberjs/ember.js/pull/20461 .skip('release-engines') // fails due to https://github.com/emberjs/ember.js/pull/20461 + .skip('lts_5_8-engines') .skip('canary-engines') // this shouldn't be run .map('with-fastboot', app => { app.linkDependency('ember-cli-fastboot', { baseDir: __dirname }); diff --git a/tests/scenarios/fastboot-app-test.ts b/tests/scenarios/fastboot-app-test.ts index 28d168c20..0cd0c444f 100644 --- a/tests/scenarios/fastboot-app-test.ts +++ b/tests/scenarios/fastboot-app-test.ts @@ -83,6 +83,7 @@ appScenarios merge(project.files, loadFromFixtureData('fastboot-app')); }) // TODO remove once https://github.com/ember-fastboot/ember-cli-fastboot/issues/925 is fixed + .skip('lts_5_8-fastboot-app-test') .skip('canary-fastboot-app-test') .forEachScenario(scenario => { Qmodule(scenario.name, function (hooks) { From c50d868a37630642080183ce95350b92e04d9adc Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Wed, 15 May 2024 17:52:13 +0100 Subject: [PATCH 22/29] fix type error --- packages/core/src/virtual-entrypoint.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/virtual-entrypoint.ts b/packages/core/src/virtual-entrypoint.ts index 1400a1569..f7b33a068 100644 --- a/packages/core/src/virtual-entrypoint.ts +++ b/packages/core/src/virtual-entrypoint.ts @@ -3,7 +3,7 @@ import { compile } from './js-handlebars'; import type { Resolver } from './module-resolver'; import type { CompatResolverOptions } from '../../compat/src/resolver-transform'; import { flatten, partition } from 'lodash'; -import { posix, join } from 'path'; +import { join } from 'path'; import { extensionsPattern } from '@embroider/shared-internals'; import walkSync from 'walk-sync'; import type { V2AddonPackage } from '@embroider/shared-internals/src/package'; From 1409b8bdeb11509f95db0a820831de653151ba8d Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Thu, 16 May 2024 10:07:02 +0100 Subject: [PATCH 23/29] save app-boot config for stage 3 to load --- packages/compat/src/compat-app-builder.ts | 5 +++++ packages/core/src/virtual-entrypoint.ts | 13 ++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/packages/compat/src/compat-app-builder.ts b/packages/compat/src/compat-app-builder.ts index bec7e8696..de6285b29 100644 --- a/packages/compat/src/compat-app-builder.ts +++ b/packages/compat/src/compat-app-builder.ts @@ -710,6 +710,7 @@ export class CompatAppBuilder { this.addResolverConfig(resolverConfig); this.addContentForConfig(this.contentForTree.readContents()); this.addEmberEnvConfig(this.configTree.readConfig().EmberENV); + this.addAppBoot(this.compatApp.appBoot.readAppBoot()); let babelConfig = await this.babelConfig(resolverConfig); this.addBabelConfig(babelConfig); writeFileSync( @@ -819,6 +820,10 @@ export class CompatAppBuilder { }); } + private addAppBoot(appBoot?: string) { + writeFileSync(join(locateEmbroiderWorkingDir(this.compatApp.root), 'ember-app-boot.js'), appBoot ?? ''); + } + private importPaths({ engine }: AppFiles, engineRelativePath: string) { let noHBS = engineRelativePath.replace(this.resolvableExtensionsPattern, '').replace(/\.hbs$/, ''); return { diff --git a/packages/core/src/virtual-entrypoint.ts b/packages/core/src/virtual-entrypoint.ts index f7b33a068..bae43b632 100644 --- a/packages/core/src/virtual-entrypoint.ts +++ b/packages/core/src/virtual-entrypoint.ts @@ -4,10 +4,11 @@ import type { Resolver } from './module-resolver'; import type { CompatResolverOptions } from '../../compat/src/resolver-transform'; import { flatten, partition } from 'lodash'; import { join } from 'path'; -import { extensionsPattern } from '@embroider/shared-internals'; +import { extensionsPattern, locateEmbroiderWorkingDir } from '@embroider/shared-internals'; import walkSync from 'walk-sync'; import type { V2AddonPackage } from '@embroider/shared-internals/src/package'; import { encodePublicRouteEntrypoint } from './virtual-route-entrypoint'; +import { readFileSync } from 'fs-extra'; const entrypointPattern = /(?.*)[\\/]-embroider-entrypoint.js/; @@ -142,14 +143,16 @@ export function renderEntrypoint( if (isApp) { // TODO figure out how to actually translate these // Object.assign(params, { - // autoRun: this.compatApp.autoRun, - // appBoot: !this.compatApp.autoRun ? this.compatApp.appBoot.readAppBoot() : '', - // mainModule: explicitRelative(dirname(relativePath), 'app'), // appConfig: this.configTree.readConfig().APP, // }); + + const appBoot = readFileSync(join(locateEmbroiderWorkingDir(resolver.options.appRoot), 'ember-app-boot.js'), { + encoding: 'utf-8', + }); + Object.assign(params, { autoRun: resolver.options.autoRun, - appBoot: '', + appBoot, mainModule: './app', appConfig: {}, }); From 2ee7c04245a00af93e3551848e0b5f04196b2dce Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Thu, 16 May 2024 10:07:19 +0100 Subject: [PATCH 24/29] fix owner-of-file for embroider_virtual: --- packages/shared-internals/src/package-cache.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/shared-internals/src/package-cache.ts b/packages/shared-internals/src/package-cache.ts index 7337bfe8d..9ec2edff8 100644 --- a/packages/shared-internals/src/package-cache.ts +++ b/packages/shared-internals/src/package-cache.ts @@ -66,7 +66,14 @@ export default class PackageCache { return p; } - ownerOfFile(filename: string): Package | undefined { + ownerOfFile(filenameInput: string): Package | undefined { + let filename = filenameInput; + const virtualPrefix = 'embroider_virtual:'; + + if (filename.includes(virtualPrefix)) { + filename = filename.replace(/^.*embroider_virtual:/, ''); + } + let segments = filename.replace(/\\/g, '/').split(posix.sep); // first we look through our cached packages for any that are rooted right From ca821cc90d40a4842719e18132014a181978d7ca Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Thu, 16 May 2024 10:25:28 +0100 Subject: [PATCH 25/29] skip watch mode tests for now --- tests/scenarios/watch-mode-test.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/scenarios/watch-mode-test.ts b/tests/scenarios/watch-mode-test.ts index 4386226e5..e2b62922b 100644 --- a/tests/scenarios/watch-mode-test.ts +++ b/tests/scenarios/watch-mode-test.ts @@ -9,12 +9,15 @@ import CommandWatcher, { DEFAULT_TIMEOUT } from './helpers/command-watcher'; const { module: Qmodule, test } = QUnit; -let app = appScenarios.skip('canary').map('watch-mode', () => { - /** - * We will create files as a part of the watch-mode tests, - * because creating files should cause appropriate watch/update behavior - */ -}); +let app = appScenarios + .skip('canary') + .skip('lts_5_8') + .map('watch-mode', () => { + /** + * We will create files as a part of the watch-mode tests, + * because creating files should cause appropriate watch/update behavior + */ + }); class File { constructor(readonly label: string, readonly fullPath: string) {} From 5f2fc6b8f43e5ac0fe1f638f3539741290dd5c6e Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Thu, 16 May 2024 10:30:57 +0100 Subject: [PATCH 26/29] fix route entrypoint filename on windows --- packages/core/src/virtual-route-entrypoint.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/virtual-route-entrypoint.ts b/packages/core/src/virtual-route-entrypoint.ts index fcdaf0bed..c099d8f5d 100644 --- a/packages/core/src/virtual-route-entrypoint.ts +++ b/packages/core/src/virtual-route-entrypoint.ts @@ -1,7 +1,7 @@ import type { V2AddonPackage } from '@embroider/shared-internals/src/package'; import { AppFiles } from './app-files'; import type { Resolver } from './module-resolver'; -import { resolve } from 'path/posix'; +import { resolve } from 'path'; import { compile } from './js-handlebars'; import { extensionsPattern } from '@embroider/shared-internals'; import { partition } from 'lodash'; From 9b29b552992cc01ad504e549d1e8cbcc52e2d547 Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Thu, 16 May 2024 12:26:16 +0100 Subject: [PATCH 27/29] get appConfig from runtime environment module --- packages/core/src/virtual-entrypoint.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/packages/core/src/virtual-entrypoint.ts b/packages/core/src/virtual-entrypoint.ts index bae43b632..9b1d8f202 100644 --- a/packages/core/src/virtual-entrypoint.ts +++ b/packages/core/src/virtual-entrypoint.ts @@ -141,11 +141,6 @@ export function renderEntrypoint( // for the top-level entry template we need to pass extra params to the template // this is new, it used to be passed into the appJS function instead if (isApp) { - // TODO figure out how to actually translate these - // Object.assign(params, { - // appConfig: this.configTree.readConfig().APP, - // }); - const appBoot = readFileSync(join(locateEmbroiderWorkingDir(resolver.options.appRoot), 'ember-app-boot.js'), { encoding: 'utf-8', }); @@ -154,7 +149,6 @@ export function renderEntrypoint( autoRun: resolver.options.autoRun, appBoot, mainModule: './app', - appConfig: {}, }); } @@ -169,6 +163,8 @@ import { importSync as i, macroCondition, getGlobalConfig } from '@embroider/mac let w = window; let d = w.define; +import environment from './config/environment'; + {{#if styles}} if (macroCondition(!getGlobalConfig().fastboot?.isRunning)) { {{#each styles as |stylePath| ~}} @@ -240,7 +236,7 @@ w._embroiderEngineBundles_ = [ {{#if autoRun ~}} if (!runningTests) { - i("{{js-string-escape mainModule}}").default.create({{json-stringify appConfig}}); + i("{{js-string-escape mainModule}}").default.create(environment.APP); } {{else if appBoot ~}} {{ appBoot }} @@ -263,7 +259,6 @@ if (!runningTests) { autoRun?: boolean; appBoot?: string; mainModule?: string; - appConfig?: unknown; testSuffix?: boolean; lazyRoutes?: { names: string[]; path: string }[]; lazyEngines?: { names: string[]; path: string }[]; From a76f939545a5c6e8b714ded6dff4d67479b55b1a Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Thu, 16 May 2024 13:45:30 +0100 Subject: [PATCH 28/29] implement staticAppPaths for virtual entrypoint --- packages/compat/src/compat-app-builder.ts | 12 ++++++++++++ packages/compat/tests/audit.test.ts | 1 + packages/core/package.json | 5 +++-- packages/core/src/app-files.ts | 9 ++++++++- packages/core/src/module-resolver.ts | 1 + packages/core/src/virtual-entrypoint.ts | 8 ++++++++ packages/core/src/virtual-route-entrypoint.ts | 3 ++- pnpm-lock.yaml | 6 +++--- tests/scenarios/compat-resolver-test.ts | 1 + tests/scenarios/compat-stage2-test.ts | 4 ++-- tests/scenarios/core-resolver-test.ts | 1 + 11 files changed, 42 insertions(+), 9 deletions(-) diff --git a/packages/compat/src/compat-app-builder.ts b/packages/compat/src/compat-app-builder.ts index de6285b29..32de08e87 100644 --- a/packages/compat/src/compat-app-builder.ts +++ b/packages/compat/src/compat-app-builder.ts @@ -51,6 +51,7 @@ import type { Package, PackageInfo } from '@embroider/core'; import { ensureDirSync, copySync, readdirSync, pathExistsSync } from 'fs-extra'; import type { TransformOptions } from '@babel/core'; import { MacrosConfig } from '@embroider/macros/src/node'; +import escapeRegExp from 'escape-string-regexp'; import type CompatApp from './compat-app'; import { SyncDir } from './sync-dir'; @@ -279,6 +280,7 @@ export class CompatAppBuilder { activePackageRules: this.activeRules(), options, autoRun: this.compatApp.autoRun, + staticAppPaths: this.options.staticAppPaths, }; return config; @@ -546,11 +548,21 @@ export class CompatAppBuilder { appSync.files, fastbootSync?.files ?? new Set(), this.resolvableExtensionsPattern, + this.staticAppPathsPattern, this.podModulePrefix() ) ); } + @Memoize() + private get staticAppPathsPattern(): RegExp | undefined { + if (this.options.staticAppPaths.length > 0) { + return new RegExp( + '^(?:' + this.options.staticAppPaths.map(staticAppPath => escapeRegExp(staticAppPath)).join('|') + ')(?:$|/)' + ); + } + } + private prepareAsset(asset: Asset, appFiles: AppFiles[], prepared: Map) { if (asset.kind === 'ember') { let prior = this.assets.get(asset.relativePath); diff --git a/packages/compat/tests/audit.test.ts b/packages/compat/tests/audit.test.ts index d153d7e52..71fa88239 100644 --- a/packages/compat/tests/audit.test.ts +++ b/packages/compat/tests/audit.test.ts @@ -52,6 +52,7 @@ describe('audit', function () { ], resolvableExtensions, autoRun: true, + staticAppPaths: [], }; let babel: TransformOptions = { diff --git a/packages/core/package.json b/packages/core/package.json index b7d23b375..3cef5649c 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -24,6 +24,7 @@ "@babel/parser": "^7.14.5", "@babel/traverse": "^7.14.5", "@embroider/macros": "workspace:*", + "@embroider/reverse-exports": "workspace:*", "@embroider/shared-internals": "workspace:*", "assert-never": "^1.2.1", "babel-plugin-ember-template-compilation": "^2.1.1", @@ -32,6 +33,7 @@ "broccoli-plugin": "^4.0.7", "broccoli-source": "^3.0.1", "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", "fast-sourcemap-concat": "^1.4.0", "filesize": "^10.0.7", "fs-extra": "^9.1.0", @@ -43,7 +45,6 @@ "resolve": "^1.20.0", "resolve-package-path": "^4.0.1", "resolve.exports": "^2.0.2", - "@embroider/reverse-exports": "workspace:*", "typescript-memoize": "^1.0.1", "walk-sync": "^3.0.0" }, @@ -56,8 +57,8 @@ "@types/babel__traverse": "^7.18.5", "@types/debug": "^4.1.5", "@types/fs-extra": "^9.0.12", - "@types/jsdom": "^16.2.11", "@types/js-string-escape": "^1.0.0", + "@types/jsdom": "^16.2.11", "@types/lodash": "^4.14.170", "@types/node": "^15.12.2", "@types/resolve": "^1.20.0", diff --git a/packages/core/src/app-files.ts b/packages/core/src/app-files.ts index d3ec4fa6b..26322af6d 100644 --- a/packages/core/src/app-files.ts +++ b/packages/core/src/app-files.ts @@ -23,6 +23,7 @@ export class AppFiles { appFiles: Set, fastbootFiles: Set, resolvableExtensions: RegExp, + staticAppPathsPattern: RegExp | undefined, podModulePrefix?: string ) { let tests: string[] = []; @@ -114,7 +115,13 @@ export class AppFiles { continue; } - otherAppFiles.push(relativePath); + if (staticAppPathsPattern) { + if (!staticAppPathsPattern.test(relativePath)) { + otherAppFiles.push(relativePath); + } + } else { + otherAppFiles.push(relativePath); + } } this.tests = tests; this.components = components; diff --git a/packages/core/src/module-resolver.ts b/packages/core/src/module-resolver.ts index e34415144..ab34268c2 100644 --- a/packages/core/src/module-resolver.ts +++ b/packages/core/src/module-resolver.ts @@ -96,6 +96,7 @@ export interface Options { podModulePrefix?: string; amdCompatibility: Required; autoRun: boolean; + staticAppPaths: string[]; } // TODO: once we can remove the stage2 entrypoint this type can get streamlined diff --git a/packages/core/src/virtual-entrypoint.ts b/packages/core/src/virtual-entrypoint.ts index 9b1d8f202..7f1b0bed4 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 { readFileSync } from 'fs-extra'; +import escapeRegExp from 'escape-string-regexp'; const entrypointPattern = /(?.*)[\\/]-embroider-entrypoint.js/; @@ -25,6 +26,12 @@ export function decodeEntrypoint(filename: string): { fromFile: string } | undef } } +export function staticAppPathsPattern(staticAppPaths: string[] | undefined): RegExp | undefined { + if (staticAppPaths && staticAppPaths.length > 0) { + return new RegExp('^(?:' + staticAppPaths.map(staticAppPath => escapeRegExp(staticAppPath)).join('|') + ')(?:$|/)'); + } +} + export function renderEntrypoint( resolver: Resolver, { fromFile }: { fromFile: string } @@ -58,6 +65,7 @@ export function renderEntrypoint( getAppFiles(owner.root), hasFastboot ? getFastbootFiles(owner.root) : new Set(), extensionsPattern(resolver.options.resolvableExtensions), + staticAppPathsPattern(resolver.options.staticAppPaths), resolver.options.podModulePrefix ); diff --git a/packages/core/src/virtual-route-entrypoint.ts b/packages/core/src/virtual-route-entrypoint.ts index c099d8f5d..82f589fe6 100644 --- a/packages/core/src/virtual-route-entrypoint.ts +++ b/packages/core/src/virtual-route-entrypoint.ts @@ -5,7 +5,7 @@ import { resolve } from 'path'; import { compile } from './js-handlebars'; import { extensionsPattern } from '@embroider/shared-internals'; import { partition } from 'lodash'; -import { getAppFiles, getFastbootFiles, importPaths, splitRoute } from './virtual-entrypoint'; +import { getAppFiles, getFastbootFiles, importPaths, splitRoute, staticAppPathsPattern } from './virtual-entrypoint'; const entrypointPattern = /(?.*)[\\/]-embroider-route-entrypoint.js:route=(?.*)/; @@ -70,6 +70,7 @@ export function renderRouteEntrypoint( getAppFiles(owner.root), hasFastboot ? getFastbootFiles(owner.root) : new Set(), extensionsPattern(resolver.options.resolvableExtensions), + staticAppPathsPattern(resolver.options.staticAppPaths), resolver.options.podModulePrefix ); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1c947a4e2..63ec42f22 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -408,6 +408,9 @@ importers: debug: specifier: ^4.3.2 version: 4.3.4(supports-color@9.4.0) + escape-string-regexp: + specifier: ^4.0.0 + version: 4.0.0 fast-sourcemap-concat: specifier: ^1.4.0 version: 1.4.0 @@ -8339,9 +8342,6 @@ packages: /ajv-formats@2.1.1: resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} - peerDependenciesMeta: - ajv: - optional: true dependencies: ajv: 8.13.0 diff --git a/tests/scenarios/compat-resolver-test.ts b/tests/scenarios/compat-resolver-test.ts index f4babf45b..724a33193 100644 --- a/tests/scenarios/compat-resolver-test.ts +++ b/tests/scenarios/compat-resolver-test.ts @@ -101,6 +101,7 @@ Scenarios.fromProject(() => new Project()) }, ], autoRun: true, + staticAppPaths: [], }; givenFiles({ diff --git a/tests/scenarios/compat-stage2-test.ts b/tests/scenarios/compat-stage2-test.ts index 30fc37b8b..8100bbeec 100644 --- a/tests/scenarios/compat-stage2-test.ts +++ b/tests/scenarios/compat-stage2-test.ts @@ -700,7 +700,7 @@ stage2Scenarios .resolves('/@embroider/core/entrypoint') .toModule() .withContents(content => { - return !/my-app\/static-dir\/my-library\.js"/.test(content); + return !/\.\/static-dir\/my-library\.js"/.test(content); }); }); @@ -710,7 +710,7 @@ stage2Scenarios .resolves('/@embroider/core/entrypoint') .toModule() .withContents(content => { - return !content.includes('my-app/top-level-static.js'); + return !content.includes('./top-level-static.js'); }); }); diff --git a/tests/scenarios/core-resolver-test.ts b/tests/scenarios/core-resolver-test.ts index e6a8664be..a4c4e8073 100644 --- a/tests/scenarios/core-resolver-test.ts +++ b/tests/scenarios/core-resolver-test.ts @@ -133,6 +133,7 @@ Scenarios.fromProject(() => new Project()) }, ], autoRun: true, + staticAppPaths: [], }; givenFiles({ From d0c97aa0eb7262f8b58d88416fc0ad192fb6ab9e Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Thu, 16 May 2024 14:17:21 +0100 Subject: [PATCH 29/29] removing @ember/legacy-built-in-components --- package.json | 3 +-- pnpm-lock.yaml | 23 +++++++++++------------ 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index dd8b70d48..5cb29950e 100644 --- a/package.json +++ b/package.json @@ -34,8 +34,7 @@ "browserslist": "^4.14.0", "graceful-fs": "^4.0.0", "@types/eslint": "^8.37.0", - "babel-plugin-module-resolver@5.0.1": "5.0.0", - "@ember/legacy-built-in-components": "^0.5.0" + "babel-plugin-module-resolver@5.0.1": "5.0.0" }, "patchedDependencies": { "ember-source@5.8.0": "patches/ember-source@5.8.0.patch" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 63ec42f22..086fe82a3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,7 +9,6 @@ overrides: graceful-fs: ^4.0.0 '@types/eslint': ^8.37.0 babel-plugin-module-resolver@5.0.1: 5.0.0 - '@ember/legacy-built-in-components': ^0.5.0 patchedDependencies: ember-source@5.8.0: @@ -1684,8 +1683,8 @@ importers: specifier: ^7.18.6 version: 7.24.5 '@ember/legacy-built-in-components': - specifier: ^0.5.0 - version: 0.5.0(ember-source@3.28.12) + specifier: ^0.4.1 + version: 0.4.2(ember-source@3.28.12) '@ember/string': specifier: ^3.0.0 version: 3.1.1 @@ -1775,7 +1774,7 @@ importers: version: /ember-data@5.3.0(@babel/core@7.24.5)(@ember/string@3.1.1)(ember-source@3.28.12) ember-engines: specifier: ^0.8.23 - version: 0.8.23(@ember/legacy-built-in-components@0.5.0)(ember-source@3.28.12) + version: 0.8.23(@ember/legacy-built-in-components@0.4.2)(ember-source@3.28.12) ember-inline-svg: specifier: ^0.2.1 version: 0.2.1(@babel/core@7.24.5) @@ -4523,11 +4522,11 @@ packages: - supports-color dev: true - /@ember/legacy-built-in-components@0.5.0(ember-source@3.28.12): - resolution: {integrity: sha512-hbUCt5rii6CT1L4mheH+aqCDeF1dzp/UjS2g7KFIKYGd9zMqyKU4OEnQGk2/O5tATXkEGPf4Zpj671BddBOrbQ==} - engines: {node: '>= 16'} + /@ember/legacy-built-in-components@0.4.2(ember-source@3.28.12): + resolution: {integrity: sha512-rJulbyVQIVe1zEDQDqAQHechHy44DsS2qxO24+NmU/AYxwPFSzWC/OZNCDFSfLU+Y5BVd/00qjxF0pu7Nk+TNA==} + engines: {node: 12.* || 14.* || >= 16} peerDependencies: - ember-source: '>= 4.8' + ember-source: '*' dependencies: '@embroider/macros': 1.16.1(@glint/template@1.4.0) ember-cli-babel: 7.26.11 @@ -13829,14 +13828,14 @@ packages: - supports-color dev: true - /ember-engines@0.8.23(@ember/legacy-built-in-components@0.5.0)(ember-source@3.28.12): + /ember-engines@0.8.23(@ember/legacy-built-in-components@0.4.2)(ember-source@3.28.12): resolution: {integrity: sha512-rrvHUkZRNrf+9u/sCw7XYrITStjP/9Ypykk1nYQHoo+6Krp11e81QNVsGTXFpXtMHXbNtH5IcRyZvfSXqUOrUQ==} engines: {node: 10.* || >= 12} peerDependencies: - '@ember/legacy-built-in-components': ^0.5.0 + '@ember/legacy-built-in-components': '*' ember-source: ^3.12 || 4 dependencies: - '@ember/legacy-built-in-components': 0.5.0(ember-source@3.28.12) + '@ember/legacy-built-in-components': 0.4.2(ember-source@3.28.12) '@embroider/macros': 1.16.1(@glint/template@1.4.0) amd-name-resolver: 1.3.1 babel-plugin-compact-reexports: 1.1.0 @@ -13865,7 +13864,7 @@ packages: resolution: {integrity: sha512-rrvHUkZRNrf+9u/sCw7XYrITStjP/9Ypykk1nYQHoo+6Krp11e81QNVsGTXFpXtMHXbNtH5IcRyZvfSXqUOrUQ==} engines: {node: 10.* || >= 12} peerDependencies: - '@ember/legacy-built-in-components': ^0.5.0 + '@ember/legacy-built-in-components': '*' ember-source: ^3.12 || 4 dependencies: '@embroider/macros': 1.16.1(@glint/template@1.4.0)