@angular/build 21.0.0-next.4 → 21.0.0-next.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/package.json +8 -8
  2. package/src/builders/karma/application_builder.js +5 -1
  3. package/src/builders/karma/coverage.js +1 -1
  4. package/src/builders/karma/find-tests.d.ts +1 -9
  5. package/src/builders/karma/find-tests.js +6 -106
  6. package/src/builders/unit-test/builder.js +20 -2
  7. package/src/builders/unit-test/options.d.ts +16 -2
  8. package/src/builders/unit-test/options.js +37 -4
  9. package/src/builders/unit-test/runners/karma/executor.js +26 -3
  10. package/src/builders/unit-test/runners/karma/index.js +1 -1
  11. package/src/builders/unit-test/runners/vitest/browser-provider.d.ts +4 -1
  12. package/src/builders/unit-test/runners/vitest/browser-provider.js +6 -2
  13. package/src/builders/unit-test/runners/vitest/build-options.js +6 -6
  14. package/src/builders/unit-test/runners/vitest/executor.js +30 -8
  15. package/src/builders/unit-test/runners/vitest/index.js +1 -1
  16. package/src/builders/unit-test/schema.d.ts +93 -13
  17. package/src/builders/unit-test/schema.js +12 -12
  18. package/src/builders/unit-test/schema.json +126 -33
  19. package/src/builders/unit-test/test-discovery.d.ts +25 -1
  20. package/src/builders/unit-test/test-discovery.js +194 -5
  21. package/src/tools/angular/transformers/jit-bootstrap-transformer.js +1 -1
  22. package/src/tools/angular/transformers/jit-resource-transformer.js +1 -1
  23. package/src/tools/babel/plugins/pure-toplevel-functions.d.ts +0 -1
  24. package/src/tools/babel/plugins/pure-toplevel-functions.js +21 -5
  25. package/src/tools/esbuild/angular/compiler-plugin.js +38 -14
  26. package/src/tools/esbuild/javascript-transformer-worker.js +2 -8
  27. package/src/tools/esbuild/stylesheets/less-language.js +2 -26
  28. package/src/tools/esbuild/stylesheets/stylesheet-plugin-factory.js +2 -1
  29. package/src/utils/normalize-cache.js +1 -1
  30. package/src/utils/server-rendering/utils.d.ts +1 -1
  31. package/src/utils/service-worker.d.ts +1 -1
