Skip to content

Commit

Permalink
Merge pull request #2102 from embroider-build/user-controlled-babel
Browse files Browse the repository at this point in the history
User-controlled babel config
  • Loading branch information
ef4 committed Sep 10, 2024
2 parents a691d39 + 3386e96 commit 2ff89c0
Show file tree
Hide file tree
Showing 30 changed files with 613 additions and 1,033 deletions.
1 change: 1 addition & 0 deletions packages/compat/babel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('./src/babel');
4 changes: 3 additions & 1 deletion packages/compat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"author": "Edward Faulkner",
"main": "src/index.js",
"files": [
"babel.js",
"src/**/*.js",
"src/**/*.d.ts",
"src/**/*.js.map"
Expand All @@ -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",
Expand Down
38 changes: 15 additions & 23 deletions packages/compat/src/audit.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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();
}

Expand All @@ -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 {
Expand Down
158 changes: 158 additions & 0 deletions packages/compat/src/babel.ts
Original file line number Diff line number Diff line change
@@ -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()];
}
Loading

0 comments on commit 2ff89c0

Please sign in to comment.