@angular/build 18.2.0-next.1 → 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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@angular/build",
3
- "version": "18.2.0-next.1",
3
+ "version": "18.2.0-next.2",
4
4
  "description": "Official build system for Angular",
5
5
  "keywords": [
6
6
  "Angular CLI",
@@ -23,12 +23,12 @@
23
23
  "builders": "builders.json",
24
24
  "dependencies": {
25
25
  "@ampproject/remapping": "2.3.0",
26
- "@angular-devkit/architect": "0.1802.0-next.1",
26
+ "@angular-devkit/architect": "0.1802.0-next.2",
27
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.15",
31
+ "@inquirer/confirm": "3.1.17",
32
32
  "@vitejs/plugin-basic-ssl": "1.1.0",
33
33
  "browserslist": "^4.23.0",
34
34
  "critters": "0.0.24",
@@ -42,7 +42,7 @@
42
42
  "parse5-html-rewriting-stream": "7.0.0",
43
43
  "picomatch": "4.0.2",
44
44
  "piscina": "4.6.1",
45
- "rollup": "4.18.1",
45
+ "rollup": "4.19.0",
46
46
  "sass": "1.77.8",
47
47
  "semver": "7.6.3",
48
48
  "vite": "5.3.4",
@@ -78,7 +78,7 @@
78
78
  "optional": true
79
79
  }
80
80
  },
