From 9897fcb882900fded47239952bbf7e58d5607fae Mon Sep 17 00:00:00 2001 From: Marine Dunstetter Date: Mon, 19 Feb 2024 15:51:20 +0100 Subject: [PATCH 1/6] feat (virtual test-support.js): create the virtual entrypoint --- packages/core/src/module-resolver.ts | 28 +++++++++++++++++++++++ packages/core/src/virtual-content.ts | 6 +++++ packages/core/src/virtual-test-support.ts | 25 ++++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 packages/core/src/virtual-test-support.ts diff --git a/packages/core/src/module-resolver.ts b/packages/core/src/module-resolver.ts index 65a51d5c2..e1f80d84d 100644 --- a/packages/core/src/module-resolver.ts +++ b/packages/core/src/module-resolver.ts @@ -193,6 +193,7 @@ export class Resolver { request = this.handleFastbootSwitch(request); request = await this.handleGlobalsCompat(request); request = this.handleImplicitModules(request); + request = this.handleImplicitTestScripts(request); request = this.handleRenaming(request); // we expect the specifier to be app relative at this point - must be after handleRenaming request = this.generateFastbootSwitch(request); @@ -421,6 +422,33 @@ export class Resolver { } } + private handleImplicitTestScripts(request: R): R { + //TODO move the extra forwardslash handling out into the vite plugin + const candidates = [ + '#embroider/core/test-support', + '@embroider/core/test-support', + '/@embroider/core/test-support', + './@embroider/core/test-support', + ]; + + if (!candidates.includes(request.specifier)) { + return request; + } + + let pkg = this.packageCache.ownerOfFile(request.fromFile); + if (pkg?.root !== this.options.engines[0].root) { + throw new Error( + `bug: found an import of ${request.specifier} in ${request.fromFile}, but this is not the top-level Ember app. The top-level Ember app is the only one that has support for @embroider/core/vendor. If you think something should be fixed in Embroider, please open an issue on https://github.com/embroider-build/embroider/issues.` + ); + } + + if (!pkg?.isV2Ember()) { + throw new Error(`bug: an import of ${request.specifier} in non-ember package at ${request.fromFile}`); + } + + return logTransition('test-support', request, request.virtualize(resolve(pkg.root, '-embroider-test-support.js'))); + } + private async handleGlobalsCompat(request: R): Promise { if (isTerminal(request)) { return request; diff --git a/packages/core/src/virtual-content.ts b/packages/core/src/virtual-content.ts index 55ac3a903..9e51dc9cf 100644 --- a/packages/core/src/virtual-content.ts +++ b/packages/core/src/virtual-content.ts @@ -2,6 +2,7 @@ import { dirname, basename, resolve, posix, sep, join } from 'path'; import type { Resolver, AddonPackage, Package } from '.'; import { explicitRelative, extensionsPattern } from '.'; import { compile } from './js-handlebars'; +import { decodeImplicitTestScripts, renderImplicitTestScripts } from './virtual-test-support'; const externalESPrefix = '/@embroider/ext-es/'; const externalCJSPrefix = '/@embroider/ext-cjs/'; @@ -40,6 +41,11 @@ export function virtualContent(filename: string, resolver: Resolver): VirtualCon return renderImplicitModules(im, resolver); } + let isImplicitTestScripts = decodeImplicitTestScripts(filename); + if (isImplicitTestScripts) { + return renderImplicitTestScripts(filename, resolver); + } + throw new Error(`not an @embroider/core virtual file: ${filename}`); } diff --git a/packages/core/src/virtual-test-support.ts b/packages/core/src/virtual-test-support.ts new file mode 100644 index 000000000..b3332d0a5 --- /dev/null +++ b/packages/core/src/virtual-test-support.ts @@ -0,0 +1,25 @@ +import { type Package } from '@embroider/shared-internals'; +import type { Resolver } from './module-resolver'; +import type { VirtualContentResult } from './virtual-content'; + +export function decodeImplicitTestScripts(filename: string): boolean { + return filename.endsWith('-embroider-test-support.js'); +} + +export function renderImplicitTestScripts(filename: string, resolver: Resolver): VirtualContentResult { + const owner = resolver.packageCache.ownerOfFile(filename); + if (!owner) { + throw new Error(`Failed to find a valid owner for ${filename}`); + } + return { src: getTestSupport(owner, resolver), watches: [] }; +} + +function getTestSupport(owner: Package, resolver: Resolver): string { + console.log(owner); + console.log(resolver); + return ` +var runningTests=true; +if (typeof Testem !== 'undefined' && (typeof QUnit !== 'undefined' || typeof Mocha !== 'undefined')) { + Testem.hookIntoTestFramework(); +}`; +} From 7025345c5ab6aa32c982c2945ae2b9b82e389122 Mon Sep 17 00:00:00 2001 From: Marine Dunstetter Date: Mon, 19 Feb 2024 18:53:58 +0100 Subject: [PATCH 2/6] feat(virtual test-support.js): implement the generation of the virtual content --- packages/core/src/virtual-test-support.ts | 73 +++++++++++++++++++++-- 1 file changed, 68 insertions(+), 5 deletions(-) diff --git a/packages/core/src/virtual-test-support.ts b/packages/core/src/virtual-test-support.ts index b3332d0a5..5c38d692a 100644 --- a/packages/core/src/virtual-test-support.ts +++ b/packages/core/src/virtual-test-support.ts @@ -1,4 +1,9 @@ -import { type Package } from '@embroider/shared-internals'; +import type { Package } from '@embroider/shared-internals'; +import type { V2AddonPackage } from '@embroider/shared-internals/src/package'; +import { readFileSync } from 'fs'; +import { sortBy } from 'lodash'; +import resolve from 'resolve'; +import type { Engine } from './app-files'; import type { Resolver } from './module-resolver'; import type { VirtualContentResult } from './virtual-content'; @@ -15,11 +20,69 @@ export function renderImplicitTestScripts(filename: string, resolver: Resolver): } function getTestSupport(owner: Package, resolver: Resolver): string { - console.log(owner); - console.log(resolver); - return ` + let engineConfig = resolver.owningEngine(owner); + let engine: Engine = { + package: owner, + addons: new Map( + engineConfig.activeAddons.map(addon => [ + resolver.packageCache.get(addon.root) as V2AddonPackage, + addon.canResolveFromFile, + ]) + ), + isApp: true, + modulePrefix: resolver.options.modulePrefix, + appRelativePath: 'NOT_USED_DELETE_ME', + }; + + return generateTestSupport(engine); +} + +function generateTestSupport(engine: Engine): string { + // Add classic addons test-support + let result: string[] = impliedAddonTestSupport(engine); + let hasEmbroiderMacrosTestSupport = result.find(sourcePath => + sourcePath.endsWith('embroider-macros-test-support.js') + ); + result = result.map((sourcePath: string): string => { + let source = readFileSync(sourcePath); + return `${source}`; + }); + + // Add _testing_suffix_.js + result.push(` var runningTests=true; if (typeof Testem !== 'undefined' && (typeof QUnit !== 'undefined' || typeof Mocha !== 'undefined')) { Testem.hookIntoTestFramework(); -}`; +}`); + + // whether or not anybody was actually using @embroider/macros explicitly + // as an addon, we ensure its test-support file is always present. + if (!hasEmbroiderMacrosTestSupport) { + result.unshift(`${readFileSync(require.resolve('@embroider/macros/src/vendor/embroider-macros-test-support'))}`); + } + + return result.join('') as string; +} + +function impliedAddonTestSupport(engine: Engine): string[] { + let result: Array = []; + for (let addon of sortBy(Array.from(engine.addons.keys()), pkg => { + switch (pkg.name) { + case 'loader.js': + return 0; + case 'ember-source': + return 10; + default: + return 1000; + } + })) { + let implicitScripts = addon.meta['implicit-test-scripts']; + if (implicitScripts) { + let options = { basedir: addon.root }; + for (let mod of implicitScripts) { + result.push(resolve.sync(mod, options)); + } + } + } + return result; } From 8a09d2ceccbd9a0267d932b67fea9794a148eb17 Mon Sep 17 00:00:00 2001 From: Marine Dunstetter Date: Tue, 20 Feb 2024 10:29:43 +0100 Subject: [PATCH 3/6] feat (virtual test-support.js): in compat-app-builder, replace the generation of test-support.js with the virtual entrypoint --- packages/compat/src/compat-app-builder.ts | 51 +---------------------- 1 file changed, 2 insertions(+), 49 deletions(-) diff --git a/packages/compat/src/compat-app-builder.ts b/packages/compat/src/compat-app-builder.ts index fa101ade7..70091140f 100644 --- a/packages/compat/src/compat-app-builder.ts +++ b/packages/compat/src/compat-app-builder.ts @@ -361,32 +361,6 @@ export class CompatAppBuilder { }); } - if (type === 'implicit-test-scripts') { - // this is the traditional test-support-suffix.js - result.push({ - kind: 'in-memory', - relativePath: '_testing_suffix_.js', - source: ` - var runningTests=true; - if (typeof Testem !== 'undefined' && (typeof QUnit !== 'undefined' || typeof Mocha !== 'undefined')) { - Testem.hookIntoTestFramework(); - }`, - }); - - // whether or not anybody was actually using @embroider/macros - // explicitly as an addon, we ensure its test-support file is always - // present. - if (!result.find(s => s.kind === 'on-disk' && s.sourcePath.endsWith('embroider-macros-test-support.js'))) { - result.unshift({ - kind: 'on-disk', - sourcePath: require.resolve('@embroider/macros/src/vendor/embroider-macros-test-support'), - mtime: 0, - size: 0, - relativePath: 'embroider-macros-test-support.js', - }); - } - } - return result; } @@ -556,10 +530,8 @@ export class CompatAppBuilder { let testJS = this.testJSEntrypoint(appFiles, prepared); html.insertScriptTag(html.testJavascript, testJS.relativePath, { type: 'module' }); - let implicitTestScriptsAsset = this.implicitTestScriptsAsset(prepared, parentEngine); - if (implicitTestScriptsAsset) { - html.insertScriptTag(html.implicitTestScripts, implicitTestScriptsAsset.relativePath); - } + // virtual test-support.js + html.insertScriptTag(html.implicitTestScripts, '@embroider/core/test-support'); let implicitTestStylesAsset = this.implicitTestStylesAsset(prepared, parentEngine); if (implicitTestStylesAsset) { @@ -596,25 +568,6 @@ export class CompatAppBuilder { return asset; } - private implicitTestScriptsAsset( - prepared: Map, - application: AppFiles - ): InternalAsset | undefined { - let testSupportJS = prepared.get('assets/test-support.js'); - if (!testSupportJS) { - let implicitTestScripts = this.impliedAssets('implicit-test-scripts', application); - if (implicitTestScripts.length > 0) { - testSupportJS = new ConcatenatedAsset( - 'assets/test-support.js', - implicitTestScripts, - this.resolvableExtensionsPattern - ); - prepared.set(testSupportJS.relativePath, testSupportJS); - } - } - return testSupportJS; - } - private implicitTestStylesAsset( prepared: Map, application: AppFiles From 99f783779852915516ccd8ec0e3f051f33e8cc68 Mon Sep 17 00:00:00 2001 From: Marine Dunstetter Date: Wed, 3 Apr 2024 16:40:45 +0200 Subject: [PATCH 4/6] feat (virtual test-support.js): emit the test-support.js time on build --- packages/compat/src/compat-app-builder.ts | 2 +- packages/core/src/module-resolver.ts | 7 +++---- packages/vite/src/resolver.ts | 11 +++++++++++ 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/packages/compat/src/compat-app-builder.ts b/packages/compat/src/compat-app-builder.ts index 70091140f..67b5a4118 100644 --- a/packages/compat/src/compat-app-builder.ts +++ b/packages/compat/src/compat-app-builder.ts @@ -531,7 +531,7 @@ export class CompatAppBuilder { html.insertScriptTag(html.testJavascript, testJS.relativePath, { type: 'module' }); // virtual test-support.js - html.insertScriptTag(html.implicitTestScripts, '@embroider/core/test-support'); + html.insertScriptTag(html.implicitTestScripts, '@embroider/core/test-support.js'); let implicitTestStylesAsset = this.implicitTestStylesAsset(prepared, parentEngine); if (implicitTestStylesAsset) { diff --git a/packages/core/src/module-resolver.ts b/packages/core/src/module-resolver.ts index e1f80d84d..dbc1e5613 100644 --- a/packages/core/src/module-resolver.ts +++ b/packages/core/src/module-resolver.ts @@ -425,10 +425,9 @@ export class Resolver { private handleImplicitTestScripts(request: R): R { //TODO move the extra forwardslash handling out into the vite plugin const candidates = [ - '#embroider/core/test-support', - '@embroider/core/test-support', - '/@embroider/core/test-support', - './@embroider/core/test-support', + '@embroider/core/test-support.js', + '/@embroider/core/test-support.js', + './@embroider/core/test-support.js', ]; if (!candidates.includes(request.specifier)) { diff --git a/packages/vite/src/resolver.ts b/packages/vite/src/resolver.ts index d5ccc34eb..1f9a46d56 100644 --- a/packages/vite/src/resolver.ts +++ b/packages/vite/src/resolver.ts @@ -3,6 +3,7 @@ import { virtualContent, ResolverLoader } from '@embroider/core'; import { RollupModuleRequest, virtualPrefix } from './request'; import assertNever from 'assert-never'; import makeDebug from 'debug'; +import { resolve } from 'path'; const debug = makeDebug('embroider:vite'); @@ -59,5 +60,15 @@ export function resolver(): Plugin { return src; } }, + buildEnd() { + this.emitFile({ + type: 'asset', + fileName: '@embroider/core/test-support.js', + source: virtualContent( + resolve(resolverLoader.resolver.options.engines[0].root, '-embroider-test-support.js'), + resolverLoader.resolver + ).src, + }); + }, }; } From 2be859de79f5551f62794e40f9b581bbefbc93f6 Mon Sep 17 00:00:00 2001 From: Marine Dunstetter Date: Thu, 4 Apr 2024 13:51:02 +0200 Subject: [PATCH 5/6] feat(virtual test-support.js): fix the checks in module-resolver --- packages/core/src/module-resolver.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/core/src/module-resolver.ts b/packages/core/src/module-resolver.ts index dbc1e5613..d10debe36 100644 --- a/packages/core/src/module-resolver.ts +++ b/packages/core/src/module-resolver.ts @@ -437,14 +437,10 @@ export class Resolver { let pkg = this.packageCache.ownerOfFile(request.fromFile); if (pkg?.root !== this.options.engines[0].root) { throw new Error( - `bug: found an import of ${request.specifier} in ${request.fromFile}, but this is not the top-level Ember app. The top-level Ember app is the only one that has support for @embroider/core/vendor. If you think something should be fixed in Embroider, please open an issue on https://github.com/embroider-build/embroider/issues.` + `bug: found an import of ${request.specifier} in ${request.fromFile}, but this is not the top-level Ember app. The top-level Ember app is the only one that has support for @embroider/core/test-support.js. If you think something should be fixed in Embroider, please open an issue on https://github.com/embroider-build/embroider/issues.` ); } - if (!pkg?.isV2Ember()) { - throw new Error(`bug: an import of ${request.specifier} in non-ember package at ${request.fromFile}`); - } - return logTransition('test-support', request, request.virtualize(resolve(pkg.root, '-embroider-test-support.js'))); } From 20503b181994064fb433e90d37858bd5bb12daa0 Mon Sep 17 00:00:00 2001 From: Marine Dunstetter Date: Thu, 4 Apr 2024 13:56:31 +0200 Subject: [PATCH 6/6] feat(virtual test-support.js): remove addons sorting when generating test-support.js --- packages/core/src/virtual-test-support.ts | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/packages/core/src/virtual-test-support.ts b/packages/core/src/virtual-test-support.ts index 5c38d692a..ff8a6a912 100644 --- a/packages/core/src/virtual-test-support.ts +++ b/packages/core/src/virtual-test-support.ts @@ -1,7 +1,6 @@ import type { Package } from '@embroider/shared-internals'; import type { V2AddonPackage } from '@embroider/shared-internals/src/package'; import { readFileSync } from 'fs'; -import { sortBy } from 'lodash'; import resolve from 'resolve'; import type { Engine } from './app-files'; import type { Resolver } from './module-resolver'; @@ -66,16 +65,7 @@ if (typeof Testem !== 'undefined' && (typeof QUnit !== 'undefined' || typeof Moc function impliedAddonTestSupport(engine: Engine): string[] { let result: Array = []; - for (let addon of sortBy(Array.from(engine.addons.keys()), pkg => { - switch (pkg.name) { - case 'loader.js': - return 0; - case 'ember-source': - return 10; - default: - return 1000; - } - })) { + for (let addon of Array.from(engine.addons.keys())) { let implicitScripts = addon.meta['implicit-test-scripts']; if (implicitScripts) { let options = { basedir: addon.root };