@angular/build 19.1.3 → 19.1.5

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.1.3",
3
+ "version": "19.1.5",
4
4
  "description": "Official build system for Angular",
5
5
  "keywords": [
6
6
  "Angular CLI",
@@ -23,7 +23,8 @@
23
23
  "builders": "builders.json",
24
24
  "dependencies": {
25
25
  "@ampproject/remapping": "2.3.0",
26
- "@angular-devkit/architect": "0.1901.3",
26
+ "@angular-devkit/core": "19.1.5",
27
+ "@angular-devkit/architect": "0.1901.5",
27
28
  "@babel/core": "7.26.0",
28
29
  "@babel/helper-annotate-as-pure": "7.25.9",
29
30
  "@babel/helper-split-export-declaration": "7.24.7",
@@ -45,7 +46,7 @@
45
46
  "rollup": "4.30.1",
46
47
  "sass": "1.83.1",
47
48
  "semver": "7.6.3",
48
- "vite": "6.0.7",
49
+ "vite": "6.0.11",
49
50
  "watchpack": "2.4.2"
50
51
  },
51
52
  "optionalDependencies": {
@@ -57,11 +58,11 @@
57
58
  "@angular/localize": "^19.0.0",
58
59
  "@angular/platform-server": "^19.0.0",
59
60
  "@angular/service-worker": "^19.0.0",
60
- "@angular/ssr": "^19.1.3",
61
+ "@angular/ssr": "^19.1.5",
61
62
  "less": "^4.2.0",
62
63
  "ng-packagr": "^19.0.0",
63
64
  "postcss": "^8.4.0",
64
- "tailwindcss": "^2.0.0 || ^3.0.0",
65
+ "tailwindcss": "^2.0.0 || ^3.0.0 || ^4.0.0",
65
66
  "typescript": ">=5.5 <5.8"
66
67
  },
67
68
  "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
+ }
@@ -6,7 +6,6 @@
6
6
  * found in the LICENSE file at https://angular.dev/license
7
7
  */
8
8
  import { Builder, BuilderContext, BuilderOutput } from '@angular-devkit/architect';
9
- import { json } from '@angular-devkit/core';
10
9
  import { ApplicationBuilderExtensions, ApplicationBuilderInternalOptions } from './options';
11
10
  import { Result } from './results';
12
11
  import { Schema as ApplicationBuilderOptions } from './schema';
