@angular/build 19.0.4 → 19.0.5

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": "19.0.4",
3
+ "version": "19.0.5",
4
4
  "description": "Official build system for Angular",
5
5
  "keywords": [
6
6
  "Angular CLI",
@@ -23,7 +23,7 @@
23
23
  "builders": "builders.json",
24
24
  "dependencies": {
25
25
  "@ampproject/remapping": "2.3.0",
26
- "@angular-devkit/architect": "0.1900.4",
26
+ "@angular-devkit/architect": "0.1900.5",
27
27
  "@babel/core": "7.26.0",
28
28
  "@babel/helper-annotate-as-pure": "7.25.9",
29
29
  "@babel/helper-split-export-declaration": "7.24.7",
@@ -57,7 +57,7 @@
57
57
  "@angular/localize": "^19.0.0",
58
58
  "@angular/platform-server": "^19.0.0",
59
59
  "@angular/service-worker": "^19.0.0",
60
- "@angular/ssr": "^19.0.4",
60
+ "@angular/ssr": "^19.0.5",
61
61
  "less": "^4.2.0",
62
62
  "postcss": "^8.4.0",
63
63
  "tailwindcss": "^2.0.0 || ^3.0.0",
@@ -7,9 +7,10 @@
7
7
  */
8
8
  import type { BuilderContext } from '@angular-devkit/architect';
9
9
  import type { Plugin } from 'esbuild';
10
- import type { Connect, DepOptimizationConfig, InlineConfig } from 'vite';
10
+ import type { Connect, InlineConfig } from 'vite';
11
11
  import type { ComponentStyleRecord } from '../../tools/vite/middlewares';
12
12
  import { ServerSsrMode } from '../../tools/vite/plugins';
13
+ import { EsbuildLoaderOption } from '../../tools/vite/utils';
13
14
  import { Result } from '../application/results';
14
15
  import { type ApplicationBuilderInternalOptions, BuildOutputFileType, type ExternalResultMetadata, JavaScriptTransformer } from './internal';
15
16
  import type { NormalizedDevServerOptions } from './options';
@@ -34,5 +35,4 @@ export declare function serveWithVite(serverOptions: NormalizedDevServerOptions,
34
35
  buildPlugins?: Plugin[];
35
36
  }): AsyncIterableIterator<DevServerBuilderOutput>;
36
37
  export declare function setupServer(serverOptions: NormalizedDevServerOptions, outputFiles: Map<string, OutputFileRecord>, assets: Map<string, string>, preserveSymlinks: boolean | undefined, externalMetadata: DevServerExternalResultMetadata, ssrMode: ServerSsrMode, prebundleTransformer: JavaScriptTransformer, target: string[], zoneless: boolean, componentStyles: Map<string, ComponentStyleRecord>, templateUpdates: Map<string, string>, prebundleLoaderExtensions: EsbuildLoaderOption | undefined, extensionMiddleware?: Connect.NextHandleFunction[], indexHtmlTransformer?: (content: string) => Promise<string>, thirdPartySourcemaps?: boolean): Promise<InlineConfig>;
37
- type EsbuildLoaderOption = Exclude<DepOptimizationConfig['esbuildOptions'], undefined>['loader'];
38
38
  export {};
@@ -40,7 +40,8 @@ const promises_1 = require("node:fs/promises");
40
40
  const node_module_1 = require("node:module");
41
41
  const node_path_1 = require("node:path");
42
42
  const plugins_1 = require("../../tools/vite/plugins");
43
- const utils_1 = require("../../utils");
43
+ const utils_1 = require("../../tools/vite/utils");
44
+ const utils_2 = require("../../utils");
44
45
  const environment_options_1 = require("../../utils/environment-options");
45
46
  const load_esm_1 = require("../../utils/load-esm");
46
47
  const results_1 = require("../application/results");
@@ -87,7 +88,7 @@ async function* serveWithVite(serverOptions, builderName, builderAction, context
87
88
  // When localization is enabled with a single locale, force a flat path to maintain behavior with the existing Webpack-based dev server.
88
89
  browserOptions.forceI18nFlatOutput = true;
89
90
  }
90
- const { vendor: thirdPartySourcemaps, scripts: scriptsSourcemaps } = (0, utils_1.normalizeSourceMaps)(browserOptions.sourceMap ?? false);
91
+ const { vendor: thirdPartySourcemaps, scripts: scriptsSourcemaps } = (0, utils_2.normalizeSourceMaps)(browserOptions.sourceMap ?? false);
91
92
  if (scriptsSourcemaps && browserOptions.server) {
92
93
  // https://nodejs.org/api/process.html#processsetsourcemapsenabledval
93
94
  process.setSourceMapsEnabled(true);
@@ -459,7 +460,7 @@ function analyzeResultFiles(normalizePath, htmlIndexPath, resultFiles, generated
459
460
  }
460
461
  }
461
462
  async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks, externalMetadata, ssrMode, prebundleTransformer, target, zoneless, componentStyles, templateUpdates, prebundleLoaderExtensions, extensionMiddleware, indexHtmlTransformer, thirdPartySourcemaps = false) {
462
- const proxy = await (0, utils_1.loadProxyConfiguration)(serverOptions.workspaceRoot, serverOptions.proxyConfig);
463
+ const proxy = await (0, utils_2.loadProxyConfiguration)(serverOptions.workspaceRoot, serverOptions.proxyConfig);
463
464
  // dynamically import Vite for ESM compatibility
464
465
  const { normalizePath } = await (0, load_esm_1.loadEsmModule)('vite');
465
466
  // Path will not exist on disk and only used to provide separate path for Vite requests
@@ -530,7 +531,7 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
530
531
  noExternal: /.*/,
531
532
  // Exclude any Node.js built in module and provided dependencies (currently build defined externals)
532
533
  external: externalMetadata.explicitServer,
533
- optimizeDeps: getDepOptimizationConfig({
534
+ optimizeDeps: (0, utils_1.getDepOptimizationConfig)({
534
535
  // Only enable with caching since it causes prebundle dependencies to be cached
535
536
  disabled: serverOptions.prebundle === false,
536
537
  // Exclude any explicitly defined dependencies (currently build defined externals and node.js built-ins)
@@ -561,12 +562,13 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
561
562
  await (0, plugins_1.createAngularMemoryPlugin)({
562
563
  virtualProjectRoot,
563
564
  outputFiles,
565
+ templateUpdates,
564
566
  external: externalMetadata.explicitBrowser,
565
567
  skipViteClient: serverOptions.liveReload === false && serverOptions.hmr === false,
566
568
  }),
567
569
  ],
568
570
  // Browser only optimizeDeps. (This does not run for SSR dependencies).
569
- optimizeDeps: getDepOptimizationConfig({
571
+ optimizeDeps: (0, utils_1.getDepOptimizationConfig)({
570
572
  // Only enable with caching since it causes prebundle dependencies to be cached
571
573
  disabled: serverOptions.prebundle === false,
572
574
  // Exclude any explicitly defined dependencies (currently build defined externals)
@@ -599,60 +601,6 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
599
601
  }
600
602
  return configuration;
601
603
  }
602
- function getDepOptimizationConfig({ disabled, exclude, include, target, zoneless, prebundleTransformer, ssr, loader, thirdPartySourcemaps, }) {
603
- const plugins = [
604
- {
605
- name: 'angular-browser-node-built-in',
606
- setup(build) {
607
- // This namespace is configured by vite.
608
- // @see: https://github.com/vitejs/vite/blob/a1dd396da856401a12c921d0cd2c4e97cb63f1b5/packages/vite/src/node/optimizer/esbuildDepPlugin.ts#L109
609
- build.onLoad({ filter: /.*/, namespace: 'browser-external' }, (args) => {
610
- if (!(0, node_module_1.isBuiltin)(args.path)) {
611
- return;
612
- }
613
- return {
614
- errors: [
615
- {
616
- text: `The package "${args.path}" wasn't found on the file system but is built into node.`,
617
- },
618
- ],
619
- };
620
- });
621
- },
622
- },
623
- {
624
- name: `angular-vite-optimize-deps${ssr ? '-ssr' : ''}${thirdPartySourcemaps ? '-vendor-sourcemap' : ''}`,
625
- setup(build) {
626
- build.onLoad({ filter: /\.[cm]?js$/ }, async (args) => {
627
- return {
628
- contents: await prebundleTransformer.transformFile(args.path),
629
- loader: 'js',
630
- };
631
- });
632
- },
633
- },
634
- ];
635
- return {
636
- // Exclude any explicitly defined dependencies (currently build defined externals)
637
- exclude,
638
- // NB: to disable the deps optimizer, set optimizeDeps.noDiscovery to true and optimizeDeps.include as undefined.
639
- // Include all implict dependencies from the external packages internal option
640
- include: disabled ? undefined : include,
641
- noDiscovery: disabled,
642
- // Add an esbuild plugin to run the Angular linker on dependencies
643
- esbuildOptions: {
644
- // Set esbuild supported targets.
645
- target,
646
- supported: (0, internal_1.getFeatureSupport)(target, zoneless),
647
- plugins,
648
- loader,
649
- define: {
650
- 'ngServerMode': `${ssr}`,
651
- },
652
- resolveExtensions: ['.mjs', '.js', '.cjs'],
653
- },
654
- };
655
- }
656
604
  /**
657
605
  * Checks if the given value is an absolute URL.
658
606
  *
@@ -102,6 +102,7 @@ class AotCompilation extends angular_compilation_1.AngularCompilation {
102
102
  if (relativePath.startsWith('..')) {
103
103
  relativePath = componentFilename;
104
104
  }
105
+ relativePath = relativePath.replaceAll('\\', '/');
105
106
  const updateId = encodeURIComponent(`${host.getCanonicalFileName(relativePath)}@${node.name?.text}`);
106
107
  const updateText = angularCompiler.emitHmrUpdateModule(node);
107
108
  if (updateText === null) {
@@ -136,6 +136,15 @@ function createServerPolyfillBundleOptions(options, target, loadResultCache) {
136
136
  if (!polyfillBundleOptions) {
137
137
  return;
138
138
  }
139
+ const jsBanner = [];
140
+ if (polyfillBundleOptions.external?.length) {
141
+ jsBanner.push(`globalThis['ngServerMode'] = true;`);
142
+ }
143
+ if (isNodePlatform) {
144
+ // Note: Needed as esbuild does not provide require shims / proxy from ESModules.
145
+ // See: https://github.com/evanw/esbuild/issues/1921.
146
+ jsBanner.push(`import { createRequire } from 'node:module';`, `globalThis['require'] ??= createRequire(import.meta.url);`);
147
+ }
139
148
  const buildOptions = {
140
149
  ...polyfillBundleOptions,
141
150
  platform: isNodePlatform ? 'node' : 'neutral',
@@ -146,16 +155,9 @@ function createServerPolyfillBundleOptions(options, target, loadResultCache) {
146
155
  // More details: https://github.com/angular/angular-cli/issues/25405.
147
156
  mainFields: ['es2020', 'es2015', 'module', 'main'],
148
157
  entryNames: '[name]',
149
- banner: isNodePlatform
150
- ? {
151
- js: [
152
- // Note: Needed as esbuild does not provide require shims / proxy from ESModules.
153
- // See: https://github.com/evanw/esbuild/issues/1921.
154
- `import { createRequire } from 'node:module';`,
155
- `globalThis['require'] ??= createRequire(import.meta.url);`,
156
- ].join('\n'),
157
- }
158
- : undefined,
158
+ banner: {
159
+ js: jsBanner.join('\n'),
160
+ },
159
161
  target,
160
162
  entryPoints: {
161
163
  'polyfills.server': namespace,
@@ -274,19 +276,21 @@ function createSsrEntryCodeBundleOptions(options, target, sourceFileCache, style
274
276
  const ssrEntryNamespace = 'angular:ssr-entry';
275
277
  const ssrInjectManifestNamespace = 'angular:ssr-entry-inject-manifest';
276
278
  const isNodePlatform = options.ssrOptions?.platform !== schema_1.ExperimentalPlatform.Neutral;
279
+ const jsBanner = [];
280
+ if (options.externalDependencies?.length) {
281
+ jsBanner.push(`globalThis['ngServerMode'] = true;`);
282
+ }
283
+ if (isNodePlatform) {
284
+ // Note: Needed as esbuild does not provide require shims / proxy from ESModules.
285
+ // See: https://github.com/evanw/esbuild/issues/1921.
286
+ jsBanner.push(`import { createRequire } from 'node:module';`, `globalThis['require'] ??= createRequire(import.meta.url);`);
287
+ }
277
288
  const buildOptions = {
278
289
  ...getEsBuildServerCommonOptions(options),
279
290
  target,
280
- banner: isNodePlatform
281
- ? {
282
- js: [
283
- // Note: Needed as esbuild does not provide require shims / proxy from ESModules.
284
- // See: https://github.com/evanw/esbuild/issues/1921.
285
- `import { createRequire } from 'node:module';`,
286
- `globalThis['require'] ??= createRequire(import.meta.url);`,
287
- ].join('\n'),
288
- }
289
- : undefined,
291
+ banner: {
292
+ js: jsBanner.join('\n'),
293
+ },
290
294
  entryPoints: {
291
295
  'server': ssrEntryNamespace,
292
296
  },
@@ -10,6 +10,7 @@ import { AngularMemoryOutputFiles } from '../utils';
10
10
  interface AngularMemoryPluginOptions {
11
11
  virtualProjectRoot: string;
12
12
  outputFiles: AngularMemoryOutputFiles;
13
+ templateUpdates?: ReadonlyMap<string, string>;
13
14
  external?: string[];
14
15
  skipViteClient?: boolean;
15
16
  }
@@ -15,6 +15,7 @@ const node_assert_1 = __importDefault(require("node:assert"));
15
15
  const promises_1 = require("node:fs/promises");
16
16
  const node_path_1 = require("node:path");
17
17
  const load_esm_1 = require("../../../utils/load-esm");
18
+ const ANGULAR_PREFIX = '/@ng/';
18
19
  async function createAngularMemoryPlugin(options) {
19
20
  const { virtualProjectRoot, outputFiles, external } = options;
20
21
  const { normalizePath } = await (0, load_esm_1.loadEsmModule)('vite');
@@ -22,7 +23,11 @@ async function createAngularMemoryPlugin(options) {
22
23
  name: 'vite:angular-memory',
23
24
  // Ensures plugin hooks run before built-in Vite hooks
24
25
  enforce: 'pre',
25
- async resolveId(source, importer) {
26
+ async resolveId(source, importer, { ssr }) {
27
+ // For SSR with component HMR, pass through as a virtual module
28
+ if (ssr && source.startsWith(ANGULAR_PREFIX)) {
29
+ return '\0' + source;
30
+ }
26
31
  // Prevent vite from resolving an explicit external dependency (`externalDependencies` option)
27
32
  if (external?.includes(source)) {
28
33
  // This is still not ideal since Vite will still transform the import specifier to
@@ -41,7 +46,14 @@ async function createAngularMemoryPlugin(options) {
41
46
  return (0, node_path_1.join)(virtualProjectRoot, source);
42
47
  }
43
48
  },
44
- load(id) {
49
+ load(id, loadOptions) {
50
+ // For SSR component updates, return the component update module or empty if none
51
+ if (loadOptions?.ssr && id.startsWith(`\0${ANGULAR_PREFIX}`)) {
52
+ // Extract component identifier (first character is rollup virtual module null)
53
+ const requestUrl = new URL(id.slice(1), 'http://localhost');
54
+ const componentId = requestUrl.searchParams.get('c');
55
+ return (componentId && options.templateUpdates?.get(componentId)) ?? '';
56
+ }
45
57
  const [file] = id.split('?', 1);
46
58
  const relativeFile = '/' + normalizePath((0, node_path_1.relative)(virtualProjectRoot, file));
47
59
  const codeContents = outputFiles.get(relativeFile)?.contents;
@@ -5,6 +5,8 @@
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 type { DepOptimizationConfig } from 'vite';
9
+ import { JavaScriptTransformer } from '../esbuild/javascript-transformer';
8
10
  export type AngularMemoryOutputFiles = Map<string, {
9
11
  contents: Uint8Array;
10
12
  hash: string;
@@ -12,3 +14,15 @@ export type AngularMemoryOutputFiles = Map<string, {
12
14
  }>;
13
15
  export declare function pathnameWithoutBasePath(url: string, basePath: string): string;
14
16
  export declare function lookupMimeTypeFromRequest(url: string): string | undefined;
17
+ export type EsbuildLoaderOption = Exclude<DepOptimizationConfig['esbuildOptions'], undefined>['loader'];
18
+ export declare function getDepOptimizationConfig({ disabled, exclude, include, target, zoneless, prebundleTransformer, ssr, loader, thirdPartySourcemaps, }: {
19
+ disabled: boolean;
20
+ exclude: string[];
21
+ include: string[];
22
+ target: string[];
23
+ prebundleTransformer: JavaScriptTransformer;
24
+ ssr: boolean;
25
+ zoneless: boolean;
26
+ loader?: EsbuildLoaderOption;
27
+ thirdPartySourcemaps: boolean;
28
+ }): DepOptimizationConfig;
@@ -9,8 +9,10 @@
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
10
  exports.pathnameWithoutBasePath = pathnameWithoutBasePath;
11
11
  exports.lookupMimeTypeFromRequest = lookupMimeTypeFromRequest;
12
+ exports.getDepOptimizationConfig = getDepOptimizationConfig;
12
13
  const mrmime_1 = require("mrmime");
13
14
  const node_path_1 = require("node:path");
15
+ const utils_1 = require("../esbuild/utils");
14
16
  function pathnameWithoutBasePath(url, basePath) {
15
17
  const parsedUrl = new URL(url, 'http://localhost');
16
18
  const pathname = decodeURIComponent(parsedUrl.pathname);
@@ -26,3 +28,38 @@ function lookupMimeTypeFromRequest(url) {
26
28
  }
27
29
  return extension && (0, mrmime_1.lookup)(extension);
28
30
  }
31
+ function getDepOptimizationConfig({ disabled, exclude, include, target, zoneless, prebundleTransformer, ssr, loader, thirdPartySourcemaps, }) {
32
+ const plugins = [
33
+ {
34
+ name: `angular-vite-optimize-deps${ssr ? '-ssr' : ''}${thirdPartySourcemaps ? '-vendor-sourcemap' : ''}`,
35
+ setup(build) {
36
+ build.onLoad({ filter: /\.[cm]?js$/ }, async (args) => {
37
+ return {
38
+ contents: await prebundleTransformer.transformFile(args.path),
39
+ loader: 'js',
40
+ };
41
+ });
42
+ },
43
+ },
44
+ ];
45
+ return {
46
+ // Exclude any explicitly defined dependencies (currently build defined externals)
47
+ exclude,
48
+ // NB: to disable the deps optimizer, set optimizeDeps.noDiscovery to true and optimizeDeps.include as undefined.
49
+ // Include all implict dependencies from the external packages internal option
50
+ include: disabled ? undefined : include,
51
+ noDiscovery: disabled,
52
+ // Add an esbuild plugin to run the Angular linker on dependencies
53
+ esbuildOptions: {
54
+ // Set esbuild supported targets.
55
+ target,
56
+ supported: (0, utils_1.getFeatureSupport)(target, zoneless),
57
+ plugins,
58
+ loader,
59
+ define: {
60
+ 'ngServerMode': `${ssr}`,
61
+ },
62
+ resolveExtensions: ['.mjs', '.js', '.cjs'],
63
+ },
64
+ };
65
+ }
@@ -97,7 +97,7 @@ async function autoCsp(html, unsafeEval = false) {
97
97
  * loader script to the collection of hashes to add to the <meta> tag CSP.
98
98
  */
99
99
  function emitLoaderScript() {
100
- const loaderScript = createLoaderScript(scriptContent);
100
+ const loaderScript = createLoaderScript(scriptContent, /* enableTrustedTypes = */ false);
101
101
  hashes.push(hashTextContent(loaderScript));
102
102
  rewriter.emitRaw(`<script>${loaderScript}</script>`);
103
103
  scriptContent = [];
@@ -151,7 +151,7 @@ async function autoCsp(html, unsafeEval = false) {
151
151
  return;
152
152
  }
153
153
  }
154
- if (tag.tagName === 'body' || tag.tagName === 'html') {
154
+ if (tag.tagName === 'head' || tag.tagName === 'body' || tag.tagName === 'html') {
155
155
  // Write the loader script if a string of <script>s were the last opening tag of the document.
156
156
  if (scriptContent.length > 0) {
157
157
  emitLoaderScript();
@@ -249,7 +249,7 @@ function createLoaderScript(srcList, enableTrustedTypes = false) {
249
249
  // URI encoding means value can't escape string, JS, or HTML context.
250
250
  const srcAttr = encodeURI(s.src).replaceAll("'", "\\'");
251
251
  // Can only be 'module' or a JS MIME type or an empty string.
252
- const typeAttr = s.type ? "'" + s.type + "'" : undefined;
252
+ const typeAttr = s.type ? "'" + s.type + "'" : "''";
253
253
  const asyncAttr = s.async ? 'true' : 'false';
254
254
  const deferAttr = s.defer ? 'true' : 'false';
255
255
  return `['${srcAttr}', ${typeAttr}, ${asyncAttr}, ${deferAttr}]`;
@@ -268,7 +268,7 @@ function createLoaderScript(srcList, enableTrustedTypes = false) {
268
268
  s.type = scriptUrl[1];
269
269
  s.async = !!scriptUrl[2];
270
270
  s.defer = !!scriptUrl[3];
271
- document.body.appendChild(s);
271
+ document.lastElementChild.appendChild(s);
272
272
  });\n`
273
273
  : `
274
274
  var scripts = [${srcListFormatted}];
@@ -278,6 +278,6 @@ function createLoaderScript(srcList, enableTrustedTypes = false) {
278
278
  s.type = scriptUrl[1];
279
279
  s.async = !!scriptUrl[2];
280
280
  s.defer = !!scriptUrl[3];
281
- document.body.appendChild(s);
281
+ document.lastElementChild.appendChild(s);
282
282
  });\n`;
283
283
  }
@@ -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 = '19.0.4';
13
+ const VERSION = '19.0.5';
14
14
  function hasCacheMetadata(value) {
15
15
  return (!!value &&
16
16
  typeof value === 'object' &&
@@ -19,6 +19,10 @@ const node_path_1 = require("node:path");
19
19
  const node_url_1 = require("node:url");
20
20
  const url_1 = require("url");
21
21
  const javascript_transformer_1 = require("../../../tools/esbuild/javascript-transformer");
22
+ /**
23
+ * @note For some unknown reason, setting `globalThis.ngServerMode = true` does not work when using ESM loader hooks.
24
+ */
25
+ const NG_SERVER_MODE_INIT_BYTES = new TextEncoder().encode('var ngServerMode=true;');
22
26
  /**
23
27
  * Node.js ESM loader to redirect imports to in memory files.
24
28
  * @see: https://nodejs.org/api/esm.html#loaders for more information about loaders.
@@ -107,7 +111,11 @@ async function load(url, context, nextLoad) {
107
111
  // need linking are ESM only.
108
112
  if (format === 'module' && isFileProtocol(url)) {
109
113
  const filePath = (0, url_1.fileURLToPath)(url);
110
- const source = await javascriptTransformer.transformFile(filePath);
114
+ let source = await javascriptTransformer.transformFile(filePath);
115
+ if (filePath.includes('@angular/')) {
116
+ // Prepend 'var ngServerMode=true;' to the source.
117
+ source = Buffer.concat([NG_SERVER_MODE_INIT_BYTES, source]);
118
+ }
111
119
  return {
112
120
  format,
113
121
  shortCircuit: true,