Skip to content
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
31 changes: 9 additions & 22 deletions packages/angular_devkit/build_angular/src/babel/webpack-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
*/

import { custom } from 'babel-loader';
import { ScriptTarget } from 'typescript';
import { loadEsmModule } from '../utils/load-esm';
import { VERSION } from '../utils/package-version';
import { ApplicationPresetOptions, I18nPluginCreators } from './presets/application';
Expand Down Expand Up @@ -72,15 +71,8 @@ export default custom<ApplicationPresetOptions>(() => {

return {
async customOptions(options, { source, map }) {
const {
i18n,
scriptTarget,
aot,
optimize,
instrumentCode,
supportedBrowsers,
...rawOptions
} = options as AngularBabelLoaderOptions;
const { i18n, aot, optimize, instrumentCode, supportedBrowsers, ...rawOptions } =
options as AngularBabelLoaderOptions;

// Must process file if plugins are added
let shouldProcess = Array.isArray(rawOptions.plugins) && rawOptions.plugins.length > 0;
Expand Down Expand Up @@ -114,24 +106,19 @@ export default custom<ApplicationPresetOptions>(() => {
}

// Analyze for ES target processing
const esTarget = scriptTarget as ScriptTarget | undefined;
const isJsFile = /\.[cm]?js$/.test(this.resourcePath);

if (isJsFile && customOptions.supportedBrowsers?.length) {
if (customOptions.supportedBrowsers?.length) {
// Applications code ES version can be controlled using TypeScript's `target` option.
// However, this doesn't effect libraries and hence we use preset-env to downlevel ES fetaures
// based on the supported browsers in browserlist.
customOptions.forcePresetEnv = true;
}

if ((esTarget !== undefined && esTarget >= ScriptTarget.ES2017) || isJsFile) {
// 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');
}
// 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.forcePresetEnv || false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,16 +182,13 @@ export function createCompilerPlugin(
enableResourceInlining: false,
});

// Adjust the esbuild output target based on the tsconfig target
if (
compilerOptions.target === undefined ||
compilerOptions.target <= ts.ScriptTarget.ES2015
) {
build.initialOptions.target = 'es2015';
} else if (compilerOptions.target >= ts.ScriptTarget.ESNext) {
build.initialOptions.target = 'esnext';
} else {
build.initialOptions.target = ts.ScriptTarget[compilerOptions.target].toLowerCase();
if (compilerOptions.target === undefined || compilerOptions.target < ts.ScriptTarget.ES2022) {
// If 'useDefineForClassFields' is already defined in the users project leave the value as is.
// Otherwise fallback to false due to https://github.com/microsoft/TypeScript/issues/45995
// which breaks the deprecated `@Effects` NGRX decorator and potentially other existing code as well.
compilerOptions.target = ts.ScriptTarget.ES2022;
compilerOptions.useDefineForClassFields ??= false;
// TODO: show warning about this override when we have access to the logger.
}

// The file emitter created during `onStart` that will be used during the build in `onLoad` callbacks for TS files
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ import * as path from 'path';
import { NormalizedOptimizationOptions, deleteOutputDir } from '../../utils';
import { copyAssets } from '../../utils/copy-assets';
import { assertIsError } from '../../utils/error';
import { transformSupportedBrowsersToTargets } from '../../utils/esbuild-targets';
import { FileInfo } from '../../utils/index-file/augment-index-html';
import { IndexHtmlGenerator } from '../../utils/index-file/index-html-generator';
import { generateEntryPoints } from '../../utils/package-chunk-sort';
import { augmentAppWithServiceWorker } from '../../utils/service-worker';
import { getSupportedBrowsers } from '../../utils/supported-browsers';
import { getIndexInputFile, getIndexOutputFile } from '../../utils/webpack-browser-config';
import { resolveGlobalStyles } from '../../webpack/configs';
import { createCompilerPlugin } from './compiler-plugin';
Expand Down Expand Up @@ -89,6 +91,10 @@ export async function buildEsbuildBrowser(
return { success: false };
}

const target = transformSupportedBrowsersToTargets(
getSupportedBrowsers(projectRoot, context.logger),
);

const [codeResults, styleResults] = await Promise.all([
// Execute esbuild to bundle the application code
bundleCode(
Expand All @@ -99,6 +105,7 @@ export async function buildEsbuildBrowser(
optimizationOptions,
sourcemapOptions,
tsconfig,
target,
),
// Execute esbuild to bundle the global stylesheets
bundleGlobalStylesheets(
Expand All @@ -107,6 +114,7 @@ export async function buildEsbuildBrowser(
options,
optimizationOptions,
sourcemapOptions,
target,
),
]);

Expand Down Expand Up @@ -248,6 +256,7 @@ async function bundleCode(
optimizationOptions: NormalizedOptimizationOptions,
sourcemapOptions: SourceMapClass,
tsconfig: string,
target: string[],
) {
let fileReplacements: Record<string, string> | undefined;
if (options.fileReplacements) {
Expand All @@ -267,7 +276,7 @@ async function bundleCode(
entryPoints,
entryNames: outputNames.bundles,
assetNames: outputNames.media,
target: 'es2020',
target,
supported: {
// Native async/await is not supported with Zone.js. Disabling support here will cause
// esbuild to downlevel async/await and for await...of to a Zone.js supported form. However, esbuild
Expand Down Expand Up @@ -313,6 +322,7 @@ async function bundleCode(
outputNames,
includePaths: options.stylePreprocessorOptions?.includePaths,
externalDependencies: options.externalDependencies,
target,
},
),
],
Expand All @@ -329,6 +339,7 @@ async function bundleGlobalStylesheets(
options: BrowserBuilderOptions,
optimizationOptions: NormalizedOptimizationOptions,
sourcemapOptions: SourceMapClass,
target: string[],
) {
const outputFiles: OutputFile[] = [];
const initialFiles: FileInfo[] = [];
Expand Down Expand Up @@ -360,6 +371,7 @@ async function bundleGlobalStylesheets(
includePaths: options.stylePreprocessorOptions?.includePaths,
preserveSymlinks: options.preserveSymlinks,
externalDependencies: options.externalDependencies,
target,
},
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface BundleStylesheetOptions {
outputNames?: { bundles?: string; media?: string };
includePaths?: string[];
externalDependencies?: string[];
target: string[];
}

async function bundleStylesheet(
Expand All @@ -43,6 +44,7 @@ async function bundleStylesheet(
outdir: options.workspaceRoot,
write: false,
platform: 'browser',
target: options.target,
preserveSymlinks: options.preserveSymlinks,
external: options.externalDependencies,
conditions: ['style', 'sass'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ describe('Browser Builder allow js', () => {

host.replaceInFile(
'tsconfig.json',
'"target": "es2020"',
'"target": "es2020", "allowJs": true',
'"target": "es2022"',
'"target": "es2022", "allowJs": true',
);

const run = await architect.scheduleTarget(targetSpec);
Expand All @@ -56,8 +56,8 @@ describe('Browser Builder allow js', () => {

host.replaceInFile(
'tsconfig.json',
'"target": "es2020"',
'"target": "es2020", "allowJs": true',
'"target": "es2022"',
'"target": "es2022", "allowJs": true',
);

const overrides = { aot: true };
Expand All @@ -83,8 +83,8 @@ describe('Browser Builder allow js', () => {

host.replaceInFile(
'tsconfig.json',
'"target": "es2020"',
'"target": "es2020", "allowJs": true',
'"target": "es2022"',
'"target": "es2022", "allowJs": true',
);

const overrides = { watch: true };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ describe('Browser Builder AOT', () => {
const run = await architect.scheduleTarget(targetSpec, overrides);
const output = (await run.result) as BrowserBuilderOutput;

expect(output.success).toBe(true);
expect(output.success).toBeTrue();

const fileName = join(normalize(output.outputPath), 'main.js');
const fileName = join(normalize(output.outputs[0].path), 'main.js');
const content = virtualFs.fileBufferToString(await host.read(normalize(fileName)).toPromise());
expect(content).toContain('AppComponent.ɵcmp');
expect(content).toContain('AppComponent_Factory');

await run.stop();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,7 @@ describe('Browser Builder lazy modules', () => {

const { files } = await browserBuild(architect, host, target, { aot: true });
const data = await files['src_app_lazy_lazy_module_ts.js'];
expect(data).not.toBeUndefined();
expect(data).toContain('LazyModule.ɵmod');
expect(data).toContain('this.ɵmod');
});
});

Expand Down Expand Up @@ -126,7 +125,7 @@ describe('Browser Builder lazy modules', () => {
});

const { files } = await browserBuild(architect, host, target);
expect(files['src_lazy-module_ts.js']).not.toBeUndefined();
expect(files['src_lazy-module_ts.js']).toBeDefined();
});

it(`supports lazy bundle for dynamic import() calls`, async () => {
Expand All @@ -140,7 +139,7 @@ describe('Browser Builder lazy modules', () => {
host.replaceInFile('src/tsconfig.app.json', '"main.ts"', `"main.ts","lazy-module.ts"`);

const { files } = await browserBuild(architect, host, target);
expect(files['lazy-module.js']).not.toBeUndefined();
expect(files['lazy-module.js']).toBeDefined();
});

it(`supports making a common bundle for shared lazy modules`, async () => {
Expand All @@ -151,8 +150,8 @@ describe('Browser Builder lazy modules', () => {
});

const { files } = await browserBuild(architect, host, target);
expect(files['src_one_ts.js']).not.toBeUndefined();
expect(files['src_two_ts.js']).not.toBeUndefined();
expect(files['src_one_ts.js']).toBeDefined();
expect(files['src_two_ts.js']).toBeDefined();
expect(files['default-node_modules_angular_common_fesm2020_http_mjs.js']).toBeDefined();
});

Expand All @@ -164,8 +163,8 @@ describe('Browser Builder lazy modules', () => {
});

const { files } = await browserBuild(architect, host, target, { commonChunk: false });
expect(files['src_one_ts.js']).not.toBeUndefined();
expect(files['src_two_ts.js']).not.toBeUndefined();
expect(files['src_one_ts.js']).toBeDefined();
expect(files['src_two_ts.js']).toBeDefined();
expect(files['default-node_modules_angular_common_fesm2020_http_mjs.js']).toBeUndefined();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ describe('Browser Builder resolve json module', () => {

host.replaceInFile(
'tsconfig.json',
'"target": "es2020"',
'"target": "es2020", "resolveJsonModule": true',
'"target": "es2022"',
'"target": "es2022", "resolveJsonModule": true',
);

const overrides = { watch: true };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,12 @@ describeBuilder(buildWebpackBrowser, BROWSER_BUILDER_INFO, (harness) => {
});

it('warns when IE is present in browserslist', async () => {
await harness.writeFile(
await harness.appendToFile(
'.browserslistrc',
`
IE 9
IE 11
`,
IE 9
IE 11
`,
);

harness.useTarget('build', {
Expand All @@ -84,9 +84,9 @@ describeBuilder(buildWebpackBrowser, BROWSER_BUILDER_INFO, (harness) => {
jasmine.objectContaining({
level: 'warn',
message:
`One or more browsers which are configured in the project's Browserslist configuration ` +
'will be ignored as ES5 output is not supported by the Angular CLI.\n' +
`Ignored browsers: ie 11, ie 9`,
`One or more browsers which are configured in the project's Browserslist ` +
'configuration will be ignored as ES5 output is not supported by the Angular CLI.\n' +
'Ignored browsers: ie 11, ie 9',
}),
);
});
Expand All @@ -96,12 +96,12 @@ describeBuilder(buildWebpackBrowser, BROWSER_BUILDER_INFO, (harness) => {
await harness.writeFile(
'src/main.ts',
`
(async () => {
for await (const o of [1, 2, 3]) {
console.log("for await...of");
}
})();
`,
(async () => {
for await (const o of [1, 2, 3]) {
console.log("for await...of");
}
})();
`,
);

harness.useTarget('build', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ describeBuilder(serveWebpackBrowser, DEV_SERVER_BUILDER_INFO, (harness) => {
};

describe('Behavior: "dev-server builder serves service worker"', () => {
beforeEach(() => {
beforeEach(async () => {
// Application code is not needed for these tests
await harness.writeFile('src/main.ts', '');
await harness.writeFile('src/polyfills.ts', '');

harness.useProject('test', {
root: '.',
sourceRoot: 'src',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ async function initialize(
context,
(wco) => {
// We use the platform to determine the JavaScript syntax output.
wco.buildOptions.supportedBrowsers ??= [];
wco.buildOptions.supportedBrowsers.push(...browserslist('maintained node versions'));

return [getPlatformServerExportsConfig(wco), getCommonConfig(wco), getStylesConfig(wco)];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export interface BuildOptions {
cache: NormalizedCachedOptions;
codeCoverage?: boolean;
codeCoverageExclude?: string[];
supportedBrowsers: string[];
supportedBrowsers?: string[];
}

export interface WebpackDevServerOptions
Expand All @@ -87,6 +87,5 @@ export interface WebpackConfigOptions<T = BuildOptions> {
buildOptions: T;
tsConfig: ParsedConfiguration;
tsConfigPath: string;
scriptTarget: import('typescript').ScriptTarget;
projectName: string;
}
Loading