mirror of
https://github.com/angular/angular-cli.git
synced 2025-05-21 22:34:21 +08:00
refactor(@angular-devkit/build-angular): directly configure internal babel plugins for application builder
The linker and build optimizer related babel plugins are now directly imported when needed within the JavaScript transformer worker code. This lowers the number of transitive modules that must be loaded for each worker instance. It also removes the use of `require` from the initialization code which provides support for full ESM output in the future.
This commit is contained in:
parent
950a44521f
commit
2fc8076a4b
@ -0,0 +1,12 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
export { default as adjustStaticMembers } from './adjust-static-class-members';
|
||||
export { default as adjustTypeScriptEnums } from './adjust-typescript-enums';
|
||||
export { default as elideAngularMetadata } from './elide-angular-metadata';
|
||||
export { default as markTopLevelPure } from './pure-toplevel-functions';
|
@ -6,9 +6,10 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import { transformAsync } from '@babel/core';
|
||||
import { type PluginItem, transformAsync } from '@babel/core';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import Piscina from 'piscina';
|
||||
import angularApplicationPreset, { requiresLinking } from '../../tools/babel/presets/application';
|
||||
import { loadEsmModule } from '../../utils/load-esm';
|
||||
|
||||
interface JavaScriptTransformRequest {
|
||||
@ -37,10 +38,18 @@ export default async function transformJavaScript(
|
||||
return Piscina.move(textEncoder.encode(transformedData));
|
||||
}
|
||||
|
||||
/**
|
||||
* Cached instance of the compiler-cli linker's createEs2015LinkerPlugin function.
|
||||
*/
|
||||
let linkerPluginCreator:
|
||||
| typeof import('@angular/compiler-cli/linker/babel').createEs2015LinkerPlugin
|
||||
| undefined;
|
||||
|
||||
/**
|
||||
* Cached instance of the compiler-cli linker's needsLinking function.
|
||||
*/
|
||||
let needsLinking: typeof import('@angular/compiler-cli/linker').needsLinking | undefined;
|
||||
|
||||
async function transformWithBabel(
|
||||
filename: string,
|
||||
data: string,
|
||||
@ -51,22 +60,36 @@ async function transformWithBabel(
|
||||
options.sourcemap &&
|
||||
(!!options.thirdPartySourcemaps || !/[\\/]node_modules[\\/]/.test(filename));
|
||||
|
||||
// If no additional transformations are needed, return the data directly
|
||||
if (!options.advancedOptimizations && !shouldLink) {
|
||||
// Strip sourcemaps if they should not be used
|
||||
return useInputSourcemap ? data : data.replace(/^\/\/# sourceMappingURL=[^\r\n]*/gm, '');
|
||||
}
|
||||
|
||||
const sideEffectFree = options.sideEffects === false;
|
||||
const safeAngularPackage = sideEffectFree && /[\\/]node_modules[\\/]@angular[\\/]/.test(filename);
|
||||
const plugins: PluginItem[] = [];
|
||||
|
||||
// Lazy load the linker plugin only when linking is required
|
||||
if (shouldLink) {
|
||||
linkerPluginCreator ??= (
|
||||
await loadEsmModule<typeof import('@angular/compiler-cli/linker/babel')>(
|
||||
'@angular/compiler-cli/linker/babel',
|
||||
)
|
||||
).createEs2015LinkerPlugin;
|
||||
const linkerPlugin = await createLinkerPlugin(options);
|
||||
plugins.push(linkerPlugin);
|
||||
}
|
||||
|
||||
if (options.advancedOptimizations) {
|
||||
const sideEffectFree = options.sideEffects === false;
|
||||
const safeAngularPackage =
|
||||
sideEffectFree && /[\\/]node_modules[\\/]@angular[\\/]/.test(filename);
|
||||
|
||||
const { adjustStaticMembers, adjustTypeScriptEnums, elideAngularMetadata, markTopLevelPure } =
|
||||
await import('../babel/plugins');
|
||||
|
||||
if (safeAngularPackage) {
|
||||
plugins.push(markTopLevelPure);
|
||||
}
|
||||
|
||||
plugins.push(elideAngularMetadata, adjustTypeScriptEnums, [
|
||||
adjustStaticMembers,
|
||||
{ wrapDecorators: sideEffectFree },
|
||||
]);
|
||||
}
|
||||
|
||||
// If no additional transformations are needed, return the data directly
|
||||
if (plugins.length === 0) {
|
||||
// Strip sourcemaps if they should not be used
|
||||
return useInputSourcemap ? data : data.replace(/^\/\/# sourceMappingURL=[^\r\n]*/gm, '');
|
||||
}
|
||||
|
||||
const result = await transformAsync(data, {
|
||||
@ -77,23 +100,7 @@ async function transformWithBabel(
|
||||
configFile: false,
|
||||
babelrc: false,
|
||||
browserslistConfigFile: false,
|
||||
plugins: [],
|
||||
presets: [
|
||||
[
|
||||
angularApplicationPreset,
|
||||
{
|
||||
angularLinker: linkerPluginCreator && {
|
||||
shouldLink,
|
||||
jitMode: options.jit,
|
||||
linkerPluginCreator,
|
||||
},
|
||||
optimize: options.advancedOptimizations && {
|
||||
pureTopLevel: safeAngularPackage,
|
||||
wrapDecorators: sideEffectFree,
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
plugins,
|
||||
});
|
||||
|
||||
const outputCode = result?.code ?? data;
|
||||
@ -104,3 +111,67 @@ async function transformWithBabel(
|
||||
? outputCode
|
||||
: outputCode.replace(/^\/\/# sourceMappingURL=[^\r\n]*/gm, '');
|
||||
}
|
||||
|
||||
async function requiresLinking(path: string, source: string): Promise<boolean> {
|
||||
// @angular/core and @angular/compiler will cause false positives
|
||||
// Also, TypeScript files do not require linking
|
||||
if (/[\\/]@angular[\\/](?:compiler|core)|\.tsx?$/.test(path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!needsLinking) {
|
||||
// Load ESM `@angular/compiler-cli/linker` using the TypeScript dynamic import workaround.
|
||||
// Once TypeScript provides support for keeping the dynamic import this workaround can be
|
||||
// changed to a direct dynamic import.
|
||||
const linkerModule = await loadEsmModule<typeof import('@angular/compiler-cli/linker')>(
|
||||
'@angular/compiler-cli/linker',
|
||||
);
|
||||
needsLinking = linkerModule.needsLinking;
|
||||
}
|
||||
|
||||
return needsLinking(path, source);
|
||||
}
|
||||
|
||||
async function createLinkerPlugin(options: Omit<JavaScriptTransformRequest, 'filename' | 'data'>) {
|
||||
linkerPluginCreator ??= (
|
||||
await loadEsmModule<typeof import('@angular/compiler-cli/linker/babel')>(
|
||||
'@angular/compiler-cli/linker/babel',
|
||||
)
|
||||
).createEs2015LinkerPlugin;
|
||||
|
||||
const linkerPlugin = linkerPluginCreator({
|
||||
linkerJitMode: options.jit,
|
||||
// This is a workaround until https://github.com/angular/angular/issues/42769 is fixed.
|
||||
sourceMapping: false,
|
||||
logger: {
|
||||
level: 1, // Info level
|
||||
debug(...args: string[]) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.debug(args);
|
||||
},
|
||||
info(...args: string[]) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.info(args);
|
||||
},
|
||||
warn(...args: string[]) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(args);
|
||||
},
|
||||
error(...args: string[]) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(args);
|
||||
},
|
||||
},
|
||||
fileSystem: {
|
||||
resolve: path.resolve,
|
||||
exists: fs.existsSync,
|
||||
dirname: path.dirname,
|
||||
relative: path.relative,
|
||||
readFile: fs.readFileSync,
|
||||
// Node.JS types don't overlap the Compiler types.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} as any,
|
||||
});
|
||||
|
||||
return linkerPlugin;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user