@angular-devkit/build-angular 19.0.0-next.6 → 19.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.
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@angular-devkit/build-angular",
3
- "version": "19.0.0-next.6",
3
+ "version": "19.0.0-next.8",
4
4
  "description": "Angular Webpack Build Facade",
5
5
  "main": "src/index.js",
6
6
  "typings": "src/index.d.ts",
7
7
  "builders": "builders.json",
8
8
  "dependencies": {
9
9
  "@ampproject/remapping": "2.3.0",
10
- "@angular-devkit/architect": "0.1900.0-next.6",
11
- "@angular-devkit/build-webpack": "0.1900.0-next.6",
12
- "@angular-devkit/core": "19.0.0-next.6",
13
- "@angular/build": "19.0.0-next.6",
10
+ "@angular-devkit/architect": "0.1900.0-next.8",
11
+ "@angular-devkit/build-webpack": "0.1900.0-next.8",
12
+ "@angular-devkit/core": "19.0.0-next.8",
13
+ "@angular/build": "19.0.0-next.8",
14
14
  "@babel/core": "7.25.2",
15
15
  "@babel/generator": "7.25.6",
16
16
  "@babel/helper-annotate-as-pure": "7.24.7",
@@ -21,16 +21,16 @@
21
21
  "@babel/preset-env": "7.25.4",
22
22
  "@babel/runtime": "7.25.6",
23
23
  "@discoveryjs/json-ext": "0.6.1",
24
- "@ngtools/webpack": "19.0.0-next.6",
24
+ "@ngtools/webpack": "19.0.0-next.8",
25
25
  "@vitejs/plugin-basic-ssl": "1.1.0",
26
26
  "ansi-colors": "4.1.3",
27
27
  "autoprefixer": "10.4.20",
28
- "babel-loader": "9.1.3",
28
+ "babel-loader": "9.2.1",
29
29
  "browserslist": "^4.21.5",
30
30
  "copy-webpack-plugin": "12.0.2",
31
31
  "critters": "0.0.24",
32
32
  "css-loader": "7.1.2",
33
- "esbuild-wasm": "0.23.1",
33
+ "esbuild-wasm": "0.24.0",
34
34
  "fast-glob": "3.3.2",
35
35
  "http-proxy-middleware": "3.0.2",
36
36
  "https-proxy-agent": "7.0.5",
@@ -48,20 +48,20 @@
48
48
  "ora": "5.4.1",
49
49
  "parse5-html-rewriting-stream": "7.0.0",
50
50
  "picomatch": "4.0.2",
51
- "piscina": "4.6.1",
52
- "postcss": "8.4.45",
51
+ "piscina": "4.7.0",
52
+ "postcss": "8.4.47",
53
53
  "postcss-loader": "8.1.1",
54
54
  "resolve-url-loader": "5.0.0",
55
55
  "rxjs": "7.8.1",
56
- "sass": "1.78.0",
57
- "sass-loader": "16.0.1",
56
+ "sass": "1.79.3",
57
+ "sass-loader": "16.0.2",
58
58
  "semver": "7.6.3",
59
59
  "source-map-loader": "5.0.0",
60
60
  "source-map-support": "0.5.21",
61
- "terser": "5.32.0",
61
+ "terser": "5.33.0",
62
62
  "tree-kill": "1.2.2",
63
63
  "tslib": "2.7.0",
64
- "vite": "5.4.4",
64
+ "vite": "5.4.8",
65
65
  "watchpack": "2.4.2",
66
66
  "webpack": "5.94.0",
67
67
  "webpack-dev-middleware": "7.4.2",
@@ -70,14 +70,14 @@
70
70
  "webpack-subresource-integrity": "5.1.0"
71
71
  },
72
72
  "optionalDependencies": {
73
- "esbuild": "0.23.1"
73
+ "esbuild": "0.24.0"
74
74
  },
75
75
  "peerDependencies": {
76
76
  "@angular/compiler-cli": "^19.0.0-next.0",
77
77
  "@angular/localize": "^19.0.0-next.0",
78
78
  "@angular/platform-server": "^19.0.0-next.0",
79
79
  "@angular/service-worker": "^19.0.0-next.0",
80
- "@angular/ssr": "^19.0.0-next.6",
80
+ "@angular/ssr": "^19.0.0-next.8",
81
81
  "@web/test-runner": "^0.19.0",
82
82
  "browser-sync": "^3.0.2",
83
83
  "jest": "^29.5.0",
@@ -133,7 +133,7 @@
133
133
  "devkit",
134
134
  "sdk"
135
135
  ],
