From 5b9a9b8d54e692e04bd302713660d0c7538cb74f Mon Sep 17 00:00:00 2001 From: Alex Kanunnikov Date: Sat, 9 Dec 2023 14:28:18 +0300 Subject: [PATCH] simple-addon-dsl fix playwright tests --- e2e/flows/404.spec.ts | 2 + e2e/flows/bootstrap-navigation.spec.ts | 3 + e2e/flows/happy-path.spec.ts | 5 + e2e/flows/login.spec.ts | 5 + e2e/utils/modal-dialog.ts | 6 + playwright.config.ts | 2 +- plugins/ember-vendor.ts | 132 +++++++ plugins/ember.ts | 293 ++++++++++++++ src/templates/application.hbs | 4 +- vite.config.ts | 527 +++++-------------------- 10 files changed, 551 insertions(+), 428 deletions(-) create mode 100644 e2e/utils/modal-dialog.ts create mode 100644 plugins/ember-vendor.ts create mode 100644 plugins/ember.ts diff --git a/e2e/flows/404.spec.ts b/e2e/flows/404.spec.ts index 6b898e6..84ae2d9 100644 --- a/e2e/flows/404.spec.ts +++ b/e2e/flows/404.spec.ts @@ -1,12 +1,14 @@ import { test, expect } from '@playwright/test'; import { captureCoverage } from './../utils/index.ts'; +import { skipModalDialog } from './../utils/modal-dialog.ts'; captureCoverage(test); test.describe('NotFound route', () => { test('it loadable', async ({ page }) => { await page.goto('http://localhost:4200/foo-bar-baz'); + await skipModalDialog(page); // Expect a title "to contain" a substring. await expect(page).toHaveTitle(/Not found/); // Expect an element "to contain" text. diff --git a/e2e/flows/bootstrap-navigation.spec.ts b/e2e/flows/bootstrap-navigation.spec.ts index 4d8b798..af3626f 100644 --- a/e2e/flows/bootstrap-navigation.spec.ts +++ b/e2e/flows/bootstrap-navigation.spec.ts @@ -1,12 +1,15 @@ import { test, expect } from '@playwright/test'; import { captureCoverage } from './../utils/index.ts'; +import { skipModalDialog } from './../utils/modal-dialog.ts'; + captureCoverage(test); test.describe('Ember-Bootstrap route', () => { test('it loadable', async ({ page }) => { await page.goto('http://localhost:4200'); + await skipModalDialog(page); await expect(page).not.toHaveTitle(/Bootstrap/); await page.click('a[href="/bootstrap"]'); // Expect a title "to contain" a substring. diff --git a/e2e/flows/happy-path.spec.ts b/e2e/flows/happy-path.spec.ts index 2d66f4c..8a6bdb7 100644 --- a/e2e/flows/happy-path.spec.ts +++ b/e2e/flows/happy-path.spec.ts @@ -1,12 +1,14 @@ import { test, expect } from '@playwright/test'; import { captureCoverage } from './../utils/index.ts'; +import { skipModalDialog } from './../utils/modal-dialog.ts'; captureCoverage(test); test.describe('Happy path', () => { test('i11n is working just fine', async ({ page }) => { await page.goto('http://localhost:4200'); + await skipModalDialog(page); const msgNode = page.locator('[data-test-welcome-msg]'); @@ -35,6 +37,7 @@ test.describe('Happy path', () => { test('dialog click is working fine', async ({ page }) => { await page.goto('http://localhost:4200'); + await skipModalDialog(page); page.on('dialog', (dialog) => { expect(dialog.message()).toBe('Fine'); @@ -45,6 +48,7 @@ test.describe('Happy path', () => { test('power select is working fine', async ({ page }) => { await page.goto('http://localhost:4200'); + await skipModalDialog(page); await expect( page.locator('.ember-power-select-trigger'), @@ -71,6 +75,7 @@ test.describe('Happy path', () => { test('timer is working fine', async ({ page }) => { await page.goto('http://localhost:4200'); + await skipModalDialog(page); const t1 = await page.locator('pre.font-mono').textContent(); // eslint-disable-next-line playwright/no-wait-for-timeout diff --git a/e2e/flows/login.spec.ts b/e2e/flows/login.spec.ts index f07b9de..1fe197b 100644 --- a/e2e/flows/login.spec.ts +++ b/e2e/flows/login.spec.ts @@ -1,23 +1,27 @@ import { test, expect } from '@playwright/test'; import { captureCoverage } from './../utils/index.ts'; +import { skipModalDialog } from './../utils/modal-dialog.ts'; captureCoverage(test); test.describe('login flow', () => { test('by default user is logged out', async ({ page }) => { await page.goto('http://localhost:4200'); + await skipModalDialog(page); await expect(page.locator('[data-login-status]')).toContainText( 'You are not authenticated' ); }); test('user could not visit profile page if logged out', async ({ page }) => { await page.goto('http://localhost:4200/profile'); + await skipModalDialog(page); await page.click('a[href="/profile"]'); await expect(page.url()).toBe('http://localhost:4200/login'); }); test('user could use login form', async ({ page }) => { await page.goto('http://localhost:4200/login'); + await skipModalDialog(page); await page.click('[data-test-login-button]'); await page.waitForURL('http://localhost:4200/profile'); @@ -31,6 +35,7 @@ test.describe('login flow', () => { }); test('user could logout', async ({ page }) => { await page.goto('http://localhost:4200/profile'); + await skipModalDialog(page); await page.click('[data-test-login-button]'); await page.waitForURL('http://localhost:4200/profile'); await expect(page.url()).toBe('http://localhost:4200/profile'); diff --git a/e2e/utils/modal-dialog.ts b/e2e/utils/modal-dialog.ts new file mode 100644 index 0000000..b740d62 --- /dev/null +++ b/e2e/utils/modal-dialog.ts @@ -0,0 +1,6 @@ +import type { Page } from '@playwright/test'; + +export async function skipModalDialog(page: Page) { + await page.click('[data-test-id="close-modal-dialog"]'); + await new Promise((resolve) => setTimeout(resolve, 500)); +} diff --git a/playwright.config.ts b/playwright.config.ts index 2ff578e..e8e84a9 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -71,7 +71,7 @@ export default defineConfig({ /* Run your local dev server before starting the tests */ webServer: { command: 'yarn dev', - url: 'http://127.0.0.1:4200', + url: 'http://localhost:4200', reuseExistingServer: !process.env.CI, }, }); diff --git a/plugins/ember-vendor.ts b/plugins/ember-vendor.ts new file mode 100644 index 0000000..c956251 --- /dev/null +++ b/plugins/ember-vendor.ts @@ -0,0 +1,132 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { Addon, compatPath, nodePath } from './ember'; + +const self = import.meta.url; + +const modeModulesPath = path.resolve( + path.dirname(fileURLToPath(self)), + './../node_modules' +); + +export function internalPackages(mode: string) { + return [ + Addon('ember & ember-source & @ember/*', mode) + .addAlias(/^ember$/, 'ember-source/dist/packages/ember') + .addAlias(/^ember\/version$/, 'ember-source/dist/packages/ember/version') + .addAliases( + emberPackages().map((pkg) => ({ + find: `@ember/${pkg}`, + replacement: nodePath(`ember-source/dist/packages/@ember/${pkg}`), + })) + ), + + Addon('@glimmer/*', mode) + .addAlias( + '@glimmer/validator', + nodePath('@glimmer/validator/dist/modules/es2017') + ) + .addAlias( + '@glimmer/tracking/primitives/cache', + nodePath( + `ember-source/dist/packages/@glimmer/tracking/primitives/cache.js` + ) + ) + .addAlias( + /@glimmer\/tracking$/, + nodePath(`ember-source/dist/packages/@glimmer/tracking`) + ) + .addAlias( + '@glimmer/component', + '@glimmer/component/addon/-private/component' + ) + .addAlias('@glimmer/env', compatPath('glimmer-env')) + .addAliases( + emberGlimmerDepsPackages().map((pkg) => ({ + find: `@glimmer/${pkg}`, + replacement: nodePath( + `ember-source/dist/dependencies/@glimmer/${pkg}` + ), + })) + ), + + Addon('backburner', mode).addAlias( + /^backburner$/, + nodePath('backburner.js/dist/es6/backburner.js') + ), + + Addon('ember-component-manager', mode).addSelfAlias( + '@glimmer/component/addon/-private/ember-component-manager' + ), + + Addon('@ember/test-helpers', mode).addSelfAlias( + '@ember/test-helpers/addon-test-support/@ember/test-helpers' + ), + + Addon('@ember/test-waiters', mode).addSelfAlias( + '@ember/test-waiters/addon/@ember/test-waiters' + ), + + Addon('ember-compatibility-helpers', mode).addSelfAlias( + compatPath('ember-compatibility-helpers/index.ts') + ), + Addon('ember-cli-htmlbars', mode).addSelfAlias( + compatPath('ember-cli-htmlbars/index.ts') + ), + Addon('ember-cli-test-loader', mode).addNestedAlias( + 'test-support/index', + compatPath('ember-cli-test-loader/index.ts') + ), + Addon('ember-cli-version-checker', mode).addSelfAlias( + compatPath('ember-cli-version-checker/index.ts') + ), + Addon('compat-ember-decorators', mode).addNestedAlias( + 'component', + compatPath('ember-decorators/component.ts') + ), + Addon('require', mode).addSelfAlias(compatPath('require/index.ts')), + Addon('ember-template-compiler', mode).addSelfAlias( + 'node_modules/ember-source/dist/ember-template-compiler.js' + ), + Addon('ember-testing', mode) + .addAlias(/^ember-testing$/, 'ember-source/dist/packages/ember-testing') + .addAlias( + /^ember-testing\//, + 'ember-source/dist/packages/ember-testing/' + ), + Addon('@embroider/macros', mode).addSelfAlias( + compatPath('embroider-macros/index.ts') + ), + Addon('@embroider/util', mode).addSelfAlias( + compatPath('embroider-util/index.ts') + ), + Addon('@ember/string', mode).needAlias(), + ]; +} + +export function emberGlimmerDepsPackages() { + return fs + .readdirSync( + path.join( + modeModulesPath, + 'ember-source', + 'dist', + 'dependencies', + '@glimmer' + ) + ) + .filter((el) => !el.includes('env.') && !el.includes('manager.')) + .map((el) => el.replace('.js', '')); +} + +export function emberPackages() { + return fs.readdirSync( + path.join(modeModulesPath, 'ember-source', 'dist', 'packages', '@ember') + ); +} + +export function eDataPackages() { + const els = fs.readdirSync(path.join(modeModulesPath, '@ember-data')); + return els.filter((e) => e !== 'private-build-infra'); +} diff --git a/plugins/ember.ts b/plugins/ember.ts new file mode 100644 index 0000000..6194969 --- /dev/null +++ b/plugins/ember.ts @@ -0,0 +1,293 @@ +import type { UserConfig } from 'vite'; +import { removeLegacyLayout as removeLegacyLayoutPlugin } from './remove-legacy-layout'; +import { dropImportSync as dropImportSyncPlugin } from './drop-import-sync'; +import emberConcurrencyTransform from 'ember-concurrency/lib/babel-plugin-transform-ember-concurrency-async-tasks'; +import refBucketTransform from 'ember-ref-bucket/lib/ref-transform'; +import { fileURLToPath, URL } from 'node:url'; +import babel from 'vite-plugin-babel'; +import hbsResolver from './hbs-resolver'; +import gtsResolver from './gts-resolver'; +import i18nLoader from './i18n-loader'; +import { babelHotReloadPlugin } from './hot-reload'; + +const selfPath = import.meta.url + '/../..'; + +export function App(mode: string) { + const addon = new EmberAddon('app', mode); + const isProd = addon.isProd; + const isDev = addon.isDev; + const enableSourceMaps = isDev; + + addon.babelPlugins = [ + hbsResolver(isProd), + gtsResolver(isProd), + i18nLoader(), + !isDev + ? babel({ + filter: /^.*@(ember|glimmer|ember-data)\/.*\.(ts|js)$/, + babelConfig: { + babelrc: false, + configFile: false, + plugins: [ + [ + 'babel-plugin-unassert', + { + variables: [ + 'assert', + 'info', + 'warn', + 'debug', + 'deprecate', + 'debugSeal', + 'debugFreeze', + 'runInDebug', + ], + modules: ['@ember/debug'], + }, + ], + [ + '@babel/plugin-proposal-decorators', + { + version: 'legacy', + }, + ], + ['@babel/plugin-proposal-class-properties', { loose: true }], + ], + presets: ['@babel/preset-typescript'], + }, + }) + : null, + // babel config for app.js code + babel({ + // regexp to match files in src folder + filter: /^.*(src|tests)\/.*\.(ts|js|hbs|gts|gjs)$/, + babelConfig: defaultBabelConfig( + [isProd ? null : babelHotReloadPlugin].filter( + (el) => el !== null + ) as never[], + isProd, + enableSourceMaps + ), + }), + ].filter((el) => el !== null); + + return addon; +} + +export function emberAppConfig( + definedConfig: UserConfig, + addons: EmberAddon[] +) { + addons.forEach((el) => el.attachToConfig(definedConfig)); + return definedConfig; +} + +function addonBabelConfig( + plugins = [], + isProd: boolean, + enableSourceMaps = false +) { + const config = defaultBabelConfig(plugins, isProd, enableSourceMaps); + config.presets[0][1].onlyRemoveTypeImports = false; + return config; +} + +class EmberAddon { + name!: string; + aliases: unknown[] = []; + babelPlugins: unknown[] = []; + mode: string; + constructor(name: string, mode: string) { + this.name = name; + this.mode = mode; + } + get isProd() { + return this.mode === 'production'; + } + get isDev() { + return this.mode === 'development'; + } + needAlias() { + addonExport(this.name).forEach((el) => { + this.aliases.push(el); + }); + return this; + } + customModuleEntry(replacement: string, find: string = this.name) { + this.aliases.push({ + find, + replacement: nodePath(replacement), + }); + return this; + } + needBabel( + { + removeLegacyLayout, + dropImportSync, + }: { removeLegacyLayout?: boolean; dropImportSync?: boolean } = { + removeLegacyLayout: false, + dropImportSync: false, + } + ) { + const addonFilterRegexp = new RegExp(`^.*(${this.name})/.*\\.(ts|js|hbs)$`); + const extraPlugins = []; + if (removeLegacyLayout) { + extraPlugins.push(removeLegacyLayoutPlugin([this.name])); + } + if (dropImportSync) { + extraPlugins.push(dropImportSyncPlugin([this.name])); + } + const plugin = babel({ + // regexp to match files in src folder + filter: addonFilterRegexp, + babelConfig: addonBabelConfig(extraPlugins, this.isProd), + }); + this.babelPlugins.push(plugin); + return this; + } + addAliases(aliases: { find: unknown; replacement: unknown }[] = []) { + aliases.forEach((el) => { + this.aliases.push(el); + }); + return this; + } + addNestedAlias(find: unknown, replacement: unknown) { + this.aliases.push({ + find: `${this.name}/${find}`, + replacement, + }); + return this; + } + addSelfAlias(replacement: unknown) { + this.aliases.push({ + find: this.name, + replacement, + }); + return this; + } + addAlias(find: unknown, replacement: unknown) { + this.aliases.push({ + find, + replacement, + }); + return this; + } + attachToConfig(config: UserConfig) { + this.aliases.forEach((el) => { + config?.resolve?.alias.push(el); + }); + this.babelPlugins.forEach((el) => { + config?.plugins?.push(el); + }); + return this; + } +} + +export function Addon(name: string, mode: string) { + return new EmberAddon(name, mode); +} + +export function defaultBabelConfig( + plugins = [], + isProd: boolean, + enableSourceMaps = false +) { + return { + babelrc: false, + configFile: false, + sourceMaps: enableSourceMaps, + plugins: [...plugins, ...defaultBabelPlugins(isProd)], + presets: [ + [ + '@babel/preset-typescript', + { allExtensions: true, onlyRemoveTypeImports: true }, + ], + ], + }; +} + +function defaultBabelPlugins(isProd: boolean) { + return [ + // ['@babel/plugin-transform-typescript', { allowDeclareFields: true }], + [ + '@babel/plugin-proposal-decorators', + { + version: 'legacy', + }, + ], + [ + '@babel/plugin-proposal-class-properties', + { + loose: true, + }, + ], + [ + '@babel/plugin-proposal-class-static-block', + { + loose: true, + }, + ], + templateCompilationPlugin(isProd), + emberConcurrencyTransform, + ]; +} + +function templateCompilationPlugin(isProd: boolean) { + return [ + 'babel-plugin-ember-template-compilation/node', + { + compilerPath: 'ember-source/dist/ember-template-compiler.js', + targetFormat: 'wire', + outputModuleOverrides: { + '@ember/template-factory': { + createTemplateFactory: [ + 'createTemplateFactory', + 'ember-source/dist/packages/@ember/template-factory/index.js', + ], + }, + }, + transforms: [ + refBucketTransform, + isProd + ? // eslint-disable-next-line @typescript-eslint/no-unused-vars + function sampleTransform(env) { + return { + name: 'skip-prod-test-selectors', + visitor: { + ElementNode(node) { + node.attributes = node.attributes.filter( + (el) => !el.name.startsWith('data-test-') + ); + }, + }, + }; + } + : null, + ].filter((el) => el !== null), + }, + ]; +} + +function addonExport(name: string) { + return [ + { + find: `${name}/app`, + replacement: nodePath(`${name}/app`), + }, + { + find: `${name}/vendor`, + replacement: nodePath(`${name}/vendor`), + }, + { + find: name, + replacement: nodePath(`${name}/addon`), + }, + ]; +} + +export function nodePath(name: string) { + return fileURLToPath(new URL(`./node_modules/${name}`, selfPath)); +} +export function compatPath(name: string) { + return fileURLToPath(new URL(`./compat/${name}`, selfPath)); +} diff --git a/src/templates/application.hbs b/src/templates/application.hbs index 6bb173f..2fa0dd5 100644 --- a/src/templates/application.hbs +++ b/src/templates/application.hbs @@ -2,7 +2,9 @@ {{#if this.showModal}} - Ember Modal Dialog test +
+ Ember Modal Dialog test +
{{/if}} diff --git a/vite.config.ts b/vite.config.ts index 8ba1345..6de53ad 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,329 +1,119 @@ import { defineConfig } from 'vite'; -import babel from 'vite-plugin-babel'; -import fs from 'node:fs'; import { fileURLToPath, URL } from 'node:url'; import { resolve } from 'node:path'; -import hbsResolver from './plugins/hbs-resolver'; -import gtsResolver from './plugins/gts-resolver'; -import i18nLoader from './plugins/i18n-loader'; import { generateDefineConfig } from './compat/ember-data-private-build-infra/index.ts'; -import refBucketTransform from 'ember-ref-bucket/lib/ref-transform.js'; -import { babelHotReloadPlugin } from './plugins/hot-reload'; -import { removeLegacyLayout } from './plugins/remove-legacy-layout'; -import { dropImportSync } from './plugins/drop-import-sync'; -import emberConcurrencyTransform from 'ember-concurrency/lib/babel-plugin-transform-ember-concurrency-async-tasks'; +import { + Addon, + compatPath, + nodePath, + emberAppConfig, + App, +} from './plugins/ember'; +import { eDataPackages, internalPackages } from './plugins/ember-vendor'; + +const projectRoot = import.meta.url; + export default defineConfig(({ mode }) => { const isProd = mode === 'production'; const isDev = mode === 'development'; const enableSourceMaps = isDev; - return { - treeshake: { - correctVarValueBeforeDeclaration: false, - moduleSideEffects: false, - preset: 'smallest', - propertyReadSideEffects: false, - unknownGlobalSideEffects: false, - }, - build: { - sourcemap: enableSourceMaps, - rollupOptions: isDev - ? { - input: { - main: resolve(__dirname, 'index.html'), - nested: resolve(__dirname, 'tests/index.html'), - }, - } - : { - output: { - // manualChunks(id) { - // if ( - // id.includes('/compat/') || - // id.includes('@ember/') || - // id.includes('/rsvp/') || - // id.includes('/router_js/') || - // id.includes('dag-map') || - // id.includes('route-recognizer') || - // id.includes('tracked-built-ins') || - // id.includes('tracked-toolbox') || - // id.includes('@ember-data/') || - // id.includes('embroider-macros') || - // id.includes('/backburner.js/') || - // id.includes('@glimmer') || - // id.includes('ember-inflector') || - // id.includes('ember-source') - // ) { - // // chunk for ember runtime - // return 'core'; - // } - // if (id.endsWith('/src/addons/index.ts')) { - // // initial addons and application chunk - // return 'app'; - // } - // return undefined; - // }, + return emberAppConfig( + { + build: { + sourcemap: enableSourceMaps, + rollupOptions: isDev + ? { + input: { + main: resolve(__dirname, 'index.html'), + nested: resolve(__dirname, 'tests/index.html'), + }, + } + : { + treeshake: 'safest', }, + }, + server: { + port: 4200, + }, + preview: { + port: 4200, + }, + define: { + ENV_DEBUG: isProd ? false : true, + ENV_CI: false, + ...generateDefineConfig(isProd), + }, + resolve: { + extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.hbs'], + alias: [ + { + find: `@/tests/`, + replacement: fileURLToPath(new URL(`./tests/`, projectRoot)), }, + ...localScopes().map((scope) => ({ + find: `@/${scope}`, + replacement: fileURLToPath(new URL(`./src/${scope}`, projectRoot)), + })), + ], + }, + plugins: [], }, - server: { - port: 4200, - }, - preview: { - port: 4200, - }, - define: { - ENV_DEBUG: isProd ? false : true, - ENV_CI: false, - ...generateDefineConfig(isProd), - }, - resolve: { - extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.hbs'], - alias: [ - addonExport('ember-ref-bucket'), - addonExport('@ember-decorators/object'), - addonExport('@ember-decorators/component'), - addonExport('@ember-decorators/utils'), - addonExport('ember-concurrency'), - addonExport('tracked-toolbox'), - addonExport('ember-concurrency-decorators'), - addonExport('ember-bootstrap'), - addonExport('ember-inflector'), - addonExport('@ember/string'), - addonExport('ember-notify'), - addonExport('ember-modal-dialog'), - { - find: 'ember-simple-auth/use-session-setup-method', - replacement: './compat/ember-simple-auth/use-session-setup-method.ts', - }, - { - find: 'ember-template-compiler', - replacement: - 'node_modules/ember-source/dist/ember-template-compiler.js', - }, - { - find: /ember-simple-auth\/(?!(app|addon)\/)(.+)/, - replacement: 'ember-simple-auth/addon/$2', - }, - { - find: 'ember-intl/-private', - replacement: nodePath('ember-intl/addon/-private'), - }, - { - find: 'ember-modifier', - replacement: nodePath('ember-modifier/dist'), - }, - { - find: '@glimmer/validator', - replacement: nodePath('@glimmer/validator/dist/modules/es2017'), - }, - { - find: /^ember-testing$/, - replacement: 'ember-source/dist/packages/ember-testing', - }, - { - find: /^ember-testing\//, - replacement: 'ember-source/dist/packages/ember-testing/', - }, - { - find: 'ember-cli-version-checker', - replacement: compatPath('ember-cli-version-checker/index.ts'), - }, - { - find: 'compat-ember-decorators/component', - replacement: compatPath('ember-decorators/component.ts'), - }, - { - find: 'require', - replacement: compatPath('require/index.ts'), - }, - { - find: 'ember-compatibility-helpers', - replacement: compatPath('ember-compatibility-helpers/index.ts'), - }, - { - find: 'ember-cli-htmlbars', - replacement: compatPath('ember-cli-htmlbars/index.ts'), - }, - { - find: 'ember-cli-test-loader/test-support/index', - replacement: compatPath('ember-cli-test-loader/index.ts'), - }, - { - find: '@ember/test-helpers', - replacement: - '@ember/test-helpers/addon-test-support/@ember/test-helpers', - }, - { - find: '@ember/test-waiters', - replacement: '@ember/test-waiters/addon/@ember/test-waiters', - }, - { - find: '@glimmer/tracking/primitives/cache', - replacement: nodePath( - `ember-source/dist/packages/@glimmer/tracking/primitives/cache.js` - ), - }, - { - find: /@glimmer\/tracking$/, - replacement: nodePath(`ember-source/dist/packages/@glimmer/tracking`), - }, - { - find: '@embroider/macros', - replacement: compatPath('embroider-macros/index.ts'), - }, - { - find: '@embroider/util', - replacement: compatPath('embroider-util/index.ts'), - }, - { find: /^ember$/, replacement: 'ember-source/dist/packages/ember' }, - { - find: /^ember\/version$/, - replacement: 'ember-source/dist/packages/ember/version', - }, - { - find: 'ember-component-manager', - replacement: - '@glimmer/component/addon/-private/ember-component-manager', - }, - { - find: '@glimmer/component', - replacement: '@glimmer/component/addon/-private/component', - }, - { - find: '@glimmer/env', - replacement: compatPath('glimmer-env'), - }, - { - find: /^backburner$/, - replacement: nodePath('backburner.js/dist/es6/backburner.js'), - }, - { - find: `@/tests/`, - replacement: fileURLToPath(new URL(`./tests/`, import.meta.url)), - }, - ...localScopes().map((scope) => ({ - find: `@/${scope}`, - replacement: fileURLToPath( - new URL(`./src/${scope}`, import.meta.url) - ), - })), - ...emberPackages().map((pkg) => ({ - find: `@ember/${pkg}`, - replacement: nodePath(`ember-source/dist/packages/@ember/${pkg}`), - })), - ...emberGlimmerDepsPackages().map((pkg) => ({ - find: `@glimmer/${pkg}`, - replacement: nodePath( - `ember-source/dist/dependencies/@glimmer/${pkg}` - ), - })), - ...eDataPackages().map((pkg) => ({ - find: `@ember-data/${pkg}`, - replacement: nodePath(`@ember-data/${pkg}/addon`), - })), - { - find: /^ember-data$/, - replacement: 'ember-data/addon', - }, - { - find: /^@ember-data\/private-build-infra$/, - replacement: compatPath('ember-data-private-build-infra'), - }, - ].reduce((acc, el) => { - const items = Array.isArray(el) ? el : [el]; - return [...acc, ...items]; - }, []), - }, - plugins: [ - hbsResolver(isProd), - gtsResolver(isProd), - i18nLoader(), - !isDev - ? babel({ - filter: /^.*@(ember|glimmer|ember-data)\/.*\.(ts|js)$/, - babelConfig: { - babelrc: false, - configFile: false, - plugins: [ - [ - 'babel-plugin-unassert', - { - variables: [ - 'assert', - 'info', - 'warn', - 'debug', - 'deprecate', - 'debugSeal', - 'debugFreeze', - 'runInDebug', - ], - modules: ['@ember/debug'], - }, - ], - [ - '@babel/plugin-proposal-decorators', - { - version: 'legacy', - }, - ], - ['@babel/plugin-proposal-class-properties', { loose: true }], - ], - presets: ['@babel/preset-typescript'], - }, - }) - : null, - // babel config for app.js code - babel({ - // regexp to match files in src folder - filter: /^.*(src|tests)\/.*\.(ts|js|hbs|gts|gjs)$/, - babelConfig: defaultBabelConfig( - [isProd ? null : babelHotReloadPlugin].filter( - (el) => el !== null - ) as never[], - isProd, - enableSourceMaps + [ + App(mode), + + ...internalPackages(mode), + + Addon('@ember-data', mode) + .needBabel() + .addAliases( + eDataPackages().map((pkg) => ({ + find: `@ember-data/${pkg}`, + replacement: nodePath(`@ember-data/${pkg}/addon`), + })) + ) + .addAlias(/^ember-data$/, 'ember-data/addon') + .addAlias( + /^@ember-data\/private-build-infra$/, + compatPath('ember-data-private-build-infra') ), - }), - // babel config for addons - babel({ - // regexp to match files in src folder - filter: - /^.*(@ember-data|ember-responsive|ember-notify|ember-wormhole|ember-modal-dialog|ember-bootstrap|ember-ref-bucket|tracked-toolbox|ember-power-select|ember-basic-dropdown|page-title)\/.*\.(ts|js|hbs)$/, - babelConfig: addonBabelConfig( - [ - dropImportSync(['ember-modal-dialog']), - removeLegacyLayout([ - 'ember-wormhole', - 'ember-modal-dialog', - 'ember-notify', - ]), - ], - isProd + Addon('ember-notify', mode) + .needAlias() + .needBabel({ removeLegacyLayout: true }), + Addon('ember-wormhole', mode).needBabel({ removeLegacyLayout: true }), + Addon('ember-modal-dialog', mode) + .needAlias() + .needBabel({ removeLegacyLayout: true, dropImportSync: true }), + Addon('ember-responsive', mode).needBabel({ removeLegacyLayout: true }), + Addon('ember-bootstrap', mode).needAlias().needBabel(), + Addon('ember-power-select', mode).needBabel(), + Addon('ember-basic-dropdown', mode).needBabel(), + Addon('ember-ref-bucket', mode).needAlias().needBabel(), + Addon('ember-page-title', mode).needBabel(), + Addon('tracked-toolbox', mode).needAlias().needBabel(), + Addon('@ember-decorators/object', mode).needAlias(), + Addon('@ember-decorators/component', mode).needAlias(), + Addon('@ember-decorators/utils', mode).needAlias(), + Addon('ember-concurrency', mode).needAlias(), + Addon('ember-concurrency-decorators', mode).needAlias(), + Addon('ember-inflector', mode).needAlias(), + Addon('ember-modifier', mode).customModuleEntry('ember-modifier/dist'), + Addon('ember-intl', mode).addAlias( + 'ember-intl/-private', + nodePath('ember-intl/addon/-private') + ), + Addon('ember-simple-auth', mode) + .addNestedAlias( + 'use-session-setup-method', + './compat/ember-simple-auth/use-session-setup-method.ts' + ) + .addAlias( + /ember-simple-auth\/(?!(app|addon)\/)(.+)/, + 'ember-simple-auth/addon/$2' ), - }), - // ... - ].filter((el) => el !== null), - - // ... - }; + ] + ); }); -function emberGlimmerDepsPackages() { - return fs - .readdirSync('node_modules/ember-source/dist/dependencies/@glimmer') - .filter((el) => !el.includes('env.') && !el.includes('manager.')) - .map((el) => el.replace('.js', '')); -} - -function emberPackages() { - return fs.readdirSync('node_modules/ember-source/dist/packages/@ember'); -} - -function eDataPackages() { - const els = fs.readdirSync('node_modules/@ember-data'); - return els.filter((e) => e !== 'private-build-infra'); -} - function localScopes() { return [ 'addons', @@ -342,118 +132,3 @@ function localScopes() { 'utils', ]; } - -function addonExport(name: string) { - return [ - { - find: `${name}/app`, - replacement: nodePath(`${name}/app`), - }, - { - find: `${name}/vendor`, - replacement: nodePath(`${name}/vendor`), - }, - { - find: name, - replacement: nodePath(`${name}/addon`), - }, - ]; -} - -function nodePath(name: string) { - return fileURLToPath(new URL(`./node_modules/${name}`, import.meta.url)); -} -function compatPath(name: string) { - return fileURLToPath(new URL(`./compat/${name}`, import.meta.url)); -} - -function templateCompilationPlugin(isProd: boolean) { - return [ - 'babel-plugin-ember-template-compilation/node', - { - compilerPath: 'ember-source/dist/ember-template-compiler.js', - targetFormat: 'wire', - outputModuleOverrides: { - '@ember/template-factory': { - createTemplateFactory: [ - 'createTemplateFactory', - 'ember-source/dist/packages/@ember/template-factory/index.js', - ], - }, - }, - transforms: [ - refBucketTransform, - isProd - ? // eslint-disable-next-line @typescript-eslint/no-unused-vars - function sampleTransform(env) { - return { - name: 'skip-prod-test-selectors', - visitor: { - ElementNode(node) { - node.attributes = node.attributes.filter( - (el) => !el.name.startsWith('data-test-') - ); - }, - }, - }; - } - : null, - ].filter((el) => el !== null), - }, - ]; -} - -function defaultBabelPlugins(isProd: boolean) { - return [ - // ['@babel/plugin-transform-typescript', { allowDeclareFields: true }], - [ - '@babel/plugin-proposal-decorators', - { - version: 'legacy', - }, - ], - [ - '@babel/plugin-proposal-class-properties', - { - loose: true, - }, - ], - [ - '@babel/plugin-proposal-class-static-block', - { - loose: true, - }, - ], - templateCompilationPlugin(isProd), - emberConcurrencyTransform, - ]; -} - -function addonBabelConfig( - plugins = [], - isProd: boolean, - enableSourceMaps = false -) { - const config = defaultBabelConfig(plugins, isProd, enableSourceMaps); - config.presets[0][1].onlyRemoveTypeImports = false; - return config; -} - -function defaultBabelConfig( - plugins = [], - isProd: boolean, - enableSourceMaps = false -) { - return { - babelrc: false, - configFile: false, - sourceMaps: enableSourceMaps, - plugins: [...plugins, ...defaultBabelPlugins(isProd)], - presets: [ - [ - '@babel/preset-typescript', - { allExtensions: true, onlyRemoveTypeImports: true }, - ], - ], - }; -}