@angular/build 20.0.0-next.1 → 20.0.0-next.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@angular/build",
3
- "version": "20.0.0-next.1",
3
+ "version": "20.0.0-next.3",
4
4
  "description": "Official build system for Angular",
5
5
  "keywords": [
6
6
  "Angular CLI",
@@ -23,30 +23,31 @@
23
23
  "builders": "builders.json",
24
24
  "dependencies": {
25
25
  "@ampproject/remapping": "2.3.0",
26
- "@angular-devkit/architect": "0.2000.0-next.1",
26
+ "@angular-devkit/architect": "0.2000.0-next.3",
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
  "@babel/plugin-syntax-import-attributes": "7.26.0",
31
- "@inquirer/confirm": "5.1.7",
31
+ "@inquirer/confirm": "5.1.8",
32
32
  "@vitejs/plugin-basic-ssl": "2.0.0",
33
33
  "beasties": "0.2.0",
34
34
  "browserslist": "^4.23.0",
35
35
  "esbuild": "0.25.1",
36
36
  "https-proxy-agent": "7.0.6",
37
37
  "istanbul-lib-instrument": "6.0.3",
38
+ "jsonc-parser": "3.3.1",
38
39
  "listr2": "8.2.5",
39
40
  "magic-string": "0.30.17",
40
41
  "mrmime": "2.0.1",
41
42
  "parse5-html-rewriting-stream": "7.0.0",
42
43
  "picomatch": "4.0.2",
43
- "piscina": "4.8.0",
44
- "rollup": "4.35.0",
45
- "sass": "1.85.1",
44
+ "piscina": "4.9.2",
45
+ "rollup": "4.37.0",
46
+ "sass": "1.86.0",
46
47
  "semver": "7.7.1",
47
48
  "source-map-support": "0.5.21",
48
49
  "tinyglobby": "0.2.12",
49
- "vite": "6.2.1",
50
+ "vite": "6.2.3",
50
51
  "watchpack": "2.4.2"
51
52
  },
52
53
  "optionalDependencies": {
@@ -56,9 +57,10 @@
56
57
  "@angular/compiler": "^20.0.0 || ^20.0.0-next.0",
57
58
  "@angular/compiler-cli": "^20.0.0 || ^20.0.0-next.0",
58
59
  "@angular/localize": "^20.0.0 || ^20.0.0-next.0",
60
+ "@angular/platform-browser": "^20.0.0 || ^20.0.0-next.0",
59
61
  "@angular/platform-server": "^20.0.0 || ^20.0.0-next.0",
60
62
  "@angular/service-worker": "^20.0.0 || ^20.0.0-next.0",
61
- "@angular/ssr": "^20.0.0-next.1",
63
+ "@angular/ssr": "^20.0.0-next.3",
62
64
  "karma": "^6.4.0",
63
65
  "less": "^4.2.0",
64
66
  "ng-packagr": "^20.0.0 || ^20.0.0-next.0",
@@ -70,6 +72,9 @@
70
72
  "@angular/localize": {
71
73
  "optional": true
72
74
  },
75
+ "@angular/platform-browser": {
76
+ "optional": true
77
+ },
73
78
  "@angular/platform-server": {
74
79
  "optional": true
75
80
  },
@@ -101,7 +106,7 @@
101
106
  },
102
107
  "packageManager": "pnpm@9.15.6",
103
108
  "engines": {
104
- "node": "^20.11.1 || >=22.0.0",
109
+ "node": "^20.11.1 || >=22.11.0",
105
110
  "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
106
111
  "yarn": ">= 1.13.0"
107
112
  },
@@ -27,6 +27,7 @@ const build_action_1 = require("./build-action");
27
27
  const execute_build_1 = require("./execute-build");
28
28
  const options_1 = require("./options");
29
29
  const results_1 = require("./results");
30
+ const isNodeV22orHigher = Number(process.versions.node.split('.', 1)[0]) >= 22;
30
31
  async function* buildApplicationInternal(options,
31
32
  // TODO: Integrate abort signal support into builder system
32
33
  context, extensions) {
@@ -162,7 +163,18 @@ async function* buildApplication(options, context, extensions) {
162
163
  }
163
164
  else {
164
165
  // Copy file contents
165
- await promises_1.default.copyFile(file.inputPath, fullFilePath, promises_1.default.constants.COPYFILE_FICLONE);
166
+ if (isNodeV22orHigher) {
167
+ // Use newer `cp` API on Node.js 22+ (minimum v22 for CLI is 22.11)
168
+ await promises_1.default.cp(file.inputPath, fullFilePath, {
169
+ mode: promises_1.default.constants.COPYFILE_FICLONE,
170
+ preserveTimestamps: true,
171
+ });
172
+ }
173
+ else {
174
+ // For Node.js 20 use `copyFile` (`cp` is not stable for v20)
175
+ // TODO: Remove when Node.js 20 is no longer supported
176
+ await promises_1.default.copyFile(file.inputPath, fullFilePath, promises_1.default.constants.COPYFILE_FICLONE);
177
+ }
166
178
  }
167
179
  });
