@angular/build 20.0.0-next.6 → 20.0.0-next.8

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.
@@ -0,0 +1,7 @@
1
+ Chrome >= 105
2
+ ChromeAndroid >= 105
3
+ Edge >= 105
4
+ Firefox >= 104
5
+ FirefoxAndroid >= 104
6
+ Safari >= 16
7
+ iOS >= 16
package/builders.json CHANGED
@@ -24,6 +24,11 @@
24
24
  "implementation": "./src/builders/ng-packagr/index",
25
25
  "schema": "./src/builders/ng-packagr/schema.json",
26
26
  "description": "Build a library with ng-packagr."
27
+ },
28
+ "unit-test": {
29
+ "implementation": "./src/builders/unit-test",
30
+ "schema": "./src/builders/unit-test/schema.json",
31
+ "description": "[EXPERIMENTAL] Run application unit tests."
27
32
  }
28
33
  }
29
34
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@angular/build",
3
- "version": "20.0.0-next.6",
3
+ "version": "20.0.0-next.8",
4
4
  "description": "Official build system for Angular",
5
5
  "keywords": [
6
6
  "Angular CLI",
@@ -23,30 +23,30 @@
23
23
  "builders": "builders.json",
24
24
  "dependencies": {
25
25
  "@ampproject/remapping": "2.3.0",
26
- "@angular-devkit/architect": "0.2000.0-next.6",
26
+ "@angular-devkit/architect": "0.2000.0-next.8",
27
27
  "@babel/core": "7.26.10",
28
28
  "@babel/helper-annotate-as-pure": "7.25.9",
29
29
  "@babel/helper-split-export-declaration": "7.24.7",
30
30
  "@inquirer/confirm": "5.1.9",
31
31
  "@vitejs/plugin-basic-ssl": "2.0.0",
32
- "beasties": "0.3.2",
32
+ "beasties": "0.3.3",
33
33
  "browserslist": "^4.23.0",
34
- "esbuild": "0.25.2",
34
+ "esbuild": "0.25.3",
35
35
  "https-proxy-agent": "7.0.6",
36
36
  "istanbul-lib-instrument": "6.0.3",
37
37
  "jsonc-parser": "3.3.1",
38
38
  "listr2": "8.3.2",
39
39
  "magic-string": "0.30.17",
40
40
  "mrmime": "2.0.1",
41
- "parse5-html-rewriting-stream": "7.0.0",
41
+ "parse5-html-rewriting-stream": "7.1.0",
42
42
  "picomatch": "4.0.2",
43
43
  "piscina": "4.9.2",
44
44
  "rollup": "4.40.0",
45
- "sass": "1.86.3",
45
+ "sass": "1.87.0",
46
46
  "semver": "7.7.1",
47
47
  "source-map-support": "0.5.21",
48
- "tinyglobby": "0.2.12",
49
- "vite": "6.2.6",
48
+ "tinyglobby": "0.2.13",
49
+ "vite": "6.3.2",
50
50
  "watchpack": "2.4.2"
51
51
  },
52
52
  "optionalDependencies": {
@@ -60,14 +60,15 @@
60
60
  "@angular/platform-browser": "^20.0.0 || ^20.0.0-next.0",
61
61
  "@angular/platform-server": "^20.0.0 || ^20.0.0-next.0",
62
62
  "@angular/service-worker": "^20.0.0 || ^20.0.0-next.0",
63
- "@angular/ssr": "^20.0.0-next.6",
63
+ "@angular/ssr": "^20.0.0-next.8",
64
64
  "karma": "^6.4.0",
65
65
  "less": "^4.2.0",
66
66
  "ng-packagr": "^20.0.0 || ^20.0.0-next.0",
67
67
  "postcss": "^8.4.0",
68
68
  "tailwindcss": "^2.0.0 || ^3.0.0 || ^4.0.0",
69
69
  "tslib": "^2.3.0",
70
- "typescript": ">=5.8 <5.9"
70
+ "typescript": ">=5.8 <5.9",
71
+ "vitest": "^3.1.1"
71
72
  },
72
73
  "peerDependenciesMeta": {
73
74
  "@angular/core": {
@@ -102,6 +103,9 @@
102
103
  },
103
104
  "tailwindcss": {
104
105
  "optional": true
106
+ },
107
+ "vitest": {
108
+ "optional": true
105
109
  }
106
110
  },
107
111
  "repository": {
@@ -49,6 +49,7 @@ const path = __importStar(require("node:path"));
49
49
  const tinyglobby_1 = require("tinyglobby");
50
50
  const bundler_context_1 = require("../../tools/esbuild/bundler-context");
51
51
  const utils_1 = require("../../tools/esbuild/utils");
52
+ const virtual_module_plugin_1 = require("../../tools/esbuild/virtual-module-plugin");
52
53
  const index_1 = require("../application/index");
53
54
  const results_1 = require("../application/results");
54
55
  const schema_1 = require("../application/schema");
@@ -72,30 +73,27 @@ class AngularAssetsMiddleware {
72
73
  this.latestBuildFiles = latestBuildFiles;
73
74
  }
74
75
  handle(req, res, next) {
75
- let err = null;
76
- try {
77
- const url = new URL(`http://${req.headers['host']}${req.url}`);
78
- // Remove the leading slash from the URL path and convert to platform specific.
79
- // The latest build files will use the platform path separator.
80
- let pathname = url.pathname.slice(1);
81
- if (isWindows) {
82
- pathname = pathname.replaceAll(path.posix.sep, path.win32.sep);
83
- }
84
- const file = this.latestBuildFiles.files[pathname];
85
- if (file?.origin === 'disk') {
76
+ const url = new URL(`http://${req.headers['host']}${req.url}`);
77
+ // Remove the leading slash from the URL path and convert to platform specific.
78
+ // The latest build files will use the platform path separator.
79
+ let pathname = url.pathname.slice(1);
80
+ if (isWindows) {
81
+ pathname = pathname.replaceAll(path.posix.sep, path.win32.sep);
82
+ }
83
+ const file = this.latestBuildFiles.files[pathname];
84
+ if (!file) {
85
+ next();
86
+ return;
87
+ }
88
+ switch (file.origin) {
89
+ case 'disk':
86
90
  this.serveFile(file.inputPath, undefined, res);
87
- return;
88
- }
89
- else if (file?.origin === 'memory') {
91
+ break;
92
+ case 'memory':
90
93
  // Include pathname to help with Content-Type headers.
91
94
  this.serveFile(`/unused/${url.pathname}`, undefined, res, undefined, file.contents, true);
92
- return;
93
- }
94
- }
95
- catch (e) {
96
- err = e;
95
+ break;
97
96
  }
98
- next(err);
99
97
  }
100
98
  static createPlugin(initialFiles) {
101
99
  return {
@@ -309,7 +307,7 @@ async function initializeApplication(options, context, karmaOptions, transforms
309
307
  entryPoints.set(mainName, options.main);
310
308
  }
311
309
  else {
312
- entryPoints.set(mainName, localResolve('./polyfills/init_test_bed.js'));
310
+ entryPoints.set(mainName, 'angular:test-bed-init');
313
311
  }
314
312
  const instrumentForCoverage = options.codeCoverage
315
313
  ? createInstrumentationFilter(projectSourceRoot, getInstrumentationExcludedPaths(context.workspaceRoot, options.codeCoverageExclude ?? []))
@@ -348,8 +346,27 @@ async function initializeApplication(options, context, karmaOptions, transforms
348
346
  loader: options.loader,
349
347
  externalDependencies: options.externalDependencies,
350
348
  };
349
+ const virtualTestBedInit = (0, virtual_module_plugin_1.createVirtualModulePlugin)({
350
+ namespace: 'angular:test-bed-init',
351
+ loadContent: async () => {
352
+ const contents = [
353
+ // Initialize the Angular testing environment
354
+ `import { getTestBed } from '@angular/core/testing';`,
355
+ `import { BrowserTestingModule, platformBrowserTesting } from '@angular/platform-browser/testing';`,
356
+ `getTestBed().initTestEnvironment(BrowserTestingModule, platformBrowserTesting(), {`,
357
+ ` errorOnUnknownElements: true,`,
358
+ ` errorOnUnknownProperties: true,`,
359
+ '});',
360
+ ];
361
+ return {
362
+ contents: contents.join('\n'),
363
+ loader: 'js',
364
+ resolveDir: projectSourceRoot,
365
+ };
366
+ },
367
+ });
351
368
  // Build tests with `application` builder, using test files as entry points.
352
- const [buildOutput, buildIterator] = await first((0, index_1.buildApplicationInternal)(buildOptions, context), { cancel: !buildOptions.watch });
369
+ const [buildOutput, buildIterator] = await first((0, index_1.buildApplicationInternal)(buildOptions, context, { codePlugins: [virtualTestBedInit] }), { cancel: !buildOptions.watch });
353
370
  if (buildOutput.kind === results_1.ResultKind.Failure) {
354
371
  throw new ApplicationBuildError('Build failed');
355
372
  }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @license
3
+ * Copyright Google LLC All Rights Reserved.
4
+ *
5
+ * Use of this source code is governed by an MIT-style license that can be
6
+ * found in the LICENSE file at https://angular.dev/license
7
+ */
8
+ import type { BuilderContext, BuilderOutput } from '@angular-devkit/architect';
9
+ import type { ApplicationBuilderExtensions } from '../application/options';
10
+ import type { Schema as UnitTestOptions } from './schema';
11
+ export type { UnitTestOptions };
12
+ /**
13
+ * @experimental Direct usage of this function is considered experimental.
14
+ */
15
+ export declare function execute(options: UnitTestOptions, context: BuilderContext, extensions?: ApplicationBuilderExtensions): AsyncIterable<BuilderOutput>;
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ /**
3
+ * @license
4
+ * Copyright Google LLC All Rights Reserved.
5
+ *
6
+ * Use of this source code is governed by an MIT-style license that can be
7
+ * found in the LICENSE file at https://angular.dev/license
8
+ */
9
+ var __importDefault = (this && this.__importDefault) || function (mod) {
10
+ return (mod && mod.__esModule) ? mod : { "default": mod };
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.execute = execute;
14
+ const node_assert_1 = __importDefault(require("node:assert"));
15
+ const node_crypto_1 = require("node:crypto");
16
+ const node_path_1 = __importDefault(require("node:path"));
17
+ const virtual_module_plugin_1 = require("../../tools/esbuild/virtual-module-plugin");
18
+ const load_esm_1 = require("../../utils/load-esm");
19
+ const application_1 = require("../application");
20
+ const results_1 = require("../application/results");
21
+ const schema_1 = require("../application/schema");
22
+ const application_builder_1 = require("../karma/application_builder");
23
+ const find_tests_1 = require("../karma/find-tests");
24
+ const options_1 = require("./options");
25
+ /**
26
+ * @experimental Direct usage of this function is considered experimental.
27
+ */
28
+ async function* execute(options, context, extensions = {}) {
29
+ // Determine project name from builder context target
30
+ const projectName = context.target?.project;
31
+ if (!projectName) {
32
+ context.logger.error(`The "${context.builder.builderName}" builder requires a target to be specified.`);
33
+ return;
34
+ }
35
+ context.logger.warn(`NOTE: The "${context.builder.builderName}" builder is currently EXPERIMENTAL and not ready for production use.`);
36
+ const normalizedOptions = await (0, options_1.normalizeOptions)(context, projectName, options);
37
+ const { projectSourceRoot, workspaceRoot, runnerName } = normalizedOptions;
38
+ if (runnerName !== 'vitest') {
39
+ context.logger.error('Unknown test runner: ' + runnerName);
40
+ return;
41
+ }
42
+ // Find test files
43
+ const testFiles = await (0, find_tests_1.findTests)(normalizedOptions.include, normalizedOptions.exclude, workspaceRoot, projectSourceRoot);
44
+ if (testFiles.length === 0) {
45
+ context.logger.error('No tests found.');
46
+ return { success: false };
47
+ }
48
+ const entryPoints = (0, find_tests_1.getTestEntrypoints)(testFiles, { projectSourceRoot, workspaceRoot });
49
+ entryPoints.set('init-testbed', 'angular:test-bed-init');
50
+ const { startVitest } = await (0, load_esm_1.loadEsmModule)('vitest/node');
51
+ // Setup test file build options based on application build target options
52
+ const buildTargetOptions = (await context.validateOptions(await context.getTargetOptions(normalizedOptions.buildTarget), await context.getBuilderNameForTarget(normalizedOptions.buildTarget)));
53
+ if (buildTargetOptions.polyfills?.includes('zone.js')) {
54
+ buildTargetOptions.polyfills.push('zone.js/testing');
55
+ }
56
+ const outputPath = node_path_1.default.join(context.workspaceRoot, 'dist/test-out', (0, node_crypto_1.randomUUID)());
57
+ const buildOptions = {
58
+ ...buildTargetOptions,
59
+ watch: normalizedOptions.watch,
60
+ outputPath,
61
+ index: false,
62
+ browser: undefined,
63
+ server: undefined,
64
+ localize: false,
65
+ budgets: [],
66
+ serviceWorker: false,
67
+ appShell: false,
68
+ ssr: false,
69
+ prerender: false,
70
+ sourceMap: { scripts: true, vendor: false, styles: false },
71
+ outputHashing: schema_1.OutputHashing.None,
72
+ optimization: false,
73
+ tsConfig: normalizedOptions.tsConfig,
74
+ entryPoints,
75
+ externalDependencies: ['vitest', ...(buildTargetOptions.externalDependencies ?? [])],
76
+ };
77
+ extensions ??= {};
78
+ extensions.codePlugins ??= [];
79
+ const virtualTestBedInit = (0, virtual_module_plugin_1.createVirtualModulePlugin)({
80
+ namespace: 'angular:test-bed-init',
81
+ loadContent: async () => {
82
+ const contents = [
83
+ // Initialize the Angular testing environment
84
+ `import { getTestBed } from '@angular/core/testing';`,
85
+ `import { BrowserTestingModule, platformBrowserTesting } from '@angular/platform-browser/testing';`,
86
+ `getTestBed().initTestEnvironment(BrowserTestingModule, platformBrowserTesting(), {`,
87
+ ` errorOnUnknownElements: true,`,
88
+ ` errorOnUnknownProperties: true,`,
89
+ '});',
90
+ ];
91
+ return {
92
+ contents: contents.join('\n'),
93
+ loader: 'js',
94
+ resolveDir: projectSourceRoot,
95
+ };
96
+ },
97
+ });
98
+ extensions.codePlugins.unshift(virtualTestBedInit);
99
+ let instance;
100
+ for await (const result of (0, application_1.buildApplicationInternal)(buildOptions, context, extensions)) {
101
+ if (result.kind === results_1.ResultKind.Failure) {
102
+ continue;
103
+ }
104
+ else if (result.kind !== results_1.ResultKind.Full) {
105
+ node_assert_1.default.fail('A full build result is required from the application builder.');
106
+ }
107
+ (0, node_assert_1.default)(result.files, 'Builder did not provide result files.');
108
+ await (0, application_builder_1.writeTestFiles)(result.files, outputPath);
109
+ const setupFiles = ['init-testbed.js'];
110
+ if (buildTargetOptions?.polyfills?.length) {
111
+ setupFiles.push('polyfills.js');
112
+ }
113
+ instance ??= await startVitest('test', undefined /* cliFilters */, undefined /* options */, {
114
+ test: {
115
+ root: outputPath,
116
+ setupFiles,
117
+ environment: 'jsdom',
118
+ watch: normalizedOptions.watch,
119
+ coverage: {
120
+ enabled: normalizedOptions.codeCoverage,
121
+ exclude: normalizedOptions.codeCoverageExclude,
122
+ excludeAfterRemap: true,
123
+ },
124
+ },
125
+ });
126
+ // Check if all the tests pass to calculate the result
127
+ const testModules = instance.state.getTestModules();
128
+ yield { success: testModules.every((testModule) => testModule.ok()) };
129
+ }
130
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @license
3
+ * Copyright Google LLC All Rights Reserved.
4
+ *
5
+ * Use of this source code is governed by an MIT-style license that can be
6
+ * found in the LICENSE file at https://angular.dev/license
7
+ */
8
+ import { type Builder } from '@angular-devkit/architect';
9
+ import { type UnitTestOptions, execute } from './builder';
10
+ export { type UnitTestOptions, execute };
11
+ declare const builder: Builder<UnitTestOptions>;
12
+ export default builder;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ /**
3
+ * @license
4
+ * Copyright Google LLC All Rights Reserved.
5
+ *
6
+ * Use of this source code is governed by an MIT-style license that can be
7
+ * found in the LICENSE file at https://angular.dev/license
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.execute = void 0;
11
+ const architect_1 = require("@angular-devkit/architect");
12
+ const builder_1 = require("./builder");
13
+ Object.defineProperty(exports, "execute", { enumerable: true, get: function () { return builder_1.execute; } });
14
+ const builder = (0, architect_1.createBuilder)(builder_1.execute);
15
+ exports.default = builder;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @license
3
+ * Copyright Google LLC All Rights Reserved.
4
+ *
5
+ * Use of this source code is governed by an MIT-style license that can be
6
+ * found in the LICENSE file at https://angular.dev/license
7
+ */
8
+ import { type BuilderContext } from '@angular-devkit/architect';
9
+ import type { Schema as UnitTestOptions } from './schema';
10
+ export type NormalizedUnitTestOptions = Awaited<ReturnType<typeof normalizeOptions>>;
11
+ export declare function normalizeOptions(context: BuilderContext, projectName: string, options: UnitTestOptions): Promise<{
12
+ workspaceRoot: string;
13
+ projectRoot: string;
14
+ projectSourceRoot: string;
15
+ cacheOptions: import("../../utils/normalize-cache").NormalizedCachedOptions;
16
+ buildTarget: import("@angular-devkit/architect").Target;
17
+ include: string[];
18
+ exclude: string[];
19
+ runnerName: import("./schema").Runner;
20
+ codeCoverage: boolean | undefined;
21
+ codeCoverageExclude: string[] | undefined;
22
+ tsConfig: string;
23
+ reporters: string[] | undefined;
24
+ watch: boolean;
25
+ }>;
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ /**
3
+ * @license
4
+ * Copyright Google LLC All Rights Reserved.
5
+ *
6
+ * Use of this source code is governed by an MIT-style license that can be
7
+ * found in the LICENSE file at https://angular.dev/license
8
+ */
9
+ var __importDefault = (this && this.__importDefault) || function (mod) {
10
+ return (mod && mod.__esModule) ? mod : { "default": mod };
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.normalizeOptions = normalizeOptions;
14
+ const architect_1 = require("@angular-devkit/architect");
15
+ const node_path_1 = __importDefault(require("node:path"));
16
+ const normalize_cache_1 = require("../../utils/normalize-cache");
17
+ async function normalizeOptions(context, projectName, options) {
18
+ // Setup base paths based on workspace root and project information
19
+ const workspaceRoot = context.workspaceRoot;
20
+ const projectMetadata = await context.getProjectMetadata(projectName);
21
+ const projectRoot = normalizeDirectoryPath(node_path_1.default.join(workspaceRoot, projectMetadata.root ?? ''));
22
+ const projectSourceRoot = normalizeDirectoryPath(node_path_1.default.join(workspaceRoot, projectMetadata.sourceRoot ?? 'src'));
23
+ // Gather persistent caching option and provide a project specific cache location
24
+ const cacheOptions = (0, normalize_cache_1.normalizeCacheOptions)(projectMetadata, workspaceRoot);
25
+ cacheOptions.path = node_path_1.default.join(cacheOptions.path, projectName);
26
+ // Target specifier defaults to the current project's build target using a development configuration
27
+ const buildTargetSpecifier = options.buildTarget ?? `::development`;
28
+ const buildTarget = (0, architect_1.targetFromTargetString)(buildTargetSpecifier, projectName, 'build');
29
+ const { codeCoverage, codeCoverageExclude, tsConfig, runner, reporters } = options;
30
+ return {
31
+ // Project/workspace information
32
+ workspaceRoot,
33
+ projectRoot,
34
+ projectSourceRoot,
35
+ cacheOptions,
36
+ // Target/configuration specified options
37
+ buildTarget,
38
+ include: options.include ?? ['**/*.spec.ts'],
39
+ exclude: options.exclude ?? [],
40
+ runnerName: runner,
41
+ codeCoverage,
42
+ codeCoverageExclude,
43
+ tsConfig,
44
+ reporters,
45
+ // TODO: Implement watch support
46
+ watch: false,
47
+ };
48
+ }
49
+ /**
50
+ * Normalize a directory path string.
51
+ * Currently only removes a trailing slash if present.
52
+ * @param path A path string.
53
+ * @returns A normalized path string.
54
+ */
55
+ function normalizeDirectoryPath(path) {
56
+ const last = path[path.length - 1];
57
+ if (last === '/' || last === '\\') {
58
+ return path.slice(0, -1);
59
+ }
60
+ return path;
61
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Unit testing options for Angular applications.
3
+ */
4
+ export type Schema = {
5
+ /**
6
+ * A build builder target to serve in the format of `project:target[:configuration]`. You
7
+ * can also pass in more than one configuration name as a comma-separated list. Example:
8
+ * `project:target:production,staging`.
9
+ */
10
+ buildTarget: string;
11
+ /**
12
+ * Output a code coverage report.
13
+ */
14
+ codeCoverage?: boolean;
15
+ /**
16
+ * Globs to exclude from code coverage.
17
+ */
18
+ codeCoverageExclude?: string[];
19
+ /**
20
+ * Globs of files to exclude, relative to the project root.
21
+ */
22
+ exclude?: string[];
23
+ /**
24
+ * Globs of files to include, relative to project root.
25
+ * There are 2 special cases:
26
+ * - when a path to directory is provided, all spec files ending ".spec.@(ts|tsx)" will be
27
+ * included
28
+ * - when a path to a file is provided, and a matching spec file exists it will be included
29
+ * instead.
30
+ */
31
+ include?: string[];
32
+ /**
33
+ * Test runner reporters to use. Directly passed to the test runner.
34
+ */
35
+ reporters?: string[];
36
+ /**
37
+ * The name of the test runner to use for test execution.
38
+ */
39
+ runner: Runner;
40
+ /**
41
+ * The name of the TypeScript configuration file.
42
+ */
43
+ tsConfig: string;
44
+ /**
45
+ * Run build when files change.
46
+ */
47
+ watch?: boolean;
48
+ };
49
+ /**
50
+ * The name of the test runner to use for test execution.
51
+ */
52
+ export declare enum Runner {
53
+ Vitest = "vitest"
54
+ }
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ // THIS FILE IS AUTOMATICALLY GENERATED. TO UPDATE THIS FILE YOU NEED TO CHANGE THE
3
+ // CORRESPONDING JSON SCHEMA FILE, THEN RUN devkit-admin build (or bazel build ...).
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.Runner = void 0;
6
+ /**
7
+ * The name of the test runner to use for test execution.
8
+ */
9
+ var Runner;
10
+ (function (Runner) {
11
+ Runner["Vitest"] = "vitest";
12
+ })(Runner || (exports.Runner = Runner = {}));
@@ -0,0 +1,64 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema",
3
+ "title": "Unit testing",
4
+ "description": "Unit testing options for Angular applications.",
5
+ "type": "object",
6
+ "properties": {
7
+ "buildTarget": {
8
+ "type": "string",
9
+ "description": "A build builder target to serve in the format of `project:target[:configuration]`. You can also pass in more than one configuration name as a comma-separated list. Example: `project:target:production,staging`.",
10
+ "pattern": "^[^:\\s]*:[^:\\s]*(:[^\\s]+)?$"
11
+ },
12
+ "tsConfig": {
13
+ "type": "string",
14
+ "description": "The name of the TypeScript configuration file."
15
+ },
16
+ "runner": {
17
+ "type": "string",
18
+ "description": "The name of the test runner to use for test execution.",
19
+ "enum": ["vitest"]
20
+ },
21
+ "include": {
22
+ "type": "array",
23
+ "items": {
24
+ "type": "string"
25
+ },
26
+ "default": ["**/*.spec.ts"],
27
+ "description": "Globs of files to include, relative to project root. \nThere are 2 special cases:\n - when a path to directory is provided, all spec files ending \".spec.@(ts|tsx)\" will be included\n - when a path to a file is provided, and a matching spec file exists it will be included instead."
28
+ },
29
+ "exclude": {
30
+ "type": "array",
31
+ "items": {
32
+ "type": "string"
33
+ },
34
+ "default": [],
35
+ "description": "Globs of files to exclude, relative to the project root."
36
+ },
37
+ "watch": {
38
+ "type": "boolean",
39
+ "description": "Run build when files change."
40
+ },
41
+ "codeCoverage": {
42
+ "type": "boolean",
43
+ "description": "Output a code coverage report.",
44
+ "default": false
45
+ },
46
+ "codeCoverageExclude": {
47
+ "type": "array",
48
+ "description": "Globs to exclude from code coverage.",
49
+ "items": {
50
+ "type": "string"
51
+ },
52
+ "default": []
53
+ },
54
+ "reporters": {
55
+ "type": "array",
56
+ "description": "Test runner reporters to use. Directly passed to the test runner.",
57
+ "items": {
58
+ "type": "string"
59
+ }
60
+ }
61
+ },
62
+ "additionalProperties": false,
63
+ "required": ["buildTarget", "tsConfig", "runner"]
64
+ }
@@ -108,7 +108,7 @@ async function loadViteClientCode(file, disableViteTransport = false) {
108
108
  (0, node_assert_1.default)(originalContents !== updatedContents, 'Failed to update Vite client error overlay text.');
109
109
  if (disableViteTransport) {
110
110
  const previousUpdatedContents = updatedContents;
111
- updatedContents = updatedContents.replace('transport.connect(handleMessage)', '');
111
+ updatedContents = updatedContents.replace('transport.connect(createHMRHandler(handleMessage));', '');
112
112
  (0, node_assert_1.default)(previousUpdatedContents !== updatedContents, 'Failed to update Vite client WebSocket disable.');
113
113
  updatedContents = updatedContents.replace('console.debug("[vite] connecting...")', '');
114
114
  }
@@ -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 = '20.0.0-next.6';
13
+ const VERSION = '20.0.0-next.8';
14
14
  function hasCacheMetadata(value) {
15
15
  return (!!value &&
16
16
  typeof value === 'object' &&
@@ -13,30 +13,40 @@ Object.defineProperty(exports, "__esModule", { value: true });
13
13
  exports.getSupportedBrowsers = getSupportedBrowsers;
14
14
  const browserslist_1 = __importDefault(require("browserslist"));
15
15
  function getSupportedBrowsers(projectRoot, logger) {
16
- browserslist_1.default.defaults = [
17
- 'last 2 Chrome versions',
18
- 'last 1 Firefox version',
19
- 'last 2 Edge major versions',
20
- 'last 2 Safari major versions',
21
- 'last 2 iOS major versions',
22
- 'last 2 Android major versions',
23
- 'Firefox ESR',
24
- ];
16
+ // Read the browserslist configuration containing Angular's browser support policy.
17
+ const angularBrowserslist = (0, browserslist_1.default)(undefined, {
18
+ path: require.resolve('../../.browserslistrc'),
19
+ });
20
+ // Use Angular's configuration as the default.
21
+ browserslist_1.default.defaults = angularBrowserslist;
22
+ // Get the minimum set of browser versions supported by Angular.
23
+ const minimumBrowsers = new Set(angularBrowserslist);
25
24
  // Get browsers from config or default.
26
25
  const browsersFromConfigOrDefault = new Set((0, browserslist_1.default)(undefined, { path: projectRoot }));
27
26
  // Get browsers that support ES6 modules.
28
27
  const browsersThatSupportEs6 = new Set((0, browserslist_1.default)('supports es6-module'));
28
+ const nonEs6Browsers = [];
29
29
  const unsupportedBrowsers = [];
30
30
  for (const browser of browsersFromConfigOrDefault) {
31
31
  if (!browsersThatSupportEs6.has(browser)) {
32
+ // Any browser which does not support ES6 is explicitly ignored, as Angular will not build successfully.
32
33
  browsersFromConfigOrDefault.delete(browser);
34
+ nonEs6Browsers.push(browser);
35
+ }
36
+ else if (!minimumBrowsers.has(browser)) {
37
+ // Any other unsupported browser we will attempt to use, but provide no support for.
33
38
  unsupportedBrowsers.push(browser);
34
39
  }
35
40
  }
36
- if (unsupportedBrowsers.length) {
41
+ if (nonEs6Browsers.length) {
37
42
  logger.warn(`One or more browsers which are configured in the project's Browserslist configuration ` +
38
43
  'will be ignored as ES5 output is not supported by the Angular CLI.\n' +
39
- `Ignored browsers: ${unsupportedBrowsers.join(', ')}`);
44
+ `Ignored browsers:\n${nonEs6Browsers.join(', ')}`);
45
+ }
46
+ if (unsupportedBrowsers.length) {
47
+ logger.warn(`One or more browsers which are configured in the project's Browserslist configuration ` +
48
+ "fall outside Angular's browser support for this version.\n" +
49
+ `Unsupported browsers:\n${unsupportedBrowsers.join(', ')}`);
40
50
  }
41
51
  return Array.from(browsersFromConfigOrDefault);
42
52
  }
@@ -1,16 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright Google LLC All Rights Reserved.
4
- *
5
- * Use of this source code is governed by an MIT-style license that can be
6
- * found in the LICENSE file at https://angular.dev/license
7
- */
8
-
9
- import { getTestBed } from '@angular/core/testing';
10
- import { BrowserTestingModule, platformBrowserTesting } from '@angular/platform-browser/testing';
11
-
12
- // Initialize the Angular testing environment.
13
- getTestBed().initTestEnvironment(BrowserTestingModule, platformBrowserTesting(), {
14
- errorOnUnknownElements: true,
15
- errorOnUnknownProperties: true,
16
- });