Skip to content

Commit b0d5dac

Browse files
committed
fix(@angular-devkit/build-angular): show warning when using TypeScript target older then ES2022 in esbuild builder
This commits adds a warning similar to that in the Webpack builder.
1 parent 2624d89 commit b0d5dac

File tree

1 file changed

+108
-99
lines changed

1 file changed

+108
-99
lines changed

packages/angular_devkit/build_angular/src/builders/browser-esbuild/compiler-plugin.ts

Lines changed: 108 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -160,37 +160,6 @@ export function createCompilerPlugin(
160160
build.initialOptions.define[key] = value.toString();
161161
}
162162

163-
// The tsconfig is loaded in setup instead of in start to allow the esbuild target build option to be modified.
164-
// esbuild build options can only be modified in setup prior to starting the build.
165-
const {
166-
options: compilerOptions,
167-
rootNames,
168-
errors: configurationDiagnostics,
169-
} = compilerCli.readConfiguration(pluginOptions.tsconfig, {
170-
noEmitOnError: false,
171-
suppressOutputPathCheck: true,
172-
outDir: undefined,
173-
inlineSources: pluginOptions.sourcemap,
174-
inlineSourceMap: pluginOptions.sourcemap,
175-
sourceMap: false,
176-
mapRoot: undefined,
177-
sourceRoot: undefined,
178-
declaration: false,
179-
declarationMap: false,
180-
allowEmptyCodegenFiles: false,
181-
annotationsAs: 'decorators',
182-
enableResourceInlining: false,
183-
});
184-
185-
if (compilerOptions.target === undefined || compilerOptions.target < ts.ScriptTarget.ES2022) {
186-
// If 'useDefineForClassFields' is already defined in the users project leave the value as is.
187-
// Otherwise fallback to false due to https://github.com/microsoft/TypeScript/issues/45995
188-
// which breaks the deprecated `@Effects` NGRX decorator and potentially other existing code as well.
189-
compilerOptions.target = ts.ScriptTarget.ES2022;
190-
compilerOptions.useDefineForClassFields ??= false;
191-
// TODO: show warning about this override when we have access to the logger.
192-
}
193-
194163
// The file emitter created during `onStart` that will be used during the build in `onLoad` callbacks for TS files
195164
let fileEmitter: FileEmitter | undefined;
196165

@@ -203,6 +172,52 @@ export function createCompilerPlugin(
203172
// Reset stylesheet resource output files
204173
stylesheetResourceFiles = [];
205174

175+
// The tsconfig is loaded in setup instead of in start to allow the esbuild target build option to be modified.
176+
// esbuild build options can only be modified in setup prior to starting the build.
177+
const {
178+
options: compilerOptions,
179+
rootNames,
180+
errors: configurationDiagnostics,
181+
} = compilerCli.readConfiguration(pluginOptions.tsconfig, {
182+
noEmitOnError: false,
183+
suppressOutputPathCheck: true,
184+
outDir: undefined,
185+
inlineSources: pluginOptions.sourcemap,
186+
inlineSourceMap: pluginOptions.sourcemap,
187+
sourceMap: false,
188+
mapRoot: undefined,
189+
sourceRoot: undefined,
190+
declaration: false,
191+
declarationMap: false,
192+
allowEmptyCodegenFiles: false,
193+
annotationsAs: 'decorators',
194+
enableResourceInlining: false,
195+
});
196+
197+
if (
198+
compilerOptions.target === undefined ||
199+
compilerOptions.target < ts.ScriptTarget.ES2022
200+
) {
201+
// If 'useDefineForClassFields' is already defined in the users project leave the value as is.
202+
// Otherwise fallback to false due to https://github.com/microsoft/TypeScript/issues/45995
203+
// which breaks the deprecated `@Effects` NGRX decorator and potentially other existing code as well.
204+
compilerOptions.target = ts.ScriptTarget.ES2022;
205+
compilerOptions.useDefineForClassFields ??= false;
206+
207+
(result.warnings ??= []).push({
208+
text:
209+
'TypeScript compiler options "target" and "useDefineForClassFields" are set to "ES2022" and ' +
210+
'"false" respectively by the Angular CLI.',
211+
location: { file: pluginOptions.tsconfig },
212+
notes: [
213+
{
214+
text: `To control ECMA version and features use the Browerslist configuration. ' +
215+
'For more information, see https://github.com/browserslist/browsers§list'`,
216+
},
217+
],
218+
});
219+
}
220+
206221
// Create TypeScript compiler host
207222
const host = ts.createIncrementalCompilerHost(compilerOptions);
208223

@@ -316,80 +331,74 @@ export function createCompilerPlugin(
316331
return result;
317332
});
318333

