@angular/build 19.2.0-next.0 → 19.2.0-next.1

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/README.md CHANGED
@@ -2,4 +2,4 @@
2
2
 
3
3
  The sources for this package are in the [Angular CLI](https://github.com/angular/angular-cli) repository. Please file issues and pull requests against that repository.
4
4
 
5
- Usage information and reference details can be found in repository [README](../../../README.md) file.
5
+ Usage information and reference details can be found in repository [README](https://github.com/angular/angular-cli/blob/main/README.md) file.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@angular/build",
3
- "version": "19.2.0-next.0",
3
+ "version": "19.2.0-next.1",
4
4
  "description": "Official build system for Angular",
5
5
  "keywords": [
6
6
  "Angular CLI",
@@ -23,13 +23,12 @@
23
23
  "builders": "builders.json",
24
24
  "dependencies": {
25
25
  "@ampproject/remapping": "2.3.0",
26
- "@angular-devkit/core": "19.2.0-next.0",
27
- "@angular-devkit/architect": "0.1902.0-next.0",
28
- "@babel/core": "7.26.0",
26
+ "@angular-devkit/architect": "0.1902.0-next.1",
27
+ "@babel/core": "7.26.7",
29
28
  "@babel/helper-annotate-as-pure": "7.25.9",
30
29
  "@babel/helper-split-export-declaration": "7.24.7",
31
30
  "@babel/plugin-syntax-import-attributes": "7.26.0",
32
- "@inquirer/confirm": "5.1.3",
31
+ "@inquirer/confirm": "5.1.4",
33
32
  "@vitejs/plugin-basic-ssl": "1.2.0",
34
33
  "beasties": "0.2.0",
35
34
  "browserslist": "^4.23.0",
@@ -43,7 +42,7 @@
43
42
  "parse5-html-rewriting-stream": "7.0.0",
44
43
  "picomatch": "4.0.2",
45
44
  "piscina": "4.8.0",
46
- "rollup": "4.31.0",
45
+ "rollup": "4.32.1",
47
46
  "sass": "1.83.4",
48
47
  "semver": "7.6.3",
49
48
  "vite": "6.0.11",
@@ -58,11 +57,11 @@
58
57
  "@angular/localize": "^19.0.0 || ^19.2.0-next.0",
59
58
  "@angular/platform-server": "^19.0.0 || ^19.2.0-next.0",
60
59
  "@angular/service-worker": "^19.0.0 || ^19.2.0-next.0",
61
- "@angular/ssr": "^19.2.0-next.0",
60
+ "@angular/ssr": "^19.2.0-next.1",
62
61
  "less": "^4.2.0",
63
62
  "ng-packagr": "^19.0.0 || ^19.2.0-next.0",
64
63
  "postcss": "^8.4.0",
65
- "tailwindcss": "^2.0.0 || ^3.0.0",
64
+ "tailwindcss": "^2.0.0 || ^3.0.0 || ^4.0.0",
66
65
  "typescript": ">=5.5 <5.8"
67
66
  },
68
67
  "peerDependenciesMeta": {
@@ -239,6 +239,7 @@ function* emitOutputResults({ outputFiles, assetFiles, errors, warnings, externa
239
239
  outputOptions,
240
240
  },
241
241
  };
242
+ let hasCssUpdates = false;
242
243
  // Initially assume all previous output files have been removed
243
244
  const removedOutputFiles = new Map(previousOutputInfo);
244
245
  for (const file of outputFiles) {
@@ -254,8 +255,11 @@ function* emitOutputResults({ outputFiles, assetFiles, errors, warnings, externa
254
255
  incrementalResult.modified.push(file.path);
255
256
  }
256
257
  if (needFile) {
257
- // Updates to non-JS files must signal an update with the dev server
258
- if (!/(?:\.m?js|\.map)$/.test(file.path)) {
258
+ if (file.path.endsWith('.css')) {
259
+ hasCssUpdates = true;
260
+ }
261
+ else if (!/(?:\.m?js|\.map)$/.test(file.path)) {
262
+ // Updates to non-JS files must signal an update with the dev server
259
263
  incrementalResult.background = false;
260
264
  }
261
265
  incrementalResult.files[file.path] = {
@@ -281,12 +285,21 @@ function* emitOutputResults({ outputFiles, assetFiles, errors, warnings, externa
281
285
  else {
282
286
  continue;
283
287
  }
288
+ hasCssUpdates ||= destination.endsWith('.css');
284
289
  incrementalResult.files[destination] = {
285
290
  type: bundler_context_1.BuildOutputFileType.Browser,
286
291
  inputPath: source,
287
292
  origin: 'disk',
288
293
  };
289
294
  }
295
+ // Do not remove stale files yet if there are template updates.
296
+ // Component chunk files may still be referenced in running browser code.
297
+ // Module evaluation time component updates will update any of these files.
298
+ // This typically occurs when a lazy component is changed that has not yet
299
+ // been accessed at runtime.
300
+ if (hasTemplateUpdates && incrementalResult.background) {
301
+ removedOutputFiles.clear();
302
+ }
290
303
  // Include the removed output and asset files
291
304
  incrementalResult.removed.push(...Array.from(removedOutputFiles, ([file, { type }]) => ({
292
305
  path: file,
@@ -299,6 +312,17 @@ function* emitOutputResults({ outputFiles, assetFiles, errors, warnings, externa
299
312
  // If there are template updates and the incremental update was background only, a component
300
313
  // update is possible.
301
314
  if (hasTemplateUpdates && incrementalResult.background) {
315
+ // Template changes may be accompanied by stylesheet changes and these should also be updated hot when possible.
316
+ if (hasCssUpdates) {
317
+ const styleResult = {
318
+ kind: results_1.ResultKind.Incremental,
319
+ added: incrementalResult.added.filter(isCssFilePath),
320
+ removed: incrementalResult.removed.filter(({ path }) => isCssFilePath(path)),
321
+ modified: incrementalResult.modified.filter(isCssFilePath),
322
+ files: Object.fromEntries(Object.entries(incrementalResult.files).filter(([path]) => isCssFilePath(path))),
323
+ };
324
+ yield styleResult;
325
+ }
302
326
  const updateResult = {
303
327
  kind: results_1.ResultKind.ComponentUpdate,
304
328
  updates: Array.from(templateUpdates, ([id, content]) => ({
@@ -310,3 +334,6 @@ function* emitOutputResults({ outputFiles, assetFiles, errors, warnings, externa
310
334
  yield updateResult;
311
335
  }
312
336
  }
337
+ function isCssFilePath(filePath) {
338
+ return /\.css(?:\.map)?$/i.test(filePath);
339
+ }
@@ -44,4 +44,5 @@ export declare function normalizeOptions(context: BuilderContext, projectName: s
44
44
  host?: string;
45
45
  port?: number;
46
46
  };
47
+ allowedHosts: true | string[];
47
48
  }>;
@@ -73,7 +73,7 @@ async function normalizeOptions(context, projectName, options) {
73
73
  }
74
74
  }
75
75
  // Initial options to keep
76
- const { host, port, poll, open, verbose, watch, liveReload, hmr, headers, proxyConfig, servePath, ssl, sslCert, sslKey, prebundle, } = options;
76
+ const { host, port, poll, open, verbose, watch, liveReload, hmr, headers, proxyConfig, servePath, ssl, sslCert, sslKey, prebundle, allowedHosts, } = options;
77
77
  // Return all the normalized options
78
78
  return {
79
79
  buildTarget,
@@ -97,5 +97,6 @@ async function normalizeOptions(context, projectName, options) {
97
97
  // Prebundling defaults to true but requires caching to function
98
98
  prebundle: cacheOptions.enabled && !optimization.scripts && prebundle,
99
99
  inspect,
100
+ allowedHosts: allowedHosts ? allowedHosts : [],
100
101
  };
101
102
  }
@@ -2,6 +2,12 @@
2
2
  * Dev Server target options for Build Facade.
3
3
  */
4
4
  export type Schema = {
5
+ /**
6
+ * The hosts that can access the development server. This option sets the Vite option of the
7
+ * same name. For further details:
8
+ * https://vite.dev/config/server-options.html#server-allowedhosts
9
+ */
10
+ allowedHosts?: AllowedHosts;
5
11
  /**
6
12
  * A build builder target to serve in the format of `project:target[:configuration]`. You
7
13
  * can also pass in more than one configuration name as a comma-separated list. Example:
@@ -79,6 +85,12 @@ export type Schema = {
79
85
  */
80
86
  watch?: boolean;
81
87
  };
88
+ /**
89
+ * The hosts that can access the development server. This option sets the Vite option of the
90
+ * same name. For further details:
91
+ * https://vite.dev/config/server-options.html#server-allowedhosts
92
+ */
93
+ export type AllowedHosts = string[] | boolean;
82
94
  /**
83
95
  * Activate debugging inspector. This option only has an effect when 'SSR' or 'SSG' are
84
96
  * enabled.
@@ -36,6 +36,23 @@
36
36
  "type": "string",
37
37
  "description": "SSL certificate to use for serving HTTPS."
38
38
  },
39
+ "allowedHosts": {
40
+ "description": "The hosts that can access the development server. This option sets the Vite option of the same name. For further details: https://vite.dev/config/server-options.html#server-allowedhosts",
41
+ "default": [],
42
+ "oneOf": [
43
+ {
44
+ "type": "array",
45
+ "description": "List of hosts that are allowed to access the development server.",
46
+ "items": {
47
+ "type": "string"
48
+ }
49
+ },
50
+ {
51
+ "type": "boolean",
52
+ "description": "Indicates that all hosts are allowed. This is not recommended and a security risk."
53
+ }
54
+ ]
55
+ },
39
56
  "headers": {
40
57
  "type": "object",
41
58
  "description": "Custom HTTP headers to be added to all responses.",
@@ -304,6 +304,23 @@ async function* serveWithVite(serverOptions, builderName, builderAction, context
304
304
  const serverConfiguration = await setupServer(serverOptions, generatedFiles, assetFiles, browserOptions.preserveSymlinks, externalMetadata, ssrMode, prebundleTransformer, target, (0, internal_1.isZonelessApp)(polyfills), componentStyles, templateUpdates, browserOptions.loader, browserOptions.define, extensions?.middleware, transformers?.indexHtml, thirdPartySourcemaps);
305
305
  server = await createServer(serverConfiguration);
306
306
  await server.listen();
307
+ // Setup builder context logging for browser clients
308
+ server.hot.on('angular:log', (data) => {
309
+ if (typeof data?.text !== 'string') {
310
+ context.logger.warn('Development server client sent invalid internal log event.');
311
+ }
312
+ switch (data.kind) {
313
+ case 'error':
314
+ context.logger.error(`[CLIENT ERROR]: ${data.text}`);
315
+ break;
316
+ case 'warning':
317
+ context.logger.warn(`[CLIENT WARNING]: ${data.text}`);
318
+ break;
319
+ default:
320
+ context.logger.info(`[CLIENT INFO]: ${data.text}`);
321
+ break;
322
+ }
323
+ });
307
324
  const urls = server.resolvedUrls;
308
325
  if (urls && (urls.local.length || urls.network.length)) {
309
326
  serverUrl = new URL(urls.local[0] ?? urls.network[0]);
@@ -420,7 +437,7 @@ function handleUpdate(server, serverOptions, logger, componentStyles, updatedFil
420
437
  type: 'update',
421
438
  updates,
422
439
  });
423
- logger.info('HMR update sent to client(s).');
440
+ logger.info('Stylesheet update sent to client(s).');
424
441
  return;
425
442
  }
426
443
  }
@@ -541,6 +558,7 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
541
558
  strictPort: true,
542
559
  host: serverOptions.host,
543
560
  open: serverOptions.open,
561
+ allowedHosts: serverOptions.allowedHosts,
544
562
  headers: serverOptions.headers,
545
563
  // Disable the websocket if live reload is disabled (false/undefined are the only valid values)
546
564
  ws: serverOptions.liveReload === false && serverOptions.hmr === false ? false : undefined,
@@ -80,6 +80,9 @@ class AngularCompilation {
80
80
  enableResourceInlining: false,
81
81
  supportTestBed: false,
82
82
  supportJitMode: false,
83
+ // Disable removing of comments as TS is quite aggressive with these and can
84
+ // remove important annotations, such as /* @__PURE__ */ and comments like /* vite-ignore */.
85
+ removeComments: false,
83
86
  }));
84
87
  }
85
88
  async diagnoseFiles(modes = DiagnosticModes.All) {
@@ -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.2.0-next.0';
13
+ const VERSION = '19.2.0-next.1';
14
14
  function hasCacheMetadata(value) {
15
15
  return (!!value &&
16
16
  typeof value === 'object' &&
@@ -9,7 +9,7 @@ import type { Config, Filesystem } from '@angular/service-worker/config';
9
9
  import { promises as fsPromises } from 'node:fs';
10
10
  import { BuildOutputFile } from '../tools/esbuild/bundler-context';
11
11
  import { BuildOutputAsset } from '../tools/esbuild/bundler-execution-result';
12
- export declare function augmentAppWithServiceWorker(appRoot: string, workspaceRoot: string, outputPath: string, baseHref: string, ngswConfigPath?: string, inputputFileSystem?: typeof fsPromises, outputFileSystem?: typeof fsPromises): Promise<void>;
12
+ export declare function augmentAppWithServiceWorker(appRoot: string, workspaceRoot: string, outputPath: string, baseHref: string, ngswConfigPath?: string, inputFileSystem?: typeof fsPromises, outputFileSystem?: typeof fsPromises): Promise<void>;
13
13
  export declare function augmentAppWithServiceWorkerEsbuild(workspaceRoot: string, configPath: string, baseHref: string, indexHtml: string | undefined, outputFiles: BuildOutputFile[], assetFiles: BuildOutputAsset[]): Promise<{
14
14
  manifest: string;
15
15
  assetFiles: BuildOutputAsset[];
@@ -133,7 +133,7 @@ class ResultFilesystem {
133
133
  throw new Error('Serviceworker manifest generator should not attempted to write.');
134
134
  }
135
135
  }
136
- async function augmentAppWithServiceWorker(appRoot, workspaceRoot, outputPath, baseHref, ngswConfigPath, inputputFileSystem = node_fs_1.promises, outputFileSystem = node_fs_1.promises) {
136
+ async function augmentAppWithServiceWorker(appRoot, workspaceRoot, outputPath, baseHref, ngswConfigPath, inputFileSystem = node_fs_1.promises, outputFileSystem = node_fs_1.promises) {
137
137
  // Determine the configuration file path
138
138
  const configPath = ngswConfigPath
139
139
  ? path.join(workspaceRoot, ngswConfigPath)
@@ -141,7 +141,7 @@ async function augmentAppWithServiceWorker(appRoot, workspaceRoot, outputPath, b
141
141
  // Read the configuration file
142
142
  let config;
143
143
  try {
144
- const configurationData = await inputputFileSystem.readFile(configPath, 'utf-8');
144
+ const configurationData = await inputFileSystem.readFile(configPath, 'utf-8');
145
145
  config = JSON.parse(configurationData);
146
146
  }
147
147
  catch (error) {
@@ -158,11 +158,7 @@ async function augmentAppWithServiceWorker(appRoot, workspaceRoot, outputPath, b
158
158
  const result = await augmentAppWithServiceWorkerCore(config, new CliFilesystem(outputFileSystem, outputPath), baseHref);
159
159
  const copy = async (src, dest) => {
160
160
  const resolvedDest = path.join(outputPath, dest);
161
- return inputputFileSystem === outputFileSystem
162
- ? // Native FS (Builder).
163
- inputputFileSystem.copyFile(src, resolvedDest, node_fs_1.constants.COPYFILE_FICLONE)
164
- : // memfs (Webpack): Read the file from the input FS (disk) and write it to the output FS (memory).
165
- outputFileSystem.writeFile(resolvedDest, await inputputFileSystem.readFile(src));
161
+ return outputFileSystem.writeFile(resolvedDest, await inputFileSystem.readFile(src));
166
162
  };
167
163
  await outputFileSystem.writeFile(path.join(outputPath, 'ngsw.json'), result.manifest);
168
164
  for (const { source, destination } of result.assetFiles) {
@@ -12,52 +12,31 @@ exports.assertCompatibleAngularVersion = assertCompatibleAngularVersion;
12
12
  const node_module_1 = require("node:module");
13
13
  const semver_1 = require("semver");
14
14
  function assertCompatibleAngularVersion(projectRoot) {
15
- let angularCliPkgJson;
16
15
  let angularPkgJson;
17
16
  // Create a custom require function for ESM compliance.
18
17
  // NOTE: The trailing slash is significant.
19
18
  const projectRequire = (0, node_module_1.createRequire)(projectRoot + '/');
20
19
  try {
21
- const angularPackagePath = projectRequire.resolve('@angular/core/package.json');
22
- angularPkgJson = projectRequire(angularPackagePath);
20
+ angularPkgJson = projectRequire('@angular/core/package.json');
23
21
  }
24
22
  catch {
25
- console.error('You seem to not be depending on "@angular/core". This is an error.');
23
+ console.error('Error: It appears that "@angular/core" is missing as a dependency. Please ensure it is included in your project.');
26
24
  process.exit(2);
27
25
  }
28
- if (!(angularPkgJson && angularPkgJson['version'])) {
29
- console.error('Cannot determine versions of "@angular/core".\n' +
30
- 'This likely means your local installation is broken. Please reinstall your packages.');
26
+ if (!angularPkgJson?.['version']) {
27
+ console.error('Error: Unable to determine the versions of "@angular/core".\n' +
28
+ 'This likely indicates a corrupted local installation. Please try reinstalling your packages.');
31
29
  process.exit(2);
32
30
  }
33
- try {
34
- const angularCliPkgPath = projectRequire.resolve('@angular/cli/package.json');
35
- angularCliPkgJson = projectRequire(angularCliPkgPath);
36
- if (!(angularCliPkgJson && angularCliPkgJson['version'])) {
37
- return;
38
- }
39
- }
40
- catch {
41
- // Not using @angular-devkit/build-angular with @angular/cli is ok too.
42
- // In this case we don't provide as many version checks.
43
- return;
44
- }
45
- if (angularCliPkgJson['version'] === '0.0.0' || angularPkgJson['version'] === '0.0.0') {
46
- // Internal CLI testing version or integration testing in the angular/angular
47
- // repository with the generated development @angular/core npm package which is versioned "0.0.0".
31
+ const supportedAngularSemver = '^19.0.0 || ^19.2.0-next.0';
32
+ if (supportedAngularSemver.startsWith('0.0.0')) {
33
+ // Internal CLI testing version.
48
34
  return;
49
35
  }
50
- let supportedAngularSemver;
51
- try {
52
- supportedAngularSemver = projectRequire('@angular/build/package.json')['peerDependencies']['@angular/compiler-cli'];
53
- }
54
- catch {
55
- supportedAngularSemver = projectRequire('@angular-devkit/build-angular/package.json')['peerDependencies']['@angular/compiler-cli'];
56
- }
57
36
  const angularVersion = new semver_1.SemVer(angularPkgJson['version']);
58
37
  if (!(0, semver_1.satisfies)(angularVersion, supportedAngularSemver, { includePrerelease: true })) {
59
- console.error(`This version of CLI is only compatible with Angular versions ${supportedAngularSemver},\n` +
60
- `but Angular version ${angularVersion} was found instead.\n` +
38
+ console.error(`Error: The current version of "@angular/build" supports Angular versions ${supportedAngularSemver},\n` +
39
+ `but detected Angular version ${angularVersion} instead.\n` +
61
40
  'Please visit the link below to find instructions on how to update Angular.\nhttps://update.angular.dev/');
62
41
  process.exit(3);
63
42
  }