@angular/build 18.0.0-rc.0 → 18.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@angular/build",
3
- "version": "18.0.0-rc.0",
3
+ "version": "18.0.0-rc.1",
4
4
  "description": "Official build system for Angular",
5
5
  "keywords": [
6
6
  "Angular CLI",
@@ -23,7 +23,7 @@
23
23
  "builders": "builders.json",
24
24
  "dependencies": {
25
25
  "@ampproject/remapping": "2.3.0",
26
- "@angular-devkit/architect": "0.1800.0-rc.0",
26
+ "@angular-devkit/architect": "0.1800.0-rc.1",
27
27
  "@babel/core": "7.24.5",
28
28
  "@babel/helper-annotate-as-pure": "7.22.5",
29
29
  "@babel/helper-split-export-declaration": "7.24.5",
@@ -42,10 +42,10 @@
42
42
  "piscina": "4.4.0",
43
43
  "parse5-html-rewriting-stream": "7.0.0",
44
44
  "postcss": "8.4.38",
45
- "sass": "1.75.0",
45
+ "sass": "1.76.0",
46
46
  "semver": "7.6.0",
47
47
  "undici": "6.15.0",
48
- "vite": "5.2.10",
48
+ "vite": "5.2.11",
49
49
  "watchpack": "2.4.1"
50
50
  },
51
51
  "peerDependencies": {
@@ -28,6 +28,7 @@ export interface InitialFileRecord {
28
28
  type: 'script' | 'style';
29
29
  external?: boolean;
30
30
  serverFile: boolean;
31
+ depth: number;
31
32
  }
32
33
  export declare enum BuildOutputFileType {
33
34
  Browser = 1,
@@ -233,6 +233,7 @@ class BundlerContext {
233
233
  type,
234
234
  entrypoint: true,
235
235
  serverFile: this.#platformIsServer,
236
+ depth: 0,
236
237
  };
237
238
  if (!this.initialFilter || this.initialFilter(record)) {
238
239
  initialFiles.set(relativeFilePath, record);
@@ -241,10 +242,17 @@ class BundlerContext {
241
242
  }
242
243
  }
243
244
  // Analyze for transitive initial files
244
- const files = [...initialFiles.keys()];
245
- for (const file of files) {
246
- for (const initialImport of result.metafile.outputs[file].imports) {
247
- if (initialFiles.has(initialImport.path)) {
245
+ const entriesToAnalyze = [...initialFiles];
246
+ let currentEntry;
247
+ while ((currentEntry = entriesToAnalyze.pop())) {
248
+ const [entryPath, entryRecord] = currentEntry;
249
+ for (const initialImport of result.metafile.outputs[entryPath].imports) {
250
+ const existingRecord = initialFiles.get(initialImport.path);
251
+ if (existingRecord) {
252
+ // Store the smallest value depth
253
+ if (existingRecord.depth > entryRecord.depth + 1) {
254
+ existingRecord.depth = entryRecord.depth + 1;
255
+ }
248
256
  continue;
249
257
  }
250
258
  if (initialImport.kind === 'import-statement' || initialImport.kind === 'import-rule') {
@@ -253,12 +261,13 @@ class BundlerContext {
253
261
  entrypoint: false,
254
262
  external: initialImport.external,
255
263
  serverFile: this.#platformIsServer,
264
+ depth: entryRecord.depth + 1,
256
265
  };
257
266
  if (!this.initialFilter || this.initialFilter(record)) {
258
267
  initialFiles.set(initialImport.path, record);
259
268
  }
260
269
  if (!initialImport.external) {
261
- files.push(initialImport.path);
270
+ entriesToAnalyze.push([initialImport.path, record]);
262
271
  }
263
272
  }
264
273
  }
@@ -15,6 +15,11 @@ const node_assert_1 = __importDefault(require("node:assert"));
15
15
  const node_path_1 = __importDefault(require("node:path"));
16
16
  const index_html_generator_1 = require("../../utils/index-file/index-html-generator");
17
17
  const bundler_context_1 = require("./bundler-context");
18
+ /**
19
+ * The maximum number of module preload link elements that should be added for
20
+ * initial scripts.
21
+ */
22
+ const MODULE_PRELOAD_MAX = 3;
18
23
  async function generateIndexHtml(initialFiles, outputFiles, buildOptions, lang) {
19
24
  // Analyze metafile for initial link-based hints.
20
25
  // Skip if the internal externalPackages option is enabled since this option requires
@@ -23,12 +28,18 @@ async function generateIndexHtml(initialFiles, outputFiles, buildOptions, lang)
23
28
  const { indexHtmlOptions, externalPackages, optimizationOptions, crossOrigin, subresourceIntegrity, baseHref, } = buildOptions;
24
29
  (0, node_assert_1.default)(indexHtmlOptions, 'indexHtmlOptions cannot be undefined.');
25
30
  if (!externalPackages && indexHtmlOptions.preloadInitial) {
31
+ let modulePreloadCount = 0;
26
32
  for (const [key, value] of initialFiles) {
27
33
  if (value.entrypoint || value.serverFile) {
28
34
  // Entry points are already referenced in the HTML
29
35
  continue;
30
36
  }
31
- if (value.type === 'script') {
37
+ // Only add shallow preloads
38
+ if (value.depth > 1) {
39
+ continue;
40
+ }
41
+ if (value.type === 'script' && modulePreloadCount < MODULE_PRELOAD_MAX) {
42
+ modulePreloadCount++;
32
43
  hints.push({ url: key, mode: 'modulepreload' });
33
44
  }
34
45
  else if (value.type === 'style') {
@@ -38,14 +38,14 @@ node_worker_threads_1.parentPort.on('message', (message) => {
38
38
  // functions combined with the shared memory `importSignal` and the Node.js
39
39
  // `receiveMessageOnPort` function are used to ensure synchronous behavior.
40
40
  const proxyImporter = {
41
- findFileUrl: (url, options) => {
41
+ findFileUrl: (url, { fromImport, containingUrl }) => {
42
42
  Atomics.store(importerSignal, 0, 0);
43
43
  workerImporterPort.postMessage({
44
44
  id,
45
45
  url,
46
46
  options: {
47
- ...options,
48
- containingUrl: options.containingUrl ? (0, node_url_1.fileURLToPath)(options.containingUrl) : null,
47
+ fromImport,
48
+ containingUrl: containingUrl ? (0, node_url_1.fileURLToPath)(containingUrl) : null,
49
49
  },
50
50
  });
51
51
  Atomics.wait(importerSignal, 0, 0);
@@ -10,7 +10,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.normalizeCacheOptions = void 0;
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 = '18.0.0-rc.0';
13
+ const VERSION = '18.0.0-rc.1';
14
14
  function hasCacheMetadata(value) {
15
15
  return (!!value &&
16
16
  typeof value === 'object' &&
@@ -68,22 +68,27 @@ async function renderPage({ route, serverContext, document, inlineCriticalCss, o
68
68
  },
69
69
  },
70
70
  ];
71
- let html;
72
71
  (0, node_assert_1.default)(bootstrapAppFnOrModule, 'The file "./main.server.mjs" does not have a default export for an AppServerModule or a bootstrapping function.');
72
+ let renderAppPromise;
73
73
  if (isBootstrapFn(bootstrapAppFnOrModule)) {
74
- html = await renderApplication(bootstrapAppFnOrModule, {
74
+ renderAppPromise = renderApplication(bootstrapAppFnOrModule, {
75
75
  document,
76
76
  url: route,
77
77
  platformProviders,
78
78
  });
79
79
  }
80
80
  else {
81
- html = await renderModule(bootstrapAppFnOrModule, {
81
+ renderAppPromise = renderModule(bootstrapAppFnOrModule, {
82
82
  document,
83
83
  url: route,
84
84
  extraProviders: platformProviders,
85
85
  });
86
86
  }
87
+ // The below should really handled by the framework!!!.
88
+ // See: https://github.com/angular/angular/issues/51549
89
+ let timer;
90
+ const renderingTimeout = new Promise((_, reject) => (timer = setTimeout(() => reject(new Error(`Page ${new URL(route, 'resolve://').pathname} did not render in 30 seconds.`)), 30_000)));
91
+ const html = await Promise.race([renderAppPromise, renderingTimeout]).finally(() => clearTimeout(timer));
87
92
  if (inlineCriticalCss) {
88
93
  const { InlineCriticalCssProcessor } = await Promise.resolve().then(() => __importStar(require('../../utils/index-file/inline-critical-css')));
89
94
  const inlineCriticalCssProcessor = new InlineCriticalCssProcessor({