diff --git a/packages/compat/src/v1-addon.ts b/packages/compat/src/v1-addon.ts index 3425db257..6d6bfc472 100644 --- a/packages/compat/src/v1-addon.ts +++ b/packages/compat/src/v1-addon.ts @@ -116,9 +116,10 @@ export default class V1Addon { @Memoize() private get templateCompilerBabelPlugin(): PluginItem | undefined { let plugins = loadAstPlugins(this.addonInstance.registry); + let hasTemplateTag = this.addonInstance.addons.find((a: any) => a.name === 'ember-template-imports'); // our macros don't run here in stage1 plugins = plugins.filter((p: any) => !isEmbroiderMacrosPlugin(p)); - if (plugins.length > 0) { + if (plugins.length > 0 || hasTemplateTag) { let compilerPath = require.resolve('ember-source/dist/ember-template-compiler.js', { paths: [findTopmostAddon(this.addonInstance).parent.root], }); @@ -205,6 +206,34 @@ export default class V1Addon { // the older inline template compiler is present return true; } + if (this.addonInstance.addons.find((a: any) => a.name === 'ember-template-imports')) { + /** + * Stage1 will always run custom broccoli preprocessors. So that's enough to convert: + * + * import Thing from './thing'; + * + * to + * + * import Thing from './thing'; + * import { template } from '@ember/template-compiler'; + * export default template("Thing", { + * eval: function() { return eval(arguments[0]) } }) + * }); + * This is really all we need to do at stage1, since this is now valid Javascript that could appear in a v2 addon. + * + * But if the addon is also using TS, we also need to run the typescript transform before it will be valid JS. And if the typescript transform was being truly correct it would not try to delete the import because the eval can see the imported binding. That's why we have an eval. It's a standards-compliant want of gaining access to everything in scope. + * + * Normally we only use babel-plugin-ember-template-compilation in stage1 to run custom AST transforms. Since there are none in the addon, we don't add it. The fix here is helping because there is a new reason to add it. It will further convert the above example to: + * + * import Thing from './thing'; + * import { template } from '@ember/template-compiler'; + * export default template("Thing", { + * scope: () => ({ Thing }) + * }); + * which typescript then respects. + */ + return true; + } if ( this.addonInstance.addons.find( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a0c683eca..f40d2bb48 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1762,6 +1762,9 @@ importers: ember-source-latest: specifier: npm:ember-source@latest version: /ember-source@5.9.0(@babel/core@7.24.7) + ember-template-imports: + specifier: ^4.1.2 + version: 4.1.2 ember-truth-helpers: specifier: ^3.0.0 version: 3.1.1 @@ -11839,7 +11842,6 @@ packages: /content-tag@2.0.1: resolution: {integrity: sha512-jxsETSDs5NbNwyiDuIp672fUMhUyu8Qxc5MOBOJOcgW/fQESI6o5K1LBDrnEE7Bh810a685lWEZHTF4jQYGEEw==} - dev: false /content-type@1.0.5: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} @@ -16349,6 +16351,17 @@ packages: - supports-color dev: true + /ember-template-imports@4.1.2: + resolution: {integrity: sha512-6A7s+9QlmKkkpTkI0VGDFqyX+HrAsHQKB32VB/vbj/ITTDN/l/CCtEfvN4/LANKoG5yAvbg6q9wzcOjK9XwmWw==} + engines: {node: 16.* || >= 18} + dependencies: + broccoli-stew: 3.0.0 + content-tag: 2.0.1 + ember-cli-version-checker: 5.1.2 + transitivePeerDependencies: + - supports-color + dev: true + /ember-template-lint@3.16.0: resolution: {integrity: sha512-hbP4JefkOLx9tMkrZ3UIvdBNoEnrT7rg6c70tIxpB9F+KpPneDbmpGMBsQVhhK4BirTXIFwAIfnwKcwkIk3bPQ==} engines: {node: '>= 10.24 < 11 || 12.* || >= 14.*'} diff --git a/tests/scenarios/compat-stage2-test.ts b/tests/scenarios/compat-stage2-test.ts index b34999c09..8928ec823 100644 --- a/tests/scenarios/compat-stage2-test.ts +++ b/tests/scenarios/compat-stage2-test.ts @@ -253,6 +253,86 @@ stage2Scenarios }); }); +stage2Scenarios + .map('gts-files-in-addons-are-pre-processed-with-template-compilation', app => { + let depA = addAddon(app, 'dep-a'); + depA.linkDependency('ember-template-imports', { baseDir: __dirname }); + depA.linkDependency('ember-cli-babel', { baseDir: __dirname, resolveName: 'ember-cli-babel-latest' }); + + merge(depA.files, { + 'index.js': ` + 'use strict'; + module.exports = { + name: require('./package').name, + options: { + 'ember-cli-babel': { enableTypeScriptTransform: true }, + }, + };`, + addon: { + components: { + 'other.gts': ` + import Component from '@glimmer/component'; + + export default class extends Component { + abc: string; + + }; + `, + 'gts-component.gts': ` + import Component from '@glimmer/component'; + import OtherComponent from './other'; + + export default class extends Component { + abc: string; + + }; + `, + }, + }, + app: { + components: { + 'gts-component.js': 'export { default } from "dep-a/components/gts-component"', + }, + }, + }); + }) + .forEachScenario(scenario => { + Qmodule(scenario.name, function (hooks) { + throwOnWarnings(hooks); + + let app: PreparedApp; + + hooks.before(async assert => { + app = await scenario.prepare(); + let result = await app.execute('ember build', { env: { STAGE2_ONLY: 'true' } }); + assert.equal(result.exitCode, 0, result.output); + }); + + let expectAudit = setupAuditTest(hooks, () => ({ app: app.dir, 'reuse-build': true })); + + test('no audit issues', function () { + expectAudit.hasNoFindings(); + }); + + test('gts is processed with template-compilation', function () { + let expectModule = expectAudit.module('./assets/my-app.js'); + // this is to make sure that the babel plugin template compilation runs and thus + // make imports that are only used in templates bound and not removed by typescript + expectModule + .resolves('my-app/components/gts-component.js') + .toModule() + .resolves('dep-a/components/gts-component') + .toModule() + .codeContains(`import OtherComponent from './other';`); + }); + }); + }); + stage2Scenarios .map('static-with-rules', app => { app.addDependency('some-library', '1.0.0'); diff --git a/tests/scenarios/package.json b/tests/scenarios/package.json index e72a5373a..9db019b92 100644 --- a/tests/scenarios/package.json +++ b/tests/scenarios/package.json @@ -94,6 +94,7 @@ "ember-source-canary": "https://s3.amazonaws.com/builds.emberjs.com/canary/shas/756f0e3f98b8ca5edf443fe57318b4dac692bffa.tgz", "ember-source-latest": "npm:ember-source@latest", "ember-truth-helpers": "^3.0.0", + "ember-template-imports": "^4.1.2", "execa": "^5.1.1", "popper.js": "^1.16.1", "tslib": "^2.6.0",