@angular-devkit/build-angular 17.0.0-rc.1 → 17.0.0-rc.3

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 (47) hide show
  1. package/package.json +11 -10
  2. package/src/builders/app-shell/render-worker.d.ts +1 -1
  3. package/src/builders/app-shell/render-worker.js +16 -9
  4. package/src/builders/application/build-action.js +5 -2
  5. package/src/builders/application/execute-build.js +28 -18
  6. package/src/builders/browser-esbuild/index.js +9 -1
  7. package/src/builders/dev-server/vite-server.js +59 -20
  8. package/src/builders/extract-i18n/application-extraction.js +1 -0
  9. package/src/builders/prerender/routes-extractor-worker.js +1 -1
  10. package/src/tools/babel/plugins/elide-angular-metadata.js +14 -2
  11. package/src/tools/esbuild/angular/compiler-plugin.js +2 -1
  12. package/src/tools/esbuild/application-code-bundle.d.ts +3 -2
  13. package/src/tools/esbuild/application-code-bundle.js +8 -12
  14. package/src/tools/esbuild/bundler-context.d.ts +1 -1
  15. package/src/tools/esbuild/bundler-context.js +7 -2
  16. package/src/tools/esbuild/bundler-execution-result.d.ts +2 -0
  17. package/src/tools/esbuild/bundler-execution-result.js +14 -0
  18. package/src/tools/esbuild/commonjs-checker.js +3 -3
  19. package/src/tools/esbuild/global-scripts.d.ts +2 -3
  20. package/src/tools/esbuild/global-scripts.js +72 -69
  21. package/src/tools/esbuild/global-styles.d.ts +2 -3
  22. package/src/tools/esbuild/global-styles.js +35 -33
  23. package/src/tools/esbuild/javascript-transformer-worker.js +3 -2
  24. package/src/tools/esbuild/javascript-transformer.d.ts +1 -0
  25. package/src/tools/esbuild/javascript-transformer.js +25 -13
  26. package/src/tools/esbuild/utils.d.ts +1 -1
  27. package/src/tools/esbuild/utils.js +18 -4
  28. package/src/tools/esbuild/watcher.d.ts +1 -0
  29. package/src/tools/esbuild/watcher.js +3 -0
  30. package/src/utils/check-port.js +3 -1
  31. package/src/utils/environment-options.d.ts +1 -0
  32. package/src/utils/environment-options.js +3 -1
  33. package/src/utils/load-esm.d.ts +0 -2
  34. package/src/utils/routes-extractor/extractor.d.ts +1 -1
  35. package/src/utils/routes-extractor/extractor.js +2 -2
  36. package/src/utils/server-rendering/esm-in-memory-loader/loader-hooks.js +3 -2
  37. package/src/utils/server-rendering/fetch-patch.d.ts +8 -0
  38. package/src/utils/server-rendering/fetch-patch.js +66 -0
  39. package/src/utils/server-rendering/prerender.js +25 -30
  40. package/src/utils/server-rendering/render-worker.d.ts +4 -2
  41. package/src/utils/server-rendering/render-worker.js +8 -4
  42. package/src/utils/server-rendering/routes-extractor-worker.d.ts +5 -3
  43. package/src/utils/server-rendering/routes-extractor-worker.js +10 -4
  44. package/src/tools/esbuild/javascript-transfomer-plugin.d.ts +0 -19
  45. package/src/tools/esbuild/javascript-transfomer-plugin.js +0 -51
  46. package/src/utils/server-rendering/prerender-server.d.ts +0 -21
  47. package/src/utils/server-rendering/prerender-server.js +0 -102
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@angular-devkit/build-angular",
3
- "version": "17.0.0-rc.1",
3
+ "version": "17.0.0-rc.3",
4
4
  "description": "Angular Webpack Build Facade",
5
5
  "main": "src/index.js",
6
6
  "typings": "src/index.d.ts",
7
7
  "builders": "builders.json",
