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); + + + } + `, + }, + }, + }); + + 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": { "./*": "./*"