168
180
  // Delete any removed files if incremental
@@ -163,7 +163,7 @@ async function normalizeOptions(context, projectName, options, extensions) {
163
163
  route: 'shell',
164
164
  };
165
165
  }
166
- const outputPath = options.outputPath;
166
+ const outputPath = options.outputPath ?? node_path_1.default.join(workspaceRoot, 'dist', projectName);
167
167
  const outputOptions = {
168
168
  browser: 'browser',
169
169
  server: 'server',
@@ -128,7 +128,7 @@ export type Schema = {
128
128
  /**
129
129
  * Specify the output path relative to workspace root.
130
130
  */
131
- outputPath: OutputPathUnion;
131
+ outputPath?: OutputPathUnion;
132
132
  /**
133
133
  * Enable and define the file watching poll time period in milliseconds.
134
134
  */
@@ -513,6 +513,10 @@ export type SourceMapClass = {
513
513
  * Output source maps for all scripts.
514
514
  */
515
515
  scripts?: boolean;
516
+ /**
517
+ * Output original source content for files within the source map.
518
+ */
519
+ sourcesContent?: boolean;
516
520
  /**
517
521
  * Output source maps for all styles.
518
522
  */
@@ -370,6 +370,11 @@
370
370
  "type": "boolean",
371
371
  "description": "Resolve vendor packages source maps.",
372
372
  "default": false
373
+ },
374
+ "sourcesContent": {
375
+ "type": "boolean",
376
+ "description": "Output original source content for files within the source map.",
377
+ "default": true
373
378
  }
374
379
  },
375
380
  "additionalProperties": false
@@ -605,7 +610,7 @@
605
610
  }
606
611
  },
607
612
  "additionalProperties": false,