8
8
  "dependencies": {
9
9
  "@ampproject/remapping": "2.2.1",
10
- "@angular-devkit/architect": "0.1700.0-rc.1",
11
- "@angular-devkit/build-webpack": "0.1700.0-rc.1",
12
- "@angular-devkit/core": "17.0.0-rc.1",
10
+ "@angular-devkit/architect": "0.1700.0-rc.3",
11
+ "@angular-devkit/build-webpack": "0.1700.0-rc.3",
12
+ "@angular-devkit/core": "17.0.0-rc.3",
13
13
  "@babel/core": "7.23.2",
14
14
  "@babel/generator": "7.23.0",
15
15
  "@babel/helper-annotate-as-pure": "7.22.5",
@@ -20,7 +20,7 @@
20
20
  "@babel/preset-env": "7.23.2",
21
21
  "@babel/runtime": "7.23.2",
22
22
  "@discoveryjs/json-ext": "0.5.7",
23
- "@ngtools/webpack": "17.0.0-rc.1",
23
+ "@ngtools/webpack": "17.0.0-rc.3",
24
24
  "@vitejs/plugin-basic-ssl": "1.0.1",
25
25
  "ansi-colors": "4.1.3",
26
26
  "autoprefixer": "10.4.16",
@@ -36,7 +36,7 @@
36
36
  "fast-glob": "3.3.1",
37
37
  "https-proxy-agent": "7.0.2",
38
38
  "http-proxy-middleware": "2.0.6",
39
- "inquirer": "8.2.6",
39
+ "inquirer": "9.2.11",
40
40
  "jsonc-parser": "3.2.0",
41
41
  "karma-source-map-support": "1.4.0",
42
42
  "less": "4.2.0",
@@ -49,21 +49,22 @@
49
49
  "open": "8.4.2",
50
50
  "ora": "5.4.1",
51
51
  "parse5-html-rewriting-stream": "7.0.0",
52
- "picomatch": "2.3.1",
52
+ "picomatch": "3.0.1",
53
53
  "piscina": "4.1.0",
54
54
  "postcss": "8.4.31",
55
55
  "postcss-loader": "7.3.3",
56
56
  "resolve-url-loader": "5.0.0",
57
57
  "rxjs": "7.8.1",
58
- "sass": "1.69.4",
58
+ "sass": "1.69.5",
59
59
  "sass-loader": "13.3.2",
60
60
  "semver": "7.5.4",
61
61
  "source-map-loader": "4.0.1",
62
62
  "source-map-support": "0.5.21",
63
- "terser": "5.22.0",
63
+ "terser": "5.24.0",
64
64
  "text-table": "0.2.0",
65
65
  "tree-kill": "1.2.2",
66
66
  "tslib": "2.6.2",
67
+ "undici": "5.27.0",
67
68
  "vite": "4.5.0",
68
69
  "webpack": "5.89.0",
69
70
  "webpack-dev-middleware": "6.1.1",
@@ -128,7 +129,7 @@
128
129
  "url": "https://github.com/angular/angular-cli.git"
129
130
  },
130
131
  "engines": {
131
- "node": ">=18.13.0",
132
+ "node": "^18.13.0 || >=20.9.0",
132
133
  "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
133
134
  "yarn": ">= 1.13.0"
134
135
  },
@@ -20,7 +20,7 @@ interface RenderRequest {
20
20
  /**
21
21
  * An optional URL path that represents the Angular route that should be rendered.
22
22
  */
23
- url: string | undefined;
23
+ url: string;
24
24
  }