@@ -141,7 +141,7 @@ function visitComponentMetadata(nodeFactory, node, styleReplacements, resourceIm
141
141
  function createResourceImport(nodeFactory, url, resourceImportDeclarations) {
142
142
  const urlLiteral = nodeFactory.createStringLiteral(url);
143
143
  const importName = nodeFactory.createIdentifier(`__NG_CLI_RESOURCE__${resourceImportDeclarations.length}`);
144
- resourceImportDeclarations.push(nodeFactory.createImportDeclaration(undefined, nodeFactory.createImportClause(false, importName, undefined), urlLiteral));
144
+ resourceImportDeclarations.push(nodeFactory.createImportDeclaration(undefined, nodeFactory.createImportClause(undefined, importName, undefined), urlLiteral));
145
145
  return importName;
146
146
  }
147
147
  function getDecoratorOrigin(decorator, typeChecker) {
@@ -8,7 +8,6 @@
8
8
  import type { PluginObj } from '@babel/core';
9
9
  /**
10
10
  * A babel plugin factory function for adding the PURE annotation to top-level new and call expressions.
11
- *
12
11
  * @returns A babel plugin object instance.
13
12
  */
14
13
  export default function (): PluginObj;
@@ -47,7 +47,11 @@ exports.default = default_1;
47
47
  const helper_annotate_as_pure_1 = __importDefault(require("@babel/helper-annotate-as-pure"));
48
48
  const tslib = __importStar(require("tslib"));
49
49
  /**
50
- * A cached set of TypeScript helper function names used by the helper name matcher utility function.
50
+ * A set of constructor names that are considered to be side-effect free.
51
+ */
52
+ const sideEffectFreeConstructors = new Set(['InjectionToken']);
53
+ /**
54
+ * A set of TypeScript helper function names used by the helper name matcher utility function.
51
55
  */
52
56
  const tslibHelpers = new Set(Object.keys(tslib).filter((h) => h.startsWith('__')));
53
57
  /**
@@ -76,13 +80,16 @@ function isBabelHelperName(name) {
76
80
  }
77
81
  /**
78
82
  * A babel plugin factory function for adding the PURE annotation to top-level new and call expressions.
79
- *
80
83
  * @returns A babel plugin object instance.
81
84
  */
82
85
  function default_1() {
83
86
  return {
84
87
  visitor: {
85
- CallExpression(path) {
88
+ CallExpression(path, state) {
89
+ const { topLevelSafeMode = false } = state.opts;
90
+ if (topLevelSafeMode) {
91
+ return;
92
+ }
86
93
  // If the expression has a function parent, it is not top-level
87
94
  if (path.getFunctionParent()) {
88
95
  return;
@@ -100,9 +107,18 @@ function default_1() {
100
107
  }
101
108
  (0, helper_annotate_as_pure_1.default)(path);
102
109
  },
103
- NewExpression(path) {
110
+ NewExpression(path, state) {
104
111
  // If the expression has a function parent, it is not top-level
105
- if (!path.getFunctionParent()) {
112
+ if (path.getFunctionParent()) {
113
+ return;
114
+ }
115
+ const { topLevelSafeMode = false } = state.opts;
116
+ if (!topLevelSafeMode) {
117
+ (0, helper_annotate_as_pure_1.default)(path);
118
+ return;
119
+ }
120
+ const callee = path.get('callee');
121
+ if (callee.isIdentifier() && sideEffectFreeConstructors.has(callee.node.name)) {
106
122
  (0, helper_annotate_as_pure_1.default)(path);
107
123
  }
108
124
  },
@@ -46,6 +46,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
46
46
  exports.createCompilerPlugin = createCompilerPlugin;
47
47
  const node_assert_1 = __importDefault(require("node:assert"));
48
48
  const node_crypto_1 = require("node:crypto");
49
+ const promises_1 = require("node:fs/promises");
49
50
  const path = __importStar(require("node:path"));
50
51
  const environment_options_1 = require("../../../utils/environment-options");
51
52
  const compilation_1 = require("../../angular/compilation");
@@ -344,11 +345,21 @@ function createCompilerPlugin(pluginOptions, compilationOrFactory, stylesheetBun
344
345
  if (!shouldTsIgnoreJs && isJS) {
345
346
  return undefined;
346
347
  }
348
+ const diangosticRoot = build.initialOptions.absWorkingDir ?? '';
349
+ // Evaluate whether the file requires the Angular compiler transpilation.
350
+ // If not, issue a warning but allow bundler to process the file (no type-checking).
351
+ const directContents = await (0, promises_1.readFile)(request, 'utf-8');
352
+ if (!requiresAngularCompiler(directContents)) {
353
+ return {
354
+ warnings: [createMissingFileDiagnostic(request, args.path, diangosticRoot, false)],
355
+ contents,
356
+ loader: 'ts',
357
+ resolveDir: path.dirname(request),
358
+ };
359
+ }
347
360
  // Otherwise return an error
348
361
  return {
349
- errors: [
350
- createMissingFileError(request, args.path, build.initialOptions.absWorkingDir ?? ''),
351
- ],
362
+ errors: [createMissingFileDiagnostic(request, args.path, diangosticRoot, true)],
352
363
  };
353
364
  }
354
365
  else if (typeof contents === 'string' && (useTypeScriptTranspilation || isJS)) {
@@ -585,21 +596,34 @@ function bundleWebWorker(build, pluginOptions, workerFile) {
585
596
  throw error;
586
597
  }
587
598
  }
588
- function createMissingFileError(request, original, root) {
599
+ function createMissingFileDiagnostic(request, original, root, angular) {
589
600
  const relativeRequest = path.relative(root, request);
590
- const error = {
591
- text: `File '${relativeRequest}' is missing from the TypeScript compilation.`,
592
- notes: [
593
- {
594
- text: `Ensure the file is part of the TypeScript program via the 'files' or 'include' property.`,
595
- },
596
- ],
597
- };
601
+ const notes = [];
602
+ if (angular) {
603
+ notes.push({
604
+ text: `Files containing Angular metadata ('@Component'/'@Directive'/etc.) must be part of the TypeScript compilation.` +
605
+ ` You can ensure the file is part of the TypeScript program via the 'files' or 'include' property.`,
606
+ });
607
+ }
608
+ else {
609
+ notes.push({
610
+ text: `The file will be bundled and included in the output but will not be type-checked at build time.` +
611
+ ` To remove this message you can add the file to the TypeScript program via the 'files' or 'include' property.`,
612
+ });
613
+ }
598
614
  const relativeOriginal = path.relative(root, original);
599
615
  if (relativeRequest !== relativeOriginal) {
600
- error.notes.push({
616
+ notes.push({
601
617
  text: `File is requested from a file replacement of '${relativeOriginal}'.`,
602
618
  });
603
619
  }
604
- return error;
620
+ const diagnostic = {
621
+ text: `File '${relativeRequest}' not found in TypeScript compilation.`,
622
+ notes,
623
+ };
624
+ return diagnostic;
625
+ }
626
+ const POTENTIAL_METADATA_REGEX = /@angular\/core|@Component|@Directive|@Injectable|@Pipe|@NgModule/;
627
+ function requiresAngularCompiler(contents) {
628
+ return POTENTIAL_METADATA_REGEX.test(contents);
605
629
  }
@@ -84,16 +84,10 @@ async function transformWithBabel(filename, data, options) {
84
84
  plugins.push(linkerPlugin);
85
85
  }
86
86
  if (options.advancedOptimizations) {
87
+ const { adjustStaticMembers, adjustTypeScriptEnums, elideAngularMetadata, markTopLevelPure } = await Promise.resolve().then(() => __importStar(require('../babel/plugins')));
87
88
  const sideEffectFree = options.sideEffects === false;
88
89
  const safeAngularPackage = sideEffectFree && /[\\/]node_modules[\\/]@angular[\\/]/.test(filename);
89
- const { adjustStaticMembers, adjustTypeScriptEnums, elideAngularMetadata, markTopLevelPure } = await Promise.resolve().then(() => __importStar(require('../babel/plugins')));
90
- if (safeAngularPackage) {
91
- plugins.push(markTopLevelPure);
92
- }
93
- plugins.push(elideAngularMetadata, adjustTypeScriptEnums, [
94
- adjustStaticMembers,
95
- { wrapDecorators: sideEffectFree },
96
- ]);
90
+ plugins.push([markTopLevelPure, { topLevelSafeMode: !safeAngularPackage }], elideAngularMetadata, adjustTypeScriptEnums, [adjustStaticMembers, { wrapDecorators: sideEffectFree }]);
97
91
  }
98
92
  // If no additional transformations are needed, return the data directly
99
93
  if (plugins.length === 0) {
@@ -55,11 +55,10 @@ exports.LessStylesheetLanguage = Object.freeze({
55
55
  componentFilter: /^less;/,
56
56
  fileFilter: /\.less$/,
57
57
  process(data, file, _, options, build) {
58
- return compileString(data, file, options, build.resolve.bind(build),
59
- /* unsafeInlineJavaScript */ false);
58
+ return compileString(data, file, options, build.resolve.bind(build));
60
59
  },
61
60
  });
62
- async function compileString(data, filename, options, resolver, unsafeInlineJavaScript) {
61
+ async function compileString(data, filename, options, resolver) {
63
62
  try {
64
63
  lessPreprocessor ??= (await Promise.resolve().then(() => __importStar(require('less')))).default;
65
64
  }
@@ -120,7 +119,6 @@ async function compileString(data, filename, options, resolver, unsafeInlineJava
120
119
  paths: options.includePaths,
121
120
  plugins: [resolverPlugin],
122
121
  rewriteUrls: 'all',
123
- javascriptEnabled: unsafeInlineJavaScript,
124
122
  sourceMap: options.sourcemap
125
123
  ? {
126
124
  sourceMapFileInline: true,
@@ -137,28 +135,6 @@ async function compileString(data, filename, options, resolver, unsafeInlineJava
137
135
  catch (error) {
138
136
  if (isLessException(error)) {
139
137
  const location = convertExceptionLocation(error);
140
- // Retry with a warning for less files requiring the deprecated inline JavaScript option
141
- if (error.message.includes('Inline JavaScript is not enabled.')) {
142
- const withJsResult = await compileString(data, filename, options, resolver,
143
- /* unsafeInlineJavaScript */ true);
144
- withJsResult.warnings = [
145
- {
146
- text: 'Deprecated inline execution of JavaScript has been enabled ("javascriptEnabled")',
147
- location,
148
- notes: [
149
- {
150
- location: null,
151
- text: 'JavaScript found within less stylesheets may be executed at build time. [https://lesscss.org/usage/#less-options]',
152
- },
153
- {
154
- location: null,
155
- text: 'Support for "javascriptEnabled" may be removed from the Angular CLI starting with Angular v19.',
156
- },
157
- ],
158
- },
159
- ];
160
- return withJsResult;
161
- }
162
138
  return {
163
139
  errors: [
164
140
  {
@@ -152,7 +152,8 @@ class StylesheetPluginFactory {
152
152
  postcssProcessor = postcss();
153
153
  const postCssPluginRequire = (0, node_module_1.createRequire)((0, node_path_1.dirname)(configPath) + '/');
154
154
  for (const [pluginName, pluginOptions] of config.plugins) {
155
- const plugin = postCssPluginRequire(pluginName);
155
+ const pluginMod = postCssPluginRequire(pluginName);
156
+ const plugin = pluginMod.__esModule ? pluginMod['default'] : pluginMod;
156
157
  if (typeof plugin !== 'function' || plugin.postcss !== true) {
157
158
  throw new Error(`Attempted to load invalid Postcss plugin: "${pluginName}"`);
158
159
  }
@@ -10,7 +10,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.normalizeCacheOptions = normalizeCacheOptions;
11
11
  const node_path_1 = require("node:path");
12
12
  /** Version placeholder is replaced during the build process with actual package version */
13
- const VERSION = '21.0.0-next.4';
13
+ const VERSION = '21.0.0-next.6';
14
14
  function hasCacheMetadata(value) {
15
15
  return (!!value &&
16
16
  typeof value === 'object' &&
@@ -6,6 +6,6 @@
6
6
  * found in the LICENSE file at https://angular.dev/license
7
7
  */
8
8
  import type { createRequestHandler } from '@angular/ssr';
9
- import type { createNodeRequestHandler } from '@angular/ssr/node';
9
+ import type { createNodeRequestHandler } from '@angular/ssr/node' with { 'resolution-mode': 'import' };
10
10
  export declare function isSsrNodeRequestHandler(value: unknown): value is ReturnType<typeof createNodeRequestHandler>;
11
11
  export declare function isSsrRequestHandler(value: unknown): value is ReturnType<typeof createRequestHandler>;
@@ -5,7 +5,7 @@
5
5
  * Use of this source code is governed by an MIT-style license that can be
6
6
  * found in the LICENSE file at https://angular.dev/license
7
7
  */
8
- import type { Config, Filesystem } from '@angular/service-worker/config';
8
+ import type { Config, Filesystem } from '@angular/service-worker/config' with { 'resolution-mode': 'import' };
9
9
  import { promises as fsPromises } from 'node:fs';
10
10
  import { BuildOutputFile } from '../tools/esbuild/bundler-context';
11
11
  import { BuildOutputAsset } from '../tools/esbuild/bundler-execution-result';