Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User-controlled babel config #2102

Merged
merged 10 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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