25
25
  /**
26
26
  * Renders an application based on a provided server bundle path, initial document, and optional URL route.
@@ -54,23 +54,30 @@ async function render({ serverBundlePath, document, url }) {
54
54
  useValue: 'app-shell',
55
55
  },
56
56
  ];
57
+ let renderAppPromise;
57
58
  // Render platform server module
58
59
  if (isBootstrapFn(bootstrapAppFn)) {
59
60
  (0, node_assert_1.default)(renderApplication, `renderApplication was not exported from: ${serverBundlePath}.`);
60
- return renderApplication(bootstrapAppFn, {
61
+ renderAppPromise = renderApplication(bootstrapAppFn, {
61
62
  document,
62
63
  url,
63
64
  platformProviders,
64
65
  });
65
66
  }
66
- (0, node_assert_1.default)(renderModule, `renderModule was not exported from: ${serverBundlePath}.`);
67
- const moduleClass = bootstrapAppFn || AppServerModule;
68
- (0, node_assert_1.default)(moduleClass, `Neither an AppServerModule nor a bootstrapping function was exported from: ${serverBundlePath}.`);
69
- return renderModule(moduleClass, {
70
- document,
71
- url,
72
- extraProviders: platformProviders,
73
- });
67
+ else {
68
+ (0, node_assert_1.default)(renderModule, `renderModule was not exported from: ${serverBundlePath}.`);
69
+ const moduleClass = bootstrapAppFn || AppServerModule;
70
+ (0, node_assert_1.default)(moduleClass, `Neither an AppServerModule nor a bootstrapping function was exported from: ${serverBundlePath}.`);
71
+ renderAppPromise = renderModule(moduleClass, {
72
+ document,
73
+ url,
74
+ extraProviders: platformProviders,
75
+ });
76
+ }
77
+ // The below should really handled by the framework!!!.
78
+ let timer;
79
+ const renderingTimeout = new Promise((_, reject) => (timer = setTimeout(() => reject(new Error(`Page ${new URL(url, 'resolve://').pathname} did not render in 30 seconds.`)), 30000)));
80
+ return Promise.race([renderAppPromise, renderingTimeout]).finally(() => clearTimeout(timer));
74
81
  }
75
82
  function isBootstrapFn(value) {
76
83
  // We can differentiate between a module and a bootstrap function by reading compiler-generated `ɵmod` static property:
@@ -38,6 +38,7 @@ const promises_1 = __importDefault(require("node:fs/promises"));
38
38
  const node_path_1 = __importDefault(require("node:path"));
39
39
  const sass_language_1 = require("../../tools/esbuild/stylesheets/sass-language");
40
40
  const utils_1 = require("../../tools/esbuild/utils");
41
+ const environment_options_1 = require("../../utils/environment-options");
41
42
  const error_1 = require("../../utils/error");
42
43
  async function* runEsBuildBuildAction(action, options) {
43
44
  const { writeToFileSystemFilter, writeToFileSystem = true, watch, poll, logger, deleteOutputPath, cacheOptions, outputPath, verbose, projectRoot, workspaceRoot, progress, } = options;
@@ -95,8 +96,10 @@ async function* runEsBuildBuildAction(action, options) {
95
96
  });
96
97
  // Setup abort support
97
98
  options.signal?.addEventListener('abort', () => void watcher?.close());
98
- // Temporarily watch the entire project
99
- watcher.add(projectRoot);
99
+ // Watch the entire project root if 'NG_BUILD_WATCH_ROOT' environment variable is set
100
+ if (environment_options_1.shouldWatchRoot) {
101
+ watcher.add(projectRoot);
102
+ }
100
103
  // Watch workspace for package manager changes
101
104
  const packageWatchFiles = [
102
105
  // manifest can affect module resolution
@@ -19,6 +19,7 @@ const global_styles_1 = require("../../tools/esbuild/global-styles");
19
19
  const license_extractor_1 = require("../../tools/esbuild/license-extractor");
20
20
  const utils_1 = require("../../tools/esbuild/utils");
21
21
  const bundle_calculator_1 = require("../../utils/bundle-calculator");
22
+ const color_1 = require("../../utils/color");
22
23
  const copy_assets_1 = require("../../utils/copy-assets");
23
24
  const supported_browsers_1 = require("../../utils/supported-browsers");
24
25
  const execute_post_bundle_1 = require("./execute-post-bundle");
@@ -49,7 +50,7 @@ async function executeBuild(options, context, rebuildState) {
49
50
  // Global Stylesheets
50
51
  if (options.globalStyles.length > 0) {
51
52
  for (const initial of [true, false]) {
52
- const bundleOptions = (0, global_styles_1.createGlobalStylesBundleOptions)(options, target, initial, codeBundleCache?.loadResultCache);
53
+ const bundleOptions = (0, global_styles_1.createGlobalStylesBundleOptions)(options, target, initial);
53
54
  if (bundleOptions) {
54
55
  bundlerContexts.push(new bundler_context_1.BundlerContext(workspaceRoot, !!options.watch, bundleOptions, () => initial));
55
56
  }
@@ -76,7 +77,7 @@ async function executeBuild(options, context, rebuildState) {
76
77
  }
77
78
  }
78
79
  }
79
- const bundlingResult = await bundler_context_1.BundlerContext.bundleAll(bundlerContexts);
80
+ const bundlingResult = await bundler_context_1.BundlerContext.bundleAll(bundlerContexts, rebuildState?.fileChanges.all);
80
81
  // Log all warnings and errors generated during bundling
81
82
  await (0, utils_1.logMessages)(context, bundlingResult);
82
83
  const executionResult = new bundler_execution_result_1.ExecutionResult(bundlerContexts, codeBundleCache);
@@ -112,13 +113,15 @@ async function executeBuild(options, context, rebuildState) {
112
113
  if (options.budgets) {
113
114
  const compatStats = (0, budget_stats_1.generateBudgetStats)(metafile, initialFiles);
114
115
  budgetFailures = [...(0, bundle_calculator_1.checkBudgets)(options.budgets, compatStats, true)];
115
- for (const { severity, message } of budgetFailures) {
116
- if (severity === 'error') {
117
- context.logger.error(message);
118
- }
119
- else {
120
- context.logger.warn(message);
121
- }
116
+ if (budgetFailures.length > 0) {
117
+ await (0, utils_1.logMessages)(context, {
118
+ errors: budgetFailures
119
+ .filter((failure) => failure.severity === 'error')
120
+ .map((failure) => ({ text: failure.message, location: null })),
121
+ warnings: budgetFailures
122
+ .filter((failure) => failure.severity !== 'error')
123
+ .map((failure) => ({ text: failure.message, location: null })),
124
+ });
122
125
  }
123
126
  }
124
127
  // Calculate estimated transfer size if scripts are optimized
@@ -146,11 +149,20 @@ async function executeBuild(options, context, rebuildState) {
146
149
  executionResult.outputFiles.push(...result.additionalOutputFiles);
147
150
  executionResult.assetFiles.push(...result.additionalAssets);
148
151
  }
152
+ await printWarningsAndErrorsToConsole(context, warnings, errors);
149
153
  if (prerenderOptions) {
150
154
  executionResult.addOutputFile('prerendered-routes.json', JSON.stringify({ routes: prerenderedRoutes.sort((a, b) => a.localeCompare(b)) }, null, 2), bundler_context_1.BuildOutputFileType.Root);
155
+ let prerenderMsg = `Prerendered ${prerenderedRoutes.length} static route`;
156
+ if (prerenderedRoutes.length > 1) {
157
+ prerenderMsg += 's.';
158
+ }
159
+ else {
160
+ prerenderMsg += '.';
161
+ }
162
+ context.logger.info(color_1.colors.magenta(prerenderMsg) + '\n');
151
163
  }
152
- printWarningsAndErrorsToConsole(context, warnings, errors);
153
- (0, utils_1.logBuildStats)(context, metafile, initialFiles, budgetFailures, estimatedTransferSizes);
164
+ const changedFiles = rebuildState && executionResult.findChangedFiles(rebuildState.previousOutputHashes);
165
+ (0, utils_1.logBuildStats)(context, metafile, initialFiles, budgetFailures, changedFiles, estimatedTransferSizes);
154
166
  // Write metafile if stats option is enabled
155
167
  if (options.stats) {
156
168
  executionResult.addOutputFile('stats.json', JSON.stringify(metafile, null, 2), bundler_context_1.BuildOutputFileType.Root);
@@ -158,11 +170,9 @@ async function executeBuild(options, context, rebuildState) {
158
170
  return executionResult;
159
171
  }
160
172
  exports.executeBuild = executeBuild;
161
- function printWarningsAndErrorsToConsole(context, warnings, errors) {
162
- for (const error of errors) {
163
- context.logger.error(error);
164
- }
165
- for (const warning of warnings) {
166
- context.logger.warn(warning);
167
- }
173
+ async function printWarningsAndErrorsToConsole(context, warnings, errors) {
174
+ await (0, utils_1.logMessages)(context, {
175
+ errors: errors.map((text) => ({ text, location: null })),
176
+ warnings: warnings.map((text) => ({ text, location: null })),
177
+ });
168
178
  }
@@ -37,7 +37,15 @@ async function* buildEsbuildBrowser(userOptions, context, infrastructureSettings
37
37
  // Write output files
38
38
  await writeResultFiles(result.outputFiles, result.assetFiles, fullOutputPath);
39
39
  }
40
- yield result;
40
+ // The builder system (architect) currently attempts to treat all results as JSON and
41
+ // attempts to validate the object with a JSON schema validator. This can lead to slow
42
+ // build completion (even after the actual build is fully complete) or crashes if the
43
+ // size and/or quantity of output files is large. Architect only requires a `success`
44
+ // property so that is all that will be passed here if the infrastructure settings have
45
+ // not been explicitly set to avoid writes. Writing is only disabled when used directly
46
+ // by the dev server which bypasses the architect behavior.
47
+ const builderResult = infrastructureSettings?.write === false ? result : { success: result.success };
48
+ yield builderResult;
41
49
  }
42
50
  }
43
51
  exports.buildEsbuildBrowser = buildEsbuildBrowser;
@@ -37,6 +37,7 @@ exports.setupServer = exports.serveWithVite = void 0;
37
37
  const remapping_1 = __importDefault(require("@ampproject/remapping"));
38
38
  const mrmime_1 = require("mrmime");
39
39
  const node_assert_1 = __importDefault(require("node:assert"));
40
+ const node_crypto_1 = require("node:crypto");
40
41
  const promises_1 = require("node:fs/promises");
41
42
  const node_path_1 = __importDefault(require("node:path"));
42
43
  const bundler_context_1 = require("../../tools/esbuild/bundler-context");
@@ -148,14 +149,16 @@ async function* serveWithVite(serverOptions, builderName, context, plugins) {
148
149
  }
149
150
  // To avoid disconnecting the array objects from the option, these arrays need to be mutated
150
151
  // instead of replaced.
151
- if (result.externalMetadata.explicit) {
152
- externalMetadata.explicit.push(...result.externalMetadata.explicit);
153
- }
154
- if (result.externalMetadata.implicit) {
155
- externalMetadata.implicit.push(...result.externalMetadata.implicit);
152
+ if (result.externalMetadata) {
153
+ if (result.externalMetadata.explicit) {
154
+ externalMetadata.explicit.push(...result.externalMetadata.explicit);
155
+ }
156
+ if (result.externalMetadata.implicit) {
157
+ externalMetadata.implicit.push(...result.externalMetadata.implicit);
158
+ }
156
159
  }
157
160
  if (server) {
158
- handleUpdate(generatedFiles, server, serverOptions, context.logger);
161
+ handleUpdate(normalizePath, generatedFiles, server, serverOptions, context.logger);
159
162
  }
160
163
  else {
161
164
  const projectName = context.target?.project;
@@ -187,13 +190,13 @@ async function* serveWithVite(serverOptions, builderName, context, plugins) {
187
190
  await new Promise((resolve) => (deferred = resolve));
188
191
  }
189
192
  exports.serveWithVite = serveWithVite;
190
- function handleUpdate(generatedFiles, server, serverOptions, logger) {
193
+ function handleUpdate(normalizePath, generatedFiles, server, serverOptions, logger) {
191
194
  const updatedFiles = [];
192
195
  // Invalidate any updated files
193
196
  for (const [file, record] of generatedFiles) {
194
197
  if (record.updated) {
195
198
  updatedFiles.push(file);
196
- const updatedModules = server.moduleGraph.getModulesByFile(file);
199
+ const updatedModules = server.moduleGraph.getModulesByFile(normalizePath(node_path_1.default.join(server.config.root, file)));
197
200
  updatedModules?.forEach((m) => server?.moduleGraph.invalidateModule(m));
198
201
  }
199
202
  }
@@ -205,8 +208,7 @@ function handleUpdate(generatedFiles, server, serverOptions, logger) {
205
208
  const timestamp = Date.now();
206
209
  server.ws.send({
207
210
  type: 'update',
208
- updates: updatedFiles.map((f) => {
209
- const filePath = f.slice(1); // Remove leading slash.
211
+ updates: updatedFiles.map((filePath) => {
210
212
  return {
211
213
  type: 'css-update',
212
214
  timestamp,
@@ -238,7 +240,7 @@ function analyzeResultFiles(normalizePath, htmlIndexPath, resultFiles, generated
238
240
  filePath = '/index.html';
239
241
  }
240
242
  else {
241
- filePath = '/' + normalizePath(file.path);
243
+ filePath = normalizePath(file.path);
242
244
  }
243
245
  seen.add(filePath);
244
246
  // Skip analysis of sourcemaps
@@ -280,11 +282,13 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
280
282
  const proxy = await (0, load_proxy_config_1.loadProxyConfiguration)(serverOptions.workspaceRoot, serverOptions.proxyConfig, true);
281
283
  // dynamically import Vite for ESM compatibility
282
284
  const { normalizePath } = await Promise.resolve().then(() => __importStar(require('vite')));
285
+ // Path will not exist on disk and only used to provide separate path for Vite requests
286
+ const virtualProjectRoot = normalizePath(node_path_1.default.join(serverOptions.workspaceRoot, `.angular/vite-root/${(0, node_crypto_1.randomUUID)()}/`));
283
287
  const configuration = {
284
288
  configFile: false,
285
289
  envFile: false,
286
290
  cacheDir: node_path_1.default.join(serverOptions.cacheOptions.path, 'vite'),
287
- root: serverOptions.workspaceRoot,
291
+ root: virtualProjectRoot,
288
292
  publicDir: false,
289
293
  esbuild: false,
290
294
  mode: 'development',
@@ -308,10 +312,13 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
308
312
  watch: {
309
313
  ignored: ['**/*'],
