@angular/build 20.2.0-next.2 → 20.2.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@angular/build",
3
- "version": "20.2.0-next.2",
3
+ "version": "20.2.0-rc.0",
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.2002.0-next.2",
26
+ "@angular-devkit/architect": "0.2002.0-rc.0",
27
27
  "@babel/core": "7.28.0",
28
28
  "@babel/helper-annotate-as-pure": "7.27.3",
29
29
  "@babel/helper-split-export-declaration": "7.24.7",
@@ -31,7 +31,7 @@
31
31
  "@vitejs/plugin-basic-ssl": "2.1.0",
32
32
  "beasties": "0.3.5",
33
33
  "browserslist": "^4.23.0",
34
- "esbuild": "0.25.8",
34
+ "esbuild": "0.25.9",
35
35
  "https-proxy-agent": "7.0.6",
36
36
  "istanbul-lib-instrument": "6.0.3",
37
37
  "jsonc-parser": "3.3.1",
@@ -41,16 +41,16 @@
41
41
  "parse5-html-rewriting-stream": "8.0.0",
42
42
  "picomatch": "4.0.3",
43
43
  "piscina": "5.1.3",
44
- "rolldown": "1.0.0-beta.29",
45
- "sass": "1.89.2",
44
+ "rolldown": "1.0.0-beta.32",
45
+ "sass": "1.90.0",
46
46
  "semver": "7.7.2",
47
47
  "source-map-support": "0.5.21",
48
48
  "tinyglobby": "0.2.14",
49
- "vite": "7.0.6",
49
+ "vite": "7.1.2",
50
50
  "watchpack": "2.4.4"
51
51
  },
52
52
  "optionalDependencies": {
53
- "lmdb": "3.4.1"
53
+ "lmdb": "3.4.2"
54
54
  },
55
55
  "peerDependencies": {
56
56
  "@angular/core": "^20.0.0 || ^20.2.0-next.0",
@@ -60,7 +60,7 @@
60
60
  "@angular/platform-browser": "^20.0.0 || ^20.2.0-next.0",
61
61
  "@angular/platform-server": "^20.0.0 || ^20.2.0-next.0",
62
62
  "@angular/service-worker": "^20.0.0 || ^20.2.0-next.0",
63
- "@angular/ssr": "^20.2.0-next.2",
63
+ "@angular/ssr": "^20.2.0-rc.0",
64
64
  "karma": "^6.4.0",
65
65
  "less": "^4.2.0",
66
66
  "ng-packagr": "^20.0.0 || ^20.2.0-next.0",
@@ -112,7 +112,7 @@
112
112
  "type": "git",
113
113
  "url": "https://github.com/angular/angular-cli.git"
114
114
  },