319-
build.onLoad(
320-
{ filter: compilerOptions.allowJs ? /\.[cm]?[jt]sx?$/ : /\.[cm]?tsx?$/ },
321-
async (args) => {
322-
assert.ok(fileEmitter, 'Invalid plugin execution order');
334+
build.onLoad({ filter: /\.[cm]?[jt]sx?$/ }, async (args) => {
335+
assert.ok(fileEmitter, 'Invalid plugin execution order');
323336

324-
const typescriptResult = await fileEmitter(
325-
pluginOptions.fileReplacements?.[args.path] ?? args.path,
326-
);
327-
if (!typescriptResult) {
328-
// No TS result indicates the file is not part of the TypeScript program.
329-
// If allowJs is enabled and the file is JS then defer to the next load hook.
330-
if (compilerOptions.allowJs && /\.[cm]?js$/.test(args.path)) {
331-
return undefined;
332-
}
333-
334-
// Otherwise return an error
335-
return {
336-
errors: [
337-
{
338-
text: 'File is missing from the TypeScript compilation.',
339-
location: { file: args.path },
340-
notes: [
341-
{
342-
text: `Ensure the file is part of the TypeScript program via the 'files' or 'include' property.`,
343-
},
344-
],
345-
},
346-
],
347-
};
348-
}
349-
350-
const data = typescriptResult.content ?? '';
351-
const forceAsyncTransformation = /async\s+function\s*\*/.test(data);
352-
const useInputSourcemap =
353-
pluginOptions.sourcemap &&
354-
(!!pluginOptions.thirdPartySourcemaps || !/[\\/]node_modules[\\/]/.test(args.path));
355-
356-
// If no additional transformations are needed, return the TypeScript output directly
357-
if (!forceAsyncTransformation && !pluginOptions.advancedOptimizations) {
358-
return {
359-
// Strip sourcemaps if they should not be used
360-
contents: useInputSourcemap
361-
? data
362-
: data.replace(/^\/\/# sourceMappingURL=[^\r\n]*/gm, ''),
363-
loader: 'js',
364-
};
365-
}
337+
const typescriptResult = await fileEmitter(
338+
pluginOptions.fileReplacements?.[args.path] ?? args.path,
339+
);
340+
if (!typescriptResult) {
341+
// No TS result indicates the file is not part of the TypeScript program.
342+
// Otherwise return an error
343+
return /\.[cm]?js$/.test(args.path)
344+
? undefined
345+
: {
346+
errors: [
347+
{
348+
text: 'File is missing from the TypeScript compilation.',
349+
location: { file: args.path },
350+
notes: [
351+
{
352+
text: `Ensure the file is part of the TypeScript program via the 'files' or 'include' property.`,
353+
},
354+
],
355+
},
356+
],
357+
};
358+
}
366359

367-
const babelResult = await transformAsync(data, {
368-
filename: args.path,
369-
inputSourceMap: (useInputSourcemap ? undefined : false) as undefined,
370-
sourceMaps: pluginOptions.sourcemap ? 'inline' : false,
371-
compact: false,
372-
configFile: false,
373-
babelrc: false,
374-
browserslistConfigFile: false,
375-
plugins: [],
376-
presets: [
377-
[
378-
angularApplicationPreset,
379-
{
380-
forceAsyncTransformation,
381-
optimize: pluginOptions.advancedOptimizations && {},
382-
},
383-
],
384-
],
385-
});
360+
const data = typescriptResult.content ?? '';
361+
const forceAsyncTransformation = /async\s+function\s*\*/.test(data);
362+
const useInputSourcemap =
363+
pluginOptions.sourcemap &&
364+
(!!pluginOptions.thirdPartySourcemaps || !/[\\/]node_modules[\\/]/.test(args.path));
386365

366+
// If no additional transformations are needed, return the TypeScript output directly
367+
if (!forceAsyncTransformation && !pluginOptions.advancedOptimizations) {
387368
return {
388-
contents: babelResult?.code ?? '',
369+
// Strip sourcemaps if they should not be used
370+
contents: useInputSourcemap
371+
? data
372+
: data.replace(/^\/\/# sourceMappingURL=[^\r\n]*/gm, ''),
389373
loader: 'js',
390374
};
391-
},
392-
);
375+
}
376+
377+
const babelResult = await transformAsync(data, {
378+
filename: args.path,
379+
inputSourceMap: (useInputSourcemap ? undefined : false) as undefined,
380+
sourceMaps: pluginOptions.sourcemap ? 'inline' : false,
381+
compact: false,
382+
configFile: false,
383+
babelrc: false,
384+
browserslistConfigFile: false,
385+
plugins: [],
386+
presets: [
387+
[
388+
angularApplicationPreset,
389+
{
390+
forceAsyncTransformation,
391+
optimize: pluginOptions.advancedOptimizations && {},
392+
},
393+
],
394+
],
395+
});
396+
397+
return {
398+
contents: babelResult?.code ?? '',
399+
loader: 'js',
400+
};
401+
});
393402

394403
build.onLoad({ filter: /\.[cm]?js$/ }, async (args) => {
395404
const data = await fs.readFile(args.path, 'utf-8');

0 commit comments

Comments
 (0)