310
314
  },
315
+ // This is needed when `externalDependencies` is used to prevent Vite load errors.
316
+ // NOTE: If Vite adds direct support for externals, this can be removed.
317
+ preTransformRequests: externalMetadata.explicit.length === 0,
311
318
  },
312
319
  ssr: {
313
320
  // Exclude any provided dependencies (currently build defined externals)
314
- external: externalMetadata.implicit,
321
+ external: externalMetadata.explicit,
315
322
  },
316
323
  plugins: [
317
324
  (0, i18n_locale_plugin_1.createAngularLocaleDataPlugin)(),
@@ -320,24 +327,37 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
320
327
  // Ensures plugin hooks run before built-in Vite hooks
321
328
  enforce: 'pre',
322
329
  async resolveId(source, importer) {
323
- if (importer && source.startsWith('.')) {
330
+ // Prevent vite from resolving an explicit external dependency (`externalDependencies` option)
331
+ if (externalMetadata.explicit.includes(source)) {
332
+ // This is still not ideal since Vite will still transform the import specifier to
333
+ // `/@id/${source}` but is currently closer to a raw external than a resolved file path.
334
+ return source;
335
+ }
336
+ if (importer && source[0] === '.' && importer.startsWith(virtualProjectRoot)) {
324
337
  // Remove query if present
325
338
  const [importerFile] = importer.split('?', 1);
326
- source = normalizePath(node_path_1.default.join(node_path_1.default.dirname(importerFile), source));
339
+ source = normalizePath(node_path_1.default.join(node_path_1.default.dirname(node_path_1.default.relative(virtualProjectRoot, importerFile)), source));
340
+ }
341
+ if (source[0] === '/') {
342
+ source = source.slice(1);
327
343
  }
328
344
  const [file] = source.split('?', 1);
329
345
  if (outputFiles.has(file)) {
330
- return source;
346
+ return node_path_1.default.join(virtualProjectRoot, source);
331
347
  }
332
348
  },
333
349
  load(id) {
334
350
  const [file] = id.split('?', 1);
335
- const codeContents = outputFiles.get(file)?.contents;
351
+ const relativeFile = normalizePath(node_path_1.default.relative(virtualProjectRoot, file));
352
+ const codeContents = outputFiles.get(relativeFile)?.contents;
336
353
  if (codeContents === undefined) {
354
+ if (relativeFile.endsWith('/node_modules/vite/dist/client/client.mjs')) {
355
+ return loadViteClientCode(file);
356
+ }
337
357
  return;
338
358
  }
339
359
  const code = Buffer.from(codeContents).toString('utf-8');
340
- const mapContents = outputFiles.get(file + '.map')?.contents;
360
+ const mapContents = outputFiles.get(relativeFile + '.map')?.contents;
341
361
  return {
342
362
  // Remove source map URL comments from the code if a sourcemap is present.
343
363
  // Vite will inline and add an additional sourcemap URL for the sourcemap.
@@ -411,15 +431,17 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
411
431
  next();
412
432
  return;
413
433
  }
414
- const rawHtml = outputFiles.get('/index.server.html')?.contents;
434
+ const rawHtml = outputFiles.get('index.server.html')?.contents;
415
435
  if (!rawHtml) {
416
436
  next();
417
437
  return;
418
438
  }
419
439
  transformIndexHtmlAndAddHeaders(url, rawHtml, res, next, async (html) => {
440
+ const protocol = serverOptions.ssl ? 'https' : 'http';
441
+ const route = `${protocol}://${req.headers.host}${req.originalUrl}`;
420
442
  const { content } = await (0, render_page_1.renderPage)({
421
443
  document: html,
422
- route: pathnameWithoutServePath(url, serverOptions),
444
+ route,
423
445
  serverContext: 'ssr',
424
446
  loadBundle: (path) =>
425
447
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -525,6 +547,23 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
525
547
  return configuration;
526
548
  }
527
549
  exports.setupServer = setupServer;
550
+ /**
551
+ * Reads the resolved Vite client code from disk and updates the content to remove
552
+ * an unactionable suggestion to update the Vite configuration file to disable the
553
+ * error overlay. The Vite configuration file is not present when used in the Angular
554
+ * CLI.
555
+ * @param file The absolute path to the Vite client code.
556
+ * @returns
557
+ */
558
+ async function loadViteClientCode(file) {
559
+ const originalContents = await (0, promises_1.readFile)(file, 'utf-8');
560
+ let contents = originalContents.replace('You can also disable this overlay by setting', '');
561
+ contents = contents.replace(
562
+ // eslint-disable-next-line max-len
563
+ '<code part="config-option-name">server.hmr.overlay</code> to <code part="config-option-value">false</code> in <code part="config-file-name">vite.config.js.</code>', '');
564
+ (0, node_assert_1.default)(originalContents !== contents, 'Failed to update Vite client error overlay text.');
565
+ return contents;
566
+ }
528
567
  function pathnameWithoutServePath(url, serverOptions) {
529
568
  const parsedUrl = new URL(url, 'http://localhost');
530
569
  let pathname = decodeURIComponent(parsedUrl.pathname);
@@ -22,6 +22,7 @@ async function extractMessages(options, builderName, context, extractorConstruct
22
22
  buildOptions.optimization = false;
23
23
  buildOptions.sourceMap = { scripts: true, vendor: true };
24
24
  buildOptions.localize = false;
25
+ buildOptions.budgets = undefined;
25
26
  let build;
26
27
  if (builderName === '@angular-devkit/build-angular:application') {
27
28
  build = application_1.buildApplicationInternal;
@@ -45,7 +45,7 @@ async function extract() {
45
45
  const bootstrapAppFnOrModule = bootstrapAppFn || AppServerModule;
46
46
  (0, node_assert_1.default)(bootstrapAppFnOrModule, `Neither an AppServerModule nor a bootstrapping function was exported from: ${serverBundlePath}.`);
47
47
  const routes = [];
48
- for await (const { route, success } of extractRoutes(bootstrapAppFnOrModule, document, '')) {
48
+ for await (const { route, success } of extractRoutes(bootstrapAppFnOrModule, document)) {
49
49
  if (success) {
50
50
  routes.push(route);
51
51
  }
@@ -17,6 +17,10 @@ const SET_CLASS_METADATA_NAME = 'ɵsetClassMetadata';
17
17
  * Name of the asynchronous Angular class metadata function created by the Angular compiler.
18
18
  */
19
19
  const SET_CLASS_METADATA_ASYNC_NAME = 'ɵsetClassMetadataAsync';
20
+ /**
21
+ * Name of the function that sets debug information on classes.
22
+ */
23
+ const SET_CLASS_DEBUG_INFO_NAME = 'ɵsetClassDebugInfo';
20
24
  /**
21
25
  * Provides one or more keywords that if found within the content of a source file indicate
22
26
  * that this plugin should be used with a source file.
@@ -24,7 +28,7 @@ const SET_CLASS_METADATA_ASYNC_NAME = 'ɵsetClassMetadataAsync';
24
28
  * @returns An a string iterable containing one or more keywords.
25
29
  */
26
30
  function getKeywords() {
27
- return [SET_CLASS_METADATA_NAME, SET_CLASS_METADATA_ASYNC_NAME];
31
+ return [SET_CLASS_METADATA_NAME, SET_CLASS_METADATA_ASYNC_NAME, SET_CLASS_DEBUG_INFO_NAME];
28
32
  }
29
33
  exports.getKeywords = getKeywords;
30
34
  /**
@@ -48,7 +52,8 @@ function default_1() {
48
52
  }
49
53
  if (calleeName !== undefined &&
50
54
  (isRemoveClassMetadataCall(calleeName, callArguments) ||
51
- isRemoveClassmetadataAsyncCall(calleeName, callArguments))) {
55
+ isRemoveClassmetadataAsyncCall(calleeName, callArguments) ||
56
+ isSetClassDebugInfoCall(calleeName, callArguments))) {
52
57
  // The metadata function is always emitted inside a function expression
53
58
  const parent = path.getFunctionParent();
54
59
  if (parent && (parent.isFunctionExpression() || parent.isArrowFunctionExpression())) {
@@ -84,6 +89,13 @@ function isRemoveClassmetadataAsyncCall(name, args) {
84
89
  isInlineFunction(args[1]) &&
85
90
  isInlineFunction(args[2]));
86
91
  }
92
+ /** Determines if a function call is a call to `setClassDebugInfo`. */
93
+ function isSetClassDebugInfoCall(name, args) {
94
+ return (name === SET_CLASS_DEBUG_INFO_NAME &&
95
+ args.length === 2 &&
96
+ core_1.types.isIdentifier(args[0]) &&
97
+ core_1.types.isObjectExpression(args[1]));
98
+ }
87
99
  /** Determines if a node is an inline function expression. */
88
100
  function isInlineFunction(node) {
89
101
  return core_1.types.isFunctionExpression(node) || core_1.types.isArrowFunctionExpression(node);
@@ -172,7 +172,8 @@ function createCompilerPlugin(pluginOptions, styleOptions) {
172
172
  // Return bundled worker file entry name to be used in the built output
173
173
  const workerCodeFile = workerResult.outputFiles.find((file) => file.path.endsWith('.js'));
174
174
  (0, node_assert_1.default)(workerCodeFile, 'Web Worker bundled code file should always be present.');
175
- return path.relative(build.initialOptions.outdir ?? '', workerCodeFile.path);
175
+ const workerCodePath = path.relative(build.initialOptions.outdir ?? '', workerCodeFile.path);
176
+ return workerCodePath.replaceAll('\\', '/');
176
177
  },
177
178
  };
178
179
  // Initialize the Angular compilation for the current build.
@@ -8,12 +8,13 @@
8
8
  import type { BuildOptions } from 'esbuild';
9
9
  import type { NormalizedApplicationBuildOptions } from '../../builders/application/options';
10
10
  import { SourceFileCache } from './angular/source-file-cache';
11
+ import { BundlerOptionsFactory } from './bundler-context';
11
12
  export declare function createBrowserCodeBundleOptions(options: NormalizedApplicationBuildOptions, target: string[], sourceFileCache?: SourceFileCache): BuildOptions;
12
- export declare function createBrowserPolyfillBundleOptions(options: NormalizedApplicationBuildOptions, target: string[], sourceFileCache?: SourceFileCache): BuildOptions | undefined;
13
+ export declare function createBrowserPolyfillBundleOptions(options: NormalizedApplicationBuildOptions, target: string[], sourceFileCache?: SourceFileCache): BuildOptions | BundlerOptionsFactory | undefined;
13
14
  /**
14
15
  * Create an esbuild 'build' options object for the server bundle.
15
16
  * @param options The builder's user-provider normalized options.
16
17
  * @returns An esbuild BuildOptions object.
17
18
  */
18
19
  export declare function createServerCodeBundleOptions(options: NormalizedApplicationBuildOptions, target: string[], sourceFileCache: SourceFileCache): BuildOptions;
19
- export declare function createServerPolyfillBundleOptions(options: NormalizedApplicationBuildOptions, target: string[], sourceFileCache?: SourceFileCache): BuildOptions | undefined;
20
+ export declare function createServerPolyfillBundleOptions(options: NormalizedApplicationBuildOptions, target: string[], sourceFileCache?: SourceFileCache): BundlerOptionsFactory | undefined;
@@ -19,7 +19,6 @@ const environment_options_1 = require("../../utils/environment-options");
19
19
  const compiler_plugin_1 = require("./angular/compiler-plugin");
20
20
  const compiler_plugin_options_1 = require("./compiler-plugin-options");
21
21
  const i18n_locale_plugin_1 = require("./i18n-locale-plugin");
22
- const javascript_transfomer_plugin_1 = require("./javascript-transfomer-plugin");
23
22
  const rxjs_esm_resolution_plugin_1 = require("./rxjs-esm-resolution-plugin");
24
23
  const sourcemap_ignorelist_plugin_1 = require("./sourcemap-ignorelist-plugin");
25
24
  const utils_1 = require("./utils");
@@ -64,6 +63,7 @@ function createBrowserPolyfillBundleOptions(options, target, sourceFileCache) {
64
63
  return;
65
64
  }
66
65
  const { outputNames, polyfills } = options;
66
+ const hasTypeScriptEntries = polyfills?.some((entry) => /\.[cm]?tsx?$/.test(entry));
67
67
  const buildOptions = {
68
68
  ...polyfillBundleOptions,
69
69
  platform: 'browser',
@@ -79,7 +79,6 @@ function createBrowserPolyfillBundleOptions(options, target, sourceFileCache) {
79
79
  },
80
80
  };
81
81
  // Only add the Angular TypeScript compiler if TypeScript files are provided in the polyfills
82
- const hasTypeScriptEntries = polyfills?.some((entry) => /\.[cm]?tsx?$/.test(entry));
83
82
  if (hasTypeScriptEntries) {
84
83
  buildOptions.plugins ??= [];
85
84
  const { pluginOptions, styleOptions } = (0, compiler_plugin_options_1.createCompilerPluginOptions)(options, target, sourceFileCache);
@@ -89,7 +88,10 @@ function createBrowserPolyfillBundleOptions(options, target, sourceFileCache) {
89
88
  // Component stylesheet options are unused for polyfills but required by the plugin
90
89
  styleOptions));
91
90
  }
92
- return buildOptions;
91
+ // Use an options factory to allow fully incremental bundling when no TypeScript files are present.
92
+ // The TypeScript compilation is not currently integrated into the bundler invalidation so
93
+ // cannot be used with fully incremental bundling yet.
94
+ return hasTypeScriptEntries ? buildOptions : () => buildOptions;
93
95
  }
94
96
  exports.createBrowserPolyfillBundleOptions = createBrowserPolyfillBundleOptions;
95
97
  /**
@@ -195,7 +197,7 @@ function createServerPolyfillBundleOptions(options, target, sourceFileCache) {
195
197
  if (!polyfillBundleOptions) {
196
198
  return;
197
199
  }
198
- const { workspaceRoot, jit, sourcemapOptions, advancedOptimizations } = options;
200
+ const { workspaceRoot } = options;
199
201
  const buildOptions = {
200
202
  ...polyfillBundleOptions,
201
203
  platform: 'node',
@@ -232,14 +234,8 @@ function createServerPolyfillBundleOptions(options, target, sourceFileCache) {
232
234
  }),
233
235
  }));
234
236
  }
235
- buildOptions.plugins.push((0, rxjs_esm_resolution_plugin_1.createRxjsEsmResolutionPlugin)(), (0, javascript_transfomer_plugin_1.createJavaScriptTransformerPlugin)({
236
- jit,
237
- sourcemap: !!sourcemapOptions.scripts,
238
- babelFileCache: sourceFileCache?.babelFileCache,
239
- advancedOptimizations,
240
- maxWorkers: 1,
241
- }));
242
- return buildOptions;
237
+ buildOptions.plugins.push((0, rxjs_esm_resolution_plugin_1.createRxjsEsmResolutionPlugin)());
238
+ return () => buildOptions;
243
239
  }
244
240
  exports.createServerPolyfillBundleOptions = createServerPolyfillBundleOptions;
245
241
  function getEsBuildCommonOptions(options) {
@@ -43,7 +43,7 @@ export declare class BundlerContext {
43
43
  private initialFilter?;
44
44
  readonly watchFiles: Set<string>;
45
45
  constructor(workspaceRoot: string, incremental: boolean, options: BuildOptions | BundlerOptionsFactory, initialFilter?: ((initial: Readonly<InitialFileRecord>) => boolean) | undefined);
46
- static bundleAll(contexts: Iterable<BundlerContext>): Promise<BundleContextResult>;
46
+ static bundleAll(contexts: Iterable<BundlerContext>, changedFiles?: Iterable<string>): Promise<BundleContextResult>;
47
47
  /**
48
48
  * Executes the esbuild build function and normalizes the build result in the event of a
49
49
  * build failure that results in no output being generated.
@@ -53,8 +53,13 @@ class BundlerContext {
53
53
  };
54
54
  };
55
55
  }
56
- static async bundleAll(contexts) {
57
- const individualResults = await Promise.all([...contexts].map((context) => context.bundle()));
56
+ static async bundleAll(contexts, changedFiles) {
57
+ const individualResults = await Promise.all([...contexts].map((context) => {
58
+ if (changedFiles) {
59
+ context.invalidate(changedFiles);
60
+ }
61
+ return context.bundle();
62
+ }));
58
63
  // Return directly if only one result
59
64
  if (individualResults.length === 1) {
60
65
  return individualResults[0];
@@ -17,6 +17,7 @@ export interface RebuildState {
17
17
  rebuildContexts: BundlerContext[];
18
18
  codeBundleCache?: SourceFileCache;
19
19
  fileChanges: ChangedFiles;
20
+ previousOutputHashes: Map<string, string>;
20
21
  }
21
22
  /**
22
23
  * Represents the result of a single builder execute call.
@@ -57,5 +58,6 @@ export declare class ExecutionResult {
57
58
  };
58
59
  get watchFiles(): string[];
59
60
  createRebuildState(fileChanges: ChangedFiles): RebuildState;
61
+ findChangedFiles(previousOutputHashes: Map<string, string>): Set<string>;
60
62
  dispose(): Promise<void>;
61
63
  }