115
- "packageManager": "pnpm@9.15.9",
115
+ "packageManager": "pnpm@10.14.0",
116
116
  "engines": {
117
117
  "node": "^20.19.0 || ^22.12.0 || >=24.0.0",
118
118
  "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
@@ -6,4 +6,16 @@
6
6
  * found in the LICENSE file at https://angular.dev/license
7
7
  */
8
8
  import { BundleContextResult } from '../../tools/esbuild/bundler-context';
9
+ /**
10
+ * Optimizes the chunks of a build result using rolldown.
11
+ *
12
+ * This function takes the output of an esbuild build, identifies the main browser entry point,
13
+ * and uses rolldown to bundle and optimize the JavaScript chunks. The optimized chunks
14
+ * replace the original ones in the build result, and the metafile is updated to reflect
15
+ * the changes.
16
+ *
17
+ * @param original The original build result from esbuild.
18
+ * @param sourcemap A boolean or 'hidden' to control sourcemap generation.
19
+ * @returns A promise that resolves to the updated build result with optimized chunks.
20
+ */
9
21
  export declare function optimizeChunks(original: BundleContextResult, sourcemap: boolean | 'hidden'): Promise<BundleContextResult>;
@@ -16,6 +16,123 @@ const rolldown_1 = require("rolldown");
16
16
  const bundler_context_1 = require("../../tools/esbuild/bundler-context");
17
17
  const utils_1 = require("../../tools/esbuild/utils");
18
18
  const error_1 = require("../../utils/error");
19
+ /**
20
+ * Converts the output of a rolldown build into an esbuild-compatible metafile.
21
+ * @param rolldownOutput The output of a rolldown build.
22
+ * @param originalMetafile The original esbuild metafile from the build.
23
+ * @returns An esbuild-compatible metafile.
24
+ */
25
+ function rolldownToEsbuildMetafile(rolldownOutput, originalMetafile) {
26
+ const newMetafile = {
27
+ inputs: originalMetafile.inputs,
28
+ outputs: {},
29
+ };
30
+ const intermediateChunkSizes = {};
31
+ for (const [path, output] of Object.entries(originalMetafile.outputs)) {
32
+ intermediateChunkSizes[path] = Object.values(output.inputs).reduce((s, i) => s + i.bytesInOutput, 0);
33
+ }
34
+ for (const chunk of rolldownOutput) {
35
+ if (chunk.type === 'asset') {
36
+ newMetafile.outputs[chunk.fileName] = {
37
+ bytes: typeof chunk.source === 'string'
38
+ ? Buffer.byteLength(chunk.source, 'utf8')
39
+ : chunk.source.length,
40
+ inputs: {},
41
+ imports: [],
42
+ exports: [],
43
+ };
44
+ continue;
45
+ }
46
+ const newOutputInputs = {};
47
+ if (chunk.modules) {
48
+ for (const [moduleId, renderedModule] of Object.entries(chunk.modules)) {
49
+ const originalOutputEntry = originalMetafile.outputs[moduleId];
50
+ if (!originalOutputEntry?.inputs) {
51
+ continue;
52
+ }
53
+ const totalOriginalBytesInModule = intermediateChunkSizes[moduleId];
54
+ if (totalOriginalBytesInModule === 0) {
55
+ continue;
56
+ }
57
+ for (const [originalInputPath, originalInputInfo] of Object.entries(originalOutputEntry.inputs)) {
58
+ const proportion = originalInputInfo.bytesInOutput / totalOriginalBytesInModule;
59
+ const newBytesInOutput = Math.floor(renderedModule.renderedLength * proportion);
60
+ const existing = newOutputInputs[originalInputPath];
61
+ if (existing) {
62
+ existing.bytesInOutput += newBytesInOutput;
63
+ }
64
+ else {
65
+ newOutputInputs[originalInputPath] = { bytesInOutput: newBytesInOutput };
66
+ }
67
+ if (!newMetafile.inputs[originalInputPath]) {
68
+ newMetafile.inputs[originalInputPath] = originalMetafile.inputs[originalInputPath];
69
+ }
70
+ }
71
+ }
72
+ }
73
+ const imports = [
74
+ ...chunk.imports.map((path) => ({ path, kind: 'import-statement' })),
75
+ ...(chunk.dynamicImports?.map((path) => ({ path, kind: 'dynamic-import' })) ?? []),
76
+ ];
77
+ newMetafile.outputs[chunk.fileName] = {
78
+ bytes: Buffer.byteLength(chunk.code, 'utf8'),
79
+ inputs: newOutputInputs,
80
+ imports,
81
+ exports: chunk.exports ?? [],
82
+ entryPoint: chunk.isEntry && chunk.facadeModuleId
83
+ ? originalMetafile.outputs[chunk.facadeModuleId]?.entryPoint
84
+ : undefined,
85
+ };
86
+ }
87
+ return newMetafile;
88
+ }
89
+ /**
90
+ * Creates an InitialFileRecord object with a specified depth.
91
+ * @param depth The depth of the file in the dependency graph.
92
+ * @returns An InitialFileRecord object.
93
+ */
94
+ function createInitialFileRecord(depth) {
95
+ return {
96
+ type: 'script',
97
+ entrypoint: false,
98
+ external: false,
99
+ serverFile: false,
100
+ depth,
101
+ };
102
+ }
103
+ /**
104
+ * Creates an esbuild message object for a chunk optimization failure.
105
+ * @param message The error message detailing the cause of the failure.
106
+ * @returns A partial esbuild message object.
107
+ */
108
+ function createChunkOptimizationFailureMessage(message) {
109
+ // Most of these fields are not actually needed for printing the error
110
+ return {
111
+ id: '',
112
+ text: 'Chunk optimization failed',
113
+ detail: undefined,
114
+ pluginName: '',
115
+ location: null,
116
+ notes: [
117
+ {
118
+ text: message,
119
+ location: null,
120
+ },
121
+ ],
122
+ };
123
+ }
124
+ /**
125
+ * Optimizes the chunks of a build result using rolldown.
126
+ *
127
+ * This function takes the output of an esbuild build, identifies the main browser entry point,
128
+ * and uses rolldown to bundle and optimize the JavaScript chunks. The optimized chunks
129
+ * replace the original ones in the build result, and the metafile is updated to reflect
130
+ * the changes.
131
+ *
132
+ * @param original The original build result from esbuild.
133
+ * @param sourcemap A boolean or 'hidden' to control sourcemap generation.
134
+ * @returns A promise that resolves to the updated build result with optimized chunks.
135
+ */
19
136
  async function optimizeChunks(original, sourcemap) {
20
137
  // Failed builds cannot be optimized
21
138
  if (original.errors) {
@@ -32,8 +149,8 @@ async function optimizeChunks(original, sourcemap) {
32
149
  break;
33
150
  }
34
151
  }
35
- // No action required if no browser main entrypoint
36
- if (!mainFile) {
152
+ // No action required if no browser main entrypoint or metafile for stats
153
+ if (!mainFile || !original.metafile) {
37
154
  return original;
38
155
  }
39
156
  const chunks = {};
@@ -90,28 +207,28 @@ async function optimizeChunks(original, sourcemap) {
90
207
  catch (e) {
91
208
  (0, error_1.assertIsError)(e);
92
209
  return {
93
- errors: [
94
- // Most of these fields are not actually needed for printing the error
95
- {
96
- id: '',
97
- text: 'Chunk optimization failed',
98
- detail: undefined,
99
- pluginName: '',
100
- location: null,
101
- notes: [
102
- {
103
- text: e.message,
104
- location: null,
105
- },
106
- ],
107
- },
108
- ],
210
+ errors: [createChunkOptimizationFailureMessage(e.message)],
109
211
  warnings: original.warnings,
110
212
  };
111
213
  }
112
214
  finally {
113
215
  await bundle?.close();
114
216
  }
217
+ // Update metafile
218
+ const newMetafile = rolldownToEsbuildMetafile(optimizedOutput, original.metafile);
219
+ // Add back the outputs that were not part of the optimization
220
+ for (const [path, output] of Object.entries(original.metafile.outputs)) {
221
+ if (usedChunks.has(path)) {
222
+ continue;
223
+ }
224
+ newMetafile.outputs[path] = output;
225
+ for (const inputPath of Object.keys(output.inputs)) {
226
+ if (!newMetafile.inputs[inputPath]) {
227
+ newMetafile.inputs[inputPath] = original.metafile.inputs[inputPath];
228
+ }
229
+ }
230
+ }
231
+ original.metafile = newMetafile;
115
232
  // Remove used chunks and associated sourcemaps from the original result
116
233
  original.outputFiles = original.outputFiles.filter((file) => !usedChunks.has(file.path) &&
117
234
  !(file.path.endsWith('.map') && usedChunks.has(file.path.slice(0, -4))));
@@ -152,13 +269,7 @@ async function optimizeChunks(original, sourcemap) {
152
269
  }
153
270
  continue;
154
271
  }
155
- const record = {
156
- type: 'script',
157
- entrypoint: false,
158
- external: false,
159
- serverFile: false,
160
- depth: entryRecord.depth + 1,
161
- };
272
+ const record = createInitialFileRecord(entryRecord.depth + 1);
162
273
  entriesToAnalyze.push([importPath, record]);
163
274
  }
164
275
  }
package/src/private.d.ts CHANGED
@@ -34,6 +34,7 @@ export declare function createCompilerPlugin(pluginOptions: CompilerPluginOption
34
34
  }): import('esbuild').Plugin;