136
- "packageManager": "yarn@4.4.0",
136
+ "packageManager": "yarn@4.5.0",
137
137
  "repository": {
138
138
  "type": "git",
139
139
  "url": "https://github.com/angular/angular-cli.git"
@@ -0,0 +1,19 @@
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 { ResultFile } from '@angular/build/private';
9
+ import { BuilderContext, BuilderOutput } from '@angular-devkit/architect';
10
+ import type { ConfigOptions } from 'karma';
11
+ import { Observable } from 'rxjs';
12
+ import { Configuration } from 'webpack';
13
+ import { ExecutionTransformer } from '../../transforms';
14
+ import { Schema as KarmaBuilderOptions } from './schema';
15
+ export declare function execute(options: KarmaBuilderOptions, context: BuilderContext, karmaOptions: ConfigOptions, transforms?: {
16
+ webpackConfiguration?: ExecutionTransformer<Configuration>;
17
+ karmaOptions?: (options: ConfigOptions) => ConfigOptions;
18
+ }): Observable<BuilderOutput>;
19
+ export declare function writeTestFiles(files: Record<string, ResultFile>, testDir: string): Promise<void>;
@@ -0,0 +1,217 @@
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 __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 (mod) {
26
+ if (mod && mod.__esModule) return mod;
27
+ var result = {};
28
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
29
+ __setModuleDefault(result, mod);
30
+ return result;
31
+ };
32
+ Object.defineProperty(exports, "__esModule", { value: true });
33
+ exports.execute = execute;
34
+ exports.writeTestFiles = writeTestFiles;
35
+ const build_1 = require("@angular/build");
36
+ const private_1 = require("@angular/build/private");
37
+ const crypto_1 = require("crypto");
38
+ const fs = __importStar(require("fs/promises"));
39
+ const path = __importStar(require("path"));
40
+ const rxjs_1 = require("rxjs");
41
+ const read_tsconfig_1 = require("../../utils/read-tsconfig");
42
+ const schema_1 = require("../browser-esbuild/schema");
43
+ const find_tests_1 = require("./find-tests");
44
+ class ApplicationBuildError extends Error {
45
+ constructor(message) {
46
+ super(message);
47
+ this.name = 'ApplicationBuildError';
48
+ }
49
+ }
50
+ function execute(options, context, karmaOptions, transforms = {}) {
51
+ return (0, rxjs_1.from)(initializeApplication(options, context, karmaOptions, transforms)).pipe((0, rxjs_1.switchMap)(([karma, karmaConfig]) => new rxjs_1.Observable((subscriber) => {
52
+ // Complete the observable once the Karma server returns.
53
+ const karmaServer = new karma.Server(karmaConfig, (exitCode) => {
54
+ subscriber.next({ success: exitCode === 0 });
55
+ subscriber.complete();
56
+ });
57
+ const karmaStart = karmaServer.start();
58
+ // Cleanup, signal Karma to exit.
59
+ return () => {
60
+ void karmaStart.then(() => karmaServer.stop());
61
+ };
62
+ })), (0, rxjs_1.catchError)((err) => {
63
+ if (err instanceof ApplicationBuildError) {
64
+ return (0, rxjs_1.of)({ success: false, message: err.message });
65
+ }
66
+ throw err;
67
+ }), (0, rxjs_1.defaultIfEmpty)({ success: false }));
68
+ }
69
+ async function getProjectSourceRoot(context) {
70
+ // We have already validated that the project name is set before calling this function.
71
+ const projectName = context.target?.project;
72
+ if (!projectName) {
73
+ return context.workspaceRoot;
74
+ }
75
+ const projectMetadata = await context.getProjectMetadata(projectName);
76
+ const sourceRoot = (projectMetadata.sourceRoot ?? projectMetadata.root ?? '');
77
+ return path.join(context.workspaceRoot, sourceRoot);
78
+ }
79
+ async function collectEntrypoints(options, context) {
80
+ const projectSourceRoot = await getProjectSourceRoot(context);
81
+ // Glob for files to test.
82
+ const testFiles = await (0, find_tests_1.findTests)(options.include ?? [], options.exclude ?? [], context.workspaceRoot, projectSourceRoot);
83
+ const entryPoints = new Set([
84
+ ...testFiles,
85
+ '@angular-devkit/build-angular/src/builders/karma/init_test_bed.js',
86
+ ]);
87
+ // Extract `zone.js/testing` to a separate entry point because it needs to be loaded after Jasmine.
88
+ const [polyfills, hasZoneTesting] = extractZoneTesting(options.polyfills);
89
+ if (hasZoneTesting) {
90
+ entryPoints.add('zone.js/testing');
91
+ }
92
+ const tsConfigPath = path.resolve(context.workspaceRoot, options.tsConfig);
93
+ const tsConfig = await (0, read_tsconfig_1.readTsconfig)(tsConfigPath);
94
+ const localizePackageInitEntryPoint = '@angular/localize/init';
95
+ const hasLocalizeType = tsConfig.options.types?.some((t) => t === '@angular/localize' || t === localizePackageInitEntryPoint);
96
+ if (hasLocalizeType) {
97
+ polyfills.push(localizePackageInitEntryPoint);
98
+ }
99
+ return [entryPoints, polyfills];
100
+ }
101
+ async function initializeApplication(options, context, karmaOptions, transforms = {}) {
102
+ if (transforms.webpackConfiguration) {
103
+ context.logger.warn(`This build is using the application builder but transforms.webpackConfiguration was provided. The transform will be ignored.`);
104
+ }
105
+ const testDir = path.join(context.workspaceRoot, 'dist/test-out', (0, crypto_1.randomUUID)());
106
+ const [karma, [entryPoints, polyfills]] = await Promise.all([
107
+ Promise.resolve().then(() => __importStar(require('karma'))),
108
+ collectEntrypoints(options, context),
109
+ fs.rm(testDir, { recursive: true, force: true }),
110
+ ]);
111
+ const outputPath = testDir;
112
+ // Build tests with `application` builder, using test files as entry points.
113
+ const buildOutput = await first((0, private_1.buildApplicationInternal)({
114
+ entryPoints,
115
+ tsConfig: options.tsConfig,
116
+ outputPath,
117
+ aot: false,
118
+ index: false,
119
+ outputHashing: schema_1.OutputHashing.None,
120
+ optimization: false,
121
+ sourceMap: {
122
+ scripts: true,
123
+ styles: true,
124
+ vendor: true,
125
+ },
126
+ styles: options.styles,
127
+ polyfills,
128
+ webWorkerTsConfig: options.webWorkerTsConfig,
129
+ }, context));
130
+ if (buildOutput.kind === private_1.ResultKind.Failure) {
131
+ throw new ApplicationBuildError('Build failed');
132
+ }
133
+ else if (buildOutput.kind !== private_1.ResultKind.Full) {
134
+ throw new ApplicationBuildError('A full build result is required from the application builder.');
135
+ }
136
+ // Write test files
137
+ await writeTestFiles(buildOutput.files, testDir);
138
+ karmaOptions.files ??= [];
139
+ karmaOptions.files.push(
140
+ // Serve polyfills first.
141
+ { pattern: `${testDir}/polyfills.js`, type: 'module' },
142
+ // Allow loading of chunk-* files but don't include them all on load.
143
+ { pattern: `${testDir}/chunk-*.js`, type: 'module', included: false },
144
+ // Allow loading of worker-* files but don't include them all on load.
145
+ { pattern: `${testDir}/worker-*.js`, type: 'module', included: false },
146
+ // `zone.js/testing`, served but not included on page load.
147
+ { pattern: `${testDir}/testing.js`, type: 'module', included: false },
148
+ // Serve remaining JS on page load, these are the test entrypoints.
149
+ { pattern: `${testDir}/*.js`, type: 'module' });
150
+ if (options.styles?.length) {
151
+ // Serve CSS outputs on page load, these are the global styles.
152
+ karmaOptions.files.push({ pattern: `${testDir}/*.css`, type: 'css' });
153
+ }
154
+ const parsedKarmaConfig = await karma.config.parseConfig(options.karmaConfig && path.resolve(context.workspaceRoot, options.karmaConfig), transforms.karmaOptions ? transforms.karmaOptions(karmaOptions) : karmaOptions, { promiseConfig: true, throwErrors: true });
155
+ // Remove the webpack plugin/framework:
156
+ // Alternative would be to make the Karma plugin "smart" but that's a tall order
157
+ // with managing unneeded imports etc..
158
+ const pluginLengthBefore = (parsedKarmaConfig.plugins ?? []).length;
159
+ parsedKarmaConfig.plugins = (parsedKarmaConfig.plugins ?? []).filter((plugin) => {
160
+ if (typeof plugin === 'string') {
161
+ return plugin !== 'framework:@angular-devkit/build-angular';
162
+ }
163
+ return !plugin['framework:@angular-devkit/build-angular'];
164
+ });
165
+ parsedKarmaConfig.frameworks = parsedKarmaConfig.frameworks?.filter((framework) => framework !== '@angular-devkit/build-angular');
166
+ const pluginLengthAfter = (parsedKarmaConfig.plugins ?? []).length;
167
+ if (pluginLengthBefore !== pluginLengthAfter) {
168
+ context.logger.warn(`Ignoring framework "@angular-devkit/build-angular" from karma config file because it's not compatible with the application builder.`);
169
+ }
170
+ // When using code-coverage, auto-add karma-coverage.
171
+ // This was done as part of the karma plugin for webpack.
172
+ if (options.codeCoverage &&
173
+ !parsedKarmaConfig.reporters?.some((r) => r === 'coverage' || r === 'coverage-istanbul')) {
174
+ parsedKarmaConfig.reporters = (parsedKarmaConfig.reporters ?? []).concat(['coverage']);
175
+ }
176
+ return [karma, parsedKarmaConfig];
177
+ }
178
+ async function writeTestFiles(files, testDir) {
179
+ const directoryExists = new Set();
180
+ // Writes the test related output files to disk and ensures the containing directories are present
181
+ await (0, private_1.emitFilesToDisk)(Object.entries(files), async ([filePath, file]) => {
182
+ if (file.type !== build_1.BuildOutputFileType.Browser && file.type !== build_1.BuildOutputFileType.Media) {
183
+ return;
184
+ }
185
+ const fullFilePath = path.join(testDir, filePath);
186
+ // Ensure output subdirectories exist
187
+ const fileBasePath = path.dirname(fullFilePath);
188
+ if (fileBasePath && !directoryExists.has(fileBasePath)) {
189
+ await fs.mkdir(fileBasePath, { recursive: true });
190
+ directoryExists.add(fileBasePath);
191
+ }
192
+ if (file.origin === 'memory') {
193
+ // Write file contents
194
+ await fs.writeFile(fullFilePath, file.contents);
195
+ }
196
+ else {
197
+ // Copy file contents
198
+ await fs.copyFile(file.inputPath, fullFilePath, fs.constants.COPYFILE_FICLONE);
199
+ }
200
+ });
201
+ }
202
+ function extractZoneTesting(polyfills) {
203
+ if (typeof polyfills === 'string') {
204
+ polyfills = [polyfills];
205
+ }
206
+ polyfills ??= [];
207
+ const polyfillsWithoutZoneTesting = polyfills.filter((polyfill) => polyfill !== 'zone.js/testing');
208
+ const hasZoneTesting = polyfills.length !== polyfillsWithoutZoneTesting.length;
209
+ return [polyfillsWithoutZoneTesting, hasZoneTesting];
210
+ }
211
+ /** Returns the first item yielded by the given generator and cancels the execution. */
212
+ async function first(generator) {
213
+ for await (const value of generator) {
214
+ return value;
215
+ }
216
+ throw new Error('Expected generator to emit at least once.');
217
+ }
@@ -0,0 +1,21 @@
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 { BuilderContext, BuilderOutput } from '@angular-devkit/architect';
9
+ import type { ConfigOptions } from 'karma';
10
+ import { Observable } from 'rxjs';
11
+ import { Configuration } from 'webpack';
12
+ import { ExecutionTransformer } from '../../transforms';
13
+ import { Schema as KarmaBuilderOptions } from './schema';
14
+ export type KarmaConfigOptions = ConfigOptions & {
15
+ buildWebpack?: unknown;
16
+ configFile?: string;
17
+ };
18
+ export declare function execute(options: KarmaBuilderOptions, context: BuilderContext, karmaOptions: KarmaConfigOptions, transforms?: {
19
+ webpackConfiguration?: ExecutionTransformer<Configuration>;
20
+ karmaOptions?: (options: KarmaConfigOptions) => KarmaConfigOptions;
21
+ }): Observable<BuilderOutput>;
@@ -0,0 +1,137 @@
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 __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 (mod) {
26
+ if (mod && mod.__esModule) return mod;
27
+ var result = {};
28
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
29
+ __setModuleDefault(result, mod);
30
+ return result;
31
+ };
32
+ Object.defineProperty(exports, "__esModule", { value: true });
33
+ exports.execute = execute;
34
+ const private_1 = require("@angular/build/private");
35
+ const path = __importStar(require("path"));
36
+ const rxjs_1 = require("rxjs");
37
+ const configs_1 = require("../../tools/webpack/configs");
38
+ const webpack_browser_config_1 = require("../../utils/webpack-browser-config");
39
+ const schema_1 = require("../browser/schema");
40
+ const find_tests_plugin_1 = require("./find-tests-plugin");
41
+ function execute(options, context, karmaOptions, transforms = {}) {
42
+ return (0, rxjs_1.from)(initializeBrowser(options, context)).pipe((0, rxjs_1.switchMap)(async ([karma, webpackConfig]) => {
43
+ const projectName = context.target?.project;
44
+ if (!projectName) {
45
+ throw new Error(`The 'karma' builder requires a target to be specified.`);
46
+ }
47
+ const projectMetadata = await context.getProjectMetadata(projectName);
48
+ const sourceRoot = (projectMetadata.sourceRoot ?? projectMetadata.root ?? '');
49
+ if (!options.main) {
50
+ webpackConfig.entry ??= {};
51
+ if (typeof webpackConfig.entry === 'object' && !Array.isArray(webpackConfig.entry)) {
52
+ if (Array.isArray(webpackConfig.entry['main'])) {
53
+ webpackConfig.entry['main'].push(getBuiltInMainFile());
54
+ }
55
+ else {
56
+ webpackConfig.entry['main'] = [getBuiltInMainFile()];
57
+ }
58
+ }
59
+ }
60
+ webpackConfig.plugins ??= [];
61
+ webpackConfig.plugins.push(new find_tests_plugin_1.FindTestsPlugin({
62
+ include: options.include,
63
+ exclude: options.exclude,
64
+ workspaceRoot: context.workspaceRoot,
65
+ projectSourceRoot: path.join(context.workspaceRoot, sourceRoot),
66
+ }));
67
+ karmaOptions.buildWebpack = {
68
+ options,
69
+ webpackConfig,
70
+ logger: context.logger,
71
+ };
72
+ const parsedKarmaConfig = await karma.config.parseConfig(options.karmaConfig && path.resolve(context.workspaceRoot, options.karmaConfig), transforms.karmaOptions ? transforms.karmaOptions(karmaOptions) : karmaOptions, { promiseConfig: true, throwErrors: true });
73
+ return [karma, parsedKarmaConfig];
74
+ }), (0, rxjs_1.switchMap)(([karma, karmaConfig]) => new rxjs_1.Observable((subscriber) => {
75
+ // Pass onto Karma to emit BuildEvents.
76
+ karmaConfig.buildWebpack ??= {};
77
+ if (typeof karmaConfig.buildWebpack === 'object') {
78
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
79
+ karmaConfig.buildWebpack.failureCb ??= () => subscriber.next({ success: false });
80
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
81
+ karmaConfig.buildWebpack.successCb ??= () => subscriber.next({ success: true });
82
+ }
83
+ // Complete the observable once the Karma server returns.
84
+ const karmaServer = new karma.Server(karmaConfig, (exitCode) => {
85
+ subscriber.next({ success: exitCode === 0 });
86
+ subscriber.complete();
87
+ });
88
+ const karmaStart = karmaServer.start();
89
+ // Cleanup, signal Karma to exit.
90
+ return () => {
91
+ void karmaStart.then(() => karmaServer.stop());
92
+ };
93
+ })), (0, rxjs_1.defaultIfEmpty)({ success: false }));
94
+ }
95
+ async function initializeBrowser(options, context, webpackConfigurationTransformer) {
96
+ // Purge old build disk cache.
97
+ await (0, private_1.purgeStaleBuildCache)(context);
98
+ const karma = await Promise.resolve().then(() => __importStar(require('karma')));
99
+ const { config } = await (0, webpack_browser_config_1.generateBrowserWebpackConfigFromContext)(
100
+ // only two properties are missing:
101
+ // * `outputPath` which is fixed for tests
102
+ // * `budgets` which might be incorrect due to extra dev libs
103
+ {
104
+ ...options,
105
+ outputPath: '',
106
+ budgets: undefined,
107
+ optimization: false,
108
+ buildOptimizer: false,
109
+ aot: false,
110
+ vendorChunk: true,
111
+ namedChunks: true,
112
+ extractLicenses: false,
113
+ outputHashing: schema_1.OutputHashing.None,
114
+ // The webpack tier owns the watch behavior so we want to force it in the config.
115
+ // When not in watch mode, webpack-dev-middleware will call `compiler.watch` anyway.
116
+ // https://github.com/webpack/webpack-dev-middleware/blob/698c9ae5e9bb9a013985add6189ff21c1a1ec185/src/index.js#L65
117
+ // https://github.com/webpack/webpack/blob/cde1b73e12eb8a77eb9ba42e7920c9ec5d29c2c9/lib/Compiler.js#L379-L388
118
+ watch: true,
119
+ }, context, (wco) => [(0, configs_1.getCommonConfig)(wco), (0, configs_1.getStylesConfig)(wco)]);
120
+ return [karma, (await webpackConfigurationTransformer?.(config)) ?? config];
121
+ }
122
+ function getBuiltInMainFile() {
123
+ const content = Buffer.from(`
124
+ import { getTestBed } from '@angular/core/testing';
125
+ import {
126
+ BrowserDynamicTestingModule,
127
+ platformBrowserDynamicTesting,
128
+ } from '@angular/platform-browser-dynamic/testing';
129
+
130
+ // Initialize the Angular testing environment.
131
+ getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), {
132
+ errorOnUnknownElements: true,
133
+ errorOnUnknownProperties: true
134
+ });
135
+ `).toString('base64');
136
+ return `ng-virtual-main.js!=!data:text/javascript;base64,${content}`;
137
+ }
@@ -6,39 +6,14 @@
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 (mod) {
26
- if (mod && mod.__esModule) return mod;
27
- var result = {};
28
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
29
- __setModuleDefault(result, mod);
30
- return result;
31
- };
32
9
  var __importDefault = (this && this.__importDefault) || function (mod) {
33
10
  return (mod && mod.__esModule) ? mod : { "default": mod };
34
11
  };
35
12
  Object.defineProperty(exports, "__esModule", { value: true });
36
13
  exports.FindTestsPlugin = void 0;
37
14
  const assert_1 = __importDefault(require("assert"));
38
- const fast_glob_1 = __importStar(require("fast-glob"));
39
- const fs_1 = require("fs");
40
15
  const mini_css_extract_plugin_1 = require("mini-css-extract-plugin");
41
- const path_1 = require("path");
16
+ const find_tests_1 = require("./find-tests");
42
17
  /**
43
18
  * The name of the plugin provided to Webpack when tapping Webpack compiler hooks.
44
19
  */
@@ -56,7 +31,7 @@ class FindTestsPlugin {
56
31
  let originalImport;
57
32
  // Add tests files are part of the entry-point.
58
33
  webpackOptions.entry = async () => {
59
- const specFiles = await findTests(include, exclude, workspaceRoot, projectSourceRoot);
34
+ const specFiles = await (0, find_tests_1.findTests)(include, exclude, workspaceRoot, projectSourceRoot);
60
35
  const entrypoints = await entry;
61
36
  const entrypoint = entrypoints['main'];
62
37
  if (!entrypoint.import) {
@@ -81,72 +56,3 @@ class FindTestsPlugin {
81
56
  }
82
57
  }
83
58
  exports.FindTestsPlugin = FindTestsPlugin;
84
- // go through all patterns and find unique list of files
85
- async function findTests(include, exclude, workspaceRoot, projectSourceRoot) {
86
- const matchingTestsPromises = include.map((pattern) => findMatchingTests(pattern, exclude, workspaceRoot, projectSourceRoot));
87
- const files = await Promise.all(matchingTestsPromises);
88
- // Unique file names
89
- return [...new Set(files.flat())];
90
- }
91
- const normalizePath = (path) => path.replace(/\\/g, '/');
92
- const removeLeadingSlash = (pattern) => {
93
- if (pattern.charAt(0) === '/') {
94
- return pattern.substring(1);
95
- }
96
- return pattern;
97
- };
98
- const removeRelativeRoot = (path, root) => {
99
- if (path.startsWith(root)) {
100
- return path.substring(root.length);
101
- }
102
- return path;
103
- };
104
- async function findMatchingTests(pattern, ignore, workspaceRoot, projectSourceRoot) {
105
- // normalize pattern, glob lib only accepts forward slashes
106
- let normalizedPattern = normalizePath(pattern);
107
- normalizedPattern = removeLeadingSlash(normalizedPattern);
108
- const relativeProjectRoot = normalizePath((0, path_1.relative)(workspaceRoot, projectSourceRoot) + '/');
109
- // remove relativeProjectRoot to support relative paths from root
110
- // such paths are easy to get when running scripts via IDEs
111
- normalizedPattern = removeRelativeRoot(normalizedPattern, relativeProjectRoot);
112
- // special logic when pattern does not look like a glob
113
- if (!(0, fast_glob_1.isDynamicPattern)(normalizedPattern)) {
114
- if (await isDirectory((0, path_1.join)(projectSourceRoot, normalizedPattern))) {
115
- normalizedPattern = `${normalizedPattern}/**/*.spec.@(ts|tsx)`;
116
- }
117
- else {
118
- // see if matching spec file exists
119
- const fileExt = (0, path_1.extname)(normalizedPattern);
120
- // Replace extension to `.spec.ext`. Example: `src/app/app.component.ts`-> `src/app/app.component.spec.ts`
121
- const potentialSpec = (0, path_1.join)(projectSourceRoot, (0, path_1.dirname)(normalizedPattern), `${(0, path_1.basename)(normalizedPattern, fileExt)}.spec${fileExt}`);
122
- if (await exists(potentialSpec)) {
123
- return [potentialSpec];
124
- }
125
- }
126
- }
127
- // normalize the patterns in the ignore list
128
- const normalizedIgnorePatternList = ignore.map((pattern) => removeRelativeRoot(removeLeadingSlash(normalizePath(pattern)), relativeProjectRoot));
129
- return (0, fast_glob_1.default)(normalizedPattern, {
130
- cwd: projectSourceRoot,
131
- absolute: true,
132
- ignore: ['**/node_modules/**', ...normalizedIgnorePatternList],
133
- });
134
- }
135
- async function isDirectory(path) {
136
- try {
137
- const stats = await fs_1.promises.stat(path);
138
- return stats.isDirectory();
139
- }
140
- catch {
141
- return false;
142
- }
143
- }
144
- async function exists(path) {
145
- try {
146
- await fs_1.promises.access(path, fs_1.constants.F_OK);
147
- return true;
148
- }
149
- catch {
150
- return false;
151
- }
152
- }
@@ -0,0 +1,8 @@
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
+ export declare function findTests(include: string[], exclude: string[], workspaceRoot: string, projectSourceRoot: string): Promise<string[]>;
@@ -0,0 +1,105 @@
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 __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 (mod) {
26
+ if (mod && mod.__esModule) return mod;
27
+ var result = {};
28
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
29
+ __setModuleDefault(result, mod);
30
+ return result;
31
+ };
32
+ Object.defineProperty(exports, "__esModule", { value: true });
33
+ exports.findTests = findTests;
34
+ const fast_glob_1 = __importStar(require("fast-glob"));
35
+ const fs_1 = require("fs");
36
+ const path_1 = require("path");
37
+ /* Go through all patterns and find unique list of files */
38
+ async function findTests(include, exclude, workspaceRoot, projectSourceRoot) {
39
+ const matchingTestsPromises = include.map((pattern) => findMatchingTests(pattern, exclude, workspaceRoot, projectSourceRoot));
40
+ const files = await Promise.all(matchingTestsPromises);
41
+ // Unique file names
42
+ return [...new Set(files.flat())];
43
+ }
44
+ const normalizePath = (path) => path.replace(/\\/g, '/');
45
+ const removeLeadingSlash = (pattern) => {
46
+ if (pattern.charAt(0) === '/') {
47
+ return pattern.substring(1);
48
+ }
49
+ return pattern;
50
+ };
51
+ const removeRelativeRoot = (path, root) => {
52
+ if (path.startsWith(root)) {
53
+ return path.substring(root.length);
54
+ }
55
+ return path;
56
+ };
57
+ async function findMatchingTests(pattern, ignore, workspaceRoot, projectSourceRoot) {
58
+ // normalize pattern, glob lib only accepts forward slashes
59
+ let normalizedPattern = normalizePath(pattern);
60
+ normalizedPattern = removeLeadingSlash(normalizedPattern);
61
+ const relativeProjectRoot = normalizePath((0, path_1.relative)(workspaceRoot, projectSourceRoot) + '/');
62
+ // remove relativeProjectRoot to support relative paths from root
63
+ // such paths are easy to get when running scripts via IDEs
64
+ normalizedPattern = removeRelativeRoot(normalizedPattern, relativeProjectRoot);
65
+ // special logic when pattern does not look like a glob
66
+ if (!(0, fast_glob_1.isDynamicPattern)(normalizedPattern)) {
67
+ if (await isDirectory((0, path_1.join)(projectSourceRoot, normalizedPattern))) {
68
+ normalizedPattern = `${normalizedPattern}/**/*.spec.@(ts|tsx)`;
69
+ }
70
+ else {
71
+ // see if matching spec file exists
72
+ const fileExt = (0, path_1.extname)(normalizedPattern);
73
+ // Replace extension to `.spec.ext`. Example: `src/app/app.component.ts`-> `src/app/app.component.spec.ts`
74
+ const potentialSpec = (0, path_1.join)(projectSourceRoot, (0, path_1.dirname)(normalizedPattern), `${(0, path_1.basename)(normalizedPattern, fileExt)}.spec${fileExt}`);
75
+ if (await exists(potentialSpec)) {
76
+ return [potentialSpec];
77
+ }
78
+ }
79
+ }
80
+ // normalize the patterns in the ignore list
81
+ const normalizedIgnorePatternList = ignore.map((pattern) => removeRelativeRoot(removeLeadingSlash(normalizePath(pattern)), relativeProjectRoot));
82
+ return (0, fast_glob_1.default)(normalizedPattern, {
83
+ cwd: projectSourceRoot,
84
+ absolute: true,
85
+ ignore: ['**/node_modules/**', ...normalizedIgnorePatternList],
86
+ });
87
+ }
88
+ async function isDirectory(path) {
89
+ try {
90
+ const stats = await fs_1.promises.stat(path);
91
+ return stats.isDirectory();
92
+ }
93
+ catch {
94
+ return false;
95
+ }
96
+ }
97
+ async function exists(path) {
98
+ try {
99
+ await fs_1.promises.access(path, fs_1.constants.F_OK);
100
+ return true;
101
+ }
102
+ catch {
103
+ return false;
104
+ }
105
+ }
@@ -37,127 +37,56 @@ const core_1 = require("@angular-devkit/core");
37
37
  const module_1 = require("module");
38
38
  const path = __importStar(require("path"));
39
39
  const rxjs_1 = require("rxjs");
40
- const configs_1 = require("../../tools/webpack/configs");
41
- const webpack_browser_config_1 = require("../../utils/webpack-browser-config");
42
- const schema_1 = require("../browser/schema");
43
- const find_tests_plugin_1 = require("./find-tests-plugin");
44
- async function initialize(options, context, webpackConfigurationTransformer) {
45
- // Purge old build disk cache.
46
- await (0, private_1.purgeStaleBuildCache)(context);
47
- const { config } = await (0, webpack_browser_config_1.generateBrowserWebpackConfigFromContext)(
48
- // only two properties are missing:
49
- // * `outputPath` which is fixed for tests
50
- // * `budgets` which might be incorrect due to extra dev libs
51
- {
52
- ...options,
53
- outputPath: '',
54
- budgets: undefined,
55
- optimization: false,
56
- buildOptimizer: false,
57
- aot: false,
58
- vendorChunk: true,
59
- namedChunks: true,
60
- extractLicenses: false,
61
- outputHashing: schema_1.OutputHashing.None,
62
- // The webpack tier owns the watch behavior so we want to force it in the config.
63
- // When not in watch mode, webpack-dev-middleware will call `compiler.watch` anyway.
64
- // https://github.com/webpack/webpack-dev-middleware/blob/698c9ae5e9bb9a013985add6189ff21c1a1ec185/src/index.js#L65
65
- // https://github.com/webpack/webpack/blob/cde1b73e12eb8a77eb9ba42e7920c9ec5d29c2c9/lib/Compiler.js#L379-L388
66
- watch: true,
67
- }, context, (wco) => [(0, configs_1.getCommonConfig)(wco), (0, configs_1.getStylesConfig)(wco)]);
68
- const karma = await Promise.resolve().then(() => __importStar(require('karma')));
69
- return [karma, (await webpackConfigurationTransformer?.(config)) ?? config];
70
- }
40
+ const schema_1 = require("./schema");
71
41
  /**
72
42
  * @experimental Direct usage of this function is considered experimental.
73
43
  */
74
44
  function execute(options, context, transforms = {}) {
75
45
  // Check Angular version.
76
46
  (0, private_1.assertCompatibleAngularVersion)(context.workspaceRoot);
47
+ return (0, rxjs_1.from)(getExecuteWithBuilder(options, context)).pipe((0, rxjs_1.mergeMap)(([useEsbuild, executeWithBuilder]) => {
48
+ const karmaOptions = getBaseKarmaOptions(options, context, useEsbuild);
49
+ return executeWithBuilder.execute(options, context, karmaOptions, transforms);
50
+ }));
51
+ }
52
+ function getBaseKarmaOptions(options, context, useEsbuild) {
77
53
  let singleRun;
78
54
  if (options.watch !== undefined) {
79
55
  singleRun = !options.watch;
80
56
  }
81
- return (0, rxjs_1.from)(initialize(options, context, transforms.webpackConfiguration)).pipe((0, rxjs_1.switchMap)(async ([karma, webpackConfig]) => {
82
- // Determine project name from builder context target
83
- const projectName = context.target?.project;
84
- if (!projectName) {
85
- throw new Error(`The 'karma' builder requires a target to be specified.`);
86
- }
87
- const karmaOptions = options.karmaConfig
88
- ? {}
89
- : getBuiltInKarmaConfig(context.workspaceRoot, projectName);
90
- karmaOptions.singleRun = singleRun;
91
- // Workaround https://github.com/angular/angular-cli/issues/28271, by clearing context by default
92
- // for single run executions. Not clearing context for multi-run (watched) builds allows the
93
- // Jasmine Spec Runner to be visible in the browser after test execution.
94
- karmaOptions.client ??= {};
95
- karmaOptions.client.clearContext ??= singleRun ?? false; // `singleRun` defaults to `false` per Karma docs.
96
- // Convert browsers from a string to an array
97
- if (typeof options.browsers === 'string' && options.browsers) {
98
- karmaOptions.browsers = options.browsers.split(',');
99
- }
100
- else if (options.browsers === false) {
101
- karmaOptions.browsers = [];
102
- }
103
- if (options.reporters) {
104
- // Split along commas to make it more natural, and remove empty strings.
105
- const reporters = options.reporters
106
- .reduce((acc, curr) => acc.concat(curr.split(',')), [])
107
- .filter((x) => !!x);
108
- if (reporters.length > 0) {
109
- karmaOptions.reporters = reporters;
110
- }
111
- }
112
- if (!options.main) {
113
- webpackConfig.entry ??= {};
114
- if (typeof webpackConfig.entry === 'object' && !Array.isArray(webpackConfig.entry)) {
115
- if (Array.isArray(webpackConfig.entry['main'])) {
116
- webpackConfig.entry['main'].push(getBuiltInMainFile());
117
- }
118
- else {
119
- webpackConfig.entry['main'] = [getBuiltInMainFile()];
120
- }
121
- }
122
- }
123
- const projectMetadata = await context.getProjectMetadata(projectName);
124
- const sourceRoot = (projectMetadata.sourceRoot ?? projectMetadata.root ?? '');
125
- webpackConfig.plugins ??= [];
126
- webpackConfig.plugins.push(new find_tests_plugin_1.FindTestsPlugin({
127
- include: options.include,
128
- exclude: options.exclude,
129
- workspaceRoot: context.workspaceRoot,
130
- projectSourceRoot: path.join(context.workspaceRoot, sourceRoot),
131
- }));
132
- karmaOptions.buildWebpack = {
133
- options,
134
- webpackConfig,
135
- logger: context.logger,
136
- };
137
- const parsedKarmaConfig = await karma.config.parseConfig(options.karmaConfig && path.resolve(context.workspaceRoot, options.karmaConfig), transforms.karmaOptions ? transforms.karmaOptions(karmaOptions) : karmaOptions, { promiseConfig: true, throwErrors: true });
138
- return [karma, parsedKarmaConfig];
139
- }), (0, rxjs_1.switchMap)(([karma, karmaConfig]) => new rxjs_1.Observable((subscriber) => {
140
- // Pass onto Karma to emit BuildEvents.
141
- karmaConfig.buildWebpack ??= {};
142
- if (typeof karmaConfig.buildWebpack === 'object') {
143
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
144
- karmaConfig.buildWebpack.failureCb ??= () => subscriber.next({ success: false });
145
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
146
- karmaConfig.buildWebpack.successCb ??= () => subscriber.next({ success: true });
57
+ // Determine project name from builder context target
58
+ const projectName = context.target?.project;
59
+ if (!projectName) {
60
+ throw new Error(`The 'karma' builder requires a target to be specified.`);
61
+ }
62
+ const karmaOptions = options.karmaConfig
63
+ ? {}
64
+ : getBuiltInKarmaConfig(context.workspaceRoot, projectName, useEsbuild);
65
+ karmaOptions.singleRun = singleRun;
66
+ // Workaround https://github.com/angular/angular-cli/issues/28271, by clearing context by default
67
+ // for single run executions. Not clearing context for multi-run (watched) builds allows the
68
+ // Jasmine Spec Runner to be visible in the browser after test execution.
69
+ karmaOptions.client ??= {};
70
+ karmaOptions.client.clearContext ??= singleRun ?? false; // `singleRun` defaults to `false` per Karma docs.
71
+ // Convert browsers from a string to an array
72
+ if (typeof options.browsers === 'string' && options.browsers) {
73
+ karmaOptions.browsers = options.browsers.split(',');
74
+ }
75
+ else if (options.browsers === false) {
76
+ karmaOptions.browsers = [];
77
+ }
78
+ if (options.reporters) {
79
+ // Split along commas to make it more natural, and remove empty strings.
80
+ const reporters = options.reporters
81
+ .reduce((acc, curr) => acc.concat(curr.split(',')), [])
82
+ .filter((x) => !!x);
83
+ if (reporters.length > 0) {
84
+ karmaOptions.reporters = reporters;
147
85
  }
148
- // Complete the observable once the Karma server returns.
149
- const karmaServer = new karma.Server(karmaConfig, (exitCode) => {
150
- subscriber.next({ success: exitCode === 0 });
151
- subscriber.complete();
152
- });
153
- const karmaStart = karmaServer.start();
154
- // Cleanup, signal Karma to exit.
155
- return () => {
156
- void karmaStart.then(() => karmaServer.stop());
157
- };
158
- })), (0, rxjs_1.defaultIfEmpty)({ success: false }));
86
+ }
87
+ return karmaOptions;
159
88
  }
160
- function getBuiltInKarmaConfig(workspaceRoot, projectName) {
89
+ function getBuiltInKarmaConfig(workspaceRoot, projectName, useEsbuild) {
161
90
  let coverageFolderName = projectName.charAt(0) === '@' ? projectName.slice(1) : projectName;
162
91
  if (/[A-Z]/.test(coverageFolderName)) {
163
92
  coverageFolderName = core_1.strings.dasherize(coverageFolderName);
@@ -166,13 +95,13 @@ function getBuiltInKarmaConfig(workspaceRoot, projectName) {
166
95
  // Any changes to the config here need to be synced to: packages/schematics/angular/config/files/karma.conf.js.template
167
96
  return {
168
97
  basePath: '',
169
- frameworks: ['jasmine', '@angular-devkit/build-angular'],
98
+ frameworks: ['jasmine', ...(useEsbuild ? [] : ['@angular-devkit/build-angular'])],
170
99
  plugins: [
171
100
  'karma-jasmine',
172
101
  'karma-chrome-launcher',
173
102
  'karma-jasmine-html-reporter',
174
103
  'karma-coverage',
175
- '@angular-devkit/build-angular/plugins/karma',
104
+ ...(useEsbuild ? [] : ['@angular-devkit/build-angular/plugins/karma']),
176
105
  ].map((p) => workspaceRootRequire(p)),
177
106
  jasmineHtmlReporter: {
178
107
  suppressAll: true, // removes the duplicated traces
@@ -200,19 +129,36 @@ function getBuiltInKarmaConfig(workspaceRoot, projectName) {
200
129
  };
201
130
  }
202
131
  exports.default = (0, architect_1.createBuilder)(execute);
203
- function getBuiltInMainFile() {
204
- const content = Buffer.from(`
205
- import { getTestBed } from '@angular/core/testing';
206
- import {
207
- BrowserDynamicTestingModule,
208
- platformBrowserDynamicTesting,
209
- } from '@angular/platform-browser-dynamic/testing';
210
-
211
- // Initialize the Angular testing environment.
212
- getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), {
213
- errorOnUnknownElements: true,
214
- errorOnUnknownProperties: true
215
- });
216
- `).toString('base64');
217
- return `ng-virtual-main.js!=!data:text/javascript;base64,${content}`;
132
+ async function getExecuteWithBuilder(options, context) {
133
+ const useEsbuild = await checkForEsbuild(options, context);
134
+ const executeWithBuilderModule = useEsbuild
135
+ ? Promise.resolve().then(() => __importStar(require('./application_builder'))) : Promise.resolve().then(() => __importStar(require('./browser_builder')));
136
+ return [useEsbuild, await executeWithBuilderModule];
137
+ }
138
+ async function checkForEsbuild(options, context) {
139
+ if (options.builderMode !== schema_1.BuilderMode.Detect) {
140
+ return options.builderMode === schema_1.BuilderMode.Application;
141
+ }
142
+ // Look up the current project's build target using a development configuration.
143
+ const buildTargetSpecifier = `::development`;
144
+ const buildTarget = (0, architect_1.targetFromTargetString)(buildTargetSpecifier, context.target?.project, 'build');
145
+ try {
146
+ const developmentBuilderName = await context.getBuilderNameForTarget(buildTarget);
147
+ return isEsbuildBased(developmentBuilderName);
148
+ }
149
+ catch (e) {
150
+ if (!(e instanceof Error) || e.message !== 'Project target does not exist.') {
151
+ throw e;
152
+ }
153
+ // If we can't find a development builder, we can't use 'detect'.
154
+ throw new Error('Failed to detect the builder used by the application. Please set builderMode explicitly.');
155
+ }
156
+ }
157
+ function isEsbuildBased(builderName) {
158
+ if (builderName === '@angular/build:application' ||
159
+ builderName === '@angular-devkit/build-angular:application' ||
160
+ builderName === '@angular-devkit/build-angular:browser-esbuild') {
161
+ return true;
162
+ }
163
+ return false;
218
164
  }
@@ -0,0 +1,19 @@
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 {
11
+ BrowserDynamicTestingModule,
12
+ platformBrowserDynamicTesting,
13
+ } from '@angular/platform-browser-dynamic/testing';
14
+
15
+ // Initialize the Angular testing environment.
16
+ getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), {
17
+ errorOnUnknownElements: true,
18
+ errorOnUnknownProperties: true,
19
+ });
@@ -10,6 +10,11 @@ export interface Schema {
10
10
  * Override which browsers tests are run against. Set to `false` to not use any browser.
11
11
  */
12
12
  browsers?: Browsers;
13
+ /**
14
+ * Determines how to build the code under test. If set to 'detect', attempts to follow the
15
+ * development builder.
16
+ */
17
+ builderMode?: BuilderMode;
13
18
  /**
14
19
  * Output a code coverage report.
15
20
  */
@@ -121,6 +126,15 @@ export interface AssetPatternClass {
121
126
  * Override which browsers tests are run against. Set to `false` to not use any browser.
122
127
  */
123
128
  export type Browsers = boolean | string;
129
+ /**
130
+ * Determines how to build the code under test. If set to 'detect', attempts to follow the
131
+ * development builder.
132
+ */
133
+ export declare enum BuilderMode {
134
+ Application = "application",
135
+ Browser = "browser",
136
+ Detect = "detect"
137
+ }
124
138
  export interface FileReplacement {
125
139
  replace?: string;
126
140
  replaceWith?: string;
@@ -2,7 +2,17 @@
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.InlineStyleLanguage = void 0;
5
+ exports.InlineStyleLanguage = exports.BuilderMode = void 0;
6
+ /**
7
+ * Determines how to build the code under test. If set to 'detect', attempts to follow the
8
+ * development builder.
9
+ */
10
+ var BuilderMode;
11
+ (function (BuilderMode) {
12
+ BuilderMode["Application"] = "application";
13
+ BuilderMode["Browser"] = "browser";
14
+ BuilderMode["Detect"] = "detect";
15
+ })(BuilderMode || (exports.BuilderMode = BuilderMode = {}));
6
16
  /**
7
17
  * The stylesheet language to use for the application's inline component styles.
8
18
  */
@@ -267,6 +267,12 @@
267
267
  "type": "string"
268
268
  }
269
269
  },
270
+ "builderMode": {
271
+ "type": "string",
272
+ "description": "Determines how to build the code under test. If set to 'detect', attempts to follow the development builder.",
273
+ "enum": ["detect", "browser", "application"],
274
+ "default": "browser"
275
+ },
270
276
  "webWorkerTsConfig": {
271
277
  "type": "string",
272
278
  "description": "TypeScript configuration for Web Worker modules."
@@ -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 = '19.0.0-next.6';
13
+ const VERSION = '19.0.0-next.8';
14
14
  function hasCacheMetadata(value) {
15
15
  return (!!value &&
16
16
  typeof value === 'object' &&