81
- "packageManager": "yarn@4.3.0",
81
+ "packageManager": "yarn@4.3.1",
82
82
  "repository": {
83
83
  "type": "git",
84
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);
@@ -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?: {
@@ -45,6 +45,7 @@ const i18n_locale_plugin_1 = require("../../tools/vite/i18n-locale-plugin");
45
45
  const id_prefix_plugin_1 = require("../../tools/vite/id-prefix-plugin");
46
46
  const utils_1 = require("../../utils");
47
47
  const load_esm_1 = require("../../utils/load-esm");
48
+ const results_1 = require("../application/results");
48
49
  const internal_1 = require("./internal");
49
50
  /**
50
51
  * Build options that are also present on the dev server but are only passed
@@ -77,12 +78,6 @@ async function* serveWithVite(serverOptions, builderName, builderAction, context
77
78
  }
78
79
  // Set all packages as external to support Vite's prebundle caching
79
80
  browserOptions.externalPackages = serverOptions.prebundle;
80
- const baseHref = browserOptions.baseHref;
81
- if (serverOptions.servePath === undefined && baseHref !== undefined) {
82
- // Remove trailing slash
83
- serverOptions.servePath =
84
- baseHref !== './' && baseHref[baseHref.length - 1] === '/' ? baseHref.slice(0, -1) : baseHref;
85
- }
86
81
  // The development server currently only supports a single locale when localizing.
87
82
  // This matches the behavior of the Webpack-based development server but could be expanded in the future.
88
83
  if (browserOptions.localize === true ||
@@ -101,15 +96,8 @@ async function* serveWithVite(serverOptions, builderName, builderAction, context
101
96
  // In a development environment the additional scope information does not
102
97
  // have a negative effect unlike production where final output size is relevant.
103
98
  { sourcemap: true, jit: true, thirdPartySourcemaps }, 1);
104
- // Extract output index from options
105
- // 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
106
100
  let htmlIndexPath = 'index.html';
107
- if (browserOptions.index && typeof browserOptions.index !== 'boolean') {
108
- htmlIndexPath =
109
- typeof browserOptions.index === 'string'
110
- ? (0, node_path_1.basename)(browserOptions.index)
111
- : browserOptions.index.output || 'index.html';
112
- }
113
101
  // dynamically import Vite for ESM compatibility
114
102
  const { createServer, normalizePath } = await (0, load_esm_1.loadEsmModule)('vite');
115
103
  let server;
@@ -131,24 +119,55 @@ async function* serveWithVite(serverOptions, builderName, builderAction, context
131
119
  });
132
120
  // TODO: Switch this to an architect schedule call when infrastructure settings are supported
133
121
  for await (const result of builderAction(browserOptions, context, extensions?.buildPlugins)) {
134
- (0, node_assert_1.default)(result.outputFiles, 'Builder did not provide result files.');
135
- // If build failed, nothing to serve
136
- if (!result.success) {
137
- // If server is active, send an error notification
138
- if (result.errors?.length && server) {
139
- hadError = true;
140
- server.hot.send({
141
- type: 'error',
142
- err: {
143
- message: result.errors[0].text,
144
- stack: '',
145
- loc: result.errors[0].location,
146
- },
147
- });
148
- }
149
- 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;
150
168
  }
151
- else if (hadError && server) {
169
+ // Clear existing error overlay on successful result
170
+ if (hadError && server) {
152
171
  hadError = false;
153
172
  // Send an empty update to clear the error overlay
154
173
  server.hot.send({
@@ -156,18 +175,10 @@ async function* serveWithVite(serverOptions, builderName, builderAction, context
156
175
  updates: [],
157
176
  });
158
177
  }
159
- // Analyze result files for changes
160
- analyzeResultFiles(normalizePath, htmlIndexPath, result.outputFiles, generatedFiles);
161
- assetFiles.clear();
162
- if (result.assetFiles) {
163
- for (const asset of result.assetFiles) {
164
- assetFiles.set('/' + normalizePath(asset.destination), normalizePath(asset.source));
165
- }
166
- }
167
178
  // To avoid disconnecting the array objects from the option, these arrays need to be mutated instead of replaced.
168
179
  let requiresServerRestart = false;
169
- if (result.externalMetadata) {
170
- const { implicitBrowser, implicitServer, explicit } = result.externalMetadata;
180
+ if (result.detail?.['externalMetadata']) {
181
+ const { implicitBrowser, implicitServer, explicit } = result.detail['externalMetadata'];
171
182
  const implicitServerFiltered = implicitServer.filter((m) => removeNodeJsBuiltinModules(m) && removeAbsoluteUrls(m));
172
183
  const implicitBrowserFiltered = implicitBrowser.filter(removeAbsoluteUrls);
173
184
  if (browserOptions.ssr && serverOptions.prebundle !== false) {
@@ -302,22 +313,26 @@ function handleUpdate(normalizePath, generatedFiles, server, serverOptions, logg
302
313
  }
303
314
  function analyzeResultFiles(normalizePath, htmlIndexPath, resultFiles, generatedFiles) {
304
315
  const seen = new Set(['/index.html']);
305
- for (const file of resultFiles) {
316
+ for (const [outputPath, file] of Object.entries(resultFiles)) {
317
+ if (file.origin === 'disk') {
318
+ continue;
319
+ }
306
320
  let filePath;
307
- if (file.path === htmlIndexPath) {
321
+ if (outputPath === htmlIndexPath) {
308
322
  // Convert custom index output path to standard index path for dev-server usage.
309
323
  // This mimics the Webpack dev-server behavior.
310
324
  filePath = '/index.html';
311
325
  }
312
326
  else {
313
- filePath = '/' + normalizePath(file.path);
327
+ filePath = '/' + normalizePath(outputPath);
314
328
  }
315
329
  seen.add(filePath);
330
+ const servable = file.type === internal_1.BuildOutputFileType.Browser || file.type === internal_1.BuildOutputFileType.Media;
316
331
  // Skip analysis of sourcemaps
317
332
  if (filePath.endsWith('.map')) {
318
333
  generatedFiles.set(filePath, {
319
334
  contents: file.contents,
320
- servable: file.type === internal_1.BuildOutputFileType.Browser || file.type === internal_1.BuildOutputFileType.Media,
335
+ servable,
321
336
  size: file.contents.byteLength,
322
337
  updated: false,
323
338
  });
@@ -337,7 +352,7 @@ function analyzeResultFiles(normalizePath, htmlIndexPath, resultFiles, generated
337
352
  size: file.contents.byteLength,
338
353
  hash: file.hash,
339
354
  updated: true,
340
- servable: file.type === internal_1.BuildOutputFileType.Browser || file.type === internal_1.BuildOutputFileType.Media,
355
+ servable,
341
356
  });
342
357
  }
343
358
  // Clear stale output files
@@ -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;
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
@@ -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;
@@ -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;
@@ -10,7 +10,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.createRemoveIdPrefixPlugin = createRemoveIdPrefixPlugin;
11
11
  // NOTE: the implementation for this Vite plugin is roughly based on:
12
12
  // https://github.com/MilanKovacic/vite-plugin-externalize-dependencies
13
- const VITE_ID_PREFIX = '/@id/';
13
+ const VITE_ID_PREFIX = '@id/';
14
14
  const escapeRegexSpecialChars = (inputString) => {
15
15
  return inputString.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
16
16
  };
@@ -24,7 +24,7 @@ function createRemoveIdPrefixPlugin(externals) {
24
24
  return;
25
25
  }
26
26
  const escapedExternals = externals.map(escapeRegexSpecialChars);
27
- const prefixedExternalRegex = new RegExp(`${VITE_ID_PREFIX}(${escapedExternals.join('|')})`, 'g');
27
+ const prefixedExternalRegex = new RegExp(`${resolvedConfig.base}${VITE_ID_PREFIX}(${escapedExternals.join('|')})`, 'g');
28
28
  // @ts-expect-error: Property 'push' does not exist on type 'readonly Plugin<any>[]'
29
29
  // Reasoning:
30
30
  // since the /@id/ prefix is added by Vite's import-analysis plugin,
@@ -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;
@@ -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.1';
13
+ const VERSION = '18.2.0-next.2';
14
14
  function hasCacheMetadata(value) {
15
15
  return (!!value &&
16
16
  typeof value === 'object' &&