@angular/build 19.0.0-next.9 → 19.0.0-rc.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.
Files changed (90) hide show
  1. package/package.json +21 -19
  2. package/src/builders/application/build-action.js +22 -10
  3. package/src/builders/application/chunk-optimizer.js +1 -4
  4. package/src/builders/application/execute-build.js +59 -24
  5. package/src/builders/application/execute-post-bundle.js +28 -5
  6. package/src/builders/application/index.d.ts +0 -16
  7. package/src/builders/application/index.js +15 -10
  8. package/src/builders/application/options.d.ts +14 -2
  9. package/src/builders/application/options.js +25 -10
  10. package/src/builders/application/results.d.ts +5 -3
  11. package/src/builders/application/schema.d.ts +86 -0
  12. package/src/builders/application/schema.js +19 -1
  13. package/src/builders/application/schema.json +73 -4
  14. package/src/builders/application/setup-bundling.d.ts +6 -1
  15. package/src/builders/application/setup-bundling.js +47 -13
  16. package/src/builders/dev-server/options.d.ts +2 -2
  17. package/src/builders/dev-server/options.js +2 -2
  18. package/src/builders/dev-server/schema.d.ts +2 -1
  19. package/src/builders/dev-server/schema.json +1 -2
  20. package/src/builders/dev-server/vite-server.d.ts +3 -2
  21. package/src/builders/dev-server/vite-server.js +123 -61
  22. package/src/index.d.ts +1 -0
  23. package/src/tools/angular/angular-host.d.ts +1 -1
  24. package/src/tools/angular/angular-host.js +14 -6
  25. package/src/tools/angular/compilation/angular-compilation.d.ts +1 -0
  26. package/src/tools/angular/compilation/aot-compilation.d.ts +1 -0
  27. package/src/tools/angular/compilation/aot-compilation.js +39 -0
  28. package/src/tools/angular/compilation/parallel-compilation.js +2 -2
  29. package/src/tools/angular/compilation/parallel-worker.d.ts +1 -0
  30. package/src/tools/angular/compilation/parallel-worker.js +5 -2
  31. package/src/tools/esbuild/angular/compiler-plugin.d.ts +3 -4
  32. package/src/tools/esbuild/angular/compiler-plugin.js +58 -33
  33. package/src/tools/esbuild/angular/component-stylesheets.d.ts +18 -18
  34. package/src/tools/esbuild/angular/component-stylesheets.js +66 -38
  35. package/src/tools/esbuild/angular/jit-plugin-callbacks.d.ts +1 -1
  36. package/src/tools/esbuild/angular/jit-plugin-callbacks.js +11 -3
  37. package/src/tools/esbuild/angular/source-file-cache.d.ts +1 -1
  38. package/src/tools/esbuild/angular/source-file-cache.js +6 -2
  39. package/src/tools/esbuild/application-code-bundle.d.ts +7 -5
  40. package/src/tools/esbuild/application-code-bundle.js +280 -249
  41. package/src/tools/esbuild/bundler-context.d.ts +2 -1
  42. package/src/tools/esbuild/bundler-context.js +10 -12
  43. package/src/tools/esbuild/bundler-execution-result.d.ts +14 -3
  44. package/src/tools/esbuild/bundler-execution-result.js +15 -8
  45. package/src/tools/esbuild/commonjs-checker.js +2 -2
  46. package/src/tools/esbuild/compiler-plugin-options.d.ts +2 -4
  47. package/src/tools/esbuild/compiler-plugin-options.js +15 -37
  48. package/src/tools/esbuild/global-scripts.js +1 -1
  49. package/src/tools/esbuild/global-styles.js +4 -1
  50. package/src/tools/esbuild/index-html-generator.js +8 -0
  51. package/src/tools/esbuild/javascript-transformer.js +3 -0
  52. package/src/tools/esbuild/server-bundle-metadata-plugin.d.ts +22 -0
  53. package/src/tools/esbuild/server-bundle-metadata-plugin.js +36 -0
  54. package/src/tools/esbuild/stylesheets/bundle-options.d.ts +2 -0
  55. package/src/tools/esbuild/stylesheets/bundle-options.js +2 -1
  56. package/src/tools/esbuild/stylesheets/sass-language.js +4 -0
  57. package/src/tools/esbuild/stylesheets/stylesheet-plugin-factory.d.ts +9 -0
  58. package/src/tools/esbuild/utils.js +13 -31
  59. package/src/tools/sass/worker.js +19 -0
  60. package/src/tools/vite/middlewares/assets-middleware.d.ts +6 -1
  61. package/src/tools/vite/middlewares/assets-middleware.js +42 -22
  62. package/src/tools/vite/middlewares/component-middleware.d.ts +9 -0
  63. package/src/tools/vite/middlewares/component-middleware.js +33 -0
  64. package/src/tools/vite/middlewares/index.d.ts +2 -1
  65. package/src/tools/vite/middlewares/index.js +3 -1
  66. package/src/tools/vite/middlewares/ssr-middleware.js +11 -8
  67. package/src/tools/vite/plugins/angular-memory-plugin.d.ts +1 -0
  68. package/src/tools/vite/plugins/angular-memory-plugin.js +5 -13
  69. package/src/tools/vite/plugins/setup-middlewares-plugin.d.ts +3 -1
  70. package/src/tools/vite/plugins/setup-middlewares-plugin.js +12 -3
  71. package/src/tools/vite/utils.d.ts +1 -0
  72. package/src/typings.d.ts +1 -1
  73. package/src/utils/environment-options.d.ts +1 -0
  74. package/src/utils/environment-options.js +4 -2
  75. package/src/utils/index-file/auto-csp.d.ts +23 -0
  76. package/src/utils/index-file/auto-csp.js +283 -0
  77. package/src/utils/index-file/html-rewriting-stream.d.ts +5 -1
  78. package/src/utils/index-file/index-html-generator.d.ts +4 -0
  79. package/src/utils/index-file/index-html-generator.js +11 -0
  80. package/src/utils/index-file/inline-critical-css.js +17 -18
  81. package/src/utils/normalize-cache.js +1 -1
  82. package/src/utils/server-rendering/esm-in-memory-loader/utils.d.ts +8 -0
  83. package/src/utils/server-rendering/esm-in-memory-loader/utils.js +13 -0
  84. package/src/utils/server-rendering/launch-server.js +5 -5
  85. package/src/utils/server-rendering/load-esm-from-memory.d.ts +1 -1
  86. package/src/utils/server-rendering/manifest.d.ts +9 -8
  87. package/src/utils/server-rendering/manifest.js +17 -23
  88. package/src/utils/server-rendering/prerender.js +30 -19
  89. package/src/utils/server-rendering/render-worker.js +4 -2
  90. package/src/utils/supported-browsers.js +1 -0
