diff --git a/packages/compat/babel.js b/packages/compat/babel.js new file mode 100644 index 000000000..3ddfa4d58 --- /dev/null +++ b/packages/compat/babel.js @@ -0,0 +1 @@ +module.exports = require('./src/babel'); diff --git a/packages/compat/package.json b/packages/compat/package.json index 86565432f..8a1300352 100644 --- a/packages/compat/package.json +++ b/packages/compat/package.json @@ -12,6 +12,7 @@ "author": "Edward Faulkner", "main": "src/index.js", "files": [ + "babel.js", "src/**/*.js", "src/**/*.d.ts", "src/**/*.js.map" @@ -36,7 +37,8 @@ "@types/yargs": "^17.0.3", "assert-never": "^1.1.0", "babel-import-util": "^2.0.0", - "babel-plugin-ember-template-compilation": "^2.1.1", + "babel-plugin-debug-macros": "^1.0.2", + "babel-plugin-ember-template-compilation": "^2.3.0", "babel-plugin-syntax-dynamic-import": "^6.18.0", "babylon": "^6.18.0", "bind-decorator": "^1.0.11", diff --git a/packages/compat/src/audit.ts b/packages/compat/src/audit.ts index b3545f7d6..a26d4c8cc 100644 --- a/packages/compat/src/audit.ts +++ b/packages/compat/src/audit.ts @@ -1,4 +1,4 @@ -import { existsSync, readFileSync, readJSONSync } from 'fs-extra'; +import { readFileSync, readJSONSync } from 'fs-extra'; import { join, resolve as resolvePath, dirname } from 'path'; import type { AppMeta, ResolverOptions } from '@embroider/core'; import { explicitRelative, hbsToJS, locateEmbroiderWorkingDir, Resolver, RewrittenPackageCache } from '@embroider/core'; @@ -122,15 +122,6 @@ export class Audit { } let audit = new this(options.app, options); - if (options['reuse-build']) { - if (!audit.meta.babel.isParallelSafe) { - throw new BuildError( - `You can't use the ${chalk.red( - '--reuse-build' - )} option because some of your babel or HBS plugins are non-serializable` - ); - } - } return audit.run(); } @@ -153,19 +144,20 @@ export class Audit { @Memoize() private get babelConfig() { - // Depending on how the app builds, the babel config is not at the same location - let embroiderLocation = join(locateEmbroiderWorkingDir(this.originAppRoot), '_babel_config_.js'); - let config = existsSync(embroiderLocation) - ? // eslint-disable-next-line @typescript-eslint/no-require-imports - require(embroiderLocation) - : // eslint-disable-next-line @typescript-eslint/no-require-imports - require(join(this.movedAppRoot, this.meta.babel.filename)); - - config = Object.assign({}, config); - config.plugins = config.plugins.filter((p: any) => !isMacrosPlugin(p)); - - config.ast = true; - return config; + let origCwd = process.cwd(); + process.chdir(this.originAppRoot); + try { + // eslint-disable-next-line @typescript-eslint/no-require-imports + let config = require(join(this.originAppRoot, 'babel.config.cjs')); + + config = Object.assign({}, config); + config.plugins = config.plugins.filter((p: any) => !isMacrosPlugin(p)); + + config.ast = true; + return config; + } finally { + process.chdir(origCwd); + } } private get resolverParams(): ResolverOptions { diff --git a/packages/compat/src/babel.ts b/packages/compat/src/babel.ts new file mode 100644 index 000000000..407bbdc9d --- /dev/null +++ b/packages/compat/src/babel.ts @@ -0,0 +1,158 @@ +import type { PluginItem } from '@babel/core'; +import { existsSync } from 'fs'; +import { + locateEmbroiderWorkingDir, + ResolverLoader, + templateColocationPluginPath, + type TemplateColocationPluginOptions, +} from '@embroider/core'; +import { join } from 'path'; +import type { Transform } from 'babel-plugin-ember-template-compilation'; +import type { Options as ResolverTransformOptions } from './resolver-transform'; +import type { Options as AdjustImportsOptions } from './babel-plugin-adjust-imports'; + +export interface CompatBabelState { + plugins: PluginItem[]; + templateTransforms: Transform[]; + babelMacros: PluginItem[]; + templateMacros: Transform[]; +} + +function loadCompatConfig(): CompatBabelState { + let compatFile = join(locateEmbroiderWorkingDir(process.cwd()), '_babel_compat_.js'); + if (existsSync(compatFile)) { + // eslint-disable-next-line @typescript-eslint/no-require-imports + return require(compatFile); + } + return { + plugins: [], + templateTransforms: [], + babelMacros: [], + templateMacros: [], + }; +} + +const resolverLoader = new ResolverLoader(process.cwd()); + +export function pluginsFromV1Addons() { + let config = loadCompatConfig(); + return config.plugins; +} + +export function transformsFromV1Addons() { + let config = loadCompatConfig(); + return config.templateTransforms; +} + +export function looseModeSupport(): Transform { + const { resolver } = resolverLoader; + let opts: ResolverTransformOptions = { + appRoot: resolver.options.appRoot, + emberVersion: resolver.options.emberVersion, + }; + return [require.resolve('./resolver-transform'), opts]; +} + +export function templateMacros() { + let config = loadCompatConfig(); + return config.templateMacros; +} + +export function babelMacros() { + let config = loadCompatConfig(); + return config.babelMacros; +} + +export function adjustImports() { + let pluginConfig: AdjustImportsOptions = { + appRoot: resolverLoader.resolver.options.appRoot, + }; + return [require.resolve('./babel-plugin-adjust-imports'), pluginConfig]; +} + +export function oldDebugMacros(): PluginItem[] { + let debugMacros = require.resolve('babel-plugin-debug-macros'); + return [ + [ + debugMacros, + { + flags: [ + { + source: '@glimmer/env', + flags: { + DEBUG: true, + CI: false, + }, + }, + ], + debugTools: { + isDebug: true, + source: '@ember/debug', + assertPredicateIndex: 1, + }, + externalizeHelpers: { + module: '@ember/debug', + }, + }, + '@ember/debug stripping', + ], + [ + debugMacros, + { + externalizeHelpers: { + module: '@ember/application/deprecations', + }, + debugTools: { + isDebug: true, + source: '@ember/application/deprecations', + assertPredicateIndex: 1, + }, + }, + '@ember/application/deprecations stripping', + ], + ]; +} + +export function templateColocation(): PluginItem { + let colocationOptions: TemplateColocationPluginOptions = { + appRoot: resolverLoader.resolver.options.appRoot, + + // This extra weirdness is a compromise in favor of build performance. + // + // 1. When auto-upgrading an addon from v1 to v2, we definitely want to + // run any custom AST transforms in stage1. + // + // 2. In general case, AST transforms are allowed to manipulate Javascript + // scope. This means that running transforms -- even when we're doing + // source-to-source compilation that emits handlebars and not wire + // format -- implies changing .hbs files into .js files. + // + // 3. So stage1 may need to rewrite .hbs to .hbs.js (to avoid colliding + // with an existing co-located .js file). + // + // 4. But stage1 doesn't necessarily want to run babel over the + // corresponding JS file. Most of the time, that's just an + // unnecessarily expensive second parse. (We only run it in stage1 to + // eliminate an addon's custom babel plugins, and many addons don't + // have any.) + // + // 5. Therefore, the work of template-colocation gets defered until here, + // and it may see co-located templates named `.hbs.js` instead of the + // usual `.hbs. + templateExtensions: ['.hbs', '.hbs.js'], + + // All of the above only applies to auto-upgraded packages that were + // authored in v1. V2 packages don't get any of this complexity, they're + // supposed to take care of colocating their own templates explicitly. + packageGuard: true, + }; + return [templateColocationPluginPath, colocationOptions]; +} + +export function babelCompatSupport(): PluginItem[] { + return [...babelMacros(), adjustImports(), ...oldDebugMacros(), templateColocation(), ...pluginsFromV1Addons()]; +} + +export function templateCompatSupport(): Transform[] { + return [...transformsFromV1Addons(), ...templateMacros(), looseModeSupport()]; +} diff --git a/packages/compat/src/compat-app-builder.ts b/packages/compat/src/compat-app-builder.ts index d2df7f03d..4d143fed4 100644 --- a/packages/compat/src/compat-app-builder.ts +++ b/packages/compat/src/compat-app-builder.ts @@ -1,29 +1,15 @@ -import type { AddonPackage, Engine, TemplateColocationPluginOptions } from '@embroider/core'; -import { - explicitRelative, - warn, - jsHandlebarsCompile, - templateColocationPluginPath, - cacheBustingPluginVersion, - cacheBustingPluginPath, - Resolver, - locateEmbroiderWorkingDir, -} from '@embroider/core'; +import type { AddonPackage, Engine } from '@embroider/core'; +import { explicitRelative, locateEmbroiderWorkingDir } from '@embroider/core'; import { resolve as resolvePath } from 'path'; import type Options from './options'; import type { CompatResolverOptions } from './resolver-transform'; import type { PackageRules } from './dependency-rules'; import { activePackageRules } from './dependency-rules'; import flatMap from 'lodash/flatMap'; -import cloneDeep from 'lodash/cloneDeep'; import bind from 'bind-decorator'; import { outputJSONSync, writeFileSync, realpathSync } from 'fs-extra'; -import type { Options as EtcOptions } from 'babel-plugin-ember-template-compilation'; -import type { Options as ResolverTransformOptions } from './resolver-transform'; -import type { Options as AdjustImportsOptions } from './babel-plugin-adjust-imports'; -import { makePortable } from '@embroider/core/src/portable-babel-config'; import type { PortableHint } from '@embroider/core/src/portable'; -import { maybeNodeModuleVersion } from '@embroider/core/src/portable'; +import { maybeNodeModuleVersion, Portable } from '@embroider/core/src/portable'; import { Memoize } from 'typescript-memoize'; import { join, dirname } from 'path'; import resolve from 'resolve'; @@ -31,10 +17,10 @@ import type ContentForConfig from './content-for-config'; import type { V1Config } from './v1-config'; import type { Package } from '@embroider/core'; import { readdirSync } from 'fs-extra'; -import type { TransformOptions } from '@babel/core'; -import { MacrosConfig } from '@embroider/macros/src/node'; import type CompatApp from './compat-app'; +import type { CompatBabelState } from './babel'; +import { MacrosConfig } from '@embroider/macros/src/node'; // This exists during the actual broccoli build step. As opposed to CompatApp, // which also exists during pipeline-construction time. @@ -171,89 +157,12 @@ export class CompatAppBuilder { options, autoRun: this.compatApp.autoRun, staticAppPaths: this.options.staticAppPaths, + emberVersion: this.emberVersion(), }; return config; } - @Memoize() - private async babelConfig(resolverConfig: CompatResolverOptions) { - let babel = cloneDeep(this.compatApp.babelConfig()); - - if (!babel.plugins) { - babel.plugins = []; - } - - // Our stage3 code is always allowed to use dynamic import. We may emit it - // ourself when splitting routes. - babel.plugins.push(require.resolve('@babel/plugin-syntax-dynamic-import')); - - // https://github.com/webpack/webpack/issues/12154 - babel.plugins.push(require.resolve('./rename-require-plugin')); - - babel.plugins.push([ - require.resolve('babel-plugin-ember-template-compilation'), - await this.etcOptions(resolverConfig), - ]); - - // this is @embroider/macros configured for full stage3 resolution - babel.plugins.push(...this.compatApp.macrosConfig.babelPluginConfig()); - - let colocationOptions: TemplateColocationPluginOptions = { - appRoot: this.origAppPackage.root, - - // This extra weirdness is a compromise in favor of build performance. - // - // 1. When auto-upgrading an addon from v1 to v2, we definitely want to - // run any custom AST transforms in stage1. - // - // 2. In general case, AST transforms are allowed to manipulate Javascript - // scope. This means that running transforms -- even when we're doing - // source-to-source compilation that emits handlebars and not wire - // format -- implies changing .hbs files into .js files. - // - // 3. So stage1 may need to rewrite .hbs to .hbs.js (to avoid colliding - // with an existing co-located .js file). - // - // 4. But stage1 doesn't necessarily want to run babel over the - // corresponding JS file. Most of the time, that's just an - // unnecessarily expensive second parse. (We only run it in stage1 to - // eliminate an addon's custom babel plugins, and many addons don't - // have any.) - // - // 5. Therefore, the work of template-colocation gets defered until here, - // and it may see co-located templates named `.hbs.js` instead of the - // usual `.hbs. - templateExtensions: ['.hbs', '.hbs.js'], - - // All of the above only applies to auto-upgraded packages that were - // authored in v1. V2 packages don't get any of this complexity, they're - // supposed to take care of colocating their own templates explicitly. - packageGuard: true, - }; - babel.plugins.push([templateColocationPluginPath, colocationOptions]); - - babel.plugins.push([ - require.resolve('./babel-plugin-adjust-imports'), - (() => { - let pluginConfig: AdjustImportsOptions = { - appRoot: resolverConfig.appRoot, - }; - return pluginConfig; - })(), - ]); - - // we can use globally shared babel runtime by default - babel.plugins.push([ - require.resolve('@babel/plugin-transform-runtime'), - { absoluteRuntime: __dirname, useESModules: true, regenerator: false }, - ]); - - const portable = makePortable(babel, { basedir: this.origAppPackage.root }, this.portableHints); - addCachablePlugin(portable.config); - return portable; - } - // recurse to find all active addons that don't cross an engine boundary. // Inner engines themselves will be returned, but not those engines' children. // The output set's insertion order is the proper ember-cli compatible @@ -338,47 +247,7 @@ export class CompatAppBuilder { this.addContentForConfig(contentForConfig); this.addEmberEnvConfig(config.EmberENV); this.outputAppBootError(config.modulePrefix, config.APP, contentForConfig); - let babelConfig = await this.babelConfig(resolverConfig); - this.addBabelConfig(babelConfig); - this.addMacrosConfig(this.compatApp.macrosConfig.babelPluginConfig()[0]); - } - - private async etcOptions(resolverConfig: CompatResolverOptions): Promise { - let transforms = this.compatApp.htmlbarsPlugins; - - let { plugins: macroPlugins, setConfig } = MacrosConfig.transforms(); - setConfig(this.compatApp.macrosConfig); - for (let macroPlugin of macroPlugins) { - transforms.push(macroPlugin as any); - } - - if ( - this.options.staticComponents || - this.options.staticHelpers || - this.options.staticModifiers || - (globalThis as any).embroider_audit - ) { - let opts: ResolverTransformOptions = { - appRoot: resolverConfig.appRoot, - emberVersion: this.emberVersion(), - }; - transforms.push([require.resolve('./resolver-transform'), opts]); - } - - let resolver = new Resolver(resolverConfig); - let resolution = await resolver.nodeResolve( - 'ember-source/vendor/ember/ember-template-compiler', - resolvePath(this.origAppPackage.root, 'package.json') - ); - if (resolution.type !== 'real') { - throw new Error(`bug: unable to resolve ember-template-compiler from ${this.origAppPackage.root}`); - } - - return { - transforms, - compilerPath: resolution.filename, - enableLegacyModules: ['ember-cli-htmlbars', 'ember-cli-htmlbars-inline-precompile', 'htmlbars-inline-precompile'], - }; + this.addBabelCompat(); } @Memoize() @@ -401,18 +270,31 @@ export class CompatAppBuilder { }); } - private addBabelConfig(pconfig: { config: TransformOptions; isParallelSafe: boolean }) { - if (!pconfig.isParallelSafe) { - warn('Your build is slower because some babel plugins are non-serializable'); + private addBabelCompat() { + let plugins = this.compatApp.extraBabelPlugins(); + let templateTransforms = this.compatApp.htmlbarsPlugins; + let babelMacros = this.compatApp.macrosConfig.babelPluginConfig(); + let { plugins: templateMacros, setConfig } = MacrosConfig.transforms(); + setConfig(this.compatApp.macrosConfig); + + let config: CompatBabelState = { + plugins, + templateTransforms, + babelMacros, + templateMacros: templateMacros as any, + }; + + let portableConfig = new Portable({ hints: this.portableHints }).dehydrate(config); + if (!portableConfig.isParallelSafe) { + throw new Error(`non-serializble babel plugins or AST transforms found in your app`); } + writeFileSync( - join(locateEmbroiderWorkingDir(this.compatApp.root), '_babel_config_.js'), - `module.exports = ${JSON.stringify(pconfig.config, null, 2)}`, - 'utf8' - ); - writeFileSync( - join(locateEmbroiderWorkingDir(this.compatApp.root), '_babel_filter_.js'), - babelFilterTemplate({ skipBabel: this.options.skipBabel, appRoot: this.origAppPackage.root }), + join(locateEmbroiderWorkingDir(this.compatApp.root), '_babel_compat_.js'), + ` + const { Portable } = require('@embroider/core/src/portable'); + module.exports = new Portable().hydrate(${JSON.stringify(portableConfig.value, null, 2)}); + `, 'utf8' ); } @@ -464,12 +346,6 @@ export class CompatAppBuilder { }); } - private addMacrosConfig(macrosConfig: any) { - outputJSONSync(join(locateEmbroiderWorkingDir(this.compatApp.root), 'macros-config.json'), macrosConfig, { - spaces: 2, - }); - } - // Classic addons providing custom content-for "app-boot" is no longer supported. // The purpose of this error message is to help developers to move the classic addons code. // Developers can deactivate it with useAddonAppBoot build option. @@ -515,35 +391,3 @@ function defaultAddonPackageRules(): PackageRules[] { .filter(Boolean) .reduce((a, b) => a.concat(b), []); } - -const babelFilterTemplate = jsHandlebarsCompile(` -const { babelFilter } = require(${JSON.stringify(require.resolve('@embroider/core'))}); -module.exports = babelFilter({{json-stringify skipBabel}}, "{{js-string-escape appRoot}}"); -`) as (params: { skipBabel: Options['skipBabel']; appRoot: string }) => string; - -function addCachablePlugin(babelConfig: TransformOptions) { - if (Array.isArray(babelConfig.plugins) && babelConfig.plugins.length > 0) { - const plugins = Object.create(null); - plugins[cacheBustingPluginPath] = cacheBustingPluginVersion; - - for (const plugin of babelConfig.plugins) { - let absolutePathToPlugin: string; - if (Array.isArray(plugin) && typeof plugin[0] === 'string') { - absolutePathToPlugin = plugin[0] as string; - } else if (typeof plugin === 'string') { - absolutePathToPlugin = plugin; - } else { - throw new Error(`[Embroider] a babel plugin without an absolute path was from: ${plugin}`); - } - - plugins[absolutePathToPlugin] = maybeNodeModuleVersion(absolutePathToPlugin); - } - - babelConfig.plugins.push([ - cacheBustingPluginPath, - { - plugins, - }, - ]); - } -} diff --git a/packages/compat/src/compat-app.ts b/packages/compat/src/compat-app.ts index 7b01dda28..b260968c0 100644 --- a/packages/compat/src/compat-app.ts +++ b/packages/compat/src/compat-app.ts @@ -16,12 +16,12 @@ import type { AddonMeta, EmberAppInstance, OutputFileToInputFileMap, PackageInfo import { writeJSONSync, ensureDirSync, copySync, pathExistsSync, existsSync, writeFileSync } from 'fs-extra'; import AddToTree from './add-to-tree'; import DummyPackage from './dummy-package'; -import type { TransformOptions } from '@babel/core'; +import type { PluginItem, TransformOptions } from '@babel/core'; import { isEmbroiderMacrosPlugin, MacrosConfig } from '@embroider/macros/src/node'; import resolvePackagePath from 'resolve-package-path'; import Concat from 'broccoli-concat'; import mapKeys from 'lodash/mapKeys'; -import { isEmberAutoImportDynamic, isInlinePrecompilePlugin } from './detect-babel-plugins'; +import { isEmberAutoImportDynamic, isHtmlbarColocation, isInlinePrecompilePlugin } from './detect-babel-plugins'; import loadAstPlugins from './prepare-htmlbars-ast-plugins'; import { readFileSync } from 'fs'; import semver from 'semver'; @@ -181,63 +181,31 @@ export default class CompatApp { } @Memoize() - babelConfig(): TransformOptions { - // this finds all the built-in babel configuration that comes with ember-cli-babel - const babelAddon = (this.legacyEmberAppInstance.project as any).findAddonByName('ember-cli-babel'); - const babelConfig = babelAddon.buildBabelOptions({ - 'ember-cli-babel': { - ...this.legacyEmberAppInstance.options['ember-cli-babel'], - includeExternalHelpers: true, - compileModules: false, - disableDebugTooling: false, - disablePresetEnv: false, - disableEmberModulesAPIPolyfill: false, - }, - }); - - let plugins = babelConfig.plugins as any[]; - let presets = babelConfig.presets; - - // this finds any custom babel configuration that's on the app (either - // because the app author explicitly added some, or because addons have - // pushed plugins into it). - let appBabel = this.legacyEmberAppInstance.options.babel; + extraBabelPlugins(): PluginItem[] { + // this finds any custom babel plugins on the app (either because the app + // author explicitly added some, or because addons have pushed plugins into + // it). + let appBabel: TransformOptions = this.legacyEmberAppInstance.options.babel; if (appBabel) { if (appBabel.plugins) { - plugins = appBabel.plugins.concat(plugins); - } - if (appBabel.presets) { - presets = appBabel.presets.concat(presets); + return appBabel.plugins.filter(p => { + // even if the app was using @embroider/macros, we drop it from the config + // here in favor of our globally-configured one. + return ( + !isEmbroiderMacrosPlugin(p) && + // similarly, if the app was already using an inline template compiler + // babel plugin, we remove it here because we have our own + // always-installed version of that (v2 addons are allowed to assume it + // will be present in the final app build, the app doesn't get to turn + // that off or configure it.) + !isInlinePrecompilePlugin(p) && + !isEmberAutoImportDynamic(p) && + !isHtmlbarColocation(p) + ); + }); } } - - plugins = plugins.filter(p => { - // even if the app was using @embroider/macros, we drop it from the config - // here in favor of our globally-configured one. - return ( - !isEmbroiderMacrosPlugin(p) && - // similarly, if the app was already using an inline template compiler - // babel plugin, we remove it here because we have our own - // always-installed version of that (v2 addons are allowed to assume it - // will be present in the final app build, the app doesn't get to turn - // that off or configure it.) - !isInlinePrecompilePlugin(p) && - !isEmberAutoImportDynamic(p) - ); - }); - - const config: TransformOptions = { - babelrc: false, - plugins, - presets, - // this is here because broccoli-middleware can't render a codeFrame full - // of terminal codes. It would be nice to add something like - // https://github.com/mmalecki/ansispan to broccoli-middleware so we can - // leave color enabled. - highlightCode: false, - }; - - return config; + return []; } @Memoize() diff --git a/packages/compat/src/detect-babel-plugins.ts b/packages/compat/src/detect-babel-plugins.ts index 894c8bc62..f27a5a07c 100644 --- a/packages/compat/src/detect-babel-plugins.ts +++ b/packages/compat/src/detect-babel-plugins.ts @@ -59,6 +59,19 @@ export function isInlinePrecompilePlugin(item: PluginItem) { return false; } +export function isHtmlbarColocation(item: PluginItem): boolean { + let pluginPath: string; + if (typeof item === 'string') { + pluginPath = item; + } else if (Array.isArray(item) && item.length > 0 && typeof item[0] === 'string') { + pluginPath = item[0]; + } else { + return false; + } + + return pluginPath.includes(join(sep, 'ember-cli-htmlbars', sep, 'lib', sep, 'colocated-babel-plugin')); +} + function matchesSourceFile(filename: string) { return Boolean(htmlbarPathMatches.find(match => filename.endsWith(match))); } diff --git a/packages/compat/src/rename-require-plugin.ts b/packages/compat/src/rename-require-plugin.ts deleted file mode 100644 index 71382e1e5..000000000 --- a/packages/compat/src/rename-require-plugin.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { types as t } from '@babel/core'; -import type { NodePath } from '@babel/traverse'; - -export default function inlineHBSTransform(): unknown { - return { - visitor: { - ImportDefaultSpecifier(path: NodePath) { - if (path.node.local.name === 'require') { - path.scope.rename('require'); - } - }, - }, - }; -} diff --git a/packages/compat/tests/audit.test.ts b/packages/compat/tests/audit.test.ts index 4634d5cad..7ab9ef8f2 100644 --- a/packages/compat/tests/audit.test.ts +++ b/packages/compat/tests/audit.test.ts @@ -8,9 +8,9 @@ import { Audit } from '../src/audit'; import type { CompatResolverOptions } from '../src/resolver-transform'; import type { TransformOptions } from '@babel/core'; import type { Options as InlinePrecompileOptions } from 'babel-plugin-ember-template-compilation'; -import { makePortable } from '@embroider/core/src/portable-babel-config'; import type { Transform } from 'babel-plugin-ember-template-compilation'; import type { Options as ResolverTransformOptions } from '../src/resolver-transform'; +import { resolve } from 'path'; describe('audit', function () { throwOnWarnings(); @@ -19,12 +19,23 @@ describe('audit', function () { async function audit() { await app.write(); - let audit = new Audit(app.baseDir); - return await audit.run(); + let origCwd = process.cwd(); + try { + process.chdir(app.baseDir); + let audit = new Audit(app.baseDir); + return await audit.run(); + } finally { + process.chdir(origCwd); + } } beforeEach(async function () { app = new Project('audit-this-app'); + app.linkDevDependency('babel-plugin-ember-template-compilation', { + baseDir: __dirname, + }); + app.linkDevDependency('@embroider/compat', { target: resolve(__dirname, '..') }); + app.linkDevDependency('@embroider/core', { baseDir: __dirname }); const resolvableExtensions = ['.js', '.hbs']; @@ -53,6 +64,7 @@ describe('audit', function () { resolvableExtensions, autoRun: true, staticAppPaths: [], + emberVersion: '4.0.0', }; let babel: TransformOptions = { @@ -77,11 +89,25 @@ describe('audit', function () { 'index.html': ``, 'app.js': `import Hello from './hello.hbs';`, 'hello.hbs': ``, - 'babel_config.js': `module.exports = ${JSON.stringify( - makePortable(babel, { basedir: '.' }, []).config, - null, - 2 - )}`, + 'babel.config.cjs': ` + const { + babelCompatSupport, + templateCompatSupport, + } = require("@embroider/compat/babel"); + module.exports = { + plugins: [ + ['babel-plugin-ember-template-compilation', { + transforms: [ + ...templateCompatSupport(), + ], + enableLegacyModules: [ + 'ember-cli-htmlbars' + ] + }], + ...babelCompatSupport() + ] + } + `, node_modules: { '.embroider': { 'resolver.json': JSON.stringify(resolverConfig), @@ -92,12 +118,6 @@ describe('audit', function () { type: 'app', version: 2, assets: ['index.html'], - babel: { - filename: 'babel_config.js', - isParallelSafe: true, - majorVersion: 7, - fileFilter: 'babel_filter.js', - }, 'root-url': '/', 'auto-upgraded': true, }; diff --git a/packages/core/package.json b/packages/core/package.json index 23bb444cc..1568c1fc2 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -27,7 +27,7 @@ "@embroider/reverse-exports": "workspace:*", "@embroider/shared-internals": "workspace:*", "assert-never": "^1.2.1", - "babel-plugin-ember-template-compilation": "^2.1.1", + "babel-plugin-ember-template-compilation": "^2.3.0", "broccoli-node-api": "^1.7.0", "broccoli-persistent-filter": "^3.1.2", "broccoli-plugin": "^4.0.7", diff --git a/packages/core/src/module-resolver.ts b/packages/core/src/module-resolver.ts index 1eca30b01..eed011d9f 100644 --- a/packages/core/src/module-resolver.ts +++ b/packages/core/src/module-resolver.ts @@ -97,6 +97,7 @@ export interface Options { amdCompatibility: Required; autoRun: boolean; staticAppPaths: string[]; + emberVersion: string; } // TODO: once we can remove the stage2 entrypoint this type can get streamlined diff --git a/packages/core/src/options.ts b/packages/core/src/options.ts index b89b5672f..d957c6b26 100644 --- a/packages/core/src/options.ts +++ b/packages/core/src/options.ts @@ -59,14 +59,6 @@ export default interface Options { // route templates, and controllers which are governed by splitAtRoutes). staticAppPaths?: string[]; - // By default, all modules that get imported into the app go through Babel, so - // that all code will conform with your Babel targets. This option allows you - // to turn Babel off for a particular package. You might need this to work - // around a transpiler bug or you might use this as a build-performance - // optimization if you've manually verified that a particular package doesn't - // need transpilation to be safe in your target browsers. - skipBabel?: { package: string; semverRange?: string }[]; - // This is a performance optimization that can help you avoid the "Your build // is slower because some babel plugins are non-serializable" penalty. If you // provide the locations of known non-serializable objects, we can discover @@ -131,7 +123,6 @@ export function optionsWithDefaults(options?: Options): Required { staticComponents: false, splitAtRoutes: [], staticAppPaths: [], - skipBabel: [], pluginHints: [], amdCompatibility: 'cjs' as const, }; diff --git a/packages/core/src/portable-babel-config.ts b/packages/core/src/portable-babel-config.ts deleted file mode 100644 index f9dc7b3ea..000000000 --- a/packages/core/src/portable-babel-config.ts +++ /dev/null @@ -1,138 +0,0 @@ -import type { TransformOptions } from '@babel/core'; -import { join } from 'path'; -import resolve from 'resolve'; -import type { PortableHint } from './portable'; -import { Portable } from './portable'; - -export type ResolveOptions = { basedir: string } | { resolve: (name: string) => any }; - -export function makePortable( - config: TransformOptions, - resolveOptions: ResolveOptions, - hints: PortableHint[] -): { config: TransformOptions; isParallelSafe: boolean } { - return new PortableBabelConfig(resolveOptions, hints).convert(config); -} - -class PortableBabelConfig { - private resolve: (name: string) => any; - private basedir: string | undefined; - - constructor(resolveOptions: ResolveOptions, private hints: PortableHint[]) { - if ('resolve' in resolveOptions) { - this.resolve = resolveOptions.resolve; - } else { - this.basedir = resolveOptions.basedir; - this.resolve = (name: string) => resolve.sync(name, { basedir: resolveOptions.basedir }); - } - } - - convert(config: TransformOptions): { config: TransformOptions; isParallelSafe: boolean } { - let portable: Portable = new Portable({ - hints: this.hints, - dehydrate: (value: any, accessPath: string[]) => { - // this custom dehydrate hook handles babel plugins & presets. If we're - // not looking at plugins or presets, continue with stock Portable - // behavior - if (accessPath.length !== 2 || (accessPath[0] !== 'plugins' && accessPath[0] !== 'presets')) { - return undefined; - } - - // standardize to always handle an array - if (!Array.isArray(value)) { - value = [value]; - } - - let [plugin, argument, asName] = value; - - // string plugins need to get resolved correctly into absolute paths, - // so they will really be portable - if (typeof plugin === 'string') { - plugin = this.resolveBabelPlugin(plugin); - } - - // next we deal with serializability. Our Portable system already - // understands the protocol used by ember-cli-babel to identify plugin - // classes and get back to their serializable forms, so this will - // handle that case. - let dehydrated = portable.dehydrate([plugin, argument, asName], accessPath.concat('_internal')); - - if (dehydrated.needsHydrate) { - // we can eliminate the need for rehydration by going through our own - // portable babel launcher - return { - value: [ - join(__dirname, 'portable-babel-launcher.js'), - { module: dehydrated.value[0], arg: dehydrated.value[1], hints: this.hints }, - dehydrated.value[2] || `portable-babel-launcher-${accessPath[1]}`, - ], - needsHydrate: false, - isParallelSafe: dehydrated.isParallelSafe, - }; - } else { - // trim back down our array, because trailing undefined will get - // converted into null via json.stringify, and babel will complain - // about that. - while (dehydrated.value.length > 0 && dehydrated.value[dehydrated.value.length - 1] == null) { - dehydrated.value.pop(); - } - if (dehydrated.value.length === 1) { - dehydrated.value = dehydrated.value[0]; - } - return { - value: dehydrated.value, - needsHydrate: dehydrated.needsHydrate, - isParallelSafe: dehydrated.isParallelSafe, - }; - } - }, - }); - let result = portable.dehydrate(config); - if (result.needsHydrate) { - throw new Error(`bug: portable babel configs aren't supposed to need hydration`); - } - return { config: result.value, isParallelSafe: result.isParallelSafe }; - } - - // babel lets you use relative paths, absolute paths, package names, and - // package name shorthands. - // - // my-plugin -> my-plugin - // my-plugin -> babel-plugin-my-plugin - // @me/thing -> @me/thing - // @me/thing -> @me/babel-plugin-thing - // ./here -> /your/app/here - // /tmp/there -> /tmp/there - // - private resolveBabelPlugin(name: string) { - try { - return this.resolve(name); - } catch (err) { - if (err.code !== 'MODULE_NOT_FOUND') { - throw err; - } - if (name.startsWith('.') || name.startsWith('/')) { - throw err; - } - try { - let expanded; - if (name.startsWith('@')) { - let [space, pkg, ...rest] = name.split('/'); - expanded = [space, `babel-plugin-${pkg}`, ...rest].join('/'); - } else { - expanded = `babel-plugin-${name}`; - } - return this.resolve(expanded); - } catch (err2) { - if (err2.code !== 'MODULE_NOT_FOUND') { - throw err2; - } - if (this.basedir) { - throw new Error(`unable to resolve babel plugin ${name} from ${this.basedir}`); - } else { - throw new Error(`unable to resolve babel plugin ${name}`); - } - } - } - } -} diff --git a/packages/core/src/portable-babel-launcher.ts b/packages/core/src/portable-babel-launcher.ts deleted file mode 100644 index 12e383845..000000000 --- a/packages/core/src/portable-babel-launcher.ts +++ /dev/null @@ -1,83 +0,0 @@ -import type { PortableHint } from './portable'; -import { Portable } from './portable'; - -export default function babelLauncher( - this: any, - babel: any, - launch: { module: any; arg: any; hints: PortableHint[] }, - key: string -) { - let p = new Portable({ hints: launch.hints }); - let hydrated = p.hydrate(launch); - let module; - if (typeof hydrated.module === 'string') { - // eslint-disable-next-line @typescript-eslint/no-require-imports - module = require(hydrated.module); - if (module.__esModule) { - module = module.default; - } - } else { - module = hydrated.module; - } - - let plugin = module.call(this, babel, hydrated.arg, key); - let innerStates = new WeakMap(); - - function convertState(state: any) { - let innerState = innerStates.get(state); - if (!innerState) { - innerState = Object.assign({}, state, { opts: hydrated.arg }); - innerStates.set(state, innerState); - } - return innerState; - } - - function wrap1(original: any) { - if (typeof original === 'function') { - return function (this: any, file: any) { - return original.call(convertState(this), file); - }; - } - } - - function wrap2(original: Function) { - return function (this: any, path: any, state: any) { - return original.call(convertState(this), path, convertState(state)); - }; - } - - let visitorProxy = { - get(target: any, prop: string) { - let original = target[prop]; - if (typeof original === 'function') { - return wrap2(original); - } - if (original && typeof original === 'object') { - let wrapped: any = {}; - if (typeof original.exit === 'function') { - wrapped.exit = wrap2(original.exit); - } - if (typeof original.enter === 'function') { - wrapped.enter = wrap2(original.enter); - } - return wrapped; - } - return original; - }, - }; - - return new Proxy(plugin, { - get(target, prop) { - let original = target[prop]; - switch (prop) { - case 'pre': - case 'post': - return wrap1(original); - case 'visitor': - return new Proxy(original, visitorProxy); - default: - return original; - } - }, - }); -} diff --git a/packages/core/tests/portable-babel-config.test.ts b/packages/core/tests/portable-babel-config.test.ts deleted file mode 100644 index 1895b3581..000000000 --- a/packages/core/tests/portable-babel-config.test.ts +++ /dev/null @@ -1,290 +0,0 @@ -import type { PluginItem, TransformOptions } from '@babel/core'; -import { makePortable } from '../src/portable-babel-config'; -import { join, sep, resolve } from 'path'; -import exampleTarget from './example-target'; -import { Portable, protocol } from '../src/portable'; - -function resolvableNames(...names: string[]) { - return { - resolve(name: string) { - if (name.startsWith('/')) { - return name; - } - if (name.startsWith('.')) { - return join('/notional-base-dir', name); - } - if (names.includes(name)) { - return join('/notional-base-dir/node_modules', name, 'index.js'); - } - let e = new Error(`stub resolver failure for ${name}`); - (e as any).code = 'MODULE_NOT_FOUND'; - throw e; - }, - }; -} - -function loadParallelSafe({ config, isParallelSafe }: { config: TransformOptions; isParallelSafe: boolean }): any { - if (!isParallelSafe) { - throw new Error(`not parallel safe`); - } - delete (global as any)[protocol]; - return load({ config }); -} - -function load({ config }: { config: TransformOptions }): any { - return JSON.parse(JSON.stringify(config)); -} - -function assertPortableBabelLauncher(plugin: PluginItem): { module: any; arg: any } { - if (!Array.isArray(plugin)) { - throw new Error(`expected array plugin not ${plugin}`); - } - expect(plugin[0]).toBe(resolve(__dirname, '../src/portable-babel-launcher.js')); - return new Portable().hydrate(plugin[1]); -} - -describe('portable-babel-config', () => { - test('absolute path', () => { - let config = makePortable( - { - plugins: ['/path/to/some/plugin.js'], - }, - resolvableNames(), - [] - ); - expect(loadParallelSafe(config).plugins).toEqual(['/path/to/some/plugin.js']); - }); - - test('local path', () => { - let config = makePortable( - { - plugins: ['./path/to/some/plugin.js'], - }, - resolvableNames(), - [] - ); - expect(loadParallelSafe(config).plugins).toEqual([ - '/notional-base-dir/path/to/some/plugin.js'.split('/').join(sep), - ]); - }); - - test('package name', () => { - let config = makePortable( - { - plugins: ['my-package'], - }, - resolvableNames('my-package'), - [] - ); - expect(loadParallelSafe(config).plugins).toEqual([ - '/notional-base-dir/node_modules/my-package/index.js'.split('/').join(sep), - ]); - }); - - test('package name shorthand', () => { - let config = makePortable( - { - plugins: ['my-package'], - }, - resolvableNames('babel-plugin-my-package'), - [] - ); - expect(loadParallelSafe(config).plugins).toEqual([ - '/notional-base-dir/node_modules/babel-plugin-my-package/index.js'.split('/').join(sep), - ]); - }); - - test('namespaced package name', () => { - let config = makePortable( - { - plugins: ['@me/my-package'], - }, - resolvableNames('@me/my-package'), - [] - ); - expect(loadParallelSafe(config).plugins).toEqual([ - '/notional-base-dir/node_modules/@me/my-package/index.js'.split('/').join(sep), - ]); - }); - - test('namespaced package name shorthand', () => { - let config = makePortable( - { - plugins: ['@me/my-package'], - }, - resolvableNames('@me/babel-plugin-my-package'), - [] - ); - expect(loadParallelSafe(config).plugins).toEqual([ - '/notional-base-dir/node_modules/@me/babel-plugin-my-package/index.js'.split('/').join(sep), - ]); - }); - - test('resolves name with json-safe config', () => { - let config = makePortable( - { - plugins: [['my-package', { theOptions: 'cool' }]], - }, - resolvableNames('babel-plugin-my-package'), - [] - ); - expect(loadParallelSafe(config).plugins).toEqual([ - ['/notional-base-dir/node_modules/babel-plugin-my-package/index.js'.split('/').join(sep), { theOptions: 'cool' }], - ]); - }); - - test('resolves name with arbitrary config', () => { - let options = { - precompile() { - return 'cool'; - }, - }; - let config = makePortable( - { - plugins: [['my-package', options]], - }, - resolvableNames('babel-plugin-my-package'), - [] - ); - expect(config.isParallelSafe).toBeFalsy(); - let { module, arg } = assertPortableBabelLauncher(load(config).plugins[0]); - expect(module).toBe('/notional-base-dir/node_modules/babel-plugin-my-package/index.js'); - expect(arg).toEqual(options); - }); - - test('passes through bare function', () => { - let func = function () {}; - let config = makePortable( - { - plugins: [func], - }, - resolvableNames(), - [] - ); - expect(config.isParallelSafe).toBeFalsy(); - let { module } = assertPortableBabelLauncher(load(config).plugins[0]); - expect(module).toBe(func); - }); - - test('passes through function with args', () => { - let func = function () {}; - let args = { theArgs: 'here' }; - let config = makePortable( - { - plugins: [[func, args]], - }, - resolvableNames(), - [] - ); - expect(config.isParallelSafe).toBeFalsy(); - let { module, arg } = assertPortableBabelLauncher(load(config).plugins[0]); - expect(module).toBe(func); - expect(arg).toEqual(args); - }); - - test('respects _parallelBabel api with buildUsing on PluginTarget', () => { - (exampleFunction as any)._parallelBabel = { - requireFile: __filename, - buildUsing: 'exampleFunction', - params: { - theParams: 'are here', - }, - }; - let config = makePortable( - { - plugins: [exampleFunction], - }, - resolvableNames(), - [] - ); - expect(config.isParallelSafe).toBeTruthy(); - let { module } = assertPortableBabelLauncher(load(config).plugins[0]); - expect(module).toEqual('this is the example function with theParams=are here'); - }); - - test('respects _parallelBabel api with useMethod on PluginTarget', () => { - (exampleFunction as any)._parallelBabel = { - requireFile: __filename, - useMethod: 'exampleFunction', - }; - let config = makePortable( - { - plugins: [exampleFunction], - }, - resolvableNames(), - [] - ); - expect(config.isParallelSafe).toBeTruthy(); - let { module } = assertPortableBabelLauncher(load(config).plugins[0]); - expect(module).toBe(exampleFunction); - }); - - test('respects _parallelBabel api with with only requireFile on PluginTarget', () => { - (exampleTarget as any)._parallelBabel = { - requireFile: resolve(__dirname, 'example-target.js'), - }; - let config = makePortable( - { - plugins: [[exampleTarget, 'hi']], - }, - resolvableNames(), - [] - ); - expect(config.isParallelSafe).toBeTruthy(); - let output = loadParallelSafe(config); - let { module, arg } = assertPortableBabelLauncher(output.plugins[0]); - expect(module).toEqual(exampleTarget); - expect(arg).toEqual('hi'); - }); - - test('respects _parallelBabel api on PluginOptions', () => { - function precompile() {} - precompile._parallelBabel = { - requireFile: __filename, - buildUsing: 'exampleFunction', - params: { theParams: 'reconstituted precompile' }, - }; - - let config = makePortable( - { - plugins: [['my-plugin', { precompile }]], - }, - resolvableNames('my-plugin'), - [] - ); - expect(config.isParallelSafe).toBeTruthy(); - let output = loadParallelSafe(config); - let { module, arg } = assertPortableBabelLauncher(output.plugins[0]); - expect(module).toEqual('/notional-base-dir/node_modules/my-plugin/index.js'); - expect(arg).toEqual({ precompile: 'this is the example function with theParams=reconstituted precompile' }); - }); - - test('undefined is a serializable value', function () { - let config = makePortable( - { - plugins: ['./x', { value: undefined }], - }, - resolvableNames(), - [] - ); - expect(config.isParallelSafe).toBeTruthy(); - let output = loadParallelSafe(config); - expect(output.plugins[0][1].value).toBeUndefined(); - }); -}); - -export function exampleFunction(params: any) { - if (params) { - return `this is the example function with theParams=${params.theParams}`; - } else { - return `this is the example function with no params`; - } -} - -export function examplePlugin(_babel: any, params: any) { - if (params) { - return `this is the example plugin with params=${params}`; - } else { - return `this is the example plugin with no params`; - } -} diff --git a/packages/macros/package.json b/packages/macros/package.json index 96555ad3d..b70f23f2e 100644 --- a/packages/macros/package.json +++ b/packages/macros/package.json @@ -47,7 +47,7 @@ "@types/node": "^15.12.2", "@types/resolve": "^1.20.0", "@types/semver": "^7.3.6", - "babel-plugin-ember-template-compilation": "^2.1.1", + "babel-plugin-ember-template-compilation": "^2.3.0", "code-equality-assertions": "^0.9.0", "scenario-tester": "^3.0.1", "typescript": "^5.4.5" diff --git a/packages/shared-internals/src/babel-filter.ts b/packages/shared-internals/src/babel-filter.ts deleted file mode 100644 index 96cebe448..000000000 --- a/packages/shared-internals/src/babel-filter.ts +++ /dev/null @@ -1,34 +0,0 @@ -import PackageCache from './package-cache'; -import semver from 'semver'; - -export default function babelFilter(skipBabel: { package: string; semverRange?: string }[], appRoot: string) { - return function shouldTranspileFile(filename: string) { - if (!babelCanHandle(filename)) { - // quick exit for non JS extensions - return false; - } - - let owner = PackageCache.shared('embroider', appRoot).ownerOfFile(filename); - if (owner) { - for (let { package: pkg, semverRange } of skipBabel) { - if (owner.name === pkg && (semverRange == null || semver.satisfies(owner.version, semverRange))) { - if (owner.isEmberAddon()) { - throw new Error( - `You can't use skipBabel to disable transpilation of Ember addons, it only works for non-Ember third-party packages` - ); - } - return false; - } - } - } - return true; - }; -} - -function babelCanHandle(filename: string) { - // we can handle .mjs, .js and .ts files with babel. If typescript is enabled, - // .ts files become resolvable and stage3 will be asking us if they should get - // transpiled and the answer is yes. If typescript is not enbled, they will - // not be resolvable, so stage3 won't ask us about them. - return /\.m?[jt]s$/i.test(filename); -} diff --git a/packages/shared-internals/src/index.ts b/packages/shared-internals/src/index.ts index 67944148b..074128482 100644 --- a/packages/shared-internals/src/index.ts +++ b/packages/shared-internals/src/index.ts @@ -12,7 +12,6 @@ export { default as Package, V2AddonPackage as AddonPackage, V2AppPackage as App export { default as PackageCache } from './package-cache'; export type { RewrittenPackageIndex } from './rewritten-package-cache'; export { RewrittenPackageCache } from './rewritten-package-cache'; -export { default as babelFilter } from './babel-filter'; export { default as packageName } from './package-name'; export { default as tmpdir } from './tmpdir'; export * from './ember-cli-models'; diff --git a/packages/shared-internals/src/metadata.ts b/packages/shared-internals/src/metadata.ts index c3bd5def5..8cd4ccfad 100644 --- a/packages/shared-internals/src/metadata.ts +++ b/packages/shared-internals/src/metadata.ts @@ -9,12 +9,6 @@ export interface AppMeta { main?: string; 'auto-upgraded'?: true; assets: Filename[]; - babel: { - filename: string; - isParallelSafe: boolean; - majorVersion: 7; - fileFilter: string; - }; 'root-url': string; version: 2; } diff --git a/packages/webpack/src/ember-webpack.ts b/packages/webpack/src/ember-webpack.ts index 8d3b15c46..0e6b4c759 100644 --- a/packages/webpack/src/ember-webpack.ts +++ b/packages/webpack/src/ember-webpack.ts @@ -42,7 +42,6 @@ import type { MinifyOptions } from 'terser'; interface AppInfo { entrypoints: HTMLEntrypoint[]; otherAssets: string[]; - babel: AppMeta['babel']; rootURL: AppMeta['root-url']; publicAssetURL: string; resolverConfig: ResolverOptions; @@ -52,7 +51,6 @@ interface AppInfo { // AppInfos are equal if they result in the same webpack config. function equalAppInfo(left: AppInfo, right: AppInfo): boolean { return ( - isEqual(left.babel, right.babel) && left.entrypoints.length === right.entrypoints.length && left.entrypoints.every((e, index) => isEqual(e.modules, right.entrypoints[index].modules)) ); @@ -161,7 +159,6 @@ const Webpack: PackagerConstructor = class Webpack implements Packager private examineApp(): AppInfo { let meta = getAppMeta(this.pathToVanillaApp); let rootURL = meta['ember-addon']['root-url']; - let babel = meta['ember-addon']['babel']; let entrypoints = []; let otherAssets = []; let publicAssetURL = this.publicAssetURL || rootURL; @@ -178,11 +175,11 @@ const Webpack: PackagerConstructor = class Webpack implements Packager join(locateEmbroiderWorkingDir(this.appRoot), 'resolver.json') ); - return { entrypoints, otherAssets, babel, rootURL, resolverConfig, publicAssetURL, packageName: meta.name }; + return { entrypoints, otherAssets, rootURL, resolverConfig, publicAssetURL, packageName: meta.name }; } private configureWebpack(appInfo: AppInfo, variant: Variant, variantIndex: number): Configuration { - const { entrypoints, babel, publicAssetURL, packageName, resolverConfig } = appInfo; + const { entrypoints, publicAssetURL, packageName, resolverConfig } = appInfo; let entry: { [name: string]: string } = {}; for (let entrypoint of entrypoints) { @@ -194,9 +191,8 @@ const Webpack: PackagerConstructor = class Webpack implements Packager let { plugins: stylePlugins, loaders: styleLoaders } = this.setupStyleConfig(variant); let babelLoaderOptions = makeBabelLoaderOptions( - babel.majorVersion, variant, - join(this.pathToVanillaApp, babel.filename), + join(this.appRoot, 'babel.config.cjs'), this.extraBabelLoaderOptions ); @@ -225,7 +221,7 @@ const Webpack: PackagerConstructor = class Webpack implements Packager { test: /\.hbs$/, use: nonNullArray([ - maybeThreadLoader(babel.isParallelSafe, this.extraThreadLoaderOptions), + maybeThreadLoader(this.extraThreadLoaderOptions), babelLoaderOptions, { loader: require.resolve('@embroider/hbs-loader'), @@ -242,16 +238,9 @@ const Webpack: PackagerConstructor = class Webpack implements Packager ]), }, { - // eslint-disable-next-line @typescript-eslint/no-require-imports - test: require(join(this.pathToVanillaApp, babel.fileFilter)), use: nonNullArray([ - maybeThreadLoader(babel.isParallelSafe, this.extraThreadLoaderOptions), - makeBabelLoaderOptions( - babel.majorVersion, - variant, - join(this.pathToVanillaApp, babel.filename), - this.extraBabelLoaderOptions - ), + maybeThreadLoader(this.extraThreadLoaderOptions), + makeBabelLoaderOptions(variant, join(this.appRoot, 'babel.config.cjs'), this.extraBabelLoaderOptions), ]), }, { @@ -680,8 +669,8 @@ function warmUp(extraOptions: object | false | undefined) { ]); } -function maybeThreadLoader(isParallelSafe: boolean, extraOptions: object | false | undefined) { - if (!canUseThreadLoader(extraOptions) || !isParallelSafe) { +function maybeThreadLoader(extraOptions: object | false | undefined) { + if (!canUseThreadLoader(extraOptions)) { return null; } @@ -708,7 +697,6 @@ function nonNullArray(array: T[]): NonNullable[] { } function makeBabelLoaderOptions( - _majorVersion: 7, variant: Variant, appBabelConfigPath: string, extraOptions: BabelLoaderOptions | undefined diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2039d7f6e..2c9ff85fc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -214,8 +214,11 @@ importers: babel-import-util: specifier: ^2.0.0 version: 2.1.1 + babel-plugin-debug-macros: + specifier: ^1.0.2 + version: 1.0.2(@babel/core@7.25.2) babel-plugin-ember-template-compilation: - specifier: ^2.1.1 + specifier: ^2.3.0 version: 2.3.0 babel-plugin-syntax-dynamic-import: specifier: ^6.18.0 @@ -402,7 +405,7 @@ importers: specifier: ^1.2.1 version: 1.3.0 babel-plugin-ember-template-compilation: - specifier: ^2.1.1 + specifier: ^2.3.0 version: 2.3.0 broccoli-node-api: specifier: ^1.7.0 @@ -599,7 +602,7 @@ importers: specifier: ^7.3.6 version: 7.5.8 babel-plugin-ember-template-compilation: - specifier: ^2.1.1 + specifier: ^2.3.0 version: 2.3.0 code-equality-assertions: specifier: ^0.9.0 @@ -1478,9 +1481,12 @@ importers: '@babel/eslint-parser': specifier: ^7.22.5 version: 7.25.1(@babel/core@7.25.2)(eslint@8.57.0) - '@babel/plugin-proposal-decorators': - specifier: ^7.22.5 - version: 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-runtime': + specifier: ^7.25.4 + version: 7.25.4(@babel/core@7.25.2) + '@babel/runtime': + specifier: ^7.25.6 + version: 7.25.6 '@ember/optional-features': specifier: ^2.0.0 version: 2.1.0 @@ -1489,7 +1495,7 @@ importers: version: 3.1.1 '@ember/test-helpers': specifier: ^3.0.3 - version: 3.3.1(@babel/core@7.25.2)(ember-source@5.8.0) + version: 3.3.1(@babel/core@7.25.2)(ember-source@5.11.0) '@embroider/compat': specifier: workspace:* version: link:../../packages/compat @@ -1517,12 +1523,15 @@ importers: '@rollup/plugin-babel': specifier: ^5.3.1 version: 5.3.1(@babel/core@7.25.2)(rollup@3.29.4) - broccoli-asset-rev: - specifier: ^3.0.0 - version: 3.0.0 + babel-plugin-ember-template-compilation: + specifier: ^2.3.0 + version: 2.3.0 concurrently: specifier: ^8.2.0 version: 8.2.2 + decorator-transforms: + specifier: ^2.0.0 + version: 2.0.0(@babel/core@7.25.2) ember-auto-import: specifier: ^2.6.3 version: 2.7.4 @@ -1531,7 +1540,7 @@ importers: version: 5.0.0 ember-cli-app-version: specifier: ^6.0.0 - version: 6.0.1(ember-source@5.8.0) + version: 6.0.1(ember-source@5.11.0) ember-cli-babel: specifier: ^7.26.11 version: 7.26.11 @@ -1558,19 +1567,19 @@ importers: version: 2.1.2(@babel/core@7.25.2) ember-modifier: specifier: ^4.1.0 - version: 4.2.0(@babel/core@7.25.2)(ember-source@5.8.0) + version: 4.2.0(@babel/core@7.25.2)(ember-source@5.11.0) ember-page-title: specifier: ^7.0.0 version: 7.0.0 ember-qunit: specifier: ^7.0.0 - version: 7.0.0(@ember/test-helpers@3.3.1)(ember-source@5.8.0)(qunit@2.22.0) + version: 7.0.0(@ember/test-helpers@3.3.1)(ember-source@5.11.0)(qunit@2.22.0) ember-resolver: specifier: ^10.1.0 - version: 10.1.1(@ember/string@3.1.1)(ember-source@5.8.0) + version: 10.1.1(@ember/string@3.1.1)(ember-source@5.11.0) ember-source: - specifier: ~5.8.0 - version: 5.8.0(patch_hash=qsivfx5huurlb5tuvochap65l4)(@babel/core@7.25.2) + specifier: 5.11.0 + version: 5.11.0(patch_hash=bo72463n2ws4z6rctmcukxpqeq)(@glimmer/component@1.1.2) ember-template-lint: specifier: ^5.10.1 version: 5.13.0 @@ -1764,7 +1773,7 @@ importers: specifier: ^7.3.6 version: 7.5.8 babel-plugin-ember-template-compilation: - specifier: ^2.1.1 + specifier: ^2.3.0 version: 2.3.0 bootstrap: specifier: ^4.3.1 @@ -5606,7 +5615,7 @@ packages: - webpack dev: true - /@ember/test-helpers@3.3.1(@babel/core@7.25.2)(ember-source@5.8.0): + /@ember/test-helpers@3.3.1(@babel/core@7.25.2)(ember-source@5.11.0): resolution: {integrity: sha512-h4uFBy4pquBtHsHI+tx9S0wtMmn1L+8dkXiDiyoqG1+3e0Awk6GBujiFM9s4ANq6wC8uIhC3wEFyts10h2OAoQ==} engines: {node: 16.* || >= 18} peerDependencies: @@ -5621,7 +5630,7 @@ packages: ember-auto-import: 2.7.4 ember-cli-babel: 8.2.0(@babel/core@7.25.2) ember-cli-htmlbars: 6.3.0 - ember-source: 5.8.0(patch_hash=qsivfx5huurlb5tuvochap65l4)(@babel/core@7.25.2) + ember-source: 5.11.0(patch_hash=bo72463n2ws4z6rctmcukxpqeq)(@glimmer/component@1.1.2) transitivePeerDependencies: - '@babel/core' - '@glint/template' @@ -9846,6 +9855,17 @@ packages: '@babel/core': 7.25.2 semver: 5.7.2 + /babel-plugin-debug-macros@1.0.2(@babel/core@7.25.2): + resolution: {integrity: sha512-ADkMh1LL45678c+4iGn3Fp8hdI9qvxGBkH5x9HNiIlgYJGdQWmYNcA2cS3XAr76N85kDCg4VpqsTN1hFX2jbEA==} + engines: {node: '>=16'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.25.2 + babel-import-util: 2.1.1 + semver: 7.6.3 + dev: false + /babel-plugin-ember-data-packages-polyfill@0.1.2: resolution: {integrity: sha512-kTHnOwoOXfPXi00Z8yAgyD64+jdSXk3pknnS7NlqnCKAU6YDkXZ4Y7irl66kaZjZn0FBBt0P4YOZFZk85jYOww==} engines: {node: 6.* || 8.* || 10.* || >= 12.*} @@ -12832,27 +12852,27 @@ packages: - supports-color dev: true - /ember-cli-app-version@6.0.1(ember-source@5.3.0): + /ember-cli-app-version@6.0.1(ember-source@5.11.0): resolution: {integrity: sha512-XA1FwkWA5QytmWF0jcJqEr3jcZoiCl9Fb33TZgOVfClL7Voxe+/RwzISEprBRQgbf7j8z1xf8/RJCKfclUy3rQ==} engines: {node: 14.* || 16.* || >= 18} peerDependencies: ember-source: ^3.28.0 || >= 4.0.0 dependencies: ember-cli-babel: 7.26.11 - ember-source: 5.3.0(@babel/core@7.25.2)(@glimmer/component@1.1.2)(@glint/template@1.4.0)(webpack@5.94.0) + ember-source: 5.11.0(patch_hash=bo72463n2ws4z6rctmcukxpqeq)(@glimmer/component@1.1.2) git-repo-info: 2.1.1 transitivePeerDependencies: - supports-color dev: true - /ember-cli-app-version@6.0.1(ember-source@5.8.0): + /ember-cli-app-version@6.0.1(ember-source@5.3.0): resolution: {integrity: sha512-XA1FwkWA5QytmWF0jcJqEr3jcZoiCl9Fb33TZgOVfClL7Voxe+/RwzISEprBRQgbf7j8z1xf8/RJCKfclUy3rQ==} engines: {node: 14.* || 16.* || >= 18} peerDependencies: ember-source: ^3.28.0 || >= 4.0.0 dependencies: ember-cli-babel: 7.26.11 - ember-source: 5.8.0(patch_hash=qsivfx5huurlb5tuvochap65l4)(@babel/core@7.25.2) + ember-source: 5.3.0(@babel/core@7.25.2)(@glimmer/component@1.1.2)(@glint/template@1.4.0)(webpack@5.94.0) git-repo-info: 2.1.1 transitivePeerDependencies: - supports-color @@ -15491,7 +15511,7 @@ packages: - supports-color dev: true - /ember-modifier@4.2.0(@babel/core@7.25.2)(ember-source@5.3.0): + /ember-modifier@4.2.0(@babel/core@7.25.2)(ember-source@5.11.0): resolution: {integrity: sha512-BJ48eTEGxD8J7+lofwVmee7xDgNDgpr5dd6+MSu4gk+I6xb35099RMNorXY5hjjwMJEyi/IRR6Yn3M7iJMz8Zw==} peerDependencies: ember-source: ^3.24 || >=4.0 @@ -15503,13 +15523,13 @@ packages: decorator-transforms: 2.0.0(@babel/core@7.25.2) ember-cli-normalize-entity-name: 1.0.0 ember-cli-string-utils: 1.1.0 - ember-source: 5.3.0(@babel/core@7.25.2)(@glimmer/component@1.1.2)(@glint/template@1.4.0)(webpack@5.94.0) + ember-source: 5.11.0(patch_hash=bo72463n2ws4z6rctmcukxpqeq)(@glimmer/component@1.1.2) transitivePeerDependencies: - '@babel/core' - supports-color dev: true - /ember-modifier@4.2.0(@babel/core@7.25.2)(ember-source@5.8.0): + /ember-modifier@4.2.0(@babel/core@7.25.2)(ember-source@5.3.0): resolution: {integrity: sha512-BJ48eTEGxD8J7+lofwVmee7xDgNDgpr5dd6+MSu4gk+I6xb35099RMNorXY5hjjwMJEyi/IRR6Yn3M7iJMz8Zw==} peerDependencies: ember-source: ^3.24 || >=4.0 @@ -15521,7 +15541,7 @@ packages: decorator-transforms: 2.0.0(@babel/core@7.25.2) ember-cli-normalize-entity-name: 1.0.0 ember-cli-string-utils: 1.1.0 - ember-source: 5.8.0(patch_hash=qsivfx5huurlb5tuvochap65l4)(@babel/core@7.25.2) + ember-source: 5.3.0(@babel/core@7.25.2)(@glimmer/component@1.1.2)(@glint/template@1.4.0)(webpack@5.94.0) transitivePeerDependencies: - '@babel/core' - supports-color @@ -15701,7 +15721,7 @@ packages: - webpack dev: true - /ember-qunit@7.0.0(@ember/test-helpers@3.3.1)(ember-source@5.8.0)(qunit@2.22.0): + /ember-qunit@7.0.0(@ember/test-helpers@3.3.1)(ember-source@5.11.0)(qunit@2.22.0): resolution: {integrity: sha512-KhrndHYEXsHnXvmsGyJLJQ6VCudXaRs5dzPZBsdttZJIhsB6PmYAvq2Q+mh3GRDT/59T/sRDrB3FD3/lATS8aA==} engines: {node: 16.* || >= 18} peerDependencies: @@ -15709,14 +15729,14 @@ packages: ember-source: '>=4.0.0' qunit: ^2.13.0 dependencies: - '@ember/test-helpers': 3.3.1(@babel/core@7.25.2)(ember-source@5.8.0) + '@ember/test-helpers': 3.3.1(@babel/core@7.25.2)(ember-source@5.11.0) broccoli-funnel: 3.0.8 broccoli-merge-trees: 3.0.2 common-tags: 1.8.2 ember-auto-import: 2.7.4 ember-cli-babel: 7.26.11 ember-cli-test-loader: 3.1.0 - ember-source: 5.8.0(patch_hash=qsivfx5huurlb5tuvochap65l4)(@babel/core@7.25.2) + ember-source: 5.11.0(patch_hash=bo72463n2ws4z6rctmcukxpqeq)(@glimmer/component@1.1.2) qunit: 2.22.0 resolve-package-path: 4.0.3 silent-error: 1.1.1 @@ -15802,7 +15822,7 @@ packages: - supports-color dev: true - /ember-resolver@10.1.1(@ember/string@3.1.1)(ember-source@5.8.0): + /ember-resolver@10.1.1(@ember/string@3.1.1)(ember-source@5.11.0): resolution: {integrity: sha512-y1zzn6C4YGJui+tJzcCKlsf1oSOSVAkRrvmg8OwqVIKnALKKb9ihx2qLCslHg8x0wJvJgMtDMXgrczvQrZW0Lw==} engines: {node: 14.* || 16.* || >= 18} peerDependencies: @@ -15814,7 +15834,7 @@ packages: dependencies: '@ember/string': 3.1.1 ember-cli-babel: 7.26.11 - ember-source: 5.8.0(patch_hash=qsivfx5huurlb5tuvochap65l4)(@babel/core@7.25.2) + ember-source: 5.11.0(patch_hash=bo72463n2ws4z6rctmcukxpqeq)(@glimmer/component@1.1.2) transitivePeerDependencies: - supports-color dev: true @@ -16085,6 +16105,62 @@ packages: - webpack dev: true + /ember-source@5.11.0(patch_hash=bo72463n2ws4z6rctmcukxpqeq)(@glimmer/component@1.1.2): + resolution: {integrity: sha512-ufjjTyyaVKBpgTf0NrX7HQqzphcgpNQdkMss2SENkHkM9cidcdEPxulqcMNFxjHCsLBE7rGxmPFSci3x7LdIzA==} + engines: {node: '>= 18.*'} + peerDependencies: + '@glimmer/component': ^1.1.2 + dependencies: + '@babel/core': 7.25.2 + '@ember/edition-utils': 1.2.0 + '@glimmer/compiler': 0.92.0 + '@glimmer/component': 1.1.2(@babel/core@7.25.2) + '@glimmer/destroyable': 0.92.0 + '@glimmer/env': 0.1.7 + '@glimmer/global-context': 0.92.0 + '@glimmer/interfaces': 0.92.0 + '@glimmer/manager': 0.92.0 + '@glimmer/node': 0.92.0 + '@glimmer/opcode-compiler': 0.92.0 + '@glimmer/owner': 0.92.0 + '@glimmer/program': 0.92.0 + '@glimmer/reference': 0.92.0 + '@glimmer/runtime': 0.92.0 + '@glimmer/syntax': 0.92.0 + '@glimmer/util': 0.92.0 + '@glimmer/validator': 0.92.0 + '@glimmer/vm': 0.92.0 + '@glimmer/vm-babel-plugins': 0.92.0(@babel/core@7.25.2) + '@simple-dom/interface': 1.4.0 + backburner.js: 2.8.0 + broccoli-file-creator: 2.1.1 + broccoli-funnel: 3.0.8 + broccoli-merge-trees: 4.2.0 + chalk: 4.1.2 + ember-auto-import: 2.7.4 + ember-cli-babel: 8.2.0(@babel/core@7.25.2) + ember-cli-get-component-path-option: 1.0.0 + ember-cli-is-package-missing: 1.0.0 + ember-cli-normalize-entity-name: 1.0.0 + ember-cli-path-utils: 1.0.0 + ember-cli-string-utils: 1.1.0 + ember-cli-typescript-blueprint-polyfill: 0.1.0 + ember-cli-version-checker: 5.1.2 + ember-router-generator: 2.0.0 + inflection: 2.0.1 + route-recognizer: 0.3.4 + router_js: 8.0.6(route-recognizer@0.3.4) + semver: 7.6.3 + silent-error: 1.1.1 + simple-html-tokenizer: 0.5.11 + transitivePeerDependencies: + - '@glint/template' + - rsvp + - supports-color + - webpack + dev: true + patched: true + /ember-source@5.11.0(patch_hash=bo72463n2ws4z6rctmcukxpqeq)(webpack@5.94.0): resolution: {integrity: sha512-ufjjTyyaVKBpgTf0NrX7HQqzphcgpNQdkMss2SENkHkM9cidcdEPxulqcMNFxjHCsLBE7rGxmPFSci3x7LdIzA==} engines: {node: '>= 18.*'} @@ -22410,7 +22486,6 @@ packages: engines: {node: '>=0.6.0', teleport: '>=0.2.0'} deprecated: |- You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other. - (For a CapTP with native promises, see @endo/eventual-send and @endo/captp) dev: true diff --git a/test-packages/support/transpiler.ts b/test-packages/support/transpiler.ts index f1cbef381..a1b8963e0 100644 --- a/test-packages/support/transpiler.ts +++ b/test-packages/support/transpiler.ts @@ -3,7 +3,6 @@ import { join } from 'path'; import type { TransformOptions } from '@babel/core'; import { transform } from '@babel/core'; import type { BoundExpectFile } from './file-assertions'; -import type { AppMeta } from '../../packages/core/src/index'; import { hbsToJS, locateEmbroiderWorkingDir, RewrittenPackageCache } from '../../packages/core/src/index'; import { Memoize } from 'typescript-memoize'; import { getRewrittenLocation } from './rewritten-path'; @@ -47,20 +46,14 @@ export class Transpiler { return readJSONSync(join(this.appOutputPath, 'package.json')); } - private get emberMeta(): AppMeta { - return this.pkgJSON['ember-addon'] as AppMeta; - } - @Memoize() private get babelConfig() { - if (this.emberMeta['babel'].majorVersion !== 7) { - throw new Error(`@embroider/test-support only suports babel 7`); + let origDir = process.cwd(); + process.chdir(this.appOutputPath); + try { + return require(join(this.appOutputPath, './babel.config.cjs')) as TransformOptions; + } finally { + process.chdir(origDir); } - - // Depending on how the app builds, the babel config is not at the same location - let embroiderLocation = join(locateEmbroiderWorkingDir(this.appDir), '_babel_config_.js'); - return existsSync(embroiderLocation) - ? (require(embroiderLocation) as TransformOptions) - : (require(join(this.appDir, this.emberMeta['babel'].filename)) as TransformOptions); } } diff --git a/tests/addon-template/babel.config.cjs b/tests/addon-template/babel.config.cjs index baf775d4b..940d2cd74 100644 --- a/tests/addon-template/babel.config.cjs +++ b/tests/addon-template/babel.config.cjs @@ -1,19 +1,40 @@ -// eslint-disable-next-line n/no-missing-require +const { + babelCompatSupport, + templateCompatSupport, +} = require("@embroider/compat/babel"); -let config; +module.exports = { + plugins: [ + [ + "babel-plugin-ember-template-compilation", + { + compilerPath: "ember-source/dist/ember-template-compiler.js", + enableLegacyModules: [ + "ember-cli-htmlbars", + "ember-cli-htmlbars-inline-precompile", + "htmlbars-inline-precompile", + ], + transforms: [...templateCompatSupport()], + }, + ], + [ + "module:decorator-transforms", + { + runtime: { import: require.resolve("decorator-transforms/runtime") }, + }, + ], + [ + "@babel/plugin-transform-runtime", + { + absoluteRuntime: __dirname, + useESModules: true, + regenerator: false, + }, + ], + ...babelCompatSupport(), + ], -// TODO - remove this once we have the better solution for injecting stage1 babel config into a real config file -// this is needed because there are things (like ember-composible-helpers) that are now finding our babel config during -// their stage1 build and historically they will never (99% of the time) have found any babel config. -// we might need to keep something like this so that prebuild will never apply babel configs during stage1 i.e. a util -// function that wraps your whole babel config -if ( - process.env.EMBROIDER_PREBUILD || - process.env.EMBROIDER_TEST_SETUP_FORCE === "classic" -) { - config = {}; -} else { - config = require("./node_modules/.embroider/_babel_config_"); -} - -module.exports = config; + generatorOpts: { + compact: false, + }, +}; diff --git a/tests/app-template/babel.config.cjs b/tests/app-template/babel.config.cjs index baf775d4b..940d2cd74 100644 --- a/tests/app-template/babel.config.cjs +++ b/tests/app-template/babel.config.cjs @@ -1,19 +1,40 @@ -// eslint-disable-next-line n/no-missing-require +const { + babelCompatSupport, + templateCompatSupport, +} = require("@embroider/compat/babel"); -let config; +module.exports = { + plugins: [ + [ + "babel-plugin-ember-template-compilation", + { + compilerPath: "ember-source/dist/ember-template-compiler.js", + enableLegacyModules: [ + "ember-cli-htmlbars", + "ember-cli-htmlbars-inline-precompile", + "htmlbars-inline-precompile", + ], + transforms: [...templateCompatSupport()], + }, + ], + [ + "module:decorator-transforms", + { + runtime: { import: require.resolve("decorator-transforms/runtime") }, + }, + ], + [ + "@babel/plugin-transform-runtime", + { + absoluteRuntime: __dirname, + useESModules: true, + regenerator: false, + }, + ], + ...babelCompatSupport(), + ], -// TODO - remove this once we have the better solution for injecting stage1 babel config into a real config file -// this is needed because there are things (like ember-composible-helpers) that are now finding our babel config during -// their stage1 build and historically they will never (99% of the time) have found any babel config. -// we might need to keep something like this so that prebuild will never apply babel configs during stage1 i.e. a util -// function that wraps your whole babel config -if ( - process.env.EMBROIDER_PREBUILD || - process.env.EMBROIDER_TEST_SETUP_FORCE === "classic" -) { - config = {}; -} else { - config = require("./node_modules/.embroider/_babel_config_"); -} - -module.exports = config; + generatorOpts: { + compact: false, + }, +}; diff --git a/tests/app-template/package.json b/tests/app-template/package.json index 0d674cf66..8c9e25fd0 100644 --- a/tests/app-template/package.json +++ b/tests/app-template/package.json @@ -31,7 +31,8 @@ "devDependencies": { "@babel/core": "^7.19.3", "@babel/eslint-parser": "^7.22.5", - "@babel/plugin-proposal-decorators": "^7.22.5", + "@babel/plugin-transform-runtime": "^7.25.4", + "@babel/runtime": "^7.25.6", "@ember/optional-features": "^2.0.0", "@ember/string": "^3.1.1", "@ember/test-helpers": "^3.0.3", @@ -44,9 +45,10 @@ "@glimmer/component": "^1.1.2", "@glimmer/tracking": "^1.1.2", "@rollup/plugin-babel": "^5.3.1", - "broccoli-asset-rev": "^3.0.0", + "babel-plugin-ember-template-compilation": "^2.3.0", "concurrently": "^8.2.0", "ember-auto-import": "^2.6.3", + "decorator-transforms": "^2.0.0", "ember-cli": "~5.0.0", "ember-cli-app-version": "^6.0.0", "ember-cli-babel": "^7.26.11", @@ -61,7 +63,7 @@ "ember-page-title": "^7.0.0", "ember-qunit": "^7.0.0", "ember-resolver": "^10.1.0", - "ember-source": "~5.8.0", + "ember-source": "5.11.0", "ember-template-lint": "^5.10.1", "eslint": "^8.42.0", "eslint-config-prettier": "^8.8.0", diff --git a/tests/scenarios/compat-resolver-test.ts b/tests/scenarios/compat-resolver-test.ts index bdce94047..197db5a1c 100644 --- a/tests/scenarios/compat-resolver-test.ts +++ b/tests/scenarios/compat-resolver-test.ts @@ -4,7 +4,6 @@ import type { ExpectFile } from '@embroider/test-support/file-assertions/qunit'; import { expectFilesAt } from '@embroider/test-support/file-assertions/qunit'; import { outputFileSync } from 'fs-extra'; import { resolve, sep } from 'path'; -import type { Options as EtcOptions } from 'babel-plugin-ember-template-compilation'; import QUnit from 'qunit'; import { Project, Scenarios } from 'scenario-tester'; @@ -21,12 +20,6 @@ Scenarios.fromProject(() => new Project()) 'auto-upgraded': true, assets: ['index.html'], 'root-url': '/', - babel: { - majorVersion: 7, - filename: '_babel_config.js', - isParallelSafe: true, - fileFilter: '_babel_filter.js', - }, }; app.pkg = { name: 'my-app', @@ -36,6 +29,11 @@ Scenarios.fromProject(() => new Project()) app.mergeFiles({ 'index.html': '', }); + app.linkDevDependency('babel-plugin-ember-template-compilation', { + baseDir: __dirname, + }); + app.linkDevDependency('@embroider/compat', { baseDir: __dirname }); + app.linkDevDependency('@embroider/core', { baseDir: __dirname }); }) .forEachScenario(scenario => { Qmodule(scenario.name, function (hooks) { @@ -66,17 +64,8 @@ Scenarios.fromProject(() => new Project()) configure = async function ( opts?: Partial, extraOpts?: ConfigureOpts, - emberVersion = '4.6.0' //based on app-template package.json + emberVersion = '4.6.0' ) { - let etcOptions: EtcOptions = { - compilerPath: require.resolve('ember-source-latest/dist/ember-template-compiler'), - targetFormat: 'hbs', - transforms: [ - ...(extraOpts?.astPlugins ?? []), - [require.resolve('@embroider/compat/src/resolver-transform'), { appRoot: app.dir, emberVersion }], - ], - }; - let resolverOptions: CompatResolverOptions = { amdCompatibility: 'cjs', renameModules: {}, @@ -110,18 +99,30 @@ Scenarios.fromProject(() => new Project()) ], autoRun: true, staticAppPaths: [], + emberVersion, }; givenFiles({ - 'node_modules/.embroider/_babel_config_.js': ` - module.exports = { - plugins: ${JSON.stringify([ - [require.resolve('babel-plugin-ember-template-compilation'), etcOptions], - [require.resolve('@embroider/compat/src/babel-plugin-adjust-imports'), { appRoot: app.dir }], - ])} - }`, - 'node_modules/.embroider/_babel_filter.js': ` - module.exports = function(filename) { return true } + 'babel.config.cjs': ` + const { + babelCompatSupport, + templateCompatSupport, + } = require("@embroider/compat/babel"); + module.exports = { + plugins: [ + ['babel-plugin-ember-template-compilation', { + targetFormat: 'hbs', + transforms: [ + ...templateCompatSupport(), + ...(${JSON.stringify(extraOpts?.astPlugins ?? [])}) + ], + enableLegacyModules: [ + 'ember-cli-htmlbars' + ] + }], + ...babelCompatSupport() + ] + } `, 'node_modules/.embroider/resolver.json': JSON.stringify(resolverOptions), }); @@ -1286,11 +1287,11 @@ Scenarios.fromProject(() => new Project()) ); expectTranspiled('templates/application.hbs').equalsCode(` import { precompileTemplate } from "@ember/template-compilation"; - import thing_ from "@embroider/virtual/components/my-addon@thing"; - export default precompileTemplate("", { + import myAddonThing_ from "@embroider/virtual/components/my-addon$thing"; + export default precompileTemplate("", { moduleName: "my-app/templates/application.hbs", scope: () => ({ - thing_ + myAddonThing_ }) }); `); @@ -1310,11 +1311,11 @@ Scenarios.fromProject(() => new Project()) ); expectTranspiled('templates/application.hbs').equalsCode(` import { precompileTemplate } from "@ember/template-compilation"; - import thing_ from "@embroider/virtual/helpers/my-addon@thing"; - export default precompileTemplate("{{(thing_)}}", { + import myAddonThing_ from "@embroider/virtual/helpers/my-addon$thing"; + export default precompileTemplate("{{(myAddonThing_)}}", { moduleName: "my-app/templates/application.hbs", scope: () => ({ - thing_ + myAddonThing_ }) }); `); diff --git a/tests/scenarios/core-resolver-test.ts b/tests/scenarios/core-resolver-test.ts index d5b7caf6a..e32c12b04 100644 --- a/tests/scenarios/core-resolver-test.ts +++ b/tests/scenarios/core-resolver-test.ts @@ -19,12 +19,6 @@ Scenarios.fromProject(() => new Project()) 'auto-upgraded': true, assets: ['index.html'], 'root-url': '/', - babel: { - majorVersion: 7, - filename: '_babel_config.js', - isParallelSafe: true, - fileFilter: '_babel_filter.js', - }, }; app.pkg = { name: 'my-app', @@ -43,6 +37,11 @@ Scenarios.fromProject(() => new Project()) 'index.js': '', }, }); + app.linkDevDependency('babel-plugin-ember-template-compilation', { + baseDir: __dirname, + }); + app.linkDevDependency('@embroider/compat', { baseDir: __dirname }); + app.linkDevDependency('@embroider/core', { baseDir: __dirname }); let v1Addon = baseAddon(); v1Addon.name = 'a-v1-addon'; @@ -138,17 +137,31 @@ Scenarios.fromProject(() => new Project()) ], autoRun: true, staticAppPaths: [], + emberVersion: '4.0.0', }; givenFiles({ - 'node_modules/.embroider/_babel_config_.js': ` - module.exports = { - plugins: [] - } - `, - 'node_modules/.embroider/_babel_filter.js': ` - module.exports = function(filename) { return true } + 'babel.config.cjs': ` + const { + babelCompatSupport, + templateCompatSupport, + } = require("@embroider/compat/babel"); + module.exports = { + plugins: [ + ['babel-plugin-ember-template-compilation', { + targetFormat: 'hbs', + transforms: [ + ...templateCompatSupport(), + ], + enableLegacyModules: [ + 'ember-cli-htmlbars' + ] + }], + ...babelCompatSupport() + ] + } `, + 'node_modules/.embroider/resolver.json': JSON.stringify(resolverOptions), 'node_modules/my-addon/package.json': addonPackageJSON('my-addon', opts?.addonMeta), }); diff --git a/tests/scenarios/package.json b/tests/scenarios/package.json index 20d8fe953..c0d3f4b05 100644 --- a/tests/scenarios/package.json +++ b/tests/scenarios/package.json @@ -57,7 +57,7 @@ "@types/lodash": "^4.14.170", "@types/node-fetch": "^2.6.11", "@types/semver": "^7.3.6", - "babel-plugin-ember-template-compilation": "^2.1.1", + "babel-plugin-ember-template-compilation": "^2.3.0", "bootstrap": "^4.3.1", "broccoli-funnel": "^3.0.5", "broccoli-merge-trees": "^3.0.2", diff --git a/tests/ts-app-template-classic/babel.config.cjs b/tests/ts-app-template-classic/babel.config.cjs index 7637754cc..29a153bfc 100644 --- a/tests/ts-app-template-classic/babel.config.cjs +++ b/tests/ts-app-template-classic/babel.config.cjs @@ -1,16 +1,37 @@ -// eslint-disable-next-line n/no-missing-require +const { babelCompatSupport, templateCompatSupport } = require('@embroider/compat/babel'); -let config; +module.exports = { + plugins: [ + [ + 'babel-plugin-ember-template-compilation', + { + compilerPath: 'ember-source/dist/ember-template-compiler.js', + enableLegacyModules: [ + 'ember-cli-htmlbars', + 'ember-cli-htmlbars-inline-precompile', + 'htmlbars-inline-precompile', + ], + transforms: [...templateCompatSupport()], + }, + ], + [ + 'module:decorator-transforms', + { + runtime: { import: require.resolve('decorator-transforms/runtime') }, + }, + ], + [ + '@babel/plugin-transform-runtime', + { + absoluteRuntime: __dirname, + useESModules: true, + regenerator: false, + }, + ], + ...babelCompatSupport(), + ], -// TODO - remove this once we have the better solution for injecting stage1 babel config into a real config file -// this is needed because there are things (like ember-composible-helpers) that are now finding our babel config during -// their stage1 build and historically they will never (99% of the time) have found any babel config. -// we might need to keep something like this so that prebuild will never apply babel configs during stage1 i.e. a util -// function that wraps your whole babel config -if (process.env.EMBROIDER_PREBUILD || process.env.EMBROIDER_TEST_SETUP_FORCE === 'classic') { - config = {}; -} else { - config = require('./node_modules/.embroider/_babel_config_'); -} - -module.exports = config; + generatorOpts: { + compact: false, + }, +}; diff --git a/tests/ts-app-template/babel.config.cjs b/tests/ts-app-template/babel.config.cjs index 7637754cc..29a153bfc 100644 --- a/tests/ts-app-template/babel.config.cjs +++ b/tests/ts-app-template/babel.config.cjs @@ -1,16 +1,37 @@ -// eslint-disable-next-line n/no-missing-require +const { babelCompatSupport, templateCompatSupport } = require('@embroider/compat/babel'); -let config; +module.exports = { + plugins: [ + [ + 'babel-plugin-ember-template-compilation', + { + compilerPath: 'ember-source/dist/ember-template-compiler.js', + enableLegacyModules: [ + 'ember-cli-htmlbars', + 'ember-cli-htmlbars-inline-precompile', + 'htmlbars-inline-precompile', + ], + transforms: [...templateCompatSupport()], + }, + ], + [ + 'module:decorator-transforms', + { + runtime: { import: require.resolve('decorator-transforms/runtime') }, + }, + ], + [ + '@babel/plugin-transform-runtime', + { + absoluteRuntime: __dirname, + useESModules: true, + regenerator: false, + }, + ], + ...babelCompatSupport(), + ], -// TODO - remove this once we have the better solution for injecting stage1 babel config into a real config file -// this is needed because there are things (like ember-composible-helpers) that are now finding our babel config during -// their stage1 build and historically they will never (99% of the time) have found any babel config. -// we might need to keep something like this so that prebuild will never apply babel configs during stage1 i.e. a util -// function that wraps your whole babel config -if (process.env.EMBROIDER_PREBUILD || process.env.EMBROIDER_TEST_SETUP_FORCE === 'classic') { - config = {}; -} else { - config = require('./node_modules/.embroider/_babel_config_'); -} - -module.exports = config; + generatorOpts: { + compact: false, + }, +};