@angular-devkit/build-angular 19.0.0-next.8 → 19.0.0-next.9

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License
2
2
 
3
- Copyright (c) 2017 Google, Inc.
3
+ Copyright (c) 2010-2024 Google LLC. https://angular.dev/license
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -9,13 +9,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
9
  copies of the Software, and to permit persons to whom the Software is
10
10
  furnished to do so, subject to the following conditions:
11
11
 
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
14
 
15
15
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
16
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
17
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
18
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@angular-devkit/build-angular",
3
- "version": "19.0.0-next.8",
3
+ "version": "19.0.0-next.9",
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.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",
10
+ "@angular-devkit/architect": "0.1900.0-next.9",
11
+ "@angular-devkit/build-webpack": "0.1900.0-next.9",
12
+ "@angular-devkit/core": "19.0.0-next.9",
13
+ "@angular/build": "19.0.0-next.9",
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,7 +21,7 @@
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.8",
24
+ "@ngtools/webpack": "19.0.0-next.9",
25
25
  "@vitejs/plugin-basic-ssl": "1.1.0",
26
26
  "ansi-colors": "4.1.3",
27
27
  "autoprefixer": "10.4.20",
@@ -53,17 +53,17 @@
53
53
  "postcss-loader": "8.1.1",
54
54
  "resolve-url-loader": "5.0.0",
55
55
  "rxjs": "7.8.1",
56
- "sass": "1.79.3",
56
+ "sass": "1.79.4",
57
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.33.0",
61
+ "terser": "5.34.1",
62
62
  "tree-kill": "1.2.2",
63
63
  "tslib": "2.7.0",
64
64
  "vite": "5.4.8",
65
65
  "watchpack": "2.4.2",
66
- "webpack": "5.94.0",
66
+ "webpack": "5.95.0",
67
67
  "webpack-dev-middleware": "7.4.2",
68
68
  "webpack-dev-server": "5.1.0",
69
69
  "webpack-merge": "6.0.1",
@@ -77,7 +77,7 @@
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.8",
80
+ "@angular/ssr": "^19.0.0-next.9",
81
81
  "@web/test-runner": "^0.19.0",
82
82
  "browser-sync": "^3.0.2",
83
83
  "jest": "^29.5.0",
@@ -29,16 +29,19 @@ var __importStar = (this && this.__importStar) || function (mod) {
29
29
  __setModuleDefault(result, mod);
30
30
  return result;
31
31
  };
32
+ var __importDefault = (this && this.__importDefault) || function (mod) {
33
+ return (mod && mod.__esModule) ? mod : { "default": mod };
34
+ };
32
35
  Object.defineProperty(exports, "__esModule", { value: true });
33
36
  exports.execute = execute;
34
37
  exports.writeTestFiles = writeTestFiles;
35
38
  const build_1 = require("@angular/build");
36
39
  const private_1 = require("@angular/build/private");
37
40
  const crypto_1 = require("crypto");
41
+ const fast_glob_1 = __importDefault(require("fast-glob"));
38
42
  const fs = __importStar(require("fs/promises"));
39
43
  const path = __importStar(require("path"));
40
44
  const rxjs_1 = require("rxjs");
41
- const read_tsconfig_1 = require("../../utils/read-tsconfig");
42
45
  const schema_1 = require("../browser-esbuild/schema");
43
46
  const find_tests_1 = require("./find-tests");
44
47
  class ApplicationBuildError extends Error {
@@ -47,8 +50,56 @@ class ApplicationBuildError extends Error {
47
50
  this.name = 'ApplicationBuildError';
48
51
  }
49
52
  }