35
35
  export type { AngularCompilation } from './tools/angular/compilation';
36
36
  export { createAngularCompilation };
37
+ export { ComponentStylesheetBundler } from './tools/esbuild/angular/component-stylesheets';
37
38
  export * from './utils/bundle-calculator';
38
39
  export { checkPort } from './utils/check-port';
39
40
  export { deleteOutputDir } from './utils/delete-output-dir';
package/src/private.js CHANGED
@@ -21,7 +21,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
21
21
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
22
22
  };
23
23
  Object.defineProperty(exports, "__esModule", { value: true });
24
- exports.loadPostcssConfiguration = exports.generateSearchDirectories = exports.findTailwindConfiguration = exports.getTestEntrypoints = exports.findTests = exports.assertCompatibleAngularVersion = exports.getSupportedBrowsers = exports.generateBuildStatsTable = exports.augmentAppWithServiceWorker = exports.purgeStaleBuildCache = exports.createTranslationLoader = exports.loadProxyConfiguration = exports.InlineCriticalCssProcessor = exports.IndexHtmlGenerator = exports.loadTranslations = exports.createI18nOptions = exports.deleteOutputDir = exports.checkPort = exports.createAngularCompilation = exports.JavaScriptTransformer = exports.createJitResourceTransformer = exports.SourceFileCache = exports.SassWorkerImplementation = exports.transformSupportedBrowsersToTargets = exports.emitFilesToDisk = exports.serveWithVite = exports.ResultKind = exports.buildApplicationInternal = void 0;
24
+ exports.loadPostcssConfiguration = exports.generateSearchDirectories = exports.findTailwindConfiguration = exports.getTestEntrypoints = exports.findTests = exports.assertCompatibleAngularVersion = exports.getSupportedBrowsers = exports.generateBuildStatsTable = exports.augmentAppWithServiceWorker = exports.purgeStaleBuildCache = exports.createTranslationLoader = exports.loadProxyConfiguration = exports.InlineCriticalCssProcessor = exports.IndexHtmlGenerator = exports.loadTranslations = exports.createI18nOptions = exports.deleteOutputDir = exports.checkPort = exports.ComponentStylesheetBundler = exports.createAngularCompilation = exports.JavaScriptTransformer = exports.createJitResourceTransformer = exports.SourceFileCache = exports.SassWorkerImplementation = exports.transformSupportedBrowsersToTargets = exports.emitFilesToDisk = exports.serveWithVite = exports.ResultKind = exports.buildApplicationInternal = void 0;
25
25
  exports.createCompilerPlugin = createCompilerPlugin;
