@angular/build 18.2.0-next.0 → 18.2.0-next.2

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 (38) hide show
  1. package/package.json +10 -11
  2. package/src/builders/application/build-action.d.ts +3 -4
  3. package/src/builders/application/build-action.js +39 -5
  4. package/src/builders/application/execute-build.js +2 -0
  5. package/src/builders/application/index.d.ts +2 -1
  6. package/src/builders/application/index.js +12 -11
  7. package/src/builders/application/options.js +1 -1
  8. package/src/builders/application/results.d.ts +67 -0
  9. package/src/builders/application/results.js +17 -0
  10. package/src/builders/dev-server/vite-server.d.ts +2 -2
  11. package/src/builders/dev-server/vite-server.js +63 -46
  12. package/src/builders/extract-i18n/application-extraction.d.ts +2 -2
  13. package/src/builders/extract-i18n/application-extraction.js +26 -32
  14. package/src/builders/extract-i18n/builder.js +7 -7
  15. package/src/builders/extract-i18n/options.js +1 -1
  16. package/src/private.d.ts +1 -0
  17. package/src/private.js +3 -1
  18. package/src/tools/angular/transformers/jit-bootstrap-transformer.js +2 -3
  19. package/src/tools/angular/transformers/jit-resource-transformer.js +2 -1
  20. package/src/tools/esbuild/application-code-bundle.js +3 -0
  21. package/src/tools/esbuild/budget-stats.js +4 -1
  22. package/src/tools/esbuild/bundler-execution-result.d.ts +2 -0
  23. package/src/tools/esbuild/bundler-execution-result.js +2 -0
  24. package/src/tools/esbuild/loader-import-attribute-plugin.d.ts +9 -0
  25. package/src/tools/esbuild/loader-import-attribute-plugin.js +41 -0
  26. package/src/tools/esbuild/utils.js +12 -9
  27. package/src/tools/esbuild/wasm-plugin.js +3 -3
  28. package/src/tools/sass/lexer.js +1 -1
  29. package/src/tools/vite/id-prefix-plugin.d.ts +9 -0
  30. package/src/tools/vite/id-prefix-plugin.js +45 -0
  31. package/src/tools/vite/middlewares/assets-middleware.js +2 -1
  32. package/src/tools/vite/middlewares/index-html-middleware.js +4 -2
  33. package/src/utils/color.d.ts +2 -3
  34. package/src/utils/color.js +14 -50
  35. package/src/utils/index-file/inline-fonts.js +2 -1
  36. package/src/utils/normalize-cache.js +1 -1
  37. package/src/utils/spinner.d.ts +0 -20
  38. package/src/utils/spinner.js +0 -55
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@angular/build",
3
- "version": "18.2.0-next.0",
3
+ "version": "18.2.0-next.2",
4
4
  "description": "Official build system for Angular",
5
5
  "keywords": [
6
6
  "Angular CLI",
@@ -23,30 +23,29 @@
23
23
  "builders": "builders.json",
24
24
  "dependencies": {
25
25
  "@ampproject/remapping": "2.3.0",
26
- "@angular-devkit/architect": "0.1802.0-next.0",
27
- "@babel/core": "7.24.7",
26
+ "@angular-devkit/architect": "0.1802.0-next.2",
27
+ "@babel/core": "7.24.9",
28
28
  "@babel/helper-annotate-as-pure": "7.24.7",
29
29
  "@babel/helper-split-export-declaration": "7.24.7",
30
30
  "@babel/plugin-syntax-import-attributes": "7.24.7",
31
- "@inquirer/confirm": "3.1.14",
31
+ "@inquirer/confirm": "3.1.17",
32
32
  "@vitejs/plugin-basic-ssl": "1.1.0",
33
- "ansi-colors": "4.1.3",
34
33
  "browserslist": "^4.23.0",
35
34
  "critters": "0.0.24",
36
35
  "esbuild": "0.23.0",
37
36
  "fast-glob": "3.3.2",
38
37
  "https-proxy-agent": "7.0.5",
38
+ "listr2": "8.2.3",
39
39
  "lmdb": "3.0.12",
40
40
  "magic-string": "0.30.10",
41
41
  "mrmime": "2.0.0",
42
- "ora": "5.4.1",
43
42
  "parse5-html-rewriting-stream": "7.0.0",
44
43
  "picomatch": "4.0.2",
45
44
  "piscina": "4.6.1",
46
- "rollup": "4.18.1",
47
- "sass": "1.77.7",
48
- "semver": "7.6.2",
49
- "vite": "5.3.3",
45
+ "rollup": "4.19.0",
46
+ "sass": "1.77.8",
47
+ "semver": "7.6.3",
48
+ "vite": "5.3.4",
50
49
  "watchpack": "2.4.1"
51
50
  },
52
51
  "peerDependencies": {
@@ -79,7 +78,7 @@
79
78
  "optional": true
80
79
  }
81
80
  },