@@ -58,10 +58,11 @@ export declare class BundlerContext {
58
58
  * All builds use the `write` option with a value of `false` to allow for the output files
59
59
  * build result array to be populated.
60
60
  *
61
+ * @param force If true, always rebundle.
61
62
  * @returns If output files are generated, the full esbuild BuildResult; if not, the
62
63
  * warnings and errors for the attempted build.
63
64
  */
64
- bundle(): Promise<BundleContextResult>;
65
+ bundle(force?: boolean): Promise<BundleContextResult>;
65
66
  /**
66
67
  * Invalidate a stored bundler result based on the previous watch files
67
68
  * and a list of changed files.
@@ -125,12 +125,13 @@ class BundlerContext {
125
125
  * All builds use the `write` option with a value of `false` to allow for the output files
126
126
  * build result array to be populated.
127
127
  *
128
+ * @param force If true, always rebundle.
128
129
  * @returns If output files are generated, the full esbuild BuildResult; if not, the
129
130
  * warnings and errors for the attempted build.
130
131
  */
131
- async bundle() {
132
+ async bundle(force) {
132
133
  // Return existing result if present
133
- if (this.#esbuildResult) {
134
+ if (!force && this.#esbuildResult) {
134
135
  return this.#esbuildResult;
135
136
  }
136
137
  const result = await this.#performBundle();
@@ -211,6 +212,7 @@ class BundlerContext {
211
212
  warnings: result.warnings,
212
213
  };
213
214
  }
215
+ const { 'ng-platform-server': isPlatformServer = false, 'ng-ssr-entry-bundle': isSsrEntryBundle = false, } = result.metafile;
214
216
  // Find all initial files
215
217
  const initialFiles = new Map();
216
218
  for (const outputFile of result.outputFiles) {
@@ -231,7 +233,7 @@ class BundlerContext {
231
233
  name,
232
234
  type,
233
235
  entrypoint: true,
234
- serverFile: this.#platformIsServer,
236
+ serverFile: isPlatformServer,
235
237
  depth: 0,
236
238
  };
237
239
  if (!this.initialFilter || this.initialFilter(record)) {
@@ -259,7 +261,7 @@ class BundlerContext {
259
261
  type: initialImport.kind === 'import-rule' ? 'style' : 'script',
260
262
  entrypoint: false,
261
263
  external: initialImport.external,
262
- serverFile: this.#platformIsServer,
264
+ serverFile: isPlatformServer,
263
265
  depth: entryRecord.depth + 1,
264
266
  };
265
267
  if (!this.initialFilter || this.initialFilter(record)) {
@@ -292,9 +294,8 @@ class BundlerContext {
292
294
  if (!/\.([cm]?js|css|wasm)(\.map)?$/i.test(file.path)) {
293
295
  fileType = BuildOutputFileType.Media;
294
296
  }
295
- else if (this.#platformIsServer) {
296
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
297
- fileType = result.metafile['ng-ssr-entry-bundle']
297
+ else if (isPlatformServer) {
298
+ fileType = isSsrEntryBundle
298
299
  ? BuildOutputFileType.ServerRoot
299
300
  : BuildOutputFileType.ServerApplication;
300
301
  }
@@ -304,7 +305,7 @@ class BundlerContext {
304
305
  return (0, utils_1.convertOutputFile)(file, fileType);
305
306
  });
306
307
  let externalConfiguration = this.#esbuildOptions.external;
307
- if (this.#platformIsServer && externalConfiguration) {
308
+ if (isPlatformServer && externalConfiguration) {
308
309
  externalConfiguration = externalConfiguration.filter((dep) => !utils_1.SERVER_GENERATED_EXTERNALS.has(dep));
309
310
  if (!externalConfiguration.length) {
310
311
  externalConfiguration = undefined;
@@ -316,7 +317,7 @@ class BundlerContext {
316
317
  outputFiles,
317
318
  initialFiles,
318
319
  externalImports: {
319
- [this.#platformIsServer ? 'server' : 'browser']: externalImports,
320
+ [isPlatformServer ? 'server' : 'browser']: externalImports,
320
321
  },
321
322
  externalConfiguration,
322
323
  errors: undefined,
@@ -336,9 +337,6 @@ class BundlerContext {
336
337
  }
337
338
  }
338
339
  }
339
- get #platformIsServer() {
340
- return this.#esbuildOptions?.platform === 'node';
341
- }
342
340
  /**
343
341
  * Invalidate a stored bundler result based on the previous watch files
344
342
  * and a list of changed files.
@@ -7,6 +7,7 @@
7
7
  */
8
8
  import type { Message, PartialMessage } from 'esbuild';
9
9
  import type { ChangedFiles } from '../../tools/esbuild/watcher';
10
+ import type { ComponentStylesheetBundler } from './angular/component-stylesheets';
10
11
  import type { SourceFileCache } from './angular/source-file-cache';
11
12
  import type { BuildOutputFile, BuildOutputFileType, BundlerContext } from './bundler-context';
12
13
  export interface BuildOutputAsset {
@@ -14,10 +15,15 @@ export interface BuildOutputAsset {
14
15
  destination: string;
15
16
  }
16
17
  export interface RebuildState {
17
- rebuildContexts: BundlerContext[];
18
+ rebuildContexts: {
19
+ typescriptContexts: BundlerContext[];
20
+ otherContexts: BundlerContext[];
21
+ };
22
+ componentStyleBundler: ComponentStylesheetBundler;
18
23
  codeBundleCache?: SourceFileCache;
19
24
  fileChanges: ChangedFiles;
20
25
  previousOutputHashes: Map<string, string>;
26
+ templateUpdates?: Map<string, string>;
21
27
  }
22
28
  export interface ExternalResultMetadata {
23
29
  implicitBrowser: string[];
@@ -32,7 +38,9 @@ export type PrerenderedRoutesRecord = Record<string, {
32
38
  */
33
39
  export declare class ExecutionResult {
34
40
  private rebuildContexts;
41
+ private componentStyleBundler;
35
42
  private codeBundleCache?;
43
+ readonly templateUpdates?: Map<string, string> | undefined;
36
44
  outputFiles: BuildOutputFile[];
37
45
  assetFiles: BuildOutputAsset[];
38
46
  errors: (Message | PartialMessage)[];
@@ -43,7 +51,10 @@ export declare class ExecutionResult {
43
51
  extraWatchFiles: string[];
44
52
  htmlIndexPath?: string;
45
53
  htmlBaseHref?: string;
46
- constructor(rebuildContexts: BundlerContext[], codeBundleCache?: SourceFileCache | undefined);
54
+ constructor(rebuildContexts: {
55
+ typescriptContexts: BundlerContext[];
56
+ otherContexts: BundlerContext[];
57
+ }, componentStyleBundler: ComponentStylesheetBundler, codeBundleCache?: SourceFileCache | undefined, templateUpdates?: Map<string, string> | undefined);
47
58
  addOutputFile(path: string, content: string | Uint8Array, type: BuildOutputFileType): void;
48
59
  addAssets(assets: BuildOutputAsset[]): void;
49
60
  addLog(value: string): void;
@@ -67,7 +78,7 @@ export declare class ExecutionResult {
67
78
  success: boolean;
68
79
  outputFiles: BuildOutputFile[];
69
80
  assetFiles: BuildOutputAsset[];
70
- errors: (Message | PartialMessage)[];
81
+ errors: (PartialMessage | Message)[];
71
82
  externalMetadata: ExternalResultMetadata | undefined;
72
83
  };
73
84
  get watchFiles(): string[];
@@ -15,7 +15,9 @@ const utils_1 = require("./utils");
15
15
  */
16
16
  class ExecutionResult {
17
17
  rebuildContexts;
18
+ componentStyleBundler;
18
19
  codeBundleCache;
20
+ templateUpdates;
19
21
  outputFiles = [];
20
22
  assetFiles = [];
21
23
  errors = [];
@@ -26,9 +28,11 @@ class ExecutionResult {
26
28
  extraWatchFiles = [];
27
29
  htmlIndexPath;
28
30
  htmlBaseHref;
29
- constructor(rebuildContexts, codeBundleCache) {
31
+ constructor(rebuildContexts, componentStyleBundler, codeBundleCache, templateUpdates) {
30
32
  this.rebuildContexts = rebuildContexts;
33
+ this.componentStyleBundler = componentStyleBundler;
31
34
  this.codeBundleCache = codeBundleCache;
35
+ this.templateUpdates = templateUpdates;
32
36
  }
33
37
  addOutputFile(path, content, type) {
34
38
  this.outputFiles.push((0, utils_1.createOutputFile)(path, content, type));
@@ -100,25 +104,24 @@ class ExecutionResult {
100
104
  }
101
105
  get watchFiles() {
102
106
  // Bundler contexts internally normalize file dependencies
103
- const files = this.rebuildContexts.flatMap((context) => [...context.watchFiles]);
107
+ const files = this.rebuildContexts.typescriptContexts
108
+ .flatMap((context) => [...context.watchFiles])
109
+ .concat(this.rebuildContexts.otherContexts.flatMap((context) => [...context.watchFiles]));
104
110
  if (this.codeBundleCache?.referencedFiles) {
105
111
  // These files originate from TS/NG and can have POSIX path separators even on Windows.
106
112
  // To ensure path comparisons are valid, all these paths must be normalized.
107
113
  files.push(...this.codeBundleCache.referencedFiles.map(node_path_1.normalize));
108
114
  }
109
- if (this.codeBundleCache?.loadResultCache) {
110
- // Load result caches internally normalize file dependencies
111
- files.push(...this.codeBundleCache.loadResultCache.watchFiles);
112
- }
113
115
  return files.concat(this.extraWatchFiles);
114
116
  }
115
117
  createRebuildState(fileChanges) {
116
- this.codeBundleCache?.invalidate([...fileChanges.modified, ...fileChanges.removed]);
117
118
  return {
118
119
  rebuildContexts: this.rebuildContexts,
119
120
  codeBundleCache: this.codeBundleCache,
121
+ componentStyleBundler: this.componentStyleBundler,
120
122
  fileChanges,
121
123
  previousOutputHashes: new Map(this.outputFiles.map((file) => [file.path, file.hash])),
124
+ templateUpdates: this.templateUpdates,
122
125
  };
123
126
  }
124
127
  findChangedFiles(previousOutputHashes) {
@@ -132,7 +135,11 @@ class ExecutionResult {
132
135
  return changed;
133
136
  }
134
137
  async dispose() {
135
- await Promise.allSettled(this.rebuildContexts.map((context) => context.dispose()));
138
+ await Promise.allSettled([
139
+ ...this.rebuildContexts.typescriptContexts.map((context) => context.dispose()),
140
+ ...this.rebuildContexts.otherContexts.map((context) => context.dispose()),
141
+ this.componentStyleBundler.dispose(),
142
+ ]);
136
143
  }
137
144
  }
138
145
  exports.ExecutionResult = ExecutionResult;
@@ -42,9 +42,9 @@ function checkCommonJSModules(metafile, allowedCommonJsDependencies) {
42
42
  // using `provideHttpClient(withFetch())`.
43
43
  allowedRequests.add('xhr2');
44
44
  // Packages used by @angular/ssr.
45
- // While critters is ESM it has a number of direct and transtive CJS deps.
45
+ // While beasties is ESM it has a number of direct and transtive CJS deps.
46
46
  allowedRequests.add('express');
47
- allowedRequests.add('critters');
47
+ allowedRequests.add('beasties');
48
48
  // Find all entry points that contain code (JS/TS)
49
49
  const files = [];
50
50
  for (const { entryPoint } of Object.values(metafile.outputs)) {
@@ -8,9 +8,7 @@
8
8
  import { NormalizedApplicationBuildOptions } from '../../builders/application/options';
9
9
  import type { createCompilerPlugin } from './angular/compiler-plugin';
10
10
  import type { SourceFileCache } from './angular/source-file-cache';
11
+ import type { LoadResultCache } from './load-result-cache';
11
12
  type CreateCompilerPluginParameters = Parameters<typeof createCompilerPlugin>;
12
- export declare function createCompilerPluginOptions(options: NormalizedApplicationBuildOptions, target: string[], sourceFileCache?: SourceFileCache): {
13
- pluginOptions: CreateCompilerPluginParameters[0];
14
- styleOptions: CreateCompilerPluginParameters[1];
15
- };
13
+ export declare function createCompilerPluginOptions(options: NormalizedApplicationBuildOptions, sourceFileCache: SourceFileCache, loadResultCache?: LoadResultCache, templateUpdates?: Map<string, string>): CreateCompilerPluginParameters[0];
16
14
  export {};
@@ -8,43 +8,21 @@
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.createCompilerPluginOptions = createCompilerPluginOptions;
11
- function createCompilerPluginOptions(options, target, sourceFileCache) {
12
- const { workspaceRoot, optimizationOptions, sourcemapOptions, tsconfig, outputNames, fileReplacements, externalDependencies, preserveSymlinks, stylePreprocessorOptions, advancedOptimizations, inlineStyleLanguage, jit, cacheOptions, tailwindConfiguration, postcssConfiguration, publicPath, externalRuntimeStyles, instrumentForCoverage, } = options;
11
+ function createCompilerPluginOptions(options, sourceFileCache, loadResultCache, templateUpdates) {
12
+ const { sourcemapOptions, tsconfig, fileReplacements, advancedOptimizations, jit, externalRuntimeStyles, instrumentForCoverage, } = options;
13
+ const incremental = !!options.watch;
13
14
  return {
14
- // JS/TS options
15
- pluginOptions: {
16
- sourcemap: !!sourcemapOptions.scripts && (sourcemapOptions.hidden ? 'external' : true),
17
- thirdPartySourcemaps: sourcemapOptions.vendor,
18
- tsconfig,
19
- jit,
20
- advancedOptimizations,
21
- fileReplacements,
22
- sourceFileCache,
23
- loadResultCache: sourceFileCache?.loadResultCache,
24
- incremental: !!options.watch,
25
- externalRuntimeStyles,
26
- instrumentForCoverage,
27
- },
28
- // Component stylesheet options
29
- styleOptions: {
30
- workspaceRoot,
31
- inlineFonts: !!optimizationOptions.fonts.inline,
32
- optimization: !!optimizationOptions.styles.minify,
33
- sourcemap:
34
- // Hidden component stylesheet sourcemaps are inaccessible which is effectively
35
- // the same as being disabled. Disabling has the advantage of avoiding the overhead
36
- // of sourcemap processing.
37
- sourcemapOptions.styles && !sourcemapOptions.hidden ? 'linked' : false,
38
- outputNames,
39
- includePaths: stylePreprocessorOptions?.includePaths,
40
- externalDependencies,
41
- target,
42
- inlineStyleLanguage,
43
- preserveSymlinks,
44
- tailwindConfiguration,
45
- postcssConfiguration,
46
- cacheOptions,
47
- publicPath,
48
- },
15
+ sourcemap: !!sourcemapOptions.scripts && (sourcemapOptions.hidden ? 'external' : true),
16
+ thirdPartySourcemaps: sourcemapOptions.vendor,
17
+ tsconfig,
18
+ jit,
19
+ advancedOptimizations,
20
+ fileReplacements,
21
+ sourceFileCache,
22
+ loadResultCache,
23
+ incremental,
24
+ externalRuntimeStyles,
25
+ instrumentForCoverage,
26
+ templateUpdates,
49
27
  };
50
28
  }
@@ -72,7 +72,7 @@ function createGlobalScriptsBundleOptions(options, target, initial) {
72
72
  entryNames: initial ? outputNames.bundles : '[name]',
73
73
  assetNames: outputNames.media,
74
74
  mainFields: ['script', 'browser', 'main'],
75
- conditions: ['script'],
75
+ conditions: ['script', optimizationOptions.scripts ? 'production' : 'development'],
76
76
  resolveExtensions: ['.mjs', '.js', '.cjs'],
77
77
  logLevel: options.verbose && !jsonLogs ? 'debug' : 'silent',
78
78
  metafile: true,
@@ -45,12 +45,15 @@ function createGlobalStylesBundleOptions(options, target, initial) {
45
45
  bundles: '[name]',
46
46
  },
47
47
  includePaths: stylePreprocessorOptions?.includePaths,
48
+ // string[] | undefined' is not assignable to type '(Version | DeprecationOrId)[] | undefined'.
49
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
50
+ sass: stylePreprocessorOptions?.sass,
48
51
  tailwindConfiguration,
49
52
  postcssConfiguration,
50
53
  cacheOptions,
51
54
  }, loadCache);
52
55
  // Keep special CSS comments `/*! comment */` in place when `removeSpecialComments` is disabled.
53
- // These comments are special for a number of CSS tools such as Critters and PurgeCSS.
56
+ // These comments are special for a number of CSS tools such as Beasties and PurgeCSS.
54
57
  buildOptions.legalComments = optimizationOptions.styles?.removeSpecialComments
55
58
  ? 'none'
56
59
  : 'inline';
@@ -59,6 +59,13 @@ async function generateIndexHtml(initialFiles, outputFiles, buildOptions, lang)
59
59
  }
60
60
  throw new Error(`Output file does not exist: ${relativefilePath}`);
61
61
  };
62
+ // Read the Auto CSP options.
63
+ const autoCsp = buildOptions.security?.autoCsp;
64
+ const autoCspOptions = autoCsp === true
65
+ ? { unsafeEval: false }
66
+ : autoCsp
67
+ ? { unsafeEval: !!autoCsp.unsafeEval }
68
+ : undefined;
62
69
  // Create an index HTML generator that reads from the in-memory output files
63
70
  const indexHtmlGenerator = new index_html_generator_1.IndexHtmlGenerator({
64
71
  indexPath: indexHtmlOptions.input,
@@ -71,6 +78,7 @@ async function generateIndexHtml(initialFiles, outputFiles, buildOptions, lang)
71
78
  generateDedicatedSSRContent: !!(buildOptions.ssrOptions ||
72
79
  buildOptions.prerenderOptions ||
73
80
  buildOptions.appShellOptions),
81
+ autoCsp: autoCspOptions,
74
82
  });
75
83
  indexHtmlGenerator.readAsset = readAsset;
76
84
  return indexHtmlGenerator.process({
@@ -10,6 +10,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.JavaScriptTransformer = void 0;
11
11
  const node_crypto_1 = require("node:crypto");
12
12
  const promises_1 = require("node:fs/promises");
13
+ const utils_1 = require("../../utils/server-rendering/esm-in-memory-loader/utils");
13
14
  const worker_pool_1 = require("../../utils/worker-pool");
14
15
  /**
15
16
  * A class that performs transformation of JavaScript files and raw data.
@@ -41,6 +42,8 @@ class JavaScriptTransformer {
41
42
  this.#workerPool ??= new worker_pool_1.WorkerPool({
42
43
  filename: require.resolve('./javascript-transformer-worker'),
43
44
  maxThreads: this.maxThreads,
45
+ // Prevent passing `--import` (loader-hooks) from parent to child worker.
46
+ execArgv: process.execArgv.filter((v) => v !== utils_1.IMPORT_EXEC_ARGV),
44
47
  });
45
48
  return this.#workerPool;
46
49
  }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @license
3
+ * Copyright Google LLC All Rights Reserved.
4
+ *
5
+ * Use of this source code is governed by an MIT-style license that can be
6
+ * found in the LICENSE file at https://angular.dev/license
7
+ */
8
+ import type { Plugin } from 'esbuild';
9
+ /**
10
+ * Generates an esbuild plugin that appends metadata to the output bundle,
11
+ * marking it with server-side rendering (SSR) details for Angular SSR scenarios.
12
+ *
13
+ * @param options Optional configuration object.
14
+ * - `ssrEntryBundle`: If `true`, marks the bundle as an SSR entry point.
15
+ *
16
+ * @remarks We can't rely on `platform: node` or `platform: neutral`, as the latter
17
+ * is used for non-SSR-related code too (e.g., global scripts).
18
+ * @returns An esbuild plugin that injects SSR metadata into the build result's metafile.
19
+ */
20
+ export declare function createServerBundleMetadata(options?: {
21
+ ssrEntryBundle?: boolean;
22
+ }): Plugin;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ /**
3
+ * @license
4
+ * Copyright Google LLC All Rights Reserved.
5
+ *
6
+ * Use of this source code is governed by an MIT-style license that can be
7
+ * found in the LICENSE file at https://angular.dev/license
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.createServerBundleMetadata = createServerBundleMetadata;
11
+ /**
12
+ * Generates an esbuild plugin that appends metadata to the output bundle,
13
+ * marking it with server-side rendering (SSR) details for Angular SSR scenarios.
14
+ *
15
+ * @param options Optional configuration object.
16
+ * - `ssrEntryBundle`: If `true`, marks the bundle as an SSR entry point.
17
+ *
18
+ * @remarks We can't rely on `platform: node` or `platform: neutral`, as the latter
19
+ * is used for non-SSR-related code too (e.g., global scripts).
20
+ * @returns An esbuild plugin that injects SSR metadata into the build result's metafile.
21
+ */
22
+ function createServerBundleMetadata(options) {
23
+ return {
24
+ name: 'angular-server-bundle-metadata',
25
+ setup(build) {
26
+ build.onEnd((result) => {
27
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
28
+ const metafile = result.metafile;
29
+ if (metafile) {
30
+ metafile['ng-ssr-entry-bundle'] = !!options?.ssrEntryBundle;
31
+ metafile['ng-platform-server'] = true;
32
+ }
33
+ });
34
+ },
35
+ };
36
+ }
@@ -9,6 +9,7 @@ import type { BuildOptions } from 'esbuild';
9
9
  import { NormalizedCachedOptions } from '../../../utils/normalize-cache';
10
10
  import { PostcssConfiguration } from '../../../utils/postcss-configuration';
11
11
  import { LoadResultCache } from '../load-result-cache';
12
+ import { StylesheetPluginsass } from './stylesheet-plugin-factory';
12
13
  export interface BundleStylesheetOptions {
13
14
  workspaceRoot: string;
14
15
  optimization: boolean;
@@ -20,6 +21,7 @@ export interface BundleStylesheetOptions {
20
21
  media: string;
21
22
  };
22
23
  includePaths?: string[];
24
+ sass?: StylesheetPluginsass;
23
25
  externalDependencies?: string[];
24
26
  target: string[];
25
27
  tailwindConfiguration?: {
@@ -27,6 +27,7 @@ function createStylesheetBundleOptions(options, cache, inlineComponentData) {
27
27
  inlineComponentData,
28
28
  tailwindConfiguration: options.tailwindConfiguration,
29
29
  postcssConfiguration: options.postcssConfiguration,
30
+ sass: options.sass,
30
31
  }, cache);
31
32
  const plugins = [
32
33
  pluginFactory.create(sass_language_1.SassStylesheetLanguage),
@@ -53,7 +54,7 @@ function createStylesheetBundleOptions(options, cache, inlineComponentData) {
53
54
  preserveSymlinks: options.preserveSymlinks,
54
55
  external: options.externalDependencies,
55
56
  publicPath: options.publicPath,
56
- conditions: ['style', 'sass', 'less'],
57
+ conditions: ['style', 'sass', 'less', options.optimization ? 'production' : 'development'],
57
58
  mainFields: ['style', 'sass'],
58
59
  // Unlike JS, CSS does not have implicit file extensions in the general case.
59
60
  // Preprocessor specific behavior is handled in each stylesheet language plugin.
@@ -99,6 +99,7 @@ async function compileString(data, filePath, syntax, options, resolveUrl) {
99
99
  const resolutionCache = new cache_1.MemoryCache();
100
100
  const packageRootCache = new cache_1.MemoryCache();
101
101
  const warnings = [];
102
+ const { silenceDeprecations, futureDeprecations, fatalDeprecations } = options.sass ?? {};
102
103
  try {
103
104
  const { css, sourceMap, loadedUrls } = await sassWorkerPool.compileStringAsync(data, {
104
105
  url: (0, node_url_1.pathToFileURL)(filePath),
@@ -107,6 +108,9 @@ async function compileString(data, filePath, syntax, options, resolveUrl) {
107
108
  loadPaths: options.includePaths,
108
109
  sourceMap: options.sourcemap,
109
110
  sourceMapIncludeSources: options.sourcemap,
111
+ silenceDeprecations,
112
+ fatalDeprecations,
113
+ futureDeprecations,
110
114
  quietDeps: true,
111
115
  importers: [
112
116
  {
@@ -6,8 +6,13 @@
6
6
  * found in the LICENSE file at https://angular.dev/license
7
7
  */
8
8
  import type { OnLoadResult, Plugin, PluginBuild } from 'esbuild';
9
+ import type { Options } from 'sass';
9
10
  import type { PostcssConfiguration } from '../../../utils/postcss-configuration';
10
11
  import { LoadResultCache } from '../load-result-cache';
12
+ /**
13
+ * Configuration options for handling Sass-specific deprecations in a stylesheet plugin.
14
+ */
15
+ export type StylesheetPluginsass = Pick<Options<'async'>, 'futureDeprecations' | 'fatalDeprecations' | 'silenceDeprecations'>;
11
16
  /**
12
17
  * An object containing the plugin options to use when processing stylesheets.
13
18
  */
@@ -42,6 +47,10 @@ export interface StylesheetPluginOptions {
42
47
  * and any tailwind usage must be manually configured in the custom postcss usage.
43
48
  */
44
49
  postcssConfiguration?: PostcssConfiguration;
50
+ /**
51
+ * Optional Options for configuring Sass behavior.
52
+ */
53
+ sass?: StylesheetPluginsass;
45
54
  }
46
55
  export interface StylesheetLanguage {
47
56
  name: string;
@@ -38,6 +38,7 @@ function logBuildStats(metafile, outputFiles, initial, budgetFailures, colors, c
38
38
  const browserStats = [];
39
39
  const serverStats = [];
40
40
  let unchangedCount = 0;
41
+ let componentStyleChange = false;
41
42
  for (const { path: file, size, type } of outputFiles) {
42
43
  // Only display JavaScript and CSS files
43
44
  if (!/\.(?:css|m?js)$/.test(file)) {
@@ -53,6 +54,11 @@ function logBuildStats(metafile, outputFiles, initial, budgetFailures, colors, c
53
54
  // Only log server build stats when SSR is enabled.
54
55
  continue;
55
56
  }
57
+ // Skip logging external component stylesheets used for HMR
58
+ if (metafile.outputs[file] && 'ng-component' in metafile.outputs[file]) {
59
+ componentStyleChange = true;
60
+ continue;
61
+ }
56
62
  const name = initial.get(file)?.name ?? getChunkNameFromMetafile(metafile, file);
57
63
  const stat = {
58
64
  initial: initial.has(file),
@@ -70,7 +76,12 @@ function logBuildStats(metafile, outputFiles, initial, budgetFailures, colors, c
70
76
  return tableText + '\n';
71
77
  }
72
78
  else if (changedFiles !== undefined) {
73
- return '\nNo output file changes.\n';
79
+ if (componentStyleChange) {
80
+ return '\nComponent stylesheet(s) changed.\n';
81
+ }
82
+ else {
83
+ return '\nNo output file changes.\n';
84
+ }
74
85
  }
75
86
  if (unchangedCount > 0) {
76
87
  return `Unchanged output files: ${unchangedCount}`;
@@ -144,7 +155,7 @@ async function withNoProgress(text, action) {
144
155
  * @returns An object that can be used with the esbuild build `supported` option.
145
156
  */
146
157
  function getFeatureSupport(target, nativeAsyncAwait) {
147
- const supported = {
158
+ return {
148
159
  // Native async/await is not supported with Zone.js. Disabling support here will cause
149
160
  // esbuild to downlevel async/await, async generators, and for await...of to a Zone.js supported form.
150
161
  'async-await': nativeAsyncAwait,
@@ -154,35 +165,6 @@ function getFeatureSupport(target, nativeAsyncAwait) {
154
165
  // For more details: https://bugs.chromium.org/p/v8/issues/detail?id=11536
155
166
  'object-rest-spread': false,
156
167
  };
157
- // Detect Safari browser versions that have a class field behavior bug
158
- // See: https://github.com/angular/angular-cli/issues/24355#issuecomment-1333477033
159
- // See: https://github.com/WebKit/WebKit/commit/e8788a34b3d5f5b4edd7ff6450b80936bff396f2
160
- let safariClassFieldScopeBug = false;
161
- for (const browser of target) {
162
- let majorVersion;
163
- if (browser.startsWith('ios')) {
164
- majorVersion = Number(browser.slice(3, 5));
165
- }
166
- else if (browser.startsWith('safari')) {
167
- majorVersion = Number(browser.slice(6, 8));
168
- }
169
- else {
170
- continue;
171
- }
172
- // Technically, 14.0 is not broken but rather does not have support. However, the behavior
173
- // is identical since it would be set to false by esbuild if present as a target.
174
- if (majorVersion === 14 || majorVersion === 15) {
175
- safariClassFieldScopeBug = true;
176
- break;
177
- }
178
- }
179
- // If class field support cannot be used set to false; otherwise leave undefined to allow
180
- // esbuild to use `target` to determine support.
181
- if (safariClassFieldScopeBug) {
182
- supported['class-field'] = false;
183
- supported['class-static-field'] = false;
184
- }
185
- return supported;
186
168
  }
187
169
  const MAX_CONCURRENT_WRITES = 64;
188
170
  async function emitFilesToDisk(files, writeFileCallback) {
@@ -40,6 +40,25 @@ async function renderSassStylesheet(request) {
40
40
  containingUrl: containingUrl ? (0, node_url_1.fileURLToPath)(containingUrl) : null,
41
41
  },
42
42
  });
43
+ // Wait for the main thread to set the signal to 1 and notify, which tells
44
+ // us that a message can be received on the port.
45
+ // If the main thread is fast, the signal will already be set to 1, and no
46
+ // sleep/notify is necessary.
47
+ // However, there can be a race condition here:
48
+ // - the main thread sets the signal to 1, but does not get to the notify instruction yet
49
+ // - the worker does not pause because the signal is set to 1
50
+ // - the worker very soon enters this method again
51
+ // - this method sets the signal to 0 and sends the message
52
+ // - the signal is 0 and so the `Atomics.wait` call blocks
53
+ // - only now the main thread runs the `notify` from the first invocation, so the
54
+ // worker continues.
55
+ // - but there is no message yet in the port, because the thread should not have been
56
+ // waken up yet.
57
+ // To combat this, wait for a non-0 value _twice_.
58
+ // Almost every time, this immediately continues with "not-equal", because
59
+ // the signal is still set to 1, except during the race condition, when the second
60
+ // wait will wait for the correct notify.
61
+ Atomics.wait(importerChannel.signal, 0, 0);
43
62
  Atomics.wait(importerChannel.signal, 0, 0);
44
63
  const result = (0, node_worker_threads_1.receiveMessageOnPort)(importerChannel.port)?.message;
45
64
  return result ? (0, node_url_1.pathToFileURL)(result) : null;
@@ -7,4 +7,9 @@
7
7
  */
8
8
  import type { Connect, ViteDevServer } from 'vite';
9
9
  import { AngularMemoryOutputFiles } from '../utils';
10
- export declare function createAngularAssetsMiddleware(server: ViteDevServer, assets: Map<string, string>, outputFiles: AngularMemoryOutputFiles, usedComponentStyles: Map<string, string[]>): Connect.NextHandleFunction;
10
+ export interface ComponentStyleRecord {
11
+ rawContent: Uint8Array;
12
+ used?: Set<string>;
13
+ reload?: boolean;
14
+ }
15
+ export declare function createAngularAssetsMiddleware(server: ViteDevServer, assets: Map<string, string>, outputFiles: AngularMemoryOutputFiles, componentStyles: Map<string, ComponentStyleRecord>, encapsulateStyle: (style: Uint8Array, componentId: string) => string): Connect.NextHandleFunction;