26
26
  /**
27
27
  * @fileoverview
@@ -59,6 +59,8 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
59
59
  ? new compilation_1.NoopCompilation()
60
60
  : () => (0, compilation_1.createAngularCompilation)(!!pluginOptions.jit, !!pluginOptions.browserOnlyBuild), new component_stylesheets_1.ComponentStylesheetBundler(styleOptions, styleOptions.inlineStyleLanguage, pluginOptions.incremental));
61
61
  }
62
+ var component_stylesheets_2 = require("./tools/esbuild/angular/component-stylesheets");
63
+ Object.defineProperty(exports, "ComponentStylesheetBundler", { enumerable: true, get: function () { return component_stylesheets_2.ComponentStylesheetBundler; } });
62
64
  // Utilities
63
65
  __exportStar(require("./utils/bundle-calculator"), exports);
64
66
  var check_port_1 = require("./utils/check-port");
@@ -10,7 +10,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.normalizeCacheOptions = normalizeCacheOptions;
11
11
  const node_path_1 = require("node:path");
12
12
  /** Version placeholder is replaced during the build process with actual package version */
13
- const VERSION = '20.2.0-next.2';
13
+ const VERSION = '20.2.0-rc.0';
14
14
  function hasCacheMetadata(value) {
15
15
  return (!!value &&
16
16
  typeof value === 'object' &&