53
+ function injectKarmaReporter(context, buildOptions, karmaConfig, subscriber) {
54
+ const reporterName = 'angular-progress-notifier';
55
+ class ProgressNotifierReporter {
56
+ emitter;
57
+ static $inject = ['emitter'];
58
+ constructor(emitter) {
59
+ this.emitter = emitter;
60
+ this.startWatchingBuild();
61
+ }
62
+ startWatchingBuild() {
63
+ void (async () => {
64
+ for await (const buildOutput of (0, private_1.buildApplicationInternal)({
65
+ ...buildOptions,
66
+ watch: true,
67
+ }, context)) {
68
+ if (buildOutput.kind === private_1.ResultKind.Failure) {
69
+ subscriber.next({ success: false, message: 'Build failed' });
70
+ }
71
+ else if (buildOutput.kind === private_1.ResultKind.Incremental ||
72
+ buildOutput.kind === private_1.ResultKind.Full) {
73
+ await writeTestFiles(buildOutput.files, buildOptions.outputPath);
74
+ this.emitter.refreshFiles();
75
+ }
76
+ }
77
+ })();
78
+ }
79
+ onRunComplete = function (_browsers, results) {
80
+ if (results.exitCode === 0) {
81
+ subscriber.next({ success: true });
82
+ }
83
+ else {
84
+ subscriber.next({ success: false });
85
+ }
86
+ };
87
+ }
88
+ karmaConfig.reporters ??= [];
89
+ karmaConfig.reporters.push(reporterName);
90
+ karmaConfig.plugins ??= [];
91
+ karmaConfig.plugins.push({
92
+ [`reporter:${reporterName}`]: [
93
+ 'factory',
94
+ Object.assign((...args) => new ProgressNotifierReporter(...args), ProgressNotifierReporter),
95
+ ],
96
+ });
97
+ }
50
98
  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) => {
99
+ return (0, rxjs_1.from)(initializeApplication(options, context, karmaOptions, transforms)).pipe((0, rxjs_1.switchMap)(([karma, karmaConfig, buildOptions]) => new rxjs_1.Observable((subscriber) => {
100
+ if (options.watch) {
101
+ injectKarmaReporter(context, buildOptions, karmaConfig, subscriber);
102
+ }
52
103
  // Complete the observable once the Karma server returns.
53
104
  const karmaServer = new karma.Server(karmaConfig, (exitCode) => {
54
105
  subscriber.next({ success: exitCode === 0 });
@@ -76,41 +127,36 @@ async function getProjectSourceRoot(context) {
76
127
  const sourceRoot = (projectMetadata.sourceRoot ?? projectMetadata.root ?? '');
77
128
  return path.join(context.workspaceRoot, sourceRoot);
78
129
  }
79
- async function collectEntrypoints(options, context) {
80
- const projectSourceRoot = await getProjectSourceRoot(context);
130
+ function normalizePolyfills(polyfills) {
131
+ if (typeof polyfills === 'string') {
132
+ return [polyfills];
133
+ }
134
+ return polyfills ?? [];
135
+ }
136
+ async function collectEntrypoints(options, context, projectSourceRoot) {
81
137
  // Glob for files to test.
82
138
  const testFiles = await (0, find_tests_1.findTests)(options.include ?? [], options.exclude ?? [], context.workspaceRoot, projectSourceRoot);
83
139
  const entryPoints = new Set([
84
140
  ...testFiles,
85
141
  '@angular-devkit/build-angular/src/builders/karma/init_test_bed.js',
86
142
  ]);
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];
143
+ return entryPoints;
100
144
  }
101
145
  async function initializeApplication(options, context, karmaOptions, transforms = {}) {
102
146
  if (transforms.webpackConfiguration) {
103
147
  context.logger.warn(`This build is using the application builder but transforms.webpackConfiguration was provided. The transform will be ignored.`);
104
148
  }
105
- const testDir = path.join(context.workspaceRoot, 'dist/test-out', (0, crypto_1.randomUUID)());
106
- const [karma, [entryPoints, polyfills]] = await Promise.all([
149
+ const outputPath = path.join(context.workspaceRoot, 'dist/test-out', (0, crypto_1.randomUUID)());
150
+ const projectSourceRoot = await getProjectSourceRoot(context);
151
+ const [karma, entryPoints] = await Promise.all([
107
152
  Promise.resolve().then(() => __importStar(require('karma'))),
108
- collectEntrypoints(options, context),
109
- fs.rm(testDir, { recursive: true, force: true }),
153
+ collectEntrypoints(options, context, projectSourceRoot),
154
+ fs.rm(outputPath, { recursive: true, force: true }),
110
155
  ]);
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)({
156
+ const instrumentForCoverage = options.codeCoverage
157
+ ? createInstrumentationFilter(projectSourceRoot, getInstrumentationExcludedPaths(context.workspaceRoot, options.codeCoverageExclude ?? []))
158
+ : undefined;
159
+ const buildOptions = {
114
160
  entryPoints,
115
161
  tsConfig: options.tsConfig,
116
162
  outputPath,
@@ -123,10 +169,13 @@ async function initializeApplication(options, context, karmaOptions, transforms
123
169
  styles: true,
124
170
  vendor: true,
125
171
  },
172
+ instrumentForCoverage,
126
173
  styles: options.styles,
127
- polyfills,
174
+ polyfills: normalizePolyfills(options.polyfills),
128
175
  webWorkerTsConfig: options.webWorkerTsConfig,
129
- }, context));
176
+ };
177
+ // Build tests with `application` builder, using test files as entry points.
178
+ const buildOutput = await first((0, private_1.buildApplicationInternal)(buildOptions, context));
130
179
  if (buildOutput.kind === private_1.ResultKind.Failure) {
131
180
  throw new ApplicationBuildError('Build failed');
132
181
  }
@@ -134,22 +183,19 @@ async function initializeApplication(options, context, karmaOptions, transforms
134
183
  throw new ApplicationBuildError('A full build result is required from the application builder.');
135
184
  }
136
185
  // Write test files
137
- await writeTestFiles(buildOutput.files, testDir);
186
+ await writeTestFiles(buildOutput.files, buildOptions.outputPath);
138
187
  karmaOptions.files ??= [];
139
188
  karmaOptions.files.push(
140
189
  // Serve polyfills first.
141
- { pattern: `${testDir}/polyfills.js`, type: 'module' },
190
+ { pattern: `${outputPath}/polyfills.js`, type: 'module' },
142
191
  // 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 },
192
+ { pattern: `${outputPath}/{chunk,worker}-*.js`, type: 'module', included: false });
193
+ karmaOptions.files.push(
148
194
  // Serve remaining JS on page load, these are the test entrypoints.
149
- { pattern: `${testDir}/*.js`, type: 'module' });
195
+ { pattern: `${outputPath}/*.js`, type: 'module' });
150
196
  if (options.styles?.length) {
151
197
  // Serve CSS outputs on page load, these are the global styles.
152
- karmaOptions.files.push({ pattern: `${testDir}/*.css`, type: 'css' });
198
+ karmaOptions.files.push({ pattern: `${outputPath}/*.css`, type: 'css' });
153
199
  }
154
200
  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
201
  // Remove the webpack plugin/framework:
@@ -173,7 +219,7 @@ async function initializeApplication(options, context, karmaOptions, transforms
173
219
  !parsedKarmaConfig.reporters?.some((r) => r === 'coverage' || r === 'coverage-istanbul')) {
174
220
  parsedKarmaConfig.reporters = (parsedKarmaConfig.reporters ?? []).concat(['coverage']);
175
221
  }
176
- return [karma, parsedKarmaConfig];
222
+ return [karma, parsedKarmaConfig, buildOptions];
177
223
  }
178
224
  async function writeTestFiles(files, testDir) {
179
225
  const directoryExists = new Set();
@@ -199,15 +245,6 @@ async function writeTestFiles(files, testDir) {
199
245
  }
200
246
  });
201
247
  }
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
248
  /** Returns the first item yielded by the given generator and cancels the execution. */
212
249
  async function first(generator) {
213
250
  for await (const value of generator) {
@@ -215,3 +252,18 @@ async function first(generator) {
215
252
  }
216
253
  throw new Error('Expected generator to emit at least once.');
217
254
  }
255
+ function createInstrumentationFilter(includedBasePath, excludedPaths) {
256
+ return (request) => {
257
+ return (!excludedPaths.has(request) &&
258
+ !/\.(e2e|spec)\.tsx?$|[\\/]node_modules[\\/]/.test(request) &&
259
+ request.startsWith(includedBasePath));
260
+ };
261
+ }
262
+ function getInstrumentationExcludedPaths(root, excludedPaths) {
263
+ const excluded = new Set();
264
+ for (const excludeGlob of excludedPaths) {
265
+ const excludePath = excludeGlob[0] === '/' ? excludeGlob.slice(1) : excludeGlob;
266
+ fast_glob_1.default.sync(excludePath, { cwd: root }).forEach((p) => excluded.add(path.join(root, p)));
267
+ }
268
+ return excluded;
269
+ }
@@ -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.8';
13
+ const VERSION = '19.0.0-next.9';
14
14
  function hasCacheMetadata(value) {
15
15
  return (!!value &&
16
16
  typeof value === 'object' &&