diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 41d41ff27..141cf842d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1526,6 +1526,9 @@ importers:
'@types/qunit':
specifier: ^2.11.1
version: 2.19.10
+ decorator-transforms:
+ specifier: ^1.0.1
+ version: 1.0.1(@babel/core@7.23.9)
ember-auto-import:
specifier: ^2.6.3
version: 2.7.2(@glint/template@1.3.0)(webpack@5.90.3)
@@ -1662,6 +1665,9 @@ importers:
ember-cli-4.4:
specifier: npm:ember-cli@~4.4.0
version: /ember-cli@4.4.1(lodash@4.17.21)
+ ember-cli-babel-8:
+ specifier: npm:ember-cli-babel@^8.2.0
+ version: /ember-cli-babel@8.2.0(@babel/core@7.23.9)
ember-cli-beta:
specifier: npm:ember-cli@beta
version: /ember-cli@5.2.0-beta.0(lodash@4.17.21)
@@ -2706,7 +2712,6 @@ packages:
dependencies:
'@babel/core': 7.23.9(supports-color@8.1.1)
'@babel/helper-plugin-utils': 7.22.5
- dev: true
/@babel/plugin-syntax-decorators@7.23.3(@babel/core@7.24.0):
resolution: {integrity: sha512-cf7Niq4/+/juY67E0PbgH0TDhLQ5J7zS8C/Q5FFx+DWyrRa9sUQdTXkjqKu8zGvuqr7vw1muKiukseihU+PJDA==}
@@ -11293,6 +11298,15 @@ packages:
mimic-response: 1.0.1
dev: true
+ /decorator-transforms@1.0.1(@babel/core@7.23.9):
+ resolution: {integrity: sha512-ZOaiw4tqiyhtqiMKCNzjS+nGsYPZltToqRAFc5rOUcc4u8d7MTlgelw1qXfL6ieg2l6xZcz6lTZ5M94Ohnk2xA==}
+ dependencies:
+ '@babel/plugin-syntax-decorators': 7.23.3(@babel/core@7.23.9)
+ babel-import-util: 2.0.1
+ transitivePeerDependencies:
+ - '@babel/core'
+ dev: false
+
/dedent@1.5.1:
resolution: {integrity: sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==}
peerDependencies:
diff --git a/tests/scenarios/package.json b/tests/scenarios/package.json
index e30a5538e..38108851f 100644
--- a/tests/scenarios/package.json
+++ b/tests/scenarios/package.json
@@ -10,6 +10,7 @@
"@embroider/test-support": "workspace:*",
"@embroider/webpack": "workspace:*",
"@types/qunit": "^2.11.1",
+ "decorator-transforms": "^1.0.1",
"ember-auto-import": "^2.6.3",
"fastboot": "^4.1.1",
"fs-extra": "^10.0.0",
@@ -67,6 +68,7 @@
"ember-cli-beta": "npm:ember-cli@beta",
"ember-cli-fastboot": "^4.1.1",
"ember-cli-latest": "npm:ember-cli@latest",
+ "ember-cli-babel-8": "npm:ember-cli-babel@^8.2.0",
"ember-composable-helpers": "^4.4.1",
"ember-data": "~3.28.0",
"ember-data-4.4": "npm:ember-data@~4.4.0",
diff --git a/tests/scenarios/v2-addon-test.ts b/tests/scenarios/v2-addon-test.ts
index 4c18982cb..f67fe825d 100644
--- a/tests/scenarios/v2-addon-test.ts
+++ b/tests/scenarios/v2-addon-test.ts
@@ -32,9 +32,9 @@ appScenarios
'example-component.css': '/* not empty */ h1 { color: red }',
},
'import-from-npm.js': `
- export default async function() {
+ export default async function() {
let { message } = await import('third-party');
- return message()
+ return message()
}
`,
});
diff --git a/tests/scenarios/v2-addon-type-module-test.ts b/tests/scenarios/v2-addon-type-module-test.ts
new file mode 100644
index 000000000..bf7ebe6a2
--- /dev/null
+++ b/tests/scenarios/v2-addon-type-module-test.ts
@@ -0,0 +1,216 @@
+import path from 'path';
+import { appScenarios, baseV2Addon } from './scenarios';
+import { PreparedApp } from 'scenario-tester';
+import QUnit from 'qunit';
+import merge from 'lodash/merge';
+import { pathExistsSync, readJsonSync, readFileSync } from 'fs-extra';
+
+const { module: Qmodule, test } = QUnit;
+
+appScenarios
+ .only('release')
+ .map('v2-addon-as-type-module', async project => {
+ let addon = baseV2Addon();
+ addon.pkg.name = 'v2-addon';
+ addon.pkg.type = 'module';
+ addon.pkg.files = ['dist'];
+ addon.pkg.exports = {
+ './*': './dist/*.js',
+ './addon-main.cjs': './addon-main.cjs',
+ // needed for our "inDependency" function defined in this test
+ './package.json': './package.json',
+ };
+ addon.pkg.scripts = {
+ build: 'node ./node_modules/rollup/dist/bin/rollup -c ./rollup.config.mjs',
+ };
+
+ merge(addon.files, {
+ 'babel.config.json': `
+ {
+ "plugins": [
+ ["babel-plugin-ember-template-compilation", {
+ "targetFormat": "hbs",
+ "transforms": []
+ }],
+ ["module:decorator-transforms", { "runtime": { "import": "decorator-transforms/runtime" } }]
+ ]
+ }
+ `,
+ 'rollup.config.mjs': `
+ import { Addon } from '@embroider/addon-dev/rollup';
+ import { babel } from '@rollup/plugin-babel';
+
+ const addon = new Addon({
+ srcDir: 'src',
+ destDir: 'dist',
+ });
+
+ export default {
+ output: addon.output(),
+ plugins: [
+ addon.publicEntrypoints(['**/*.js']),
+ addon.appReexports(['components/*.js']),
+ addon.dependencies(),
+ babel({ extensions: ['.js', '.gjs', '.ts', '.gts'], babelHelpers: 'bundled' }),
+ addon.gjs(),
+ addon.hbs(),
+ addon.keepAssets(["**/*.css"]),
+ addon.clean(),
+ ],
+ };
+
+ `,
+ src: {
+ components: {
+ 'styles.css': `button { font-weight: bold; color: blue; }`,
+ 'demo.gjs': `
+ import Component from '@glimmer/component';
+ import { tracked } from '@glimmer/tracking';
+ import { on } from '@ember/modifier';
+
+ import { importSync, isDevelopingApp, macroCondition } from '@embroider/macros';
+
+ if (macroCondition(isDevelopingApp())) {
+ importSync('./styles.css');
+ }
+
+ export default class ExampleComponent extends Component {
+ @tracked active = false;
+
+ flip = () => (this.active = !this.active);
+
+
+ Hello there!
+
+ {{this.active}}
+
+
+
+ }
+ `,
+ },
+ },
+ });
+
+ addon.linkDependency('@embroider/addon-shim', { baseDir: __dirname });
+ addon.linkDependency('@embroider/addon-dev', { baseDir: __dirname });
+ addon.linkDependency('@babel/runtime', { baseDir: __dirname });
+ addon.linkDevDependency('@babel/core', { baseDir: __dirname });
+ addon.linkDevDependency('@rollup/plugin-babel', { baseDir: __dirname });
+ addon.linkDependency('decorator-transforms', { baseDir: __dirname });
+ addon.linkDevDependency('rollup', { baseDir: __dirname });
+
+ project.addDevDependency(addon);
+ project.linkDevDependency('ember-cli-babel', { baseDir: __dirname, resolveName: 'ember-cli-babel-8' });
+ project.linkDevDependency('@embroider/macros', { baseDir: __dirname });
+
+ merge(project.files, {
+ tests: {
+ // the app is not set up with typescript
+ 'the-test.js': `
+ import { click, render } from '@ember/test-helpers';
+ import { hbs } from 'ember-cli-htmlbars';
+ import { module, test } from 'qunit';
+ import { setupRenderingTest } from 'ember-qunit';
+
+ module('v2 addon tests', function (hooks) {
+ setupRenderingTest(hooks);
+
+ test('', async function (assert) {
+ await render(hbs\`\`);
+
+ assert.dom('out').containsText('false');
+
+ await click('button');
+
+ assert.dom('out').containsText('true');
+ });
+ });
+ `,
+ },
+ 'ember-cli-build.js': `
+ 'use strict';
+
+ const EmberApp = require('ember-cli/lib/broccoli/ember-app');
+
+ module.exports = function (defaults) {
+ let app = new EmberApp(defaults, {
+ });
+
+ const { compatBuild, recommendedOptions } = require('@embroider/compat');
+
+ const Webpack = require('@embroider/webpack').Webpack;
+ return compatBuild(app, Webpack, {
+ ...recommendedOptions.optimized,
+ skipBabel: [
+ { package: 'qunit' },
+ ],
+ });
+ };
+ `,
+ });
+ })
+ .forEachScenario(scenario => {
+ Qmodule(scenario.name, function (hooks) {
+ let app: PreparedApp;
+
+ hooks.before(async () => {
+ app = await scenario.prepare();
+ let result = await inDependency(app, 'v2-addon').execute('pnpm build');
+ if (result.exitCode !== 0) {
+ throw new Error(result.output);
+ }
+ });
+
+ Qmodule('The addon', function () {
+ test('output directories exist', async function (assert) {
+ let { dir } = inDependency(app, 'v2-addon');
+ assert.strictEqual(pathExistsSync(path.join(dir, 'dist')), true, 'dist/');
+ assert.strictEqual(pathExistsSync(path.join(dir, 'dist', '_app_')), true, 'dist/_app_');
+ });
+
+ test('package.json is modified appropriately', async function (assert) {
+ let { dir } = inDependency(app, 'v2-addon');
+ let reExports = readJsonSync(path.join(dir, 'package.json'))['ember-addon']['app-js'];
+
+ assert.deepEqual(reExports, {
+ './components/demo.js': './dist/_app_/components/demo.js',
+ });
+ });
+
+ test('the addon was built successfully', async function (assert) {
+ let { dir } = inDependency(app, 'v2-addon');
+ let expectedModules = {
+ './dist/_app_/components/demo.js': 'export { default } from "v2-addon/components/demo";\n',
+ };
+
+ assert.strictEqual(
+ Object.keys(readJsonSync(path.join(dir, 'package.json'))['ember-addon']['app-js']).length,
+ Object.keys(expectedModules).length
+ );
+
+ for (let [pathName, moduleContents] of Object.entries(expectedModules)) {
+ let filePath = path.join(dir, pathName);
+ assert.deepEqual(pathExistsSync(filePath), true, `pathExists: ${pathName}`);
+ assert.strictEqual(
+ readFileSync(filePath, { encoding: 'utf8' }),
+ moduleContents,
+ `has correct reexport: ${pathName}`
+ );
+ }
+ });
+ });
+
+ Qmodule('Consuming app', function () {
+ test(`pnpm test`, async function (assert) {
+ let result = await app.execute('pnpm test');
+ assert.equal(result.exitCode, 0, result.output);
+ });
+ });
+ });
+ });
+
+// https://github.com/ef4/scenario-tester/issues/5
+function inDependency(app: PreparedApp, dependencyName: string): PreparedApp {
+ return new PreparedApp(path.dirname(require.resolve(`${dependencyName}/package.json`, { paths: [app.dir] })));
+}
diff --git a/tests/v2-addon-template/addon-main.js b/tests/v2-addon-template/addon-main.cjs
similarity index 100%
rename from tests/v2-addon-template/addon-main.js
rename to tests/v2-addon-template/addon-main.cjs
diff --git a/tests/v2-addon-template/package.json b/tests/v2-addon-template/package.json
index 8fe72de7d..c87ddcc83 100644
--- a/tests/v2-addon-template/package.json
+++ b/tests/v2-addon-template/package.json
@@ -12,7 +12,7 @@
"type": "addon",
"version": 2,
"app-js": {},
- "main": "addon-main.js"
+ "main": "addon-main.cjs"
},
"exports": {
"./*": "./*"