608
- "required": ["outputPath", "index", "browser", "tsConfig"],
613
+ "required": ["index", "browser", "tsConfig"],
609
614
  "definitions": {
610
615
  "assetPattern": {
611
616
  "oneOf": [
@@ -86,6 +86,7 @@ function createComponentStyleBundler(options, target) {
86
86
  // the same as being disabled. Disabling has the advantage of avoiding the overhead
87
87
  // of sourcemap processing.
88
88
  sourcemapOptions.styles && !sourcemapOptions.hidden ? 'linked' : false,
89
+ sourcesContent: sourcemapOptions.sourcesContent,
89
90
  outputNames,
90
91
  includePaths: stylePreprocessorOptions?.includePaths,
91
92
  // string[] | undefined' is not assignable to type '(Version | DeprecationOrId)[] | undefined'.
@@ -54,6 +54,7 @@ const results_1 = require("../application/results");
54
54
  const schema_1 = require("../application/schema");
55
55
  const find_tests_1 = require("./find-tests");
56
56
  const localResolve = (0, node_module_1.createRequire)(__filename).resolve;
57
+ const isWindows = process.platform === 'win32';
57
58
  class ApplicationBuildError extends Error {
58
59
  constructor(message) {
59
60
  super(message);
@@ -74,7 +75,13 @@ class AngularAssetsMiddleware {
74
75
  let err = null;
75
76
  try {
76
77
  const url = new URL(`http://${req.headers['host']}${req.url}`);
77
- const file = this.latestBuildFiles.files[url.pathname.slice(1)];
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];
78
85
  if (file?.origin === 'disk') {
79
86
  this.serveFile(file.inputPath, undefined, res);
80
87
  return;
@@ -350,7 +357,8 @@ async function initializeApplication(options, context, karmaOptions, transforms
350
357
  // Write test files
351
358
  await writeTestFiles(buildOutput.files, buildOptions.outputPath);
352
359
  // We need to add this to the beginning *after* the testing framework has
353
- // prepended its files.
360
+ // prepended its files. The output path is required for each since they are
361
+ // added later in the test process via a plugin.
354
362
  const polyfillsFile = {
355
363
  pattern: `${outputPath}/polyfills.js`,
356
364
  included: true,
@@ -365,28 +373,29 @@ async function initializeApplication(options, context, karmaOptions, transforms
365
373
  type: 'module',
366
374
  watched: false,
367
375
  };
376
+ karmaOptions.basePath = outputPath;
368
377
  karmaOptions.files ??= [];
369
378
  if (options.scripts?.length) {
370
379
  // This should be more granular to support named bundles.
371
380
  // However, it replicates the behavior of the Karma Webpack-based builder.
372
381
  karmaOptions.files.push({
373
- pattern: `${outputPath}/scripts.js`,
382
+ pattern: `scripts.js`,
374
383
  watched: false,
375
384
  type: 'js',
376
385
  });
377
386
  }
378
387
  karmaOptions.files.push(
379
388
  // Serve global setup script.
380
- { pattern: `${outputPath}/${mainName}.js`, type: 'module', watched: false },
389
+ { pattern: `${mainName}.js`, type: 'module', watched: false },
381
390
  // Serve all source maps.
382
- { pattern: `${outputPath}/*.map`, included: false, watched: false },
391
+ { pattern: `*.map`, included: false, watched: false },
383
392
  // These are the test entrypoints.
384
- { pattern: `${outputPath}/spec-*.js`, type: 'module', watched: false });
393
+ { pattern: `spec-*.js`, type: 'module', watched: false });
385
394
  if (hasChunkOrWorkerFiles(buildOutput.files)) {
386
395
  karmaOptions.files.push(
387
396
  // Allow loading of chunk-* files but don't include them all on load.
388
397
  {
389
- pattern: `${outputPath}/{chunk,worker}-*.js`,
398
+ pattern: `{chunk,worker}-*.js`,
390
399
  type: 'module',
391
400
  included: false,
392
401
  watched: false,
@@ -394,7 +403,7 @@ async function initializeApplication(options, context, karmaOptions, transforms
394
403
  }
395
404
  if (options.styles?.length) {
396
405
  // Serve CSS outputs on page load, these are the global styles.
397
- karmaOptions.files.push({ pattern: `${outputPath}/*.css`, type: 'css', watched: false });
406
+ karmaOptions.files.push({ pattern: `*.css`, type: 'css', watched: false });
398
407
  }
399
408
  const parsedKarmaConfig = await karma.config.parseConfig(options.karmaConfig && path.resolve(context.workspaceRoot, options.karmaConfig), transforms.karmaOptions ? transforms.karmaOptions(karmaOptions) : karmaOptions, { promiseConfig: true, throwErrors: true });
400
409
  // Remove the webpack plugin/framework:
@@ -7,13 +7,12 @@
7
7
  */
8
8
 
9
9
  import { getTestBed } from '@angular/core/testing';
10
- import {
11
- BrowserDynamicTestingModule,
12
- platformBrowserDynamicTesting,
13
- } from '@angular/platform-browser-dynamic/testing';
10
+ import { platformBrowser } from '@angular/platform-browser';
11
+ import { BrowserTestingModule } from '@angular/platform-browser/testing';
14
12
 
13
+ // TODO(alanagius): replace with `platformBrowserTesting` once https://github.com/angular/angular/pull/60480 is released.
15
14
  // Initialize the Angular testing environment.
16
- getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), {
15
+ getTestBed().initTestEnvironment(BrowserTestingModule, platformBrowser(), {
17
16
  errorOnUnknownElements: true,
18
17
  errorOnUnknownProperties: true,
19
18
  });
@@ -118,6 +118,9 @@ function createAngularCompilerHost(typescript, compilerOptions, hostOptions, pac
118
118
  };
119
119
  host.resourceNameToFileName = function (resourceName, containingFile) {
120
120
  const resolvedPath = node_path_1.default.join(node_path_1.default.dirname(containingFile), resourceName);
121
+ if (!this.fileExists(resolvedPath)) {
122
+ return null;
123
+ }
121
124
  // All resource names that have template file extensions are assumed to be templates
122
125
  // TODO: Update compiler to provide the resource type to avoid extension matching here.
123
126
  if (!hostOptions.externalStylesheets || hasTemplateExtension(resolvedPath)) {
@@ -416,6 +416,7 @@ function getEsBuildCommonOptions(options) {
416
416
  outdir: workspaceRoot,
417
417
  outExtension: outExtension ? { '.js': `.${outExtension}` } : undefined,
418
418
  sourcemap: sourcemapOptions.scripts && (sourcemapOptions.hidden ? 'external' : true),
419
+ sourcesContent: sourcemapOptions.sourcesContent,
419
420
  splitting: true,
420
421
  chunkNames: options.namedChunks ? '[name]-[hash]' : 'chunk-[hash]',
421
422
  tsconfig,
@@ -430,6 +431,7 @@ function getEsBuildCommonOptions(options) {
430
431
  ...(optimizationOptions.scripts ? { 'ngDevMode': 'false' } : undefined),
431
432
  'ngJitMode': jit ? 'true' : 'false',
432
433
  'ngServerMode': 'false',
434
+ 'ngHmrMode': options.templateUpdates ? 'true' : 'false',
433
435
  },
434
436
  loader: loaderExtensions,
435
437
  footer,
@@ -35,6 +35,7 @@ function createGlobalStylesBundleOptions(options, target, initial) {
35
35
  optimization: !!optimizationOptions.styles.minify,
36
36
  inlineFonts: !!optimizationOptions.fonts.inline,
37
37
  sourcemap: !!sourcemapOptions.styles && (sourcemapOptions.hidden ? 'external' : true),
38
+ sourcesContent: sourcemapOptions.sourcesContent,
38
39
  preserveSymlinks,
39
40
  target,
40
41
  externalDependencies,
@@ -16,6 +16,7 @@ export interface BundleStylesheetOptions {
16
16
  inlineFonts: boolean;
17
17
  preserveSymlinks?: boolean;
18
18
  sourcemap: boolean | 'external' | 'inline' | 'linked';
19
+ sourcesContent?: boolean;
19
20
  outputNames: {
20
21
  bundles: string;
21
22
  media: string;
@@ -47,6 +47,7 @@ function createStylesheetBundleOptions(options, cache, inlineComponentData) {
47
47
  minify: options.optimization,
48
48
  metafile: true,
49
49
  sourcemap: options.sourcemap,
50
+ sourcesContent: options.sourcesContent,
50
51
  outdir: options.workspaceRoot,
51
52
  write: false,
52
53
  platform: 'browser',
@@ -71,7 +71,8 @@ exports.SassStylesheetLanguage = Object.freeze({
71
71
  if (options.containingUrl) {
72
72
  resolveDir = (0, node_path_1.dirname)((0, node_url_1.fileURLToPath)(options.containingUrl));
73
73
  }
74
- const result = await build.resolve(url, {
74
+ const path = url.startsWith('pkg:') ? url.slice(4) : url;
75
+ const result = await build.resolve(path, {
75
76
  kind: 'import-rule',
76
77
  resolveDir,
77
78
  });
@@ -81,8 +82,8 @@ exports.SassStylesheetLanguage = Object.freeze({
81
82
  },
82
83
  });
83
84
  function parsePackageName(url) {
84
- const parts = url.split('/');
85
- const hasScope = parts.length >= 2 && parts[0].startsWith('@');
85
+ const parts = (url.startsWith('pkg:') ? url.slice(4) : url).split('/');
86
+ const hasScope = parts.length >= 2 && parts[0][0] === '@';
86
87
  const [nameOrScope, nameOrFirstPath, ...pathPart] = parts;
87
88
  const packageName = hasScope ? `${nameOrScope}/${nameOrFirstPath}` : nameOrScope;
88
89
  return {
@@ -315,7 +315,7 @@ function transformSupportedBrowsersToTargets(supportedBrowsers) {
315
315
  }
316
316
  return transformed;
317
317
  }
318
- const SUPPORTED_NODE_VERSIONS = '^20.11.1 || >=22.0.0';
318
+ const SUPPORTED_NODE_VERSIONS = '^20.11.1 || >=22.11.0';
319
319
  /**
320
320
  * Transform supported Node.js versions to esbuild target.
321
321
  * @see https://esbuild.github.io/api/#target
@@ -22,11 +22,12 @@ function createAngularSsrInternalMiddleware(server, indexHtmlTransformer) {
22
22
  // which must be processed by the runtime linker, even if they are not used.
23
23
  await (0, load_esm_1.loadEsmModule)('@angular/compiler');
24
24
  const { writeResponseToNodeResponse, createWebRequestFromNodeRequest } = await (0, load_esm_1.loadEsmModule)('@angular/ssr/node');
25
- // The following is necessary because accessing the module after invalidation may result in an empty module,
26
- // which can trigger a `TypeError: ɵgetOrCreateAngularServerApp is not a function` error.
27
- // TODO: look into why.
28
- await server.ssrLoadModule('/main.server.mjs');
29
25
  const { ɵgetOrCreateAngularServerApp } = (await server.ssrLoadModule('/main.server.mjs'));
26
+ // `ɵgetOrCreateAngularServerApp` can be undefined right after an error.
27
+ // See: https://github.com/angular/angular-cli/issues/29907
28
+ if (!ɵgetOrCreateAngularServerApp) {
29
+ return next();
30
+ }
30
31
  const angularServerApp = ɵgetOrCreateAngularServerApp({
31
32
  allowStaticRouteRender: true,
32
33
  });
@@ -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.1';
13
+ const VERSION = '20.0.0-next.3';
14
14
  function hasCacheMetadata(value) {
15
15
  return (!!value &&
16
16
  typeof value === 'object' &&
@@ -13,10 +13,12 @@ function normalizeSourceMaps(sourceMap) {
13
13
  const styles = typeof sourceMap === 'object' ? sourceMap.styles : sourceMap;
14
14
  const hidden = (typeof sourceMap === 'object' && sourceMap.hidden) || false;
15
15
  const vendor = (typeof sourceMap === 'object' && sourceMap.vendor) || false;
16
+ const sourcesContent = typeof sourceMap === 'object' ? sourceMap.sourcesContent : sourceMap;
16
17
  return {
17
18
  vendor,
18
19
  hidden,
19
20
  scripts,
20
21
  styles,
22
+ sourcesContent,
21
23
  };
22
24
  }
@@ -34,7 +34,7 @@ async function launchServer() {
34
34
  // handle request
35
35
  if ((0, utils_1.isSsrNodeRequestHandler)(reqHandler)) {
36
36
  await reqHandler(req, res, (e) => {
37
- throw e;
37
+ throw e ?? new Error(`Unable to handle request: '${req.url}'.`);
38
38
  });
39
39
  }
40
40
  else {
@@ -54,7 +54,7 @@ async function prerenderPages(workspaceRoot, baseHref, appShellOptions, prerende
54
54
  // Get routes to prerender
55
55
  const { errors: extractionErrors, serializedRouteTree: serializableRouteTreeNode, appShellRoute, } = await getAllRoutes(workspaceRoot, baseHref, outputFilesForWorker, assetsReversed, appShellOptions, prerenderOptions, sourcemap, outputMode).catch((err) => {
56
56
  return {
57
- errors: [`An error occurred while extracting routes.\n\n${err.stack ?? err.message ?? err}`],
57
+ errors: [`An error occurred while extracting routes.\n\n${err.message ?? err.stack ?? err}`],
58
58
  serializedRouteTree: [],
59
59
  appShellRoute: undefined,
60
60
  };
@@ -150,7 +150,7 @@ async function renderPages(baseHref, sourcemap, serializableRouteTreeNode, maxTh
150
150
  }
151
151
  })
152
152
  .catch((err) => {
153
- errors.push(`An error occurred while prerendering route '${route}'.\n\n${err.stack ?? err.message ?? err.code ?? err}`);
153
+ errors.push(`An error occurred while prerendering route '${route}'.\n\n${err.message ?? err.stack ?? err.code ?? err}`);
154
154
  void renderWorker.destroy();
155
155
  });
156
156
  renderingPromises.push(renderResult);
@@ -222,7 +222,7 @@ async function getAllRoutes(workspaceRoot, baseHref, outputFilesForWorker, asset
222
222
  (0, error_1.assertIsError)(err);
223
223
  return {
224
224
  errors: [
225
- `An error occurred while extracting routes.\n\n${err.stack ?? err.message ?? err.code ?? err}`,
225
+ `An error occurred while extracting routes.\n\n${err.message ?? err.stack ?? err.code ?? err}`,
226
226
  ],
227
227
  serializedRouteTree: [],
228
228
  };