in packages/angular_devkit/build_angular/src/babel/webpack-loader.ts [78:214]
async customOptions(options, { source, map }) {
const { i18n, scriptTarget, aot, optimize, instrumentCode, ...rawOptions } =
options as AngularBabelLoaderOptions;
// Must process file if plugins are added
let shouldProcess = Array.isArray(rawOptions.plugins) && rawOptions.plugins.length > 0;
const customOptions: ApplicationPresetOptions = {
forceAsyncTransformation: false,
forceES5: false,
angularLinker: undefined,
i18n: undefined,
instrumentCode: undefined,
};
// Analyze file for linking
if (await requiresLinking(this.resourcePath, source)) {
// Load ESM `@angular/compiler-cli/linker/babel` 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.
linkerPluginCreator ??= (
await loadEsmModule<typeof import('@angular/compiler-cli/linker/babel')>(
'@angular/compiler-cli/linker/babel',
)
).createEs2015LinkerPlugin;
customOptions.angularLinker = {
shouldLink: true,
jitMode: aot !== true,
linkerPluginCreator,
};
shouldProcess = true;
}
// Analyze for ES target processing
const esTarget = scriptTarget as ScriptTarget | undefined;
if (esTarget !== undefined) {
if (esTarget < ScriptTarget.ES2015) {
customOptions.forceES5 = true;
} else if (esTarget >= ScriptTarget.ES2017 || /\.[cm]?js$/.test(this.resourcePath)) {
// Application code (TS files) will only contain native async if target is ES2017+.
// However, third-party libraries can regardless of the target option.
// APF packages with code in [f]esm2015 directories is downlevelled to ES2015 and
// will not have native async.
customOptions.forceAsyncTransformation =
!/[\\/][_f]?esm2015[\\/]/.test(this.resourcePath) && source.includes('async');
}
shouldProcess ||= customOptions.forceAsyncTransformation || customOptions.forceES5 || false;
}
// Analyze for i18n inlining
if (
i18n &&
!/[\\/]@angular[\\/](?:compiler|localize)/.test(this.resourcePath) &&
source.includes('$localize')
) {
// Load the i18n plugin creators from the new `@angular/localize/tools` entry point.
// This may fail during the transition to ESM due to the entry point not yet existing.
// During the transition, this will always attempt to load the entry point for each file.
// This will only occur during prerelease and will be automatically corrected once the new
// entry point exists.
if (i18nPluginCreators === undefined) {
// Load ESM `@angular/localize/tools` 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.
i18nPluginCreators = await loadEsmModule<I18nPluginCreators>('@angular/localize/tools');
}
customOptions.i18n = {
...(i18n as NonNullable<ApplicationPresetOptions['i18n']>),
pluginCreators: i18nPluginCreators,
};
// Add translation files as dependencies of the file to support rebuilds
// Except for `@angular/core` which needs locale injection but has no translations
if (
customOptions.i18n.translationFiles &&
!/[\\/]@angular[\\/]core/.test(this.resourcePath)
) {
for (const file of customOptions.i18n.translationFiles) {
this.addDependency(file);
}
}
shouldProcess = true;
}
if (optimize) {
const angularPackage = /[\\/]node_modules[\\/]@angular[\\/]/.test(this.resourcePath);
customOptions.optimize = {
// Angular packages provide additional tested side effects guarantees and can use
// otherwise unsafe optimizations.
looseEnums: angularPackage,
pureTopLevel: angularPackage,
// JavaScript modules that are marked as side effect free are considered to have
// no decorators that contain non-local effects.
wrapDecorators: !!this._module?.factoryMeta?.sideEffectFree,
};
shouldProcess = true;
}
if (
instrumentCode &&
!instrumentCode.excludedPaths.has(this.resourcePath) &&
!/\.(e2e|spec)\.tsx?$|[\\/]node_modules[\\/]/.test(this.resourcePath) &&
this.resourcePath.startsWith(instrumentCode.includedBasePath)
) {
// `babel-plugin-istanbul` has it's own includes but we do the below so that we avoid running the the loader.
customOptions.instrumentCode = {
includedBasePath: instrumentCode.includedBasePath,
inputSourceMap: map,
};
shouldProcess = true;
}
// Add provided loader options to default base options
const loaderOptions: Record<string, unknown> = {
...baseOptions,
...rawOptions,
cacheIdentifier: JSON.stringify({
buildAngular: VERSION,
customOptions,
baseOptions,
rawOptions,
}),
};
// Skip babel processing if no actions are needed
if (!shouldProcess) {
// Force the current file to be ignored
loaderOptions.ignore = [() => true];
}
return { custom: customOptions, loader: loaderOptions };
},