82
- "packageManager": "yarn@4.3.0",
81
+ "packageManager": "yarn@4.3.1",
83
82
  "repository": {
84
83
  "type": "git",
85
84
  "url": "https://github.com/angular/angular-cli.git"
@@ -5,12 +5,12 @@
5
5
  * Use of this source code is governed by an MIT-style license that can be
6
6
  * found in the LICENSE file at https://angular.dev/license
7
7
  */
8
- import { BuilderContext, BuilderOutput } from '@angular-devkit/architect';
8
+ import { BuilderContext } from '@angular-devkit/architect';
9
9
  import { BuildOutputFile } from '../../tools/esbuild/bundler-context';
10
10
  import { ExecutionResult, RebuildState } from '../../tools/esbuild/bundler-execution-result';
11
11
  import { NormalizedCachedOptions } from '../../utils/normalize-cache';
12
12
  import { NormalizedOutputOptions } from './options';
13
- type BuildActionOutput = (ExecutionResult['outputWithFiles'] | ExecutionResult['output']) & BuilderOutput;
13
+ import { Result } from './results';
14
14
  export declare function runEsBuildBuildAction(action: (rebuildState?: RebuildState) => Promise<ExecutionResult>, options: {
15
15
  workspaceRoot: string;
16
16
  projectRoot: string;
@@ -29,5 +29,4 @@ export declare function runEsBuildBuildAction(action: (rebuildState?: RebuildSta
29
29
  clearScreen?: boolean;
30
30
  colors?: boolean;
31
31
  jsonLogs?: boolean;
32
- }): AsyncIterable<BuildActionOutput>;
33
- export {};
32
+ }): AsyncIterable<Result>;
@@ -36,10 +36,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.runEsBuildBuildAction = runEsBuildBuildAction;
37
37
  const node_fs_1 = require("node:fs");
38
38
  const node_path_1 = __importDefault(require("node:path"));
39
+ const bundler_context_1 = require("../../tools/esbuild/bundler-context");
39
40
  const sass_language_1 = require("../../tools/esbuild/stylesheets/sass-language");
40
41
  const utils_1 = require("../../tools/esbuild/utils");
41
42
  const delete_output_dir_1 = require("../../utils/delete-output-dir");
42
43
  const environment_options_1 = require("../../utils/environment-options");
44
+ const results_1 = require("./results");
43
45
  // Watch workspace for package manager changes
44
46
  const packageWatchFiles = [
45
47
  // manifest can affect module resolution
@@ -167,18 +169,50 @@ async function* runEsBuildBuildAction(action, options) {
167
169
  (0, sass_language_1.shutdownSassWorkerPool)();
168
170
  }
169
171
  }
170
- async function writeAndEmitOutput(writeToFileSystem, { outputFiles, output, outputWithFiles, assetFiles }, outputOptions, writeToFileSystemFilter) {
172
+ async function writeAndEmitOutput(writeToFileSystem, { outputFiles, outputWithFiles, assetFiles, externalMetadata, htmlIndexPath, htmlBaseHref, }, outputOptions, writeToFileSystemFilter) {
173
+ if (!outputWithFiles.success) {
174
+ return {
175
+ kind: results_1.ResultKind.Failure,
176
+ errors: outputWithFiles.errors,
177
+ };
178
+ }
171
179
  if (writeToFileSystem) {
172
180
  // Write output files
173
181
  const outputFilesToWrite = writeToFileSystemFilter
174
182
  ? outputFiles.filter(writeToFileSystemFilter)
175
183
  : outputFiles;
176
184
  await (0, utils_1.writeResultFiles)(outputFilesToWrite, assetFiles, outputOptions);
177
- return output;
185
+ // Currently unused other than indicating success if writing to disk.
186
+ return {
187
+ kind: results_1.ResultKind.Full,
188
+ files: {},
189
+ };
178
190
  }
179
191
  else {
180
- // Requires casting due to unneeded `JsonObject` requirement. Remove once fixed.
181
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
182
- return outputWithFiles;
192
+ const result = {
193
+ kind: results_1.ResultKind.Full,
194
+ files: {},
195
+ detail: {
196
+ externalMetadata,
197
+ htmlIndexPath,
198
+ htmlBaseHref,
199
+ },
200
+ };
201
+ for (const file of outputWithFiles.assetFiles) {
202
+ result.files[file.destination] = {
203
+ type: bundler_context_1.BuildOutputFileType.Browser,
204
+ inputPath: file.source,
205
+ origin: 'disk',
206
+ };
207
+ }
208
+ for (const file of outputWithFiles.outputFiles) {
209
+ result.files[file.path] = {
210
+ type: file.type,
211
+ contents: file.contents,
212
+ origin: 'memory',
213
+ hash: file.hash,
214
+ };
215
+ }
216
+ return result;
183
217
  }
184
218
  }
@@ -101,6 +101,8 @@ async function executeBuild(options, context, rebuildState) {
101
101
  // Watch input index HTML file if configured
102
102
  if (options.indexHtmlOptions) {
103
103
  executionResult.extraWatchFiles.push(options.indexHtmlOptions.input);
104
+ executionResult.htmlIndexPath = options.indexHtmlOptions.output;
105
+ executionResult.htmlBaseHref = options.baseHref;
104
106
  }
105
107
  // Perform i18n translation inlining if enabled
106
108
  if (i18nOptions.shouldInline) {
@@ -9,13 +9,14 @@ import { BuilderContext, BuilderOutput } from '@angular-devkit/architect';
9
9
  import type { Plugin } from 'esbuild';
10
10
  import { BuildOutputFile } from '../../tools/esbuild/bundler-context';
11
11
  import { ApplicationBuilderExtensions, ApplicationBuilderInternalOptions } from './options';
12
+ import { Result } from './results';
12
13
  import { Schema as ApplicationBuilderOptions } from './schema';
13
14
  export type { ApplicationBuilderOptions };
14
15
  export declare function buildApplicationInternal(options: ApplicationBuilderInternalOptions, context: BuilderContext & {
15
16
  signal?: AbortSignal;
16
17
  }, infrastructureSettings?: {
17
18
  write?: boolean;
18
- }, extensions?: ApplicationBuilderExtensions): AsyncIterable<ApplicationBuilderOutput>;
19
+ }, extensions?: ApplicationBuilderExtensions): AsyncIterable<Result>;
19
20
  export interface ApplicationBuilderOutput extends BuilderOutput {
20
21
  outputFiles?: BuildOutputFile[];
21
22
  assetFiles?: {
@@ -18,6 +18,7 @@ const version_1 = require("../../utils/version");
18
18
  const build_action_1 = require("./build-action");
19
19
  const execute_build_1 = require("./execute-build");
20
20
  const options_1 = require("./options");
21
+ const results_1 = require("./results");
21
22
  async function* buildApplicationInternal(options,
22
23
  // TODO: Integrate abort signal support into builder system
23
24
  context, infrastructureSettings, extensions) {
@@ -29,7 +30,9 @@ context, infrastructureSettings, extensions) {
29
30
  // Determine project name from builder context target
30
31
  const projectName = target?.project;
31
32
  if (!projectName) {
32
- yield { success: false, error: `The 'application' builder requires a target to be specified.` };
33
+ context.logger.error(`The 'application' builder requires a target to be specified.`);
34
+ // Only the vite-based dev server current uses the errors value
35
+ yield { kind: results_1.ResultKind.Failure, errors: [] };
33
36
  return;
34
37
  }
35
38
  const normalizedOptions = await (0, options_1.normalizeOptions)(context, projectName, options, extensions);
@@ -38,17 +41,13 @@ context, infrastructureSettings, extensions) {
38
41
  if (writeServerBundles) {
39
42
  const { browser, server } = normalizedOptions.outputOptions;
40
43
  if (browser === '') {
41
- yield {
42
- success: false,
43
- error: `'outputPath.browser' cannot be configured to an empty string when SSR is enabled.`,
44
- };
44
+ context.logger.error(`'outputPath.browser' cannot be configured to an empty string when SSR is enabled.`);
45
+ yield { kind: results_1.ResultKind.Failure, errors: [] };
45
46
  return;
46
47
  }
47
48
  if (browser === server) {
48
- yield {
49
- success: false,
50
- error: `'outputPath.browser' and 'outputPath.server' cannot be configured to the same value.`,
51
- };
49
+ context.logger.error(`'outputPath.browser' and 'outputPath.server' cannot be configured to the same value.`);
50
+ yield { kind: results_1.ResultKind.Failure, errors: [] };
52
51
  return;
53
52
  }
54
53
  }
@@ -105,7 +104,7 @@ context, infrastructureSettings, extensions) {
105
104
  signal,
106
105
  });
107
106
  }
108
- function buildApplication(options, context, pluginsOrExtensions) {
107
+ async function* buildApplication(options, context, pluginsOrExtensions) {
109
108
  let extensions;
110
109
  if (pluginsOrExtensions && Array.isArray(pluginsOrExtensions)) {
111
110
  extensions = {
@@ -115,6 +114,8 @@ function buildApplication(options, context, pluginsOrExtensions) {
115
114
  else {
116
115
  extensions = pluginsOrExtensions;
117
116
  }
118
- return buildApplicationInternal(options, context, undefined, extensions);
117
+ for await (const result of buildApplicationInternal(options, context, undefined, extensions)) {
118
+ yield { success: result.kind !== results_1.ResultKind.Failure };
119
+ }
119
120
  }
120
121
  exports.default = (0, architect_1.createBuilder)(buildApplication);
@@ -242,7 +242,7 @@ async function normalizeOptions(context, projectName, options, extensions) {
242
242
  plugins: extensions?.codePlugins?.length ? extensions?.codePlugins : undefined,
243
243
  loaderExtensions,
244
244
  jsonLogs: environment_options_1.useJSONBuildLogs,
245
- colors: color_1.colors.enabled,
245
+ colors: (0, color_1.supportColor)(),
246
246
  clearScreen,
247
247
  define,
248
248
  };
@@ -0,0 +1,67 @@
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 { BuildOutputFileType } from '../../tools/esbuild/bundler-context';
9
+ export declare enum ResultKind {
10
+ Failure = 0,
11
+ Full = 1,
12
+ Incremental = 2,
13
+ ComponentUpdate = 3
14
+ }
15
+ export type Result = FailureResult | FullResult | IncrementalResult | ComponentUpdateResult;
16
+ export interface BaseResult {
17
+ kind: ResultKind;
18
+ warnings?: ResultMessage[];
19
+ duration?: number;
20
+ detail?: Record<string, unknown>;
21
+ }
22
+ export interface FailureResult extends BaseResult {
23
+ kind: ResultKind.Failure;
24
+ errors: ResultMessage[];
25
+ }
26
+ export interface FullResult extends BaseResult {
27
+ kind: ResultKind.Full;
28
+ files: Record<string, ResultFile>;
29
+ }
30
+ export interface IncrementalResult extends BaseResult {
31
+ kind: ResultKind.Incremental;
32
+ added: string[];
33
+ removed: string[];
34
+ modified: string[];
35
+ files: Record<string, ResultFile>;
36
+ }
37
+ export type ResultFile = DiskFile | MemoryFile;
38
+ export interface BaseResultFile {
39
+ origin: 'memory' | 'disk';
40
+ type: BuildOutputFileType;
41
+ }
42
+ export interface DiskFile extends BaseResultFile {
43
+ origin: 'disk';
44
+ inputPath: string;
45
+ }
46
+ export interface MemoryFile extends BaseResultFile {
47
+ origin: 'memory';
48
+ hash: string;
49
+ contents: Uint8Array;
50
+ }
51
+ export interface ResultMessage {
52
+ text: string;
53
+ location?: {
54
+ file: string;
55
+ line: number;
56
+ column: number;
57
+ } | null;
58
+ notes?: {
59
+ text: string;
60
+ }[];
61
+ }
62
+ export interface ComponentUpdateResult extends BaseResult {
63
+ kind: ResultKind.ComponentUpdate;
64
+ id: string;
65
+ type: 'style' | 'template';
66
+ content: string;
67
+ }
@@ -0,0 +1,17 @@
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.ResultKind = void 0;
11
+ var ResultKind;
12
+ (function (ResultKind) {
13
+ ResultKind[ResultKind["Failure"] = 0] = "Failure";
14
+ ResultKind[ResultKind["Full"] = 1] = "Full";
15
+ ResultKind[ResultKind["Incremental"] = 2] = "Incremental";
16
+ ResultKind[ResultKind["ComponentUpdate"] = 3] = "ComponentUpdate";
17
+ })(ResultKind || (exports.ResultKind = ResultKind = {}));
@@ -8,7 +8,7 @@
8
8
  import type { BuilderContext } from '@angular-devkit/architect';
9
9
  import type { Plugin } from 'esbuild';
10
10
  import type { Connect, DepOptimizationConfig, InlineConfig } from 'vite';
11
- import { ApplicationBuilderOutput } from '../application';
11
+ import { Result } from '../application/results';
12
12
  import { type ApplicationBuilderInternalOptions, type ExternalResultMetadata, JavaScriptTransformer } from './internal';
13
13
  import type { NormalizedDevServerOptions } from './options';
14
14
  import type { DevServerBuilderOutput } from './output';
@@ -19,7 +19,7 @@ interface OutputFileRecord {
19
19
  updated: boolean;
20
20
  servable: boolean;
21
21
  }
22
- export type BuilderAction = (options: ApplicationBuilderInternalOptions, context: BuilderContext, plugins?: Plugin[]) => AsyncIterable<ApplicationBuilderOutput>;
22
+ export type BuilderAction = (options: ApplicationBuilderInternalOptions, context: BuilderContext, plugins?: Plugin[]) => AsyncIterable<Result>;
23
23
  export declare function serveWithVite(serverOptions: NormalizedDevServerOptions, builderName: string, builderAction: BuilderAction, context: BuilderContext, transformers?: {
24
24
  indexHtml?: (content: string) => Promise<string>;
25
25
  }, extensions?: {
@@ -42,8 +42,10 @@ const node_module_1 = require("node:module");
42
42
  const node_path_1 = require("node:path");
43
43
  const angular_memory_plugin_1 = require("../../tools/vite/angular-memory-plugin");
44
44
  const i18n_locale_plugin_1 = require("../../tools/vite/i18n-locale-plugin");
45
+ const id_prefix_plugin_1 = require("../../tools/vite/id-prefix-plugin");
45
46
  const utils_1 = require("../../utils");
46
47
  const load_esm_1 = require("../../utils/load-esm");
48
+ const results_1 = require("../application/results");
47
49
  const internal_1 = require("./internal");
48
50
  /**
49
51
  * Build options that are also present on the dev server but are only passed
@@ -76,12 +78,6 @@ async function* serveWithVite(serverOptions, builderName, builderAction, context
76
78
  }
77
79
  // Set all packages as external to support Vite's prebundle caching
78
80
  browserOptions.externalPackages = serverOptions.prebundle;
79
- const baseHref = browserOptions.baseHref;
80
- if (serverOptions.servePath === undefined && baseHref !== undefined) {
81
- // Remove trailing slash
82
- serverOptions.servePath =
83
- baseHref !== './' && baseHref[baseHref.length - 1] === '/' ? baseHref.slice(0, -1) : baseHref;
84
- }
85
81
  // The development server currently only supports a single locale when localizing.
86
82
  // This matches the behavior of the Webpack-based development server but could be expanded in the future.
87
83
  if (browserOptions.localize === true ||
@@ -100,15 +96,8 @@ async function* serveWithVite(serverOptions, builderName, builderAction, context
100
96
  // In a development environment the additional scope information does not
101
97
  // have a negative effect unlike production where final output size is relevant.
102
98
  { sourcemap: true, jit: true, thirdPartySourcemaps }, 1);
103
- // Extract output index from options
104
- // TODO: Provide this info from the build results
99
+ // The index HTML path will be updated from the build results if provided by the builder
105
100
  let htmlIndexPath = 'index.html';
106
- if (browserOptions.index && typeof browserOptions.index !== 'boolean') {
107
- htmlIndexPath =
108
- typeof browserOptions.index === 'string'
109
- ? (0, node_path_1.basename)(browserOptions.index)
110
- : browserOptions.index.output || 'index.html';
111
- }
112
101
  // dynamically import Vite for ESM compatibility
113
102
  const { createServer, normalizePath } = await (0, load_esm_1.loadEsmModule)('vite');
114
103
  let server;
@@ -130,24 +119,55 @@ async function* serveWithVite(serverOptions, builderName, builderAction, context
130
119
  });
131
120
  // TODO: Switch this to an architect schedule call when infrastructure settings are supported
132
121
  for await (const result of builderAction(browserOptions, context, extensions?.buildPlugins)) {
133
- (0, node_assert_1.default)(result.outputFiles, 'Builder did not provide result files.');
134
- // If build failed, nothing to serve
135
- if (!result.success) {
136
- // If server is active, send an error notification
137
- if (result.errors?.length && server) {
138
- hadError = true;
139
- server.hot.send({
140
- type: 'error',
141
- err: {
142
- message: result.errors[0].text,
143
- stack: '',
144
- loc: result.errors[0].location,
145
- },
146
- });
147
- }
148
- continue;
122
+ switch (result.kind) {
123
+ case results_1.ResultKind.Failure:
124
+ if (result.errors.length && server) {
125
+ hadError = true;
126
+ server.hot.send({
127
+ type: 'error',
128
+ err: {
129
+ message: result.errors[0].text,
130
+ stack: '',
131
+ loc: result.errors[0].location ?? undefined,
132
+ },
133
+ });
134
+ }
135
+ continue;
136
+ case results_1.ResultKind.Full:
137
+ if (result.detail?.['htmlIndexPath']) {
138
+ htmlIndexPath = result.detail['htmlIndexPath'];
139
+ }
140
+ if (serverOptions.servePath === undefined && result.detail?.['htmlBaseHref']) {
141
+ const baseHref = result.detail['htmlBaseHref'];
142
+ // Remove trailing slash
143
+ serverOptions.servePath =
144
+ baseHref !== './' && baseHref[baseHref.length - 1] === '/'
145
+ ? baseHref.slice(0, -1)
146
+ : baseHref;
147
+ }
148
+ assetFiles.clear();
149
+ for (const [outputPath, file] of Object.entries(result.files)) {
150
+ if (file.origin === 'disk') {
151
+ assetFiles.set('/' + normalizePath(outputPath), normalizePath(file.inputPath));
152
+ }
153
+ }
154
+ // Analyze result files for changes
155
+ analyzeResultFiles(normalizePath, htmlIndexPath, result.files, generatedFiles);
156
+ break;
157
+ case results_1.ResultKind.Incremental:
158
+ (0, node_assert_1.default)(server, 'Builder must provide an initial full build before incremental results.');
159
+ // TODO: Implement support -- application builder currently does not use
160
+ break;
161
+ case results_1.ResultKind.ComponentUpdate:
162
+ (0, node_assert_1.default)(serverOptions.hmr, 'Component updates are only supported with HMR enabled.');
163
+ // TODO: Implement support -- application builder currently does not use
164
+ break;
165
+ default:
166
+ context.logger.warn(`Unknown result kind [${result.kind}] provided by build.`);
167
+ continue;
149
168
  }
150
- else if (hadError && server) {
169
+ // Clear existing error overlay on successful result
170
+ if (hadError && server) {
151
171
  hadError = false;
152
172
  // Send an empty update to clear the error overlay
153
173
  server.hot.send({
@@ -155,18 +175,10 @@ async function* serveWithVite(serverOptions, builderName, builderAction, context
155
175
  updates: [],
156
176
  });
157
177
  }
158
- // Analyze result files for changes
159
- analyzeResultFiles(normalizePath, htmlIndexPath, result.outputFiles, generatedFiles);
160
- assetFiles.clear();
161
- if (result.assetFiles) {
162
- for (const asset of result.assetFiles) {
163
- assetFiles.set('/' + normalizePath(asset.destination), normalizePath(asset.source));
164
- }
165
- }
166
178
  // To avoid disconnecting the array objects from the option, these arrays need to be mutated instead of replaced.
167
179
  let requiresServerRestart = false;
168
- if (result.externalMetadata) {
169
- const { implicitBrowser, implicitServer, explicit } = result.externalMetadata;
180
+ if (result.detail?.['externalMetadata']) {
181
+ const { implicitBrowser, implicitServer, explicit } = result.detail['externalMetadata'];
170
182
  const implicitServerFiltered = implicitServer.filter((m) => removeNodeJsBuiltinModules(m) && removeAbsoluteUrls(m));
171
183
  const implicitBrowserFiltered = implicitBrowser.filter(removeAbsoluteUrls);
172
184
  if (browserOptions.ssr && serverOptions.prebundle !== false) {
@@ -301,22 +313,26 @@ function handleUpdate(normalizePath, generatedFiles, server, serverOptions, logg
301
313
  }
302
314
  function analyzeResultFiles(normalizePath, htmlIndexPath, resultFiles, generatedFiles) {
303
315
  const seen = new Set(['/index.html']);
304
- for (const file of resultFiles) {
316
+ for (const [outputPath, file] of Object.entries(resultFiles)) {
317
+ if (file.origin === 'disk') {
318
+ continue;
319
+ }
305
320
  let filePath;
306
- if (file.path === htmlIndexPath) {
321
+ if (outputPath === htmlIndexPath) {
307
322
  // Convert custom index output path to standard index path for dev-server usage.
308
323
  // This mimics the Webpack dev-server behavior.
309
324
  filePath = '/index.html';
310
325
  }
311
326
  else {
312
- filePath = '/' + normalizePath(file.path);
327
+ filePath = '/' + normalizePath(outputPath);
313
328
  }
314
329
  seen.add(filePath);
330
+ const servable = file.type === internal_1.BuildOutputFileType.Browser || file.type === internal_1.BuildOutputFileType.Media;
315
331
  // Skip analysis of sourcemaps
316
332
  if (filePath.endsWith('.map')) {
317
333
  generatedFiles.set(filePath, {
318
334
  contents: file.contents,
319
- servable: file.type === internal_1.BuildOutputFileType.Browser || file.type === internal_1.BuildOutputFileType.Media,
335
+ servable,
320
336
  size: file.contents.byteLength,
321
337
  updated: false,
322
338
  });
@@ -336,7 +352,7 @@ function analyzeResultFiles(normalizePath, htmlIndexPath, resultFiles, generated
336
352
  size: file.contents.byteLength,
337
353
  hash: file.hash,
338
354
  updated: true,
339
- servable: file.type === internal_1.BuildOutputFileType.Browser || file.type === internal_1.BuildOutputFileType.Media,
355
+ servable,
340
356
  });
341
357
  }
342
358
  // Clear stale output files
@@ -441,6 +457,7 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
441
457
  extensionMiddleware,
442
458
  normalizePath,
443
459
  }),
460
+ (0, id_prefix_plugin_1.createRemoveIdPrefixPlugin)(externalMetadata.explicit),
444
461
  ],
445
462
  // Browser only optimizeDeps. (This does not run for SSR dependencies).
446
463
  optimizeDeps: getDepOptimizationConfig({
@@ -7,11 +7,11 @@
7
7
  */
8
8
  import type { ɵParsedMessage as LocalizeMessage } from '@angular/localize';
9
9
  import type { MessageExtractor } from '@angular/localize/tools';
10
- import type { BuilderContext, BuilderOutput } from '@angular-devkit/architect';
10
+ import type { BuilderContext } from '@angular-devkit/architect';
11
11
  import type { ApplicationBuilderExtensions } from '../application/options';
12
12
  import type { NormalizedExtractI18nOptions } from './options';
13
13
  export declare function extractMessages(options: NormalizedExtractI18nOptions, builderName: string, context: BuilderContext, extractorConstructor: typeof MessageExtractor, extensions?: ApplicationBuilderExtensions): Promise<{
14
- builderResult: BuilderOutput;
14
+ success: boolean;
15
15
  basePath: string;
16
16
  messages: LocalizeMessage[];
17
17
  useLegacyIds: boolean;
@@ -11,9 +11,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
11
11
  };
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
13
  exports.extractMessages = extractMessages;
14
- const node_assert_1 = __importDefault(require("node:assert"));
15
14
  const node_path_1 = __importDefault(require("node:path"));
16
15
  const application_1 = require("../application");
16
+ const results_1 = require("../application/results");
17
17
  async function extractMessages(options, builderName, context, extractorConstructor, extensions) {
18
18
  const messages = [];
19
19
  // Setup the build options for the application based on the buildTarget option
@@ -28,46 +28,30 @@ async function extractMessages(options, builderName, context, extractorConstruct
28
28
  buildOptions.appShell = false;
29
29
  buildOptions.prerender = false;
30
30
  // Build the application with the build options
31
- let builderResult;
32
- try {
33
- for await (const result of (0, application_1.buildApplicationInternal)(buildOptions, context, { write: false }, extensions)) {
34
- builderResult = result;
35
- break;
36
- }
37
- (0, node_assert_1.default)(builderResult !== undefined, 'Application builder did not provide a result.');
31
+ const builderResult = await first((0, application_1.buildApplicationInternal)(buildOptions, context, { write: false }, extensions));
32
+ let success = false;
33
+ if (!builderResult || builderResult.kind === results_1.ResultKind.Failure) {
34
+ context.logger.error('Application build failed.');
38
35
  }
39
- catch (err) {
40
- builderResult = {
41
- success: false,
42
- error: err.message,
43
- };
36
+ else if (builderResult.kind !== results_1.ResultKind.Full) {
37
+ context.logger.error('Application build did not provide a full output.');
44
38
  }
45
- // Extract messages from each output JavaScript file.
46
- // Output files are only present on a successful build.
47
- if (builderResult.outputFiles) {
48
- // Store the JS and JS map files for lookup during extraction
49
- const files = new Map();
50
- for (const outputFile of builderResult.outputFiles) {
51
- if (outputFile.path.endsWith('.js')) {
52
- files.set(outputFile.path, outputFile.text);
53
- }
54
- else if (outputFile.path.endsWith('.js.map')) {
55
- files.set(outputFile.path, outputFile.text);
56
- }
57
- }
39
+ else {
58
40
  // Setup the localize message extractor based on the in-memory files
59
- const extractor = setupLocalizeExtractor(extractorConstructor, files, context);
60
- // Attempt extraction of all output JS files
61
- for (const filePath of files.keys()) {
41
+ const extractor = setupLocalizeExtractor(extractorConstructor, builderResult.files, context);
42
+ // Extract messages from each output JavaScript file.
43
+ // Output files are only present on a successful build.
44
+ for (const filePath of Object.keys(builderResult.files)) {
62
45
  if (!filePath.endsWith('.js')) {
63
46
  continue;
64
47
  }
65
48
  const fileMessages = extractor.extractMessages(filePath);
66
49
  messages.push(...fileMessages);
67
50
  }
51
+ success = true;
68
52
  }
69
53
  return {
70
- builderResult,
54
+ success,
71
55
  basePath: context.workspaceRoot,
72
56
  messages,
73
57
  // Legacy i18n identifiers are not supported with the new application builder
@@ -75,6 +59,7 @@ async function extractMessages(options, builderName, context, extractorConstruct
75
59
  };
76
60
  }
77
61
  function setupLocalizeExtractor(extractorConstructor, files, context) {
62
+ const textDecoder = new TextDecoder();
78
63
  // Setup a virtual file system instance for the extractor
79
64
  // * MessageExtractor itself uses readFile, relative and resolve
80
65
  // * Internal SourceFileLoader (sourcemap support) uses dirname, exists, readFile, and resolve
@@ -82,7 +67,11 @@ function setupLocalizeExtractor(extractorConstructor, files, context) {
82
67
  readFile(path) {
83
68
  // Output files are stored as relative to the workspace root
84
69
  const requestedPath = node_path_1.default.relative(context.workspaceRoot, path);
85
- const content = files.get(requestedPath);
70
+ const file = files[requestedPath];
71
+ let content;
72
+ if (file?.origin === 'memory') {
73
+ content = textDecoder.decode(file.contents);
74
+ }
86
75
  if (content === undefined) {
87
76
  throw new Error('Unknown file requested: ' + requestedPath);
88
77
  }
@@ -97,7 +86,7 @@ function setupLocalizeExtractor(extractorConstructor, files, context) {
97
86
  exists(path) {
98
87
  // Output files are stored as relative to the workspace root
99
88
  const requestedPath = node_path_1.default.relative(context.workspaceRoot, path);
100
- return files.has(requestedPath);
89
+ return files[requestedPath] !== undefined;
101
90
  },
102
91
  dirname(path) {
103
92
  return node_path_1.default.dirname(path);
@@ -128,3 +117,8 @@ function setupLocalizeExtractor(extractorConstructor, files, context) {
128
117
  });
129
118
  return extractor;
130
119
  }
120
+ async function first(iterable) {
121
+ for await (const value of iterable) {
122
+ return value;
123
+ }
124
+ }
@@ -72,9 +72,8 @@ async function execute(options, context, extensions) {
72
72
  // Extract messages based on configured builder
73
73
  const { extractMessages } = await Promise.resolve().then(() => __importStar(require('./application-extraction')));
74
74
  const extractionResult = await extractMessages(normalizedOptions, builderName, context, localizeToolsModule.MessageExtractor, extensions);
75
- // Return the builder result if it failed
76
- if (!extractionResult.builderResult.success) {
77
- return extractionResult.builderResult;
75
+ if (!extractionResult.success) {
76
+ return { success: false };
78
77
  }
79
78
  // Perform duplicate message checks
80
79
  const { checkDuplicateMessages } = localizeToolsModule;
@@ -128,12 +127,13 @@ async function createSerializer(localizeToolsModule, format, sourceLocale, baseP
128
127
  case schema_1.Format.LegacyMigrate:
129
128
  return new LegacyMessageIdMigrationSerializer(diagnostics);
130
129
  case schema_1.Format.Arb:
131
- const fileSystem = {
130
+ return new ArbTranslationSerializer(sourceLocale,
131
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
132
+ basePath, {
132
133
  relative(from, to) {
133
134
  return node_path_1.default.relative(from, to);
134
135
  },
135
- };
136
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
137
- return new ArbTranslationSerializer(sourceLocale, basePath, fileSystem);
136
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
137
+ });
138
138
  }
139
139
  }
@@ -37,8 +37,8 @@ async function normalizeOptions(context, projectName, options) {
37
37
  // Normalize xliff format extensions
38
38
  let format = options.format;
39
39
  switch (format) {
40
- case undefined:
41
40
  // Default format is xliff1
41
+ case undefined:
42
42
  case schema_1.Format.Xlf:
43
43
  case schema_1.Format.Xlif:
44
44
  case schema_1.Format.Xliff:
package/src/private.d.ts CHANGED
@@ -13,6 +13,7 @@
13
13
  */
14
14
  export { buildApplicationInternal } from './builders/application';
15
15
  export type { ApplicationBuilderInternalOptions } from './builders/application/options';
16
+ export { type Result, type ResultFile, ResultKind } from './builders/application/results';
16
17
  export { serveWithVite } from './builders/dev-server/vite-server';
17
18
  export * from './tools/babel/plugins';
18
19
  export type { ExternalResultMetadata } from './tools/esbuild/bundler-execution-result';
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.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.createCompilerPlugin = exports.JavaScriptTransformer = exports.createJitResourceTransformer = exports.SourceFileCache = exports.SassWorkerImplementation = exports.transformSupportedBrowsersToTargets = exports.emitFilesToDisk = exports.serveWithVite = exports.buildApplicationInternal = void 0;
24
+ 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.createCompilerPlugin = exports.JavaScriptTransformer = exports.createJitResourceTransformer = exports.SourceFileCache = exports.SassWorkerImplementation = exports.transformSupportedBrowsersToTargets = exports.emitFilesToDisk = exports.serveWithVite = exports.ResultKind = exports.buildApplicationInternal = void 0;
25
25
  /**
26
26
  * @fileoverview
27
27
  * Private exports intended only for use with the @angular-devkit/build-angular package.
@@ -31,6 +31,8 @@ exports.assertCompatibleAngularVersion = exports.getSupportedBrowsers = exports.
31
31
  // Builders
32
32
  var application_1 = require("./builders/application");
33
33
  Object.defineProperty(exports, "buildApplicationInternal", { enumerable: true, get: function () { return application_1.buildApplicationInternal; } });
34
+ var results_1 = require("./builders/application/results");
35
+ Object.defineProperty(exports, "ResultKind", { enumerable: true, get: function () { return results_1.ResultKind; } });
34
36
  var vite_server_1 = require("./builders/dev-server/vite-server");
35
37
  Object.defineProperty(exports, "serveWithVite", { enumerable: true, get: function () { return vite_server_1.serveWithVite; } });
36
38
  // Tools
@@ -98,9 +98,8 @@ function elideImports(sourceFile, removedNodes, getTypeChecker, compilerOptions)
98
98
  let symbol;
99
99
  switch (node.kind) {
100
100
  case typescript_1.default.SyntaxKind.Identifier:
101
- const parent = node.parent;
102
- if (parent && typescript_1.default.isShorthandPropertyAssignment(parent)) {
103
- const shorthandSymbol = typeChecker.getShorthandAssignmentValueSymbol(parent);
101
+ if (node.parent && typescript_1.default.isShorthandPropertyAssignment(node.parent)) {
102
+ const shorthandSymbol = typeChecker.getShorthandAssignmentValueSymbol(node.parent);
104
103
  if (shorthandSymbol) {
105
104
  symbol = shorthandSymbol;
106
105
  }
@@ -116,7 +116,7 @@ function visitComponentMetadata(nodeFactory, node, styleReplacements, resourceIm
116
116
  return undefined;
117
117
  }
118
118
  return node;
119
- case 'styleUrls':
119
+ case 'styleUrls': {
120
120
  if (!typescript_1.default.isArrayLiteralExpression(node.initializer)) {
121
121
  return node;
122
122
  }
@@ -132,6 +132,7 @@ function visitComponentMetadata(nodeFactory, node, styleReplacements, resourceIm
132
132
  styleReplacements.push(...externalStyles);
133
133
  // The external styles will be added afterwards in combination with any inline styles
134
134
  return undefined;
135
+ }
135
136
  default:
136
137
  // All other elements are passed through
137
138
  return node;
@@ -23,6 +23,7 @@ const compiler_plugin_1 = require("./angular/compiler-plugin");
23
23
  const compiler_plugin_options_1 = require("./compiler-plugin-options");
24
24
  const external_packages_plugin_1 = require("./external-packages-plugin");
25
25
  const i18n_locale_plugin_1 = require("./i18n-locale-plugin");
26
+ const loader_import_attribute_plugin_1 = require("./loader-import-attribute-plugin");
26
27
  const rxjs_esm_resolution_plugin_1 = require("./rxjs-esm-resolution-plugin");
27
28
  const sourcemap_ignorelist_plugin_1 = require("./sourcemap-ignorelist-plugin");
28
29
  const utils_1 = require("./utils");
@@ -45,6 +46,7 @@ function createBrowserCodeBundleOptions(options, target, sourceFileCache) {
45
46
  target,
46
47
  supported: (0, utils_1.getFeatureSupport)(target, zoneless),
47
48
  plugins: [
49
+ (0, loader_import_attribute_plugin_1.createLoaderImportAttributePlugin)(),
48
50
  (0, wasm_plugin_1.createWasmPlugin)({ allowAsync: zoneless, cache: sourceFileCache?.loadResultCache }),
49
51
  (0, sourcemap_ignorelist_plugin_1.createSourcemapIgnorelistPlugin)(),
50
52
  (0, compiler_plugin_1.createCompilerPlugin)(
@@ -146,6 +148,7 @@ function createServerCodeBundleOptions(options, target, sourceFileCache) {
146
148
  entryPoints,
147
149
  supported: (0, utils_1.getFeatureSupport)(target, zoneless),
148
150
  plugins: [
151
+ (0, loader_import_attribute_plugin_1.createLoaderImportAttributePlugin)(),
149
152
  (0, wasm_plugin_1.createWasmPlugin)({ allowAsync: zoneless, cache: sourceFileCache?.loadResultCache }),
150
153
  (0, sourcemap_ignorelist_plugin_1.createSourcemapIgnorelistPlugin)(),
151
154
  (0, compiler_plugin_1.createCompilerPlugin)(
@@ -45,7 +45,10 @@ function generateBudgetStats(metafile, outputFiles, initialFiles) {
45
45
  }
46
46
  // Add component styles from metafile
47
47
  // TODO: Provide this information directly from the AOT compiler
48
- for (const entry of Object.values(metafile.outputs)) {
48
+ for (const [file, entry] of Object.entries(metafile.outputs)) {
49
+ if (!file.endsWith('.css')) {
50
+ continue;
51
+ }
49
52
  // 'ng-component' is set by the angular plugin's component stylesheet bundler
50
53
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
51
54
  const componentStyle = entry['ng-component'];
@@ -38,6 +38,8 @@ export declare class ExecutionResult {
38
38
  logs: string[];
39
39
  externalMetadata?: ExternalResultMetadata;
40
40
  extraWatchFiles: string[];
41
+ htmlIndexPath?: string;
42
+ htmlBaseHref?: string;
41
43
  constructor(rebuildContexts: BundlerContext[], codeBundleCache?: SourceFileCache | undefined);
42
44
  addOutputFile(path: string, content: string | Uint8Array, type: BuildOutputFileType): void;
43
45
  addAssets(assets: BuildOutputAsset[]): void;
@@ -24,6 +24,8 @@ class ExecutionResult {
24
24
  logs = [];
25
25
  externalMetadata;
26
26
  extraWatchFiles = [];
27
+ htmlIndexPath;
28
+ htmlBaseHref;
27
29
  constructor(rebuildContexts, codeBundleCache) {
28
30
  this.rebuildContexts = rebuildContexts;
29
31
  this.codeBundleCache = codeBundleCache;
@@ -0,0 +1,9 @@
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
+ export declare function createLoaderImportAttributePlugin(): Plugin;
@@ -0,0 +1,41 @@
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.createLoaderImportAttributePlugin = createLoaderImportAttributePlugin;
11
+ const promises_1 = require("node:fs/promises");
12
+ const SUPPORTED_LOADERS = ['binary', 'file', 'text'];
13
+ function createLoaderImportAttributePlugin() {
14
+ return {
15
+ name: 'angular-loader-import-attributes',
16
+ setup(build) {
17
+ build.onLoad({ filter: /./ }, async (args) => {
18
+ const loader = args.with['loader'];
19
+ if (!loader) {
20
+ return undefined;
21
+ }
22
+ if (!SUPPORTED_LOADERS.includes(loader)) {
23
+ return {
24
+ errors: [
25
+ {
26
+ text: 'Unsupported loader import attribute',
27
+ notes: [
28
+ { text: 'Attribute value must be one of: ' + SUPPORTED_LOADERS.join(', ') },
29
+ ],
30
+ },
31
+ ],
32
+ };
33
+ }
34
+ return {
35
+ contents: await (0, promises_1.readFile)(args.path),
36
+ loader,
37
+ };
38
+ });
39
+ },
40
+ };
41
+ }
@@ -27,6 +27,7 @@ exports.logMessages = logMessages;
27
27
  exports.isZonelessApp = isZonelessApp;
28
28
  exports.getEntryPointName = getEntryPointName;
29
29
  const esbuild_1 = require("esbuild");
30
+ const listr2_1 = require("listr2");
30
31
  const node_crypto_1 = require("node:crypto");
31
32
  const node_fs_1 = require("node:fs");
32
33
  const promises_1 = __importDefault(require("node:fs/promises"));
@@ -34,7 +35,6 @@ const node_path_1 = require("node:path");
34
35
  const node_url_1 = require("node:url");
35
36
  const node_zlib_1 = require("node:zlib");
36
37
  const semver_1 = require("semver");
37
- const spinner_1 = require("../../utils/spinner");
38
38
  const stats_table_1 = require("../../utils/stats-table");
39
39
  const bundler_context_1 = require("./bundler-context");
40
40
  function logBuildStats(metafile, outputFiles, initial, budgetFailures, colors, changedFiles, estimatedTransferSizes, ssrOutputEnabled, verbose) {
@@ -124,14 +124,17 @@ async function calculateEstimatedTransferSizes(outputFiles) {
124
124
  });
125
125
  }
126
126
  async function withSpinner(text, action) {
127
- const spinner = new spinner_1.Spinner(text);
128
- spinner.start();
129
- try {
130
- return await action();
131
- }
132
- finally {
133
- spinner.stop();
134
- }
127
+ let result;
128
+ const taskList = new listr2_1.Listr([
129
+ {
130
+ title: text,
131
+ async task() {
132
+ result = await action();
133
+ },
134
+ },
135
+ ], { rendererOptions: { clearOutput: true } });
136
+ await taskList.run();
137
+ return result;
135
138
  }
136
139
  async function withNoProgress(text, action) {
137
140
  return action();
@@ -35,7 +35,7 @@ function createWasmPlugin(options) {
35
35
  return {
36
36
  name: 'angular-wasm',
37
37
  setup(build) {
38
- build.onResolve({ filter: /.wasm$/ }, async (args) => {
38
+ build.onResolve({ filter: /\.wasm$/ }, async (args) => {
39
39
  // Skip if already resolving the WASM file to avoid infinite resolution
40
40
  if (args.pluginData?.[WASM_RESOLVE_SYMBOL]) {
41
41
  return;
@@ -75,7 +75,7 @@ function createWasmPlugin(options) {
75
75
  namespace: WASM_INIT_NAMESPACE,
76
76
  };
77
77
  });
78
- build.onLoad({ filter: /.wasm$/, namespace: WASM_INIT_NAMESPACE }, (0, load_result_cache_1.createCachedLoad)(cache, async (args) => {
78
+ build.onLoad({ filter: /\.wasm$/, namespace: WASM_INIT_NAMESPACE }, (0, load_result_cache_1.createCachedLoad)(cache, async (args) => {
79
79
  // Ensure async mode is supported
80
80
  if (!allowAsync) {
81
81
  return {
@@ -155,7 +155,7 @@ function createWasmPlugin(options) {
155
155
  watchFiles: [args.path],
156
156
  };
157
157
  }));
158
- build.onLoad({ filter: /.wasm$/, namespace: WASM_CONTENTS_NAMESPACE }, async (args) => {
158
+ build.onLoad({ filter: /\.wasm$/, namespace: WASM_CONTENTS_NAMESPACE }, async (args) => {
159
159
  const contents = args.pluginData.wasmContents ?? (await (0, promises_1.readFile)(args.path));
160
160
  let loader = 'file';
161
161
  if (args.with.loader) {
@@ -50,7 +50,7 @@ function* findUrls(contents) {
50
50
  if (pos > 0) {
51
51
  pos -= 2;
52
52
  next();
53
- if (!isWhitespace(current) && current !== 0x0027 && current !== 0x003a) {
53
+ if (!isWhitespace(current) && current !== 0x002c && current !== 0x003a) {
54
54
  // Skip - not a url token
55
55
  pos += 3;
56
56
  continue;
@@ -0,0 +1,9 @@
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 'vite';
9
+ export declare function createRemoveIdPrefixPlugin(externals: string[]): Plugin;
@@ -0,0 +1,45 @@
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.createRemoveIdPrefixPlugin = createRemoveIdPrefixPlugin;
11
+ // NOTE: the implementation for this Vite plugin is roughly based on:
12
+ // https://github.com/MilanKovacic/vite-plugin-externalize-dependencies
13
+ const VITE_ID_PREFIX = '@id/';
14
+ const escapeRegexSpecialChars = (inputString) => {
15
+ return inputString.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
16
+ };
17
+ function createRemoveIdPrefixPlugin(externals) {
18
+ return {
19
+ name: 'angular-plugin-remove-id-prefix',
20
+ apply: 'serve',
21
+ configResolved: (resolvedConfig) => {
22
+ // don't do anything when the list of externals is empty
23
+ if (externals.length === 0) {
24
+ return;
25
+ }
26
+ const escapedExternals = externals.map(escapeRegexSpecialChars);
27
+ const prefixedExternalRegex = new RegExp(`${resolvedConfig.base}${VITE_ID_PREFIX}(${escapedExternals.join('|')})`, 'g');
28
+ // @ts-expect-error: Property 'push' does not exist on type 'readonly Plugin<any>[]'
29
+ // Reasoning:
30
+ // since the /@id/ prefix is added by Vite's import-analysis plugin,
31
+ // we must add our actual plugin dynamically, to ensure that it will run
32
+ // AFTER the import-analysis.
33
+ resolvedConfig.plugins.push({
34
+ name: 'angular-plugin-remove-id-prefix-transform',
35
+ transform: (code) => {
36
+ // don't do anything when code does not contain the Vite prefix
37
+ if (!code.includes(VITE_ID_PREFIX)) {
38
+ return code;
39
+ }
40
+ return code.replace(prefixedExternalRegex, (_, externalName) => externalName);
41
+ },
42
+ });
43
+ },
44
+ };
45
+ }
@@ -69,7 +69,8 @@ function createAngularAssetsMiddleware(server, assets, outputFiles) {
69
69
  if (!pathnameHasTrailingSlash) {
70
70
  for (const assetPath of assets.keys()) {
71
71
  if (pathname === assetPath.substring(0, assetPath.lastIndexOf('/'))) {
72
- const location = req.url + '/';
72
+ const { pathname, search, hash } = new URL(req.url, 'http://localhost');
73
+ const location = [pathname, '/', search, hash].join('');
73
74
  res.statusCode = 301;
74
75
  res.setHeader('Content-Type', 'text/html');
75
76
  res.setHeader('Location', location);
@@ -8,6 +8,7 @@
8
8
  */
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.createAngularIndexHtmlMiddleware = createAngularIndexHtmlMiddleware;
11
+ const node_path_1 = require("node:path");
11
12
  const utils_1 = require("../utils");
12
13
  function createAngularIndexHtmlMiddleware(server, outputFiles, indexHtmlTransformer) {
13
14
  return function (req, res, next) {
@@ -18,11 +19,12 @@ function createAngularIndexHtmlMiddleware(server, outputFiles, indexHtmlTransfor
18
19
  // Parse the incoming request.
19
20
  // The base of the URL is unused but required to parse the URL.
20
21
  const pathname = (0, utils_1.pathnameWithoutBasePath)(req.url, server.config.base);
21
- if (pathname !== '/' && pathname !== '/index.html') {
22
+ const extension = (0, node_path_1.extname)(pathname);
23
+ if (extension !== '.html') {
22
24
  next();
23
25
  return;
24
26
  }
25
- const rawHtml = outputFiles.get('/index.html')?.contents;
27
+ const rawHtml = outputFiles.get(pathname)?.contents;
26
28
  if (!rawHtml) {
27
29
  next();
28
30
  return;
@@ -5,6 +5,5 @@
5
5
  * Use of this source code is governed by an MIT-style license that can be
6
6
  * found in the LICENSE file at https://angular.dev/license
7
7
  */
8
- import * as ansiColors from 'ansi-colors';
9
- declare const colors: typeof ansiColors;
10
- export { colors };
8
+ export { color as colors, figures } from 'listr2';
9
+ export declare function supportColor(stream?: NodeJS.WritableStream): boolean;
@@ -6,58 +6,22 @@
6
6
  * Use of this source code is governed by an MIT-style license that can be
7
7
  * found in the LICENSE file at https://angular.dev/license
8
8
  */
9
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- var desc = Object.getOwnPropertyDescriptor(m, k);
12
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
- desc = { enumerable: true, get: function() { return m[k]; } };
14
- }
15
- Object.defineProperty(o, k2, desc);
16
- }) : (function(o, m, k, k2) {
17
- if (k2 === undefined) k2 = k;
18
- o[k2] = m[k];
19
- }));
20
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
- Object.defineProperty(o, "default", { enumerable: true, value: v });
22
- }) : function(o, v) {
23
- o["default"] = v;
24
- });
25
- var __importStar = (this && this.__importStar) || function (mod) {
26
- if (mod && mod.__esModule) return mod;
27
- var result = {};
28
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
29
- __setModuleDefault(result, mod);
30
- return result;
31
- };
32
9
  Object.defineProperty(exports, "__esModule", { value: true });
33
- exports.colors = void 0;
34
- const ansiColors = __importStar(require("ansi-colors"));
10
+ exports.figures = exports.colors = void 0;
11
+ exports.supportColor = supportColor;
35
12
  const node_tty_1 = require("node:tty");
36
- function supportColor() {
37
- if (process.env.FORCE_COLOR !== undefined) {
38
- // 2 colors: FORCE_COLOR = 0 (Disables colors), depth 1
39
- // 16 colors: FORCE_COLOR = 1, depth 4
40
- // 256 colors: FORCE_COLOR = 2, depth 8
41
- // 16,777,216 colors: FORCE_COLOR = 3, depth 16
42
- // See: https://nodejs.org/dist/latest-v12.x/docs/api/tty.html#tty_writestream_getcolordepth_env
43
- // and https://github.com/nodejs/node/blob/b9f36062d7b5c5039498e98d2f2c180dca2a7065/lib/internal/tty.js#L106;
44
- switch (process.env.FORCE_COLOR) {
45
- case '':
46
- case 'true':
47
- case '1':
48
- case '2':
49
- case '3':
50
- return true;
51
- default:
52
- return false;
53
- }
13
+ var listr2_1 = require("listr2");
14
+ Object.defineProperty(exports, "colors", { enumerable: true, get: function () { return listr2_1.color; } });
15
+ Object.defineProperty(exports, "figures", { enumerable: true, get: function () { return listr2_1.figures; } });
16
+ function supportColor(stream = process.stdout) {
17
+ if (stream instanceof node_tty_1.WriteStream) {
18
+ return stream.hasColors();
19
+ }
20
+ try {
21
+ // The hasColors function does not rely on any instance state and should ideally be static
22
+ return node_tty_1.WriteStream.prototype.hasColors();
54
23
  }
55
- if (process.stdout instanceof node_tty_1.WriteStream) {
56
- return process.stdout.hasColors();
24
+ catch {
25
+ return process.env['FORCE_COLOR'] !== undefined && process.env['FORCE_COLOR'] !== '0';
57
26
  }
58
- return false;
59
27
  }
60
- // Create a separate instance to prevent unintended global changes to the color configuration
61
- const colors = ansiColors.create();
62
- exports.colors = colors;
63
- colors.enabled = supportColor();
@@ -137,7 +137,7 @@ class InlineFontsProcessor {
137
137
  rewriter.emitRaw(`<link rel="preconnect" href="${url}" crossorigin>`);
138
138
  }
139
139
  break;
140
- case 'link':
140
+ case 'link': {
141
141
  const hrefAttr = attrs.some(({ name, value }) => name === 'rel' && value === 'stylesheet') &&
142
142
  attrs.find(({ name, value }) => name === 'href' && hrefsContent.has(value));
143
143
  if (hrefAttr) {
@@ -149,6 +149,7 @@ class InlineFontsProcessor {
149
149
  rewriter.emitStartTag(tag);
150
150
  }
151
151
  break;
152
+ }
152
153
  default:
153
154
  rewriter.emitStartTag(tag);
154
155
  break;
@@ -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 = '18.2.0-next.0';
13
+ const VERSION = '18.2.0-next.2';
14
14
  function hasCacheMetadata(value) {
15
15
  return (!!value &&
16
16
  typeof value === 'object' &&
@@ -1,20 +0,0 @@
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
- export declare class Spinner {
9
- #private;
10
- private readonly spinner;
11
- /** When false, only fail messages will be displayed. */
12
- enabled: boolean;
13
- constructor(text?: string);
14
- set text(text: string);
15
- get isSpinning(): boolean;
16
- succeed(text?: string): void;
17
- fail(text?: string): void;
18
- stop(): void;
19
- start(text?: string): void;
20
- }
@@ -1,55 +0,0 @@
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
- var __importDefault = (this && this.__importDefault) || function (mod) {
10
- return (mod && mod.__esModule) ? mod : { "default": mod };
11
- };
12
- Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.Spinner = void 0;
14
- const ora_1 = __importDefault(require("ora"));
15
- const color_1 = require("./color");
16
- const tty_1 = require("./tty");
17
- class Spinner {
18
- spinner;
19
- /** When false, only fail messages will be displayed. */
20
- enabled = true;
21
- #isTTY = (0, tty_1.isTTY)();
22
- constructor(text) {
23
- this.spinner = (0, ora_1.default)({
24
- text: text === undefined ? undefined : text + '\n',
25
- // The below 2 options are needed because otherwise CTRL+C will be delayed
26
- // when the underlying process is sync.
27
- hideCursor: false,
28
- discardStdin: false,
29
- isEnabled: this.#isTTY,
30
- });
31
- }
32
- set text(text) {
33
- this.spinner.text = text;
34
- }
35
- get isSpinning() {
36
- return this.spinner.isSpinning || !this.#isTTY;
37
- }
38
- succeed(text) {
39
- if (this.enabled) {
40
- this.spinner.succeed(text);
41
- }
42
- }
43
- fail(text) {
44
- this.spinner.fail(text && color_1.colors.redBright(text));
45
- }
46
- stop() {
47
- this.spinner.stop();
48
- }
49
- start(text) {
50
- if (this.enabled) {
51
- this.spinner.start(text);
52
- }
53
- }
54
- }
55
- exports.Spinner = Spinner;