@@ -29,5 +28,5 @@ export declare function buildApplicationInternal(options: ApplicationBuilderInte
29
28
  * @returns The build output results of the build.
30
29
  */
31
30
  export declare function buildApplication(options: ApplicationBuilderOptions, context: BuilderContext, extensions?: ApplicationBuilderExtensions): AsyncIterable<BuilderOutput>;
32
- declare const builder: Builder<ApplicationBuilderOptions & json.JsonObject>;
31
+ declare const builder: Builder<ApplicationBuilderOptions>;
33
32
  export default builder;
@@ -63,7 +63,7 @@ async function normalizeOptions(context, projectName, options, extensions) {
63
63
  // Gather persistent caching option and provide a project specific cache location
64
64
  const cacheOptions = (0, normalize_cache_1.normalizeCacheOptions)(projectMetadata, workspaceRoot);
65
65
  cacheOptions.path = node_path_1.default.join(cacheOptions.path, projectName);
66
- const i18nOptions = (0, i18n_options_1.createI18nOptions)(projectMetadata, options.localize, context.logger);
66
+ const i18nOptions = (0, i18n_options_1.createI18nOptions)(projectMetadata, options.localize, context.logger, !!options.ssr);
67
67
  i18nOptions.duplicateTranslationBehavior = options.i18nDuplicateTranslation;
68
68
  i18nOptions.missingTranslationBehavior = options.i18nMissingTranslation;
69
69
  if (options.forceI18nFlatOutput) {
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Application builder target options
3
3
  */
4
- export interface Schema {
4
+ export type Schema = {
5
5
  /**
6
6
  * A list of CommonJS or AMD packages that are allowed to be used without a build time
7
7
  * warning. Use `'*'` to allow all.
@@ -208,9 +208,9 @@ export interface Schema {
208
208
  * TypeScript configuration for Web Worker modules.
209
209
  */
210
210
  webWorkerTsConfig?: string;
211
- }
211
+ };
212
212
  export type AssetPattern = AssetPatternClass | string;
213
- export interface AssetPatternClass {
213
+ export type AssetPatternClass = {
214
214
  /**
215
215
  * Allow glob patterns to follow symlink directories. This allows subdirectories of the
216
216
  * symlink to be searched.
@@ -232,8 +232,8 @@ export interface AssetPatternClass {
232
232
  * Absolute path within the output.
233
233
  */
234
234
  output?: string;
235
- }
236
- export interface Budget {
235
+ };
236
+ export type Budget = {
237
237
  /**
238
238
  * The baseline size for comparison.
239
239
  */
@@ -270,7 +270,7 @@ export interface Budget {
270
270
  * The threshold for warning relative to the baseline (min & max).
271
271
  */
272
272
  warning?: string;
273
- }
273
+ };
274
274
  /**
275
275
  * The type of budget.
276
276
  */
@@ -291,10 +291,10 @@ export declare enum CrossOrigin {
291
291
  None = "none",
292
292
  UseCredentials = "use-credentials"
293
293
  }
294
- export interface FileReplacement {
294
+ export type FileReplacement = {
295
295
  replace: string;
296
296
  with: string;
297
- }
297
+ };
298
298
  /**
299
299
  * How to handle duplicate translations for i18n.
300
300
  *
@@ -309,7 +309,7 @@ export declare enum I18NTranslation {
309
309
  * Configures the generation of the application's HTML index.
310
310
  */
311
311
  export type IndexUnion = boolean | IndexObject | string;
312
- export interface IndexObject {
312
+ export type IndexObject = {
313
313
  /**
314
314
  * The path of a file to use for the application's generated HTML index.
315
315
  */
@@ -325,7 +325,7 @@ export interface IndexObject {
325
325
  */
326
326
  preloadInitial?: boolean;
327
327
  [property: string]: any;
328
- }
328
+ };
329
329
  /**
330
330
  * The stylesheet language to use for the application's inline component styles.
331
331
  */
@@ -346,7 +346,7 @@ export type Localize = string[] | boolean;
346
346
  * https://angular.dev/reference/configs/workspace-config#optimization-configuration.
347
347
  */
348
348
  export type OptimizationUnion = boolean | OptimizationClass;
349
- export interface OptimizationClass {
349
+ export type OptimizationClass = {
350
350
  /**
351
351
  * Enables optimization for fonts. This option requires internet access. `HTTPS_PROXY`
352
352
  * environment variable can be used to specify a proxy server.
@@ -360,25 +360,25 @@ export interface OptimizationClass {
360
360
  * Enables optimization of the styles output.
361
361
  */
362
362
  styles?: StylesUnion;
363
- }
363
+ };
364
364
  /**
365
365
  * Enables optimization for fonts. This option requires internet access. `HTTPS_PROXY`
366
366
  * environment variable can be used to specify a proxy server.
367
367
  */
368
368
  export type FontsUnion = boolean | FontsClass;
369
- export interface FontsClass {
369
+ export type FontsClass = {
370
370
  /**
371
371
  * Reduce render blocking requests by inlining external Google Fonts and Adobe Fonts CSS
372
372
  * definitions in the application's HTML index file. This option requires internet access.
373
373
  * `HTTPS_PROXY` environment variable can be used to specify a proxy server.
374
374
  */
375
375
  inline?: boolean;
376
- }
376
+ };
377
377
  /**
378
378
  * Enables optimization of the styles output.
379
379
  */
380
380
  export type StylesUnion = boolean | StylesClass;
381
- export interface StylesClass {
381
+ export type StylesClass = {
382
382
  /**
383
383
  * Extract and inline critical CSS definitions to improve first paint time.
384
384
  */
@@ -393,7 +393,7 @@ export interface StylesClass {
393
393
  * '//!' or '/*!'.
394
394
  */
395
395
  removeSpecialComments?: boolean;
396
- }
396
+ };
397
397
  /**
398
398
  * Define the output filename cache-busting hashing mode.
399
399
  */
@@ -416,7 +416,7 @@ export declare enum OutputMode {
416
416
  * Specify the output path relative to workspace root.
417
417
  */
418
418
  export type OutputPathUnion = OutputPathClass | string;
419
- export interface OutputPathClass {
419
+ export type OutputPathClass = {
420
420
  /**
421
421
  * Specify the output path relative to workspace root.
422
422
  */
@@ -436,12 +436,12 @@ export interface OutputPathClass {
436
436
  * 'server'.
437
437
  */
438
438
  server?: string;
439
- }
439
+ };
440
440
  /**
441
441
  * Prerender (SSG) pages of your application during build time.
442
442
  */
443
443
  export type PrerenderUnion = boolean | PrerenderClass;
444
- export interface PrerenderClass {
444
+ export type PrerenderClass = {
445
445
  /**
446
446
  * Whether the builder should process the Angular Router configuration to find all
447
447
  * unparameterized routes and prerender them.
@@ -452,9 +452,9 @@ export interface PrerenderClass {
452
452
  * newlines. This option is useful if you want to prerender routes with parameterized URLs.
453
453
  */
454
454
  routesFile?: string;
455
- }
455
+ };
456
456
  export type ScriptElement = ScriptClass | string;
457
- export interface ScriptClass {
457
+ export type ScriptClass = {
458
458
  /**
459
459
  * The bundle name for this extra entry point.
460
460
  */
@@ -467,32 +467,32 @@ export interface ScriptClass {
467
467
  * The file to include.
468
468
  */
469
469
  input: string;
470
- }
470
+ };
471
471
  /**
472
472
  * Security features to protect against XSS and other common attacks
473
473
  */
474
- export interface Security {
474
+ export type Security = {
475
475
  /**
476
476
  * Enables automatic generation of a hash-based Strict Content Security Policy
477
477
  * (https://web.dev/articles/strict-csp#choose-hash) based on scripts in index.html. Will
478
478
  * default to true once we are out of experimental/preview phases.
479
479
  */
480
480
  autoCsp?: AutoCspUnion;
481
- }
481
+ };
482
482
  /**
483
483
  * Enables automatic generation of a hash-based Strict Content Security Policy
484
484
  * (https://web.dev/articles/strict-csp#choose-hash) based on scripts in index.html. Will
485
485
  * default to true once we are out of experimental/preview phases.
486
486
  */
487
487
  export type AutoCspUnion = boolean | AutoCspClass;
488
- export interface AutoCspClass {
488
+ export type AutoCspClass = {
489
489
  /**
490
490
  * Include the `unsafe-eval` directive (https://web.dev/articles/strict-csp#remove-eval) in
491
491
  * the auto-CSP. Please only enable this if you are absolutely sure that you need to, as
492
492
  * allowing calls to eval will weaken the XSS defenses provided by the auto-CSP.
493
493
  */
494
494
  unsafeEval?: boolean;
495
- }
495
+ };
496
496
  /**
497
497
  * Generates a service worker configuration.
498
498
  */
@@ -502,7 +502,7 @@ export type ServiceWorker = boolean | string;
502
502
  * https://angular.dev/reference/configs/workspace-config#source-map-configuration.
503
503
  */
504
504
  export type SourceMapUnion = boolean | SourceMapClass;
505
- export interface SourceMapClass {
505
+ export type SourceMapClass = {
506
506
  /**
507
507
  * Output source maps used for error reporting tools.
508
508
  */
@@ -519,12 +519,12 @@ export interface SourceMapClass {
519
519
  * Resolve vendor packages source maps.
520
520
  */
521
521
  vendor?: boolean;
522
- }
522
+ };
523
523
  /**
524
524
  * Server side render (SSR) pages of your application during runtime.
525
525
  */
526
526
  export type SsrUnion = boolean | SsrClass;
527
- export interface SsrClass {
527
+ export type SsrClass = {
528
528
  /**
529
529
  * The server entry-point that when executed will spawn the web server.
530
530
  */
@@ -543,7 +543,7 @@ export interface SsrClass {
543
543
  * versions.
544
544
  */
545
545
  experimentalPlatform?: ExperimentalPlatform;
546
- }
546
+ };
547
547
  /**
548
548
  * Specifies the platform for which the server bundle is generated. This affects the APIs
549
549
  * and modules available in the server-side code.
@@ -564,7 +564,7 @@ export declare enum ExperimentalPlatform {
564
564
  /**
565
565
  * Options to pass to style preprocessors.
566
566
  */
567
- export interface StylePreprocessorOptions {
567
+ export type StylePreprocessorOptions = {
568
568
  /**
569
569
  * Paths to include. Paths will be resolved to workspace root.
570
570
  */
@@ -573,11 +573,11 @@ export interface StylePreprocessorOptions {
573
573
  * Options to pass to the sass preprocessor.
574
574
  */
575
575
  sass?: Sass;
576
- }
576
+ };
577
577
  /**
578
578
  * Options to pass to the sass preprocessor.
579
579
  */
580
- export interface Sass {
580
+ export type Sass = {
581
581
  /**
582
582
  * A set of deprecations to treat as fatal. If a deprecation warning of any provided type is
583
583
  * encountered during compilation, the compiler will error instead. If a Version is
@@ -595,9 +595,9 @@ export interface Sass {
595
595
  * encountered during compilation, the compiler will ignore it instead.
596
596
  */
597
597
  silenceDeprecations?: string[];
598
- }
598
+ };
599
599
  export type StyleElement = StyleClass | string;
600
- export interface StyleClass {
600
+ export type StyleClass = {
601
601
  /**
602
602
  * The bundle name for this extra entry point.
603
603
  */
@@ -610,4 +610,4 @@ export interface StyleClass {
610
610
  * The file to include.
611
611
  */
612
612
  input: string;
613
- }
613
+ };
@@ -6,11 +6,10 @@
6
6
  * found in the LICENSE file at https://angular.dev/license
7
7
  */
8
8
  import { Builder } from '@angular-devkit/architect';
9
- import { json } from '@angular-devkit/core';
10
9
  import { execute } from './builder';
11
10
  import type { DevServerBuilderOutput } from './output';
12
11
  import type { Schema as DevServerBuilderOptions } from './schema';
13
12
  export { type DevServerBuilderOptions, type DevServerBuilderOutput, execute as executeDevServerBuilder, };
14
- declare const builder: Builder<DevServerBuilderOptions & json.JsonObject>;
13
+ declare const builder: Builder<DevServerBuilderOptions>;
15
14
  export default builder;
16
15
  export { execute as executeDevServer };
@@ -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
  }
@@ -1,7 +1,13 @@
1
1
  /**
2
2
  * Dev Server target options for Build Facade.
3
3
  */
4
- export interface Schema {
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:
@@ -78,7 +84,13 @@ export interface Schema {
78
84
  * Rebuild on change.
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.
@@ -89,10 +101,10 @@ export type Inspect = boolean | string;
89
101
  * enable prebundling, the Angular CLI cache must also be enabled.
90
102
  */
91
103
  export type PrebundleUnion = boolean | PrebundleClass;
92
- export interface PrebundleClass {
104
+ export type PrebundleClass = {
93
105
  /**
94
106
  * List of package imports that should not be prebundled by the development server. The
95
107
  * packages will be bundled into the application code itself.
96
108
  */
97
109
  exclude: string[];
98
- }
110
+ };
@@ -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]);
@@ -353,26 +370,22 @@ async function invalidateUpdatedFiles(normalizePath, generatedFiles, assetFiles,
353
370
  updatedFiles.push(file);
354
371
  }
355
372
  // Invalidate any updated files
356
- let destroyAngularServerAppCalled = false;
373
+ let serverApplicationChanged = false;
357
374
  for (const [file, record] of generatedFiles) {
358
375
  if (!record.updated) {
359
376
  continue;
360
377
  }
361
378
  record.updated = false;
362
- if (record.type === internal_1.BuildOutputFileType.ServerApplication && !destroyAngularServerAppCalled) {
363
- // Clear the server app cache
364
- // This must be done before module invalidation.
365
- const { ɵdestroyAngularServerApp } = (await server.ssrLoadModule('/main.server.mjs'));
366
- ɵdestroyAngularServerApp();
367
- destroyAngularServerAppCalled = true;
368
- }
369
379
  updatedFiles.push(file);
380
+ serverApplicationChanged ||= record.type === internal_1.BuildOutputFileType.ServerApplication;
370
381
  const updatedModules = server.moduleGraph.getModulesByFile(normalizePath((0, node_path_1.join)(server.config.root, file)));
371
382
  updatedModules?.forEach((m) => server.moduleGraph.invalidateModule(m));
372
383
  }
373
- if (destroyAngularServerAppCalled) {
374
- // Trigger module evaluation before reload to initiate dependency optimization.
375
- await server.ssrLoadModule('/main.server.mjs');
384
+ if (serverApplicationChanged) {
385
+ // Clear the server app cache and
386
+ // trigger module evaluation before reload to initiate dependency optimization.
387
+ const { ɵdestroyAngularServerApp } = (await server.ssrLoadModule('/main.server.mjs'));
388
+ ɵdestroyAngularServerApp();
376
389
  }
377
390
  return updatedFiles;
378
391
  }
@@ -424,7 +437,7 @@ function handleUpdate(server, serverOptions, logger, componentStyles, updatedFil
424
437
  type: 'update',
425
438
  updates,
426
439
  });
427
- logger.info('HMR update sent to client(s).');
440
+ logger.info('Stylesheet update sent to client(s).');
428
441
  return;
429
442
  }
430
443
  }
@@ -545,6 +558,7 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
545
558
  strictPort: true,
546
559
  host: serverOptions.host,
547
560
  open: serverOptions.open,
561
+ allowedHosts: serverOptions.allowedHosts,
548
562
  headers: serverOptions.headers,
549
563
  // Disable the websocket if live reload is disabled (false/undefined are the only valid values)
550
564
  ws: serverOptions.liveReload === false && serverOptions.hmr === false ? false : undefined,
@@ -6,9 +6,8 @@
6
6
  * found in the LICENSE file at https://angular.dev/license
7
7
  */
8
8
  import { Builder } from '@angular-devkit/architect';
9
- import { json } from '@angular-devkit/core';
10
9
  import { execute } from './builder';
11
10
  import type { Schema as ExtractI18nBuilderOptions } from './schema';
12
11
  export { ExtractI18nBuilderOptions, execute };
13
- declare const builder: Builder<ExtractI18nBuilderOptions & json.JsonObject>;
12
+ declare const builder: Builder<ExtractI18nBuilderOptions>;
14
13
  export default builder;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Extract i18n target options for Build Facade.
3
3
  */
4
- export interface Schema {
4
+ export type Schema = {
5
5
  /**
6
6
  * A builder target to extract i18n messages in the format of
7
7
  * `project:target[:configuration]`. You can also pass in more than one configuration name
@@ -24,7 +24,7 @@ export interface Schema {
24
24
  * Log progress to the console.
25
25
  */
26
26
  progress?: boolean;
27
- }
27
+ };
28
28
  /**
29
29
  * Output format for the generated file.
30
30
  */
@@ -6,9 +6,8 @@
6
6
  * found in the LICENSE file at https://angular.dev/license
7
7
  */
8
8
  import { Builder } from '@angular-devkit/architect';
9
- import { json } from '@angular-devkit/core';
10
9
  import { execute } from './builder';
11
10
  import type { Schema as NgPackagrBuilderOptions } from './schema';
12
11
  export { type NgPackagrBuilderOptions, execute };
13
- declare const builder: Builder<NgPackagrBuilderOptions & json.JsonObject>;
12
+ declare const builder: Builder<NgPackagrBuilderOptions>;
14
13
  export default builder;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * ng-packagr target options for Build Architect. Use to build library projects.
3
3
  */
4
- export interface Schema {
4
+ export type Schema = {
5
5
  /**
6
6
  * Enable and define the file watching poll time period in milliseconds.
7
7
  */
@@ -18,4 +18,4 @@ export interface Schema {
18
18
  * Run build when files change.
19
19
  */
20
20
  watch?: boolean;
21
- }
21
+ };
@@ -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) {
@@ -22,6 +22,10 @@ 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');
25
29
  const { ɵgetOrCreateAngularServerApp } = (await server.ssrLoadModule('/main.server.mjs'));
26
30
  const angularServerApp = ɵgetOrCreateAngularServerApp({
27
31
  allowStaticRouteRender: true,
@@ -35,11 +35,7 @@ async function createAngularMemoryPlugin(options) {
35
35
  // Vite will resolve these these files example:
36
36
  // `file:///@ng/component?c=src%2Fapp%2Fapp.component.ts%40AppComponent&t=1737017253850`
37
37
  const sourcePath = (0, node_url_1.fileURLToPath)(source);
38
- const sourceWithoutRoot = sourcePath.startsWith(virtualProjectRoot)
39
- ? normalizePath('/' + (0, node_path_1.relative)(virtualProjectRoot, sourcePath))
40
- : // TODO: remove once https://github.com/angular/angular/blob/4e6017a9f5cda389c5fbf4f2c1519ce1bba23e11/packages/compiler/src/render3/r3_hmr_compiler.ts#L57
41
- // is changed from `/@ng` to `./@ng/`
42
- normalizePath('/' + sourcePath.slice((0, node_path_1.parse)(sourcePath).root.length));
38
+ const sourceWithoutRoot = normalizePath('/' + (0, node_path_1.relative)(virtualProjectRoot, sourcePath));
43
39
  if (sourceWithoutRoot.startsWith(ANGULAR_PREFIX)) {
44
40
  const [, query] = source.split('?', 2);
45
41
  return `\0${sourceWithoutRoot}?${query}`;
@@ -29,7 +29,7 @@ export declare function createI18nOptions(projectMetadata: {
29
29
  i18n?: unknown;
30
30
  }, inline?: boolean | string[], logger?: {
31
31
  warn(message: string): void;
32
- }): I18nOptions;
32
+ }, ssrEnabled?: boolean): I18nOptions;
33
33
  export declare function loadTranslations(locale: string, desc: LocaleDescription, workspaceRoot: string, loader: TranslationLoader, logger: {
34
34
  warn: (message: string) => void;
35
35
  error: (message: string) => void;
@@ -45,7 +45,7 @@ function ensureValidSubPath(value, name) {
45
45
  throw new Error(`Project field '${name}' is invalid. It can only contain letters, numbers, hyphens, and underscores.`);
46
46
  }
47
47
  }
48
- function createI18nOptions(projectMetadata, inline, logger) {
48
+ function createI18nOptions(projectMetadata, inline, logger, ssrEnabled) {
49
49
  const { i18n: metadata = {} } = projectMetadata;
50
50
  ensureObject(metadata, 'i18n');
51
51
  const i18n = {
@@ -71,10 +71,12 @@ function createI18nOptions(projectMetadata, inline, logger) {
71
71
  }
72
72
  if (metadata.sourceLocale.baseHref !== undefined) {
73
73
  ensureString(metadata.sourceLocale.baseHref, 'i18n.sourceLocale.baseHref');
74
- logger?.warn(`The 'baseHref' field under 'i18n.sourceLocale' is deprecated and will be removed in future versions. ` +
75
- `Please use 'subPath' instead.\nNote: 'subPath' defines the URL segment for the locale, acting ` +
76
- `as both the HTML base HREF and the directory name for output.\nBy default, ` +
77
- `if not specified, 'subPath' uses the locale code.`);
74
+ if (ssrEnabled) {
75
+ logger?.warn(`'baseHref' in 'i18n.sourceLocale' may lead to undefined behavior when used with SSR. ` +
76
+ `Consider using 'subPath' instead.\n\n` +
77
+ `Note: 'subPath' specifies the URL segment for the locale, serving as both the HTML base HREF ` +
78
+ `and the output directory name.\nBy default, if not explicitly set, 'subPath' defaults to the locale code.`);
79
+ }
78
80
  rawSourceLocaleBaseHref = metadata.sourceLocale.baseHref;
79
81
  }
80
82
  if (metadata.sourceLocale.subPath !== undefined) {
@@ -104,10 +106,12 @@ function createI18nOptions(projectMetadata, inline, logger) {
104
106
  translationFiles = normalizeTranslationFileOption(options.translation, locale, false);
105
107
  if ('baseHref' in options) {
106
108
  ensureString(options.baseHref, `i18n.locales.${locale}.baseHref`);
107
- logger?.warn(`The 'baseHref' field under 'i18n.locales.${locale}' is deprecated and will be removed in future versions. ` +
108
- `Please use 'subPath' instead.\nNote: 'subPath' defines the URL segment for the locale, acting ` +
109
- `as both the HTML base HREF and the directory name for output.\nBy default, ` +
110
- `if not specified, 'subPath' uses the locale code.`);
109
+ if (ssrEnabled) {
110
+ logger?.warn(`'baseHref' in 'i18n.locales.${locale}' may lead to undefined behavior when used with SSR. ` +
111
+ `Consider using 'subPath' instead.\n\n` +
112
+ `Note: 'subPath' specifies the URL segment for the locale, serving as both the HTML base HREF ` +
113
+ `and the output directory name.\nBy default, if not explicitly set, 'subPath' defaults to the locale code.`);
114
+ }
111
115
  baseHref = options.baseHref;
112
116
  }
113
117
  if ('subPath' in options) {
@@ -131,17 +135,6 @@ function createI18nOptions(projectMetadata, inline, logger) {
131
135
  };
132
136
  }
133
137
  }
134
- // Check that subPaths are unique.
135
- const localesData = Object.entries(i18n.locales);
136
- for (let i = 0; i < localesData.length; i++) {
137
- const [localeA, { subPath: subPathA }] = localesData[i];
138
- for (let j = i + 1; j < localesData.length; j++) {
139
- const [localeB, { subPath: subPathB }] = localesData[j];
140
- if (subPathA === subPathB) {
141
- throw new Error(`Invalid i18n configuration: Locales '${localeA}' and '${localeB}' cannot have the same subPath: '${subPathB}'.`);
142
- }
143
- }
144
- }
145
138
  if (inline === true) {
146
139
  i18n.inlineLocales.add(i18n.sourceLocale);
147
140
  Object.keys(i18n.locales).forEach((locale) => i18n.inlineLocales.add(locale));
@@ -154,6 +147,17 @@ function createI18nOptions(projectMetadata, inline, logger) {
154
147
  i18n.inlineLocales.add(locale);
155
148
  }
156
149
  }
150
+ // Check that subPaths are unique only the locales that we are inlining.
151
+ const localesData = Object.entries(i18n.locales).filter(([locale]) => i18n.inlineLocales.has(locale));
152
+ for (let i = 0; i < localesData.length; i++) {
153
+ const [localeA, { subPath: subPathA }] = localesData[i];
154
+ for (let j = i + 1; j < localesData.length; j++) {
155
+ const [localeB, { subPath: subPathB }] = localesData[j];
156
+ if (subPathA === subPathB) {
157
+ throw new Error(`Invalid i18n configuration: Locales '${localeA}' and '${localeB}' cannot have the same subPath: '${subPathB}'.`);
158
+ }
159
+ }
160
+ }
157
161
  return i18n;
158
162
  }
159
163
  function loadTranslations(locale, desc, workspaceRoot, loader, logger, usedFormats, duplicateTranslation) {
@@ -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.1.3';
13
+ const VERSION = '19.1.5';
14
14
  function hasCacheMetadata(value) {
15
15
  return (!!value &&
16
16
  typeof value === 'object' &&
@@ -16,6 +16,7 @@ const error_1 = require("../error");
16
16
  const url_1 = require("../url");
17
17
  const worker_pool_1 = require("../worker-pool");
18
18
  const utils_1 = require("./esm-in-memory-loader/utils");
19
+ const manifest_1 = require("./manifest");
19
20
  const models_1 = require("./models");
20
21
  async function prerenderPages(workspaceRoot, baseHref, appShellOptions, prerenderOptions, outputFiles, assets, outputMode, sourcemap = false, maxThreads = 1) {
21
22
  const outputFilesForWorker = {};
@@ -89,6 +90,12 @@ async function prerenderPages(workspaceRoot, baseHref, appShellOptions, prerende
89
90
  serializableRouteTreeNode,
90
91
  };
91
92
  }
93
+ // Add the extracted routes to the manifest file.
94
+ // We could re-generate it from the start, but that would require a number of options to be passed down.
95
+ const manifest = outputFilesForWorker[manifest_1.SERVER_APP_MANIFEST_FILENAME];
96
+ if (manifest) {
97
+ outputFilesForWorker[manifest_1.SERVER_APP_MANIFEST_FILENAME] = manifest.replace('routes: undefined,', `routes: ${JSON.stringify(serializableRouteTreeNodeForPrerender, undefined, 2)},`);
98
+ }
92
99
  // Render routes
93
100
  const { errors: renderingErrors, output } = await renderPages(baseHref, sourcemap, serializableRouteTreeNodeForPrerender, maxThreads, workspaceRoot, outputFilesForWorker, assetsReversed, outputMode, appShellRoute ?? appShellOptions?.route);
94
101
  errors.push(...renderingErrors);
@@ -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) {