@angular/build 21.0.0-next.5 → 21.0.0-next.7

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@angular/build",
3
- "version": "21.0.0-next.5",
3
+ "version": "21.0.0-next.7",
4
4
  "description": "Official build system for Angular",
5
5
  "keywords": [
6
6
  "Angular CLI",
@@ -23,7 +23,7 @@
23
23
  "builders": "builders.json",
24
24
  "dependencies": {
25
25
  "@ampproject/remapping": "2.3.0",
26
- "@angular-devkit/architect": "0.2100.0-next.5",
26
+ "@angular-devkit/architect": "0.2100.0-next.7",
27
27
  "@babel/core": "7.28.4",
28
28
  "@babel/helper-annotate-as-pure": "7.27.3",
29
29
  "@babel/helper-split-export-declaration": "7.24.7",
@@ -41,16 +41,16 @@
41
41
  "parse5-html-rewriting-stream": "8.0.0",
42
42
  "picomatch": "4.0.3",
43
43
  "piscina": "5.1.3",
44
- "rolldown": "1.0.0-beta.39",
45
- "sass": "1.93.1",
44
+ "rolldown": "1.0.0-beta.42",
45
+ "sass": "1.93.2",
46
46
  "semver": "7.7.2",
47
47
  "source-map-support": "0.5.21",
48
48
  "tinyglobby": "0.2.15",
49
- "vite": "7.1.7",
49
+ "vite": "7.1.9",
50
50
  "watchpack": "2.4.4"
51
51
  },
52
52
  "optionalDependencies": {
53
- "lmdb": "3.4.2"
53
+ "lmdb": "3.4.3"
54
54
  },
55
55
  "peerDependencies": {
56
56
  "@angular/core": "^21.0.0-next.0",
@@ -60,7 +60,7 @@
60
60
  "@angular/platform-browser": "^21.0.0-next.0",
61
61
  "@angular/platform-server": "^21.0.0-next.0",
62
62
  "@angular/service-worker": "^21.0.0-next.0",
63
- "@angular/ssr": "^21.0.0-next.5",
63
+ "@angular/ssr": "^21.0.0-next.7",
64
64
  "karma": "^6.4.0",
65
65
  "less": "^4.2.0",
66
66
  "ng-packagr": "^21.0.0-next.0",
@@ -112,7 +112,7 @@
112
112
  "type": "git",
113
113
  "url": "https://github.com/angular/angular-cli.git"
114
114
  },
115
- "packageManager": "pnpm@10.17.1",
115
+ "packageManager": "pnpm@10.18.1",
116
116
  "engines": {
117
117
  "node": "^20.19.0 || ^22.12.0 || >=24.0.0",
118
118
  "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
@@ -45,6 +45,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
45
45
  Object.defineProperty(exports, "__esModule", { value: true });
46
46
  exports.execute = execute;
47
47
  const node_crypto_1 = require("node:crypto");
48
+ const node_fs_1 = require("node:fs");
48
49
  const fs = __importStar(require("node:fs/promises"));
49
50
  const node_path_1 = __importDefault(require("node:path"));
50
51
  const web_1 = require("node:stream/web");
@@ -105,8 +106,14 @@ function execute(options, context, transforms) {
105
106
  async function initializeApplication(options, context, karmaOptions, transforms) {
106
107
  const karma = await Promise.resolve().then(() => __importStar(require('karma')));
107
108
  const projectSourceRoot = await (0, utils_1.getProjectSourceRoot)(context);
109
+ // Setup temporary output path and ensure it is empty
108
110
  const outputPath = node_path_1.default.join(context.workspaceRoot, 'dist/test-out', (0, node_crypto_1.randomUUID)());
109
111
  await fs.rm(outputPath, { recursive: true, force: true });
112
+ // Setup exit cleanup for temporary directory
113
+ const handleProcessExit = () => (0, node_fs_1.rmSync)(outputPath, { recursive: true, force: true });
114
+ process.once('exit', handleProcessExit);
115
+ process.once('SIGINT', handleProcessExit);
116
+ process.once('uncaughtException', handleProcessExit);
110
117
  const { buildOptions, mainName } = await setupBuildOptions(options, context, projectSourceRoot, outputPath);
111
118
  const [buildOutput, buildIterator] = await runEsbuild(buildOptions, context, projectSourceRoot);
112
119
  const karmaConfig = await configureKarma(karma, context, karmaOptions, options, buildOptions, buildOutput, mainName, transforms);
@@ -266,7 +266,15 @@ async function* execute(options, context, extensions) {
266
266
  // Get base build options from the buildTarget
267
267
  let buildTargetOptions;
268
268
  try {
269
- buildTargetOptions = (await context.validateOptions(await context.getTargetOptions(normalizedOptions.buildTarget), await context.getBuilderNameForTarget(normalizedOptions.buildTarget)));
269
+ const builderName = await context.getBuilderNameForTarget(normalizedOptions.buildTarget);
270
+ if (builderName !== '@angular/build:application' &&
271
+ // TODO: Add comprehensive support for ng-packagr.
272
+ builderName !== '@angular/build:ng-packagr') {
273
+ context.logger.warn(`The 'buildTarget' is configured to use '${builderName}', which is not supported. ` +
274
+ `The 'unit-test' builder is designed to work with '@angular/build:application'. ` +
275
+ 'Unexpected behavior or build failures may occur.');
276
+ }
277
+ buildTargetOptions = (await context.validateOptions(await context.getTargetOptions(normalizedOptions.buildTarget), builderName));
270
278
  }
271
279
  catch (e) {
272
280
  (0, error_1.assertIsError)(e);
@@ -303,8 +311,8 @@ async function* execute(options, context, extensions) {
303
311
  ...buildTargetOptions,
304
312
  ...runnerBuildOptions,
305
313
  watch: normalizedOptions.watch,
306
- tsConfig: normalizedOptions.tsConfig,
307
314
  progress: normalizedOptions.buildProgress ?? buildTargetOptions.progress,
315
+ ...(normalizedOptions.tsConfig ? { tsConfig: normalizedOptions.tsConfig } : {}),
308
316
  };
309
317
  const dumpDirectory = normalizedOptions.dumpVirtualFiles
310
318
  ? node_path_1.default.join(normalizedOptions.cacheOptions.path, 'unit-test', 'output-files')
@@ -18,15 +18,28 @@ export declare function normalizeOptions(context: BuilderContext, projectName: s
18
18
  exclude: string[] | undefined;
19
19
  filter: string | undefined;
20
20
  runnerName: import("./schema").Runner;
21
- codeCoverage: {
21
+ coverage: {
22
+ all: boolean | undefined;
22
23
  exclude: string[] | undefined;
24
+ include: string[] | undefined;
23
25
  reporters: [string, Record<string, unknown>][] | undefined;
26
+ thresholds: import("./schema").CoverageThresholds | undefined;
27
+ watermarks: {
28
+ statements?: [number, number];
29
+ branches?: [number, number];
30
+ functions?: [number, number];
31
+ lines?: [number, number];
32
+ };
24
33
  } | undefined;
25
- tsConfig: string;
34
+ tsConfig: string | undefined;
26
35
  buildProgress: boolean | undefined;
27
36
  reporters: [string, Record<string, unknown>][] | undefined;
28
37
  outputFile: string | undefined;
29
38
  browsers: string[] | undefined;
39
+ browserViewport: {
40
+ width: number;
41
+ height: number;
42
+ } | undefined;
30
43
  watch: boolean;
31
44
  debug: boolean;
32
45
  providersFile: string | undefined;
@@ -13,10 +13,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
13
13
  exports.normalizeOptions = normalizeOptions;
14
14
  exports.injectTestingPolyfills = injectTestingPolyfills;
15
15
  const architect_1 = require("@angular-devkit/architect");
16
+ const node_fs_1 = require("node:fs");
16
17
  const node_path_1 = __importDefault(require("node:path"));
17
18
  const normalize_cache_1 = require("../../utils/normalize-cache");
18
19
  const project_metadata_1 = require("../../utils/project-metadata");
19
20
  const tty_1 = require("../../utils/tty");
21
+ async function exists(path) {
22
+ try {
23
+ await node_fs_1.promises.access(path, node_fs_1.constants.F_OK);
24
+ return true;
25
+ }
26
+ catch {
27
+ return false;
28
+ }
29
+ }
20
30
  function normalizeReporterOption(reporters) {
21
31
  return reporters?.map((entry) => typeof entry === 'string'
22
32
  ? [entry, {}]
@@ -33,7 +43,22 @@ async function normalizeOptions(context, projectName, options) {
33
43
  // Target specifier defaults to the current project's build target using a development configuration
34
44
  const buildTargetSpecifier = options.buildTarget ?? `::development`;
35
45
  const buildTarget = (0, architect_1.targetFromTargetString)(buildTargetSpecifier, projectName, 'build');
36
- const { tsConfig, runner, browsers, progress, filter } = options;
46
+ const { runner, browsers, progress, filter, browserViewport } = options;
47
+ const [width, height] = browserViewport?.split('x').map(Number) ?? [];
48
+ let tsConfig = options.tsConfig;
49
+ if (tsConfig) {
50
+ const fullTsConfigPath = node_path_1.default.join(workspaceRoot, tsConfig);
51
+ if (!(await exists(fullTsConfigPath))) {
52
+ throw new Error(`The specified tsConfig file '${tsConfig}' does not exist.`);
53
+ }
54
+ }
55
+ else {
56
+ const tsconfigSpecPath = node_path_1.default.join(projectRoot, 'tsconfig.spec.json');
57
+ if (await exists(tsconfigSpecPath)) {
58
+ // The application builder expects a path relative to the workspace root.
59
+ tsConfig = node_path_1.default.relative(workspaceRoot, tsconfigSpecPath);
60
+ }
61
+ }
37
62
  return {
38
63
  // Project/workspace information
39
64
  workspaceRoot,
@@ -46,10 +71,16 @@ async function normalizeOptions(context, projectName, options) {
46
71
  exclude: options.exclude,
47
72
  filter,
48
73
  runnerName: runner,
49
- codeCoverage: options.codeCoverage
74
+ coverage: options.coverage
50
75
  ? {
51
- exclude: options.codeCoverageExclude,
52
- reporters: normalizeReporterOption(options.codeCoverageReporters),
76
+ all: options.coverageAll,
77
+ exclude: options.coverageExclude,
78
+ include: options.coverageInclude,
79
+ reporters: normalizeReporterOption(options.coverageReporters),
80
+ thresholds: options.coverageThresholds,
81
+ // The schema generation tool doesn't support tuple types for items, but the schema validation
82
+ // does ensure that the array has exactly two numbers.
83
+ watermarks: options.coverageWatermarks,
53
84
  }
54
85
  : undefined,
55
86
  tsConfig,
@@ -57,6 +88,7 @@ async function normalizeOptions(context, projectName, options) {
57
88
  reporters: normalizeReporterOption(options.reporters),
58
89
  outputFile: options.outputFile,
59
90
  browsers,
91
+ browserViewport: width && height ? { width, height } : undefined,
60
92
  watch: options.watch ?? (0, tty_1.isTTY)(),
61
93
  debug: options.debug ?? false,
62
94
  providersFile: options.providersFile && node_path_1.default.join(workspaceRoot, options.providersFile),
@@ -50,15 +50,24 @@ class KarmaExecutor {
50
50
  }
51
51
  async *execute() {
52
52
  const { context, options: unitTestOptions } = this;
53
+ if (unitTestOptions.browserViewport) {
54
+ context.logger.warn('The "karma" test runner does not support the "browserViewport" option. The option will be ignored.');
55
+ }
53
56
  if (unitTestOptions.debug) {
54
57
  context.logger.warn('The "karma" test runner does not support the "debug" option. The option will be ignored.');
55
58
  }
56
59
  if (unitTestOptions.setupFiles.length) {
57
60
  context.logger.warn('The "karma" test runner does not support the "setupFiles" option. The option will be ignored.');
58
61
  }
62
+ if (unitTestOptions.coverage?.all) {
63
+ context.logger.warn('The "karma" test runner does not support the "coverageAll" option. The option will be ignored.');
64
+ }
65
+ if (unitTestOptions.coverage?.include) {
66
+ context.logger.warn('The "karma" test runner does not support the "coverageInclude" option. The option will be ignored.');
67
+ }
59
68
  const buildTargetOptions = (await context.validateOptions(await context.getTargetOptions(unitTestOptions.buildTarget), await context.getBuilderNameForTarget(unitTestOptions.buildTarget)));
60
69
  const karmaOptions = {
61
- tsConfig: unitTestOptions.tsConfig,
70
+ tsConfig: unitTestOptions.tsConfig ?? buildTargetOptions.tsConfig,
62
71
  polyfills: buildTargetOptions.polyfills,
63
72
  assets: buildTargetOptions.assets,
64
73
  scripts: buildTargetOptions.scripts,
@@ -76,8 +85,8 @@ class KarmaExecutor {
76
85
  poll: buildTargetOptions.poll,
77
86
  preserveSymlinks: buildTargetOptions.preserveSymlinks,
78
87
  browsers: unitTestOptions.browsers?.join(','),
79
- codeCoverage: !!unitTestOptions.codeCoverage,
80
- codeCoverageExclude: unitTestOptions.codeCoverage?.exclude,
88
+ codeCoverage: !!unitTestOptions.coverage,
89
+ codeCoverageExclude: unitTestOptions.coverage?.exclude,
81
90
  fileReplacements: buildTargetOptions.fileReplacements,
82
91
  reporters: unitTestOptions.reporters?.map((reporter) => {
83
92
  // Karma only supports string reporters.
@@ -104,6 +113,20 @@ class KarmaExecutor {
104
113
  options.client.args ??= [];
105
114
  options.client.args.push('--grep', filter);
106
115
  }
116
+ // Add coverage options
117
+ if (unitTestOptions.coverage) {
118
+ const { thresholds, watermarks } = unitTestOptions.coverage;
119
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
120
+ const coverageReporter = (options.coverageReporter ??= {});
121
+ if (thresholds) {
122
+ coverageReporter.check = thresholds.perFile
123
+ ? { each: thresholds }
124
+ : { global: thresholds };
125
+ }
126
+ if (watermarks) {
127
+ coverageReporter.watermarks = watermarks;
128
+ }
129
+ }
107
130
  return options;
108
131
  },
109
132
  };
@@ -26,7 +26,7 @@ const KarmaTestRunner = {
26
26
  checker.check(launcherName);
27
27
  }
28
28
  }
29
- if (options.codeCoverage) {
29
+ if (options.coverage) {
30
30
  checker.check('karma-coverage');
31
31
  }
32
32
  checker.report();
@@ -9,4 +9,7 @@ export interface BrowserConfiguration {
9
9
  browser?: import('vitest/node').BrowserConfigOptions;
10
10
  errors?: string[];
11
11
  }
12
- export declare function setupBrowserConfiguration(browsers: string[] | undefined, debug: boolean, projectSourceRoot: string): BrowserConfiguration;
12
+ export declare function setupBrowserConfiguration(browsers: string[] | undefined, debug: boolean, projectSourceRoot: string, viewport: {
13
+ width: number;
14
+ height: number;
15
+ } | undefined): BrowserConfiguration;
@@ -28,7 +28,7 @@ function normalizeBrowserName(browserName) {
28
28
  const normalized = browserName.toLowerCase();
29
29
  return normalized.replace(/headless$/, '');
30
30
  }
31
- function setupBrowserConfiguration(browsers, debug, projectSourceRoot) {
31
+ function setupBrowserConfiguration(browsers, debug, projectSourceRoot, viewport) {
32
32
  if (browsers === undefined) {
33
33
  return {};
34
34
  }
@@ -57,10 +57,14 @@ function setupBrowserConfiguration(browsers, debug, projectSourceRoot) {
57
57
  if (errors) {
58
58
  return { errors };
59
59
  }
60
+ const isCI = !!process.env['CI'];
61
+ const headless = isCI || browsers.some((name) => name.toLowerCase().includes('headless'));
60
62
  const browser = {
61
63
  enabled: true,
62
64
  provider,
63
- headless: browsers.some((name) => name.toLowerCase().includes('headless')),
65
+ headless,
66
+ ui: !headless,
67
+ viewport,
64
68
  instances: browsers.map((browserName) => ({
65
69
  browser: normalizeBrowserName(browserName),
66
70
  })),
@@ -55,7 +55,7 @@ function adjustOutputHashing(hashing) {
55
55
  }
56
56
  }
57
57
  async function getVitestBuildOptions(options, baseBuildOptions) {
58
- const { workspaceRoot, projectSourceRoot, include, exclude = [], watch, tsConfig, providersFile, } = options;
58
+ const { workspaceRoot, projectSourceRoot, include, exclude = [], watch, providersFile } = options;
59
59
  // Find test files
60
60
  const testFiles = await (0, test_discovery_1.findTests)(include, exclude, workspaceRoot, projectSourceRoot);
61
61
  if (testFiles.length === 0) {
@@ -87,7 +87,6 @@ async function getVitestBuildOptions(options, baseBuildOptions) {
87
87
  sourceMap: { scripts: true, vendor: false, styles: false },
88
88
  outputHashing: adjustOutputHashing(baseBuildOptions.outputHashing),
89
89
  optimization: false,
90
- tsConfig,
91
90
  entryPoints,
92
91
  externalDependencies: ['vitest', '@vitest/browser/context'],
93
92
  };
@@ -11,6 +11,7 @@ import { NormalizedUnitTestBuilderOptions } from '../../options';
11
11
  import type { TestExecutor } from '../api';
12
12
  export declare class VitestExecutor implements TestExecutor {
13
13
  private vitest;
14
+ private normalizePath;
14
15
  private readonly projectName;
15
16
  private readonly options;
16
17
  private readonly buildResultFiles;
@@ -6,6 +6,39 @@
6
6
  * Use of this source code is governed by an MIT-style license that can be
7
7
  * found in the LICENSE file at https://angular.dev/license
8
8
  */
9
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ var desc = Object.getOwnPropertyDescriptor(m, k);
12
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
+ desc = { enumerable: true, get: function() { return m[k]; } };
14
+ }
15
+ Object.defineProperty(o, k2, desc);
16
+ }) : (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ o[k2] = m[k];
19
+ }));
20
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
22
+ }) : function(o, v) {
23
+ o["default"] = v;
24
+ });
25
+ var __importStar = (this && this.__importStar) || (function () {
26
+ var ownKeys = function(o) {
27
+ ownKeys = Object.getOwnPropertyNames || function (o) {
28
+ var ar = [];
29
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
30
+ return ar;
31
+ };
32
+ return ownKeys(o);
33
+ };
34
+ return function (mod) {
35
+ if (mod && mod.__esModule) return mod;
36
+ var result = {};
37
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
38
+ __setModuleDefault(result, mod);
39
+ return result;
40
+ };
41
+ })();
9
42
  var __importDefault = (this && this.__importDefault) || function (mod) {
10
43
  return (mod && mod.__esModule) ? mod : { "default": mod };
11
44
  };
@@ -21,6 +54,7 @@ const browser_provider_1 = require("./browser-provider");
21
54
  const plugins_1 = require("./plugins");
22
55
  class VitestExecutor {
23
56
  vitest;
57
+ normalizePath;
24
58
  projectName;
25
59
  options;
26
60
  buildResultFiles = new Map();
@@ -41,18 +75,19 @@ class VitestExecutor {
41
75
  }
42
76
  }
43
77
  async *execute(buildResult) {
78
+ this.normalizePath ??= (await Promise.resolve().then(() => __importStar(require('vite')))).normalizePath;
44
79
  if (buildResult.kind === results_1.ResultKind.Full) {
45
80
  this.buildResultFiles.clear();
46
81
  for (const [path, file] of Object.entries(buildResult.files)) {
47
- this.buildResultFiles.set(path, file);
82
+ this.buildResultFiles.set(this.normalizePath(path), file);
48
83
  }
49
84
  }
50
85
  else {
51
86
  for (const file of buildResult.removed) {
52
- this.buildResultFiles.delete(file.path);
87
+ this.buildResultFiles.delete(this.normalizePath(file.path));
53
88
  }
54
89
  for (const [path, file] of Object.entries(buildResult.files)) {
55
- this.buildResultFiles.set(path, file);
90
+ this.buildResultFiles.set(this.normalizePath(path), file);
56
91
  }
57
92
  }
58
93
  // Initialize Vitest if not already present.
@@ -101,7 +136,7 @@ class VitestExecutor {
101
136
  return testSetupFiles;
102
137
  }
103
138
  async initializeVitest() {
104
- const { codeCoverage, reporters, outputFile, workspaceRoot, browsers, debug, watch } = this.options;
139
+ const { coverage, reporters, outputFile, workspaceRoot, browsers, debug, watch, browserViewport, } = this.options;
105
140
  let vitestNodeModule;
106
141
  try {
107
142
  vitestNodeModule = await (0, load_esm_1.loadEsmModule)('vitest/node');
@@ -115,7 +150,7 @@ class VitestExecutor {
115
150
  }
116
151
  const { startVitest } = vitestNodeModule;
117
152
  // Setup vitest browser options if configured
118
- const browserOptions = (0, browser_provider_1.setupBrowserConfiguration)(browsers, debug, this.options.projectSourceRoot);
153
+ const browserOptions = (0, browser_provider_1.setupBrowserConfiguration)(browsers, debug, this.options.projectSourceRoot, browserViewport);
119
154
  if (browserOptions.errors?.length) {
120
155
  throw new Error(browserOptions.errors.join('\n'));
121
156
  }
@@ -148,7 +183,7 @@ class VitestExecutor {
148
183
  reporters: reporters ?? ['default'],
149
184
  outputFile,
150
185
  watch,
151
- coverage: generateCoverageOption(codeCoverage, this.projectName),
186
+ coverage: await generateCoverageOption(coverage, this.projectName),
152
187
  ...debugOptions,
153
188
  }, {
154
189
  server: {
@@ -161,20 +196,41 @@ class VitestExecutor {
161
196
  }
162
197
  }
163
198
  exports.VitestExecutor = VitestExecutor;
164
- function generateCoverageOption(codeCoverage, projectName) {
165
- if (!codeCoverage) {
199
+ async function generateCoverageOption(coverage, projectName) {
200
+ if (!coverage) {
166
201
  return {
167
202
  enabled: false,
168
203
  };
169
204
  }
205
+ let defaultExcludes = [];
206
+ if (coverage.exclude) {
207
+ try {
208
+ const vitestConfig = await (0, load_esm_1.loadEsmModule)('vitest/config');
209
+ defaultExcludes = vitestConfig.coverageConfigDefaults.exclude;
210
+ }
211
+ catch { }
212
+ }
170
213
  return {
171
214
  enabled: true,
215
+ all: coverage.all,
172
216
  excludeAfterRemap: true,
217
+ include: coverage.include,
173
218
  reportsDirectory: (0, path_1.toPosixPath)(node_path_1.default.join('coverage', projectName)),
219
+ thresholds: coverage.thresholds,
220
+ watermarks: coverage.watermarks,
174
221
  // Special handling for `exclude`/`reporters` due to an undefined value causing upstream failures
175
- ...(codeCoverage.exclude ? { exclude: codeCoverage.exclude } : {}),
176
- ...(codeCoverage.reporters
177
- ? { reporter: codeCoverage.reporters }
222
+ ...(coverage.exclude
223
+ ? {
224
+ exclude: [
225
+ // Augment the default exclude https://vitest.dev/config/#coverage-exclude
226
+ // with the user defined exclusions
227
+ ...coverage.exclude,
228
+ ...defaultExcludes,
229
+ ],
230
+ }
231
+ : {}),
232
+ ...(coverage.reporters
233
+ ? { reporter: coverage.reporters }
178
234
  : {}),
179
235
  };
180
236
  }
@@ -30,7 +30,7 @@ const VitestTestRunner = {
30
30
  // JSDOM is used when no browsers are specified
31
31
  checker.check('jsdom');
32
32
  }
33
- if (options.codeCoverage) {
33
+ if (options.coverage) {
34
34
  checker.check('@vitest/coverage-v8');
35
35
  }
36
36
  checker.report();
@@ -2,6 +2,11 @@
2
2
  * Unit testing options for Angular applications.
3
3
  */
4
4
  export type Schema = {
5
+ /**
6
+ * Specifies the browser viewport dimensions for browser-based tests in the format
7
+ * `widthxheight`.
8
+ */
9
+ browserViewport?: string;
5
10
  /**
6
11
  * Specifies the browsers to use for test execution. When not specified, tests are run in a
7
12
  * Node.js environment using jsdom. For both Vitest and Karma, browser names ending with
@@ -10,25 +15,45 @@ export type Schema = {
10
15
  browsers?: string[];
11
16
  /**
12
17
  * Specifies the build target to use for the unit test build in the format
13
- * `project:target[:configuration]`. You can also pass a comma-separated list of
18
+ * `project:target[:configuration]`. This defaults to the `build` target of the current
19
+ * project with the `development` configuration. You can also pass a comma-separated list of
14
20
  * configurations. Example: `project:target:production,staging`.
15
21
  */
16
- buildTarget: string;
22
+ buildTarget?: string;
17
23
  /**
18
- * Enables code coverage reporting for tests.
24
+ * Enables coverage reporting for tests.
19
25
  */
20
- codeCoverage?: boolean;
26
+ coverage?: boolean;
21
27
  /**
22
- * Specifies glob patterns of files to exclude from the code coverage report.
28
+ * Includes all files that match the `coverageInclude` pattern in the coverage report, not
29
+ * just those touched by tests.
23
30
  */
24
- codeCoverageExclude?: string[];
31
+ coverageAll?: boolean;
25
32
  /**
26
- * Specifies the reporters to use for code coverage results. Each reporter can be a string
33
+ * Specifies glob patterns of files to exclude from the coverage report.
34
+ */
35
+ coverageExclude?: string[];
36
+ /**
37
+ * Specifies glob patterns of files to include in the coverage report.
38
+ */
39
+ coverageInclude?: string[];
40
+ /**
41
+ * Specifies the reporters to use for coverage results. Each reporter can be a string
27
42
  * representing its name, or a tuple containing the name and an options object. Built-in
28
43
  * reporters include 'html', 'lcov', 'lcovonly', 'text', 'text-summary', 'cobertura',
29
44
  * 'json', and 'json-summary'.
30
45
  */
31
- codeCoverageReporters?: SchemaCodeCoverageReporter[];
46
+ coverageReporters?: SchemaCoverageReporter[];
47
+ /**
48
+ * Specifies minimum coverage thresholds that must be met. If thresholds are not met, the
49
+ * builder will exit with an error.
50
+ */
51
+ coverageThresholds?: CoverageThresholds;
52
+ /**
53
+ * Specifies coverage watermarks for the HTML reporter. These determine the color coding for
54
+ * high, medium, and low coverage.
55
+ */
56
+ coverageWatermarks?: CoverageWatermarks;
32
57
  /**
33
58
  * Enables debugging mode for tests, allowing the use of the Node Inspector.
34
59
  */
@@ -92,20 +117,22 @@ export type Schema = {
92
117
  */
93
118
  setupFiles?: string[];
94
119
  /**
95
- * The path to the TypeScript configuration file, relative to the workspace root.
120
+ * The path to the TypeScript configuration file, relative to the workspace root. Defaults
121
+ * to `tsconfig.spec.json` in the project root if it exists. If not specified and the
122
+ * default does not exist, the `tsConfig` from the specified `buildTarget` will be used.
96
123
  */
97
- tsConfig: string;
124
+ tsConfig?: string;
98
125
  /**
99
126
  * Enables watch mode, which re-runs tests when source files change. Defaults to `true` in
100
127
  * TTY environments and `false` otherwise.
101
128
  */
102
129
  watch?: boolean;
103
130
  };
104
- export type SchemaCodeCoverageReporter = CodeCoverageReporterCodeCoverageReporter[] | CoverageReporters;
105
- export type CodeCoverageReporterCodeCoverageReporter = CoverageReporters | {
131
+ export type SchemaCoverageReporter = CoverageReporterCoverageReporterUnion[] | CoverageReporterEnum;
132
+ export type CoverageReporterCoverageReporterUnion = CoverageReporterEnum | {
106
133
  [key: string]: any;
107
134
  };
108
- export declare enum CoverageReporters {
135
+ export declare enum CoverageReporterEnum {
109
136
  Cobertura = "cobertura",
110
137
  Html = "html",
111
138
  Json = "json",
@@ -115,6 +142,54 @@ export declare enum CoverageReporters {
115
142
  Text = "text",
116
143
  TextSummary = "text-summary"
117
144
  }
145
+ /**
146
+ * Specifies minimum coverage thresholds that must be met. If thresholds are not met, the
147
+ * builder will exit with an error.
148
+ */
149
+ export type CoverageThresholds = {
150
+ /**
151
+ * Minimum percentage of branches covered.
152
+ */
153
+ branches?: number;
154
+ /**
155
+ * Minimum percentage of functions covered.
156
+ */
157
+ functions?: number;
158
+ /**
159
+ * Minimum percentage of lines covered.
160
+ */
161
+ lines?: number;
162
+ /**
163
+ * When true, thresholds are enforced for each file individually.
164
+ */
165
+ perFile?: boolean;
166
+ /**
167
+ * Minimum percentage of statements covered.
168
+ */
169
+ statements?: number;
170
+ };
171
+ /**
172
+ * Specifies coverage watermarks for the HTML reporter. These determine the color coding for
173
+ * high, medium, and low coverage.
174
+ */
175
+ export type CoverageWatermarks = {
176
+ /**
177
+ * The high and low watermarks for branches coverage. `[low, high]`
178
+ */
179
+ branches?: number[];
180
+ /**
181
+ * The high and low watermarks for functions coverage. `[low, high]`
182
+ */
183
+ functions?: number[];
184
+ /**
185
+ * The high and low watermarks for lines coverage. `[low, high]`
186
+ */
187
+ lines?: number[];
188
+ /**
189
+ * The high and low watermarks for statements coverage. `[low, high]`
190
+ */
191
+ statements?: number[];
192
+ };
118
193
  export type SchemaReporter = ReporterReporter[] | string;
119
194
  export type ReporterReporter = {
120
195
  [key: string]: any;
@@ -2,18 +2,18 @@
2
2
  // THIS FILE IS AUTOMATICALLY GENERATED. TO UPDATE THIS FILE YOU NEED TO CHANGE THE
3
3
  // CORRESPONDING JSON SCHEMA FILE, THEN RUN devkit-admin build (or bazel build ...).
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
- exports.Runner = exports.CoverageReporters = void 0;
6
- var CoverageReporters;
7
- (function (CoverageReporters) {
8
- CoverageReporters["Cobertura"] = "cobertura";
9
- CoverageReporters["Html"] = "html";
10
- CoverageReporters["Json"] = "json";
11
- CoverageReporters["JsonSummary"] = "json-summary";
12
- CoverageReporters["Lcov"] = "lcov";
13
- CoverageReporters["Lcovonly"] = "lcovonly";
14
- CoverageReporters["Text"] = "text";
15
- CoverageReporters["TextSummary"] = "text-summary";
16
- })(CoverageReporters || (exports.CoverageReporters = CoverageReporters = {}));
5
+ exports.Runner = exports.CoverageReporterEnum = void 0;
6
+ var CoverageReporterEnum;
7
+ (function (CoverageReporterEnum) {
8
+ CoverageReporterEnum["Cobertura"] = "cobertura";
9
+ CoverageReporterEnum["Html"] = "html";
10
+ CoverageReporterEnum["Json"] = "json";
11
+ CoverageReporterEnum["JsonSummary"] = "json-summary";
12
+ CoverageReporterEnum["Lcov"] = "lcov";
13
+ CoverageReporterEnum["Lcovonly"] = "lcovonly";
14
+ CoverageReporterEnum["Text"] = "text";
15
+ CoverageReporterEnum["TextSummary"] = "text-summary";
16
+ })(CoverageReporterEnum || (exports.CoverageReporterEnum = CoverageReporterEnum = {}));
17
17
  /**
18
18
  * Specifies the test runner to use for test execution.
19
19
  */
@@ -6,12 +6,12 @@
6
6
  "properties": {
7
7
  "buildTarget": {
8
8
  "type": "string",
9
- "description": "Specifies the build target to use for the unit test build in the format `project:target[:configuration]`. You can also pass a comma-separated list of configurations. Example: `project:target:production,staging`.",
9
+ "description": "Specifies the build target to use for the unit test build in the format `project:target[:configuration]`. This defaults to the `build` target of the current project with the `development` configuration. You can also pass a comma-separated list of configurations. Example: `project:target:production,staging`.",
10
10
  "pattern": "^[^:\\s]*:[^:\\s]*(:[^\\s]+)?$"
11
11
  },
12
12
  "tsConfig": {
13
13
  "type": "string",
14
- "description": "The path to the TypeScript configuration file, relative to the workspace root."
14
+ "description": "The path to the TypeScript configuration file, relative to the workspace root. Defaults to `tsconfig.spec.json` in the project root if it exists. If not specified and the default does not exist, the `tsConfig` from the specified `buildTarget` will be used."
15
15
  },
16
16
  "runner": {
17
17
  "type": "string",
@@ -26,6 +26,11 @@
26
26
  },
27
27
  "minItems": 1
28
28
  },
29
+ "browserViewport": {
30
+ "description": "Specifies the browser viewport dimensions for browser-based tests in the format `widthxheight`.",
31
+ "type": "string",
32
+ "pattern": "^\\d+x\\d+$"
33
+ },
29
34
  "include": {
30
35
  "type": "array",
31
36
  "items": {
@@ -54,25 +59,46 @@
54
59
  "description": "Enables debugging mode for tests, allowing the use of the Node Inspector.",
55
60
  "default": false
56
61
  },
57
- "codeCoverage": {
62
+ "coverage": {
58
63
  "type": "boolean",
59
- "description": "Enables code coverage reporting for tests.",
64
+ "description": "Enables coverage reporting for tests.",
60
65
  "default": false
61
66
  },
62
- "codeCoverageExclude": {
67
+ "coverageAll": {
68
+ "type": "boolean",
69
+ "description": "Includes all files that match the `coverageInclude` pattern in the coverage report, not just those touched by tests.",
70
+ "default": true
71
+ },
72
+ "coverageInclude": {
63
73
  "type": "array",
64
- "description": "Specifies glob patterns of files to exclude from the code coverage report.",
74
+ "description": "Specifies glob patterns of files to include in the coverage report.",
65
75
  "items": {
66
76
  "type": "string"
67
77
  }
68
78
  },
69
- "codeCoverageReporters": {
79
+ "coverageExclude": {
70
80
  "type": "array",
71
- "description": "Specifies the reporters to use for code coverage results. Each reporter can be a string representing its name, or a tuple containing the name and an options object. Built-in reporters include 'html', 'lcov', 'lcovonly', 'text', 'text-summary', 'cobertura', 'json', and 'json-summary'.",
81
+ "description": "Specifies glob patterns of files to exclude from the coverage report.",
82
+ "items": {
83
+ "type": "string"
84
+ }
85
+ },
86
+ "coverageReporters": {
87
+ "type": "array",
88
+ "description": "Specifies the reporters to use for coverage results. Each reporter can be a string representing its name, or a tuple containing the name and an options object. Built-in reporters include 'html', 'lcov', 'lcovonly', 'text', 'text-summary', 'cobertura', 'json', and 'json-summary'.",
72
89
  "items": {
73
90
  "oneOf": [
74
91
  {
75
- "$ref": "#/definitions/coverage-reporters"
92
+ "enum": [
93
+ "html",
94
+ "lcov",
95
+ "lcovonly",
96
+ "text",
97
+ "text-summary",
98
+ "cobertura",
99
+ "json",
100
+ "json-summary"
101
+ ]
76
102
  },
77
103
  {
78
104
  "type": "array",
@@ -80,7 +106,16 @@
80
106
  "maxItems": 2,
81
107
  "items": [
82
108
  {
83
- "$ref": "#/definitions/coverage-reporters"
109
+ "enum": [
110
+ "html",
111
+ "lcov",
112
+ "lcovonly",
113
+ "text",
114
+ "text-summary",
115
+ "cobertura",
116
+ "json",
117
+ "json-summary"
118
+ ]
84
119
  },
85
120
  {
86
121
  "type": "object"
@@ -90,6 +125,68 @@
90
125
  ]
91
126
  }
92
127
  },
128
+ "coverageThresholds": {
129
+ "type": "object",
130
+ "description": "Specifies minimum coverage thresholds that must be met. If thresholds are not met, the builder will exit with an error.",
131
+ "properties": {
132
+ "perFile": {
133
+ "type": "boolean",
134
+ "description": "When true, thresholds are enforced for each file individually."
135
+ },
136
+ "statements": {
137
+ "type": "number",
138
+ "description": "Minimum percentage of statements covered."
139
+ },
140
+ "branches": {
141
+ "type": "number",
142
+ "description": "Minimum percentage of branches covered."
143
+ },
144
+ "functions": {
145
+ "type": "number",
146
+ "description": "Minimum percentage of functions covered."
147
+ },
148
+ "lines": {
149
+ "type": "number",
150
+ "description": "Minimum percentage of lines covered."
151
+ }
152
+ },
153
+ "additionalProperties": false
154
+ },
155
+ "coverageWatermarks": {
156
+ "type": "object",
157
+ "description": "Specifies coverage watermarks for the HTML reporter. These determine the color coding for high, medium, and low coverage.",
158
+ "properties": {
159
+ "statements": {
160
+ "type": "array",
161
+ "description": "The high and low watermarks for statements coverage. `[low, high]`",
162
+ "items": { "type": "number" },
163
+ "minItems": 2,
164
+ "maxItems": 2
165
+ },
166
+ "branches": {
167
+ "type": "array",
168
+ "description": "The high and low watermarks for branches coverage. `[low, high]`",
169
+ "items": { "type": "number" },
170
+ "minItems": 2,
171
+ "maxItems": 2
172
+ },
173
+ "functions": {
174
+ "type": "array",
175
+ "description": "The high and low watermarks for functions coverage. `[low, high]`",
176
+ "items": { "type": "number" },
177
+ "minItems": 2,
178
+ "maxItems": 2
179
+ },
180
+ "lines": {
181
+ "type": "array",
182
+ "description": "The high and low watermarks for lines coverage. `[low, high]`",
183
+ "items": { "type": "number" },
184
+ "minItems": 2,
185
+ "maxItems": 2
186
+ }
187
+ },
188
+ "additionalProperties": false
189
+ },
93
190
  "reporters": {
94
191
  "type": "array",
95
192
  "description": "Specifies the reporters to use during test execution. Each reporter can be a string representing its name, or a tuple containing the name and an options object. Built-in reporters include 'default', 'verbose', 'dots', 'json', 'junit', 'tap', 'tap-flat', and 'html'. You can also provide a path to a custom reporter.",
@@ -98,10 +195,10 @@
98
195
  {
99
196
  "anyOf": [
100
197
  {
101
- "$ref": "#/definitions/reporters-enum"
198
+ "type": "string"
102
199
  },
103
200
  {
104
- "type": "string"
201
+ "enum": ["default", "verbose", "dots", "json", "junit", "tap", "tap-flat", "html"]
105
202
  }
106
203
  ]
107
204
  },
@@ -113,10 +210,19 @@
113
210
  {
114
211
  "anyOf": [
115
212
  {
116
- "$ref": "#/definitions/reporters-enum"
213
+ "type": "string"
117
214
  },
118
215
  {
119
- "type": "string"
216
+ "enum": [
217
+ "default",
218
+ "verbose",
219
+ "dots",
220
+ "json",
221
+ "junit",
222
+ "tap",
223
+ "tap-flat",
224
+ "html"
225
+ ]
120
226
  }
121
227
  ]
122
228
  },
@@ -161,23 +267,5 @@
161
267
  }
162
268
  },
163
269
  "additionalProperties": false,
164
- "required": ["buildTarget", "tsConfig", "runner"],
165
- "definitions": {
166
- "coverage-reporters": {
167
- "enum": [
168
- "html",
169
- "lcov",
170
- "lcovonly",
171
- "text",
172
- "text-summary",
173
- "cobertura",
174
- "json",
175
- "json-summary"
176
- ]
177
- },
178
- "reporters-enum": {
179
- "type": "string",
180
- "enum": ["default", "verbose", "dots", "json", "junit", "tap", "tap-flat", "html"]
181
- }
182
- }
270
+ "required": ["runner"]
183
271
  }
@@ -48,6 +48,7 @@ async function findTests(include, exclude, workspaceRoot, projectSourceRoot) {
48
48
  const globMatches = await (0, tinyglobby_1.glob)(dynamicPatterns, {
49
49
  cwd: projectSourceRoot,
50
50
  absolute: true,
51
+ expandDirectories: false,
51
52
  ignore: ['**/node_modules/**', ...normalizedExcludes],
52
53
  });
53
54
  for (const match of globMatches) {
@@ -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) {
@@ -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.5';
13
+ const VERSION = '21.0.0-next.7';
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';