diff --git a/package.json b/package.json index 52bde376..11492ed7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@angular/build", - "version": "19.2.0-next.2+sha-8f73dad", + "version": "19.2.15+sha-c6eba4d", "description": "Official build system for Angular", "keywords": [ "Angular CLI", @@ -23,16 +23,16 @@ "builders": "builders.json", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "github:angular/angular-devkit-architect-builds#8f73dad", - "@babel/core": "7.26.9", + "@angular-devkit/architect": "github:angular/angular-devkit-architect-builds#c6eba4d", + "@babel/core": "7.26.10", "@babel/helper-annotate-as-pure": "7.25.9", "@babel/helper-split-export-declaration": "7.24.7", "@babel/plugin-syntax-import-attributes": "7.26.0", "@inquirer/confirm": "5.1.6", "@vitejs/plugin-basic-ssl": "1.2.0", - "beasties": "0.2.0", + "beasties": "0.3.2", "browserslist": "^4.23.0", - "esbuild": "0.25.0", + "esbuild": "0.25.4", "fast-glob": "3.3.3", "https-proxy-agent": "7.0.6", "istanbul-lib-instrument": "6.0.3", @@ -46,7 +46,7 @@ "sass": "1.85.0", "semver": "7.7.1", "source-map-support": "0.5.21", - "vite": "6.1.0", + "vite": "6.2.7", "watchpack": "2.4.2" }, "optionalDependencies": { @@ -58,7 +58,7 @@ "@angular/localize": "^19.0.0 || ^19.2.0-next.0", "@angular/platform-server": "^19.0.0 || ^19.2.0-next.0", "@angular/service-worker": "^19.0.0 || ^19.2.0-next.0", - "@angular/ssr": "github:angular/angular-ssr-builds#8f73dad", + "@angular/ssr": "github:angular/angular-ssr-builds#c6eba4d", "karma": "^6.4.0", "less": "^4.2.0", "ng-packagr": "^19.0.0 || ^19.2.0-next.0", @@ -76,7 +76,9 @@ "@angular/service-worker": { "optional": true }, - "@angular/ssr": "github:angular/angular-ssr-builds#8f73dad", + "@angular/ssr": { + "optional": true + }, "karma": { "optional": true }, @@ -97,6 +99,7 @@ "type": "git", "url": "https://github.com/angular/angular-cli.git" }, + "packageManager": "pnpm@9.15.6", "engines": { "node": "^18.19.1 || ^20.11.1 || >=22.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", diff --git a/src/builders/application/build-action.js b/src/builders/application/build-action.js index 5b0a7e9c..0d012009 100644 --- a/src/builders/application/build-action.js +++ b/src/builders/application/build-action.js @@ -263,8 +263,7 @@ function* emitOutputResults({ outputFiles, assetFiles, errors, warnings, externa if (file.path.endsWith('.css')) { hasCssUpdates = true; } - else if (!/(?:\.m?js|\.map)$/.test(file.path)) { - // Updates to non-JS files must signal an update with the dev server + else if (!canBackgroundUpdate(file)) { incrementalResult.background = false; } incrementalResult.files[file.path] = { @@ -342,3 +341,13 @@ function* emitOutputResults({ outputFiles, assetFiles, errors, warnings, externa function isCssFilePath(filePath) { return /\.css(?:\.map)?$/i.test(filePath); } +function canBackgroundUpdate(file) { + // Files in the output root are not served and do not affect the + // application available with the development server. + if (file.type === bundler_context_1.BuildOutputFileType.Root) { + return true; + } + // Updates to non-JS files must signal an update with the dev server + // except the service worker configuration which is special cased. + return /(?:\.m?js|\.map)$/.test(file.path) || file.path === 'ngsw.json'; +} diff --git a/src/builders/application/execute-build.js b/src/builders/application/execute-build.js index 1c1d7526..7c6901ff 100644 --- a/src/builders/application/execute-build.js +++ b/src/builders/application/execute-build.js @@ -136,15 +136,43 @@ async function executeBuild(options, context, rebuildState) { } // Analyze external imports if external options are enabled if (options.externalPackages || bundlingResult.externalConfiguration) { - const { externalConfiguration, externalImports: { browser, server }, } = bundlingResult; - const implicitBrowser = browser ? [...browser] : []; - const implicitServer = server ? [...server] : []; - // TODO: Implement wildcard externalConfiguration filtering - executionResult.setExternalMetadata(externalConfiguration - ? implicitBrowser.filter((value) => !externalConfiguration.includes(value)) - : implicitBrowser, externalConfiguration - ? implicitServer.filter((value) => !externalConfiguration.includes(value)) - : implicitServer, externalConfiguration); + const { externalConfiguration = [], externalImports: { browser = [], server = [] }, } = bundlingResult; + // Similar to esbuild, --external:@foo/bar automatically implies --external:@foo/bar/*, + // which matches import paths like @foo/bar/baz. + // This means all paths within the @foo/bar package are also marked as external. + const exclusionsPrefixes = externalConfiguration.map((exclusion) => exclusion + '/'); + const exclusions = new Set(externalConfiguration); + const explicitExternal = new Set(); + const isExplicitExternal = (dep) => { + if (exclusions.has(dep)) { + return true; + } + for (const prefix of exclusionsPrefixes) { + if (dep.startsWith(prefix)) { + return true; + } + } + return false; + }; + const implicitBrowser = []; + for (const dep of browser) { + if (isExplicitExternal(dep)) { + explicitExternal.add(dep); + } + else { + implicitBrowser.push(dep); + } + } + const implicitServer = []; + for (const dep of server) { + if (isExplicitExternal(dep)) { + explicitExternal.add(dep); + } + else { + implicitServer.push(dep); + } + } + executionResult.setExternalMetadata(implicitBrowser, implicitServer, [...explicitExternal]); } const { metafile, initialFiles, outputFiles } = bundlingResult; executionResult.outputFiles.push(...outputFiles); diff --git a/src/builders/application/options.d.ts b/src/builders/application/options.d.ts index ed7e118f..69c18f59 100644 --- a/src/builders/application/options.d.ts +++ b/src/builders/application/options.d.ts @@ -108,6 +108,9 @@ export declare function normalizeOptions(context: BuilderContext, projectName: s cacheOptions: import("../../utils/normalize-cache").NormalizedCachedOptions; crossOrigin: import("./schema").CrossOrigin | undefined; externalDependencies: string[] | undefined; + externalPackages: boolean | { + exclude: string[] | undefined; + } | undefined; extractLicenses: boolean | undefined; inlineStyleLanguage: string; jit: boolean; @@ -115,9 +118,6 @@ export declare function normalizeOptions(context: BuilderContext, projectName: s polyfills: string[] | undefined; poll: number | undefined; progress: boolean; - externalPackages: boolean | { - exclude: string[]; - } | undefined; preserveSymlinks: boolean; stylePreprocessorOptions: import("./schema").StylePreprocessorOptions | undefined; subresourceIntegrity: boolean | undefined; diff --git a/src/builders/application/options.js b/src/builders/application/options.js index 06d26f11..0c2ffcd4 100644 --- a/src/builders/application/options.js +++ b/src/builders/application/options.js @@ -132,12 +132,12 @@ async function normalizeOptions(context, projectName, options, extensions) { ? undefined : await getTailwindConfig(searchDirectories, workspaceRoot, context); let serverEntryPoint; - if (options.server) { + if (typeof options.server === 'string') { + if (options.server === '') { + throw new Error('The "server" option cannot be an empty string.'); + } serverEntryPoint = node_path_1.default.join(workspaceRoot, options.server); } - else if (options.server === '') { - throw new Error('The "server" option cannot be an empty string.'); - } let prerenderOptions; if (options.prerender) { const { discoverRoutes = true, routesFile = undefined } = options.prerender === true ? {} : options.prerender; @@ -253,7 +253,13 @@ async function normalizeOptions(context, projectName, options, extensions) { baseHref, cacheOptions, crossOrigin, - externalDependencies, + externalDependencies: normalizeExternals(externalDependencies), + externalPackages: typeof externalPackages === 'object' + ? { + ...externalPackages, + exclude: normalizeExternals(externalPackages.exclude), + } + : externalPackages, extractLicenses, inlineStyleLanguage, jit: !aot, @@ -261,7 +267,6 @@ async function normalizeOptions(context, projectName, options, extensions) { polyfills: polyfills === undefined || Array.isArray(polyfills) ? polyfills : [polyfills], poll, progress, - externalPackages, preserveSymlinks, stylePreprocessorOptions, subresourceIntegrity, @@ -447,3 +452,25 @@ function getLocaleBaseHref(baseHref = '', i18n, locale) { const baseHrefSuffix = localeData.baseHref ?? localeData.subPath + '/'; return baseHrefSuffix !== '' ? (0, url_1.urlJoin)(baseHref, baseHrefSuffix) : undefined; } +/** + * Normalizes an array of external dependency paths by ensuring that + * wildcard patterns (`/*`) are removed from package names. + * + * This avoids the need to handle this normalization repeatedly in our plugins, + * as esbuild already treats `--external:@foo/bar` as implicitly including + * `--external:@foo/bar/*`. By standardizing the input, we ensure consistency + * and reduce redundant checks across our plugins. + * + * @param value - An optional array of dependency paths to normalize. + * @returns A new array with wildcard patterns removed from package names, or `undefined` if input is `undefined`. + */ +function normalizeExternals(value) { + if (!value) { + return undefined; + } + return [ + ...new Set(value.map((d) => + // remove "/*" wildcard in the end if provided string is not path-like + d.endsWith('/*') && !/^\.{0,2}\//.test(d) ? d.slice(0, -2) : d)), + ]; +} diff --git a/src/builders/application/schema.d.ts b/src/builders/application/schema.d.ts index ee155991..47a1cee8 100644 --- a/src/builders/application/schema.d.ts +++ b/src/builders/application/schema.d.ts @@ -62,7 +62,9 @@ export type Schema = { deployUrl?: string; /** * Exclude the listed external dependencies from being bundled into the bundle. Instead, the - * created bundle relies on these dependencies to be available during runtime. + * created bundle relies on these dependencies to be available during runtime. Note: + * `@foo/bar` marks all paths within the `@foo/bar` package as external, including sub-paths + * like `@foo/bar/baz`. */ externalDependencies?: string[]; /** @@ -161,11 +163,11 @@ export type Schema = { * The full path for the server entry point to the application, relative to the current * workspace. */ - server?: string; + server?: Serv; /** * Generates a service worker configuration. */ - serviceWorker?: ServiceWorker; + serviceWorker?: Serv; /** * Output source maps for scripts and styles. For more information, see * https://angular.dev/reference/configs/workspace-config#source-map-configuration. @@ -494,9 +496,12 @@ export type AutoCspClass = { unsafeEval?: boolean; }; /** + * The full path for the server entry point to the application, relative to the current + * workspace. + * * Generates a service worker configuration. */ -export type ServiceWorker = boolean | string; +export type Serv = boolean | string; /** * Output source maps for scripts and styles. For more information, see * https://angular.dev/reference/configs/workspace-config#source-map-configuration. diff --git a/src/builders/application/schema.json b/src/builders/application/schema.json index a8e8e13a..e2bf1f3a 100644 --- a/src/builders/application/schema.json +++ b/src/builders/application/schema.json @@ -17,7 +17,6 @@ "description": "The full path for the browser entry point to the application, relative to the current workspace." }, "server": { - "type": "string", "description": "The full path for the server entry point to the application, relative to the current workspace.", "oneOf": [ { @@ -196,7 +195,7 @@ "additionalProperties": false }, "externalDependencies": { - "description": "Exclude the listed external dependencies from being bundled into the bundle. Instead, the created bundle relies on these dependencies to be available during runtime.", + "description": "Exclude the listed external dependencies from being bundled into the bundle. Instead, the created bundle relies on these dependencies to be available during runtime. Note: `@foo/bar` marks all paths within the `@foo/bar` package as external, including sub-paths like `@foo/bar/baz`.", "type": "array", "items": { "type": "string" diff --git a/src/builders/dev-server/builder.js b/src/builders/dev-server/builder.js index 163c4703..3965771a 100644 --- a/src/builders/dev-server/builder.js +++ b/src/builders/dev-server/builder.js @@ -33,10 +33,6 @@ async function* execute(options, context, extensions) { return; } const { builderName, normalizedOptions } = await initialize(options, projectName, context); - // Warn if the initial options provided by the user enable prebundling but caching is disabled - if (options.prebundle && !normalizedOptions.cacheOptions.enabled) { - context.logger.warn(`Prebundling has been configured but will not be used because caching has been disabled.`); - } yield* (0, vite_server_1.serveWithVite)(normalizedOptions, builderName, (options, context, plugins) => (0, internal_1.buildApplicationInternal)(options, context, { codePlugins: plugins }), context, { indexHtml: extensions?.indexHtmlTransformer }, extensions); } async function initialize(initialOptions, projectName, context) { diff --git a/src/builders/dev-server/schema.d.ts b/src/builders/dev-server/schema.d.ts index cb68a19f..39742e19 100644 --- a/src/builders/dev-server/schema.d.ts +++ b/src/builders/dev-server/schema.d.ts @@ -104,7 +104,9 @@ export type PrebundleUnion = boolean | PrebundleClass; export type PrebundleClass = { /** * List of package imports that should not be prebundled by the development server. The - * packages will be bundled into the application code itself. + * packages will be bundled into the application code itself. Note: specifying `@foo/bar` + * marks all paths within the `@foo/bar` package as excluded, including sub-paths like + * `@foo/bar/baz`. */ exclude: string[]; }; diff --git a/src/builders/dev-server/schema.json b/src/builders/dev-server/schema.json index c36d8614..41902e43 100644 --- a/src/builders/dev-server/schema.json +++ b/src/builders/dev-server/schema.json @@ -115,7 +115,7 @@ "type": "object", "properties": { "exclude": { - "description": "List of package imports that should not be prebundled by the development server. The packages will be bundled into the application code itself.", + "description": "List of package imports that should not be prebundled by the development server. The packages will be bundled into the application code itself. Note: specifying `@foo/bar` marks all paths within the `@foo/bar` package as excluded, including sub-paths like `@foo/bar/baz`.", "type": "array", "items": { "type": "string" } } diff --git a/src/builders/dev-server/vite-server.js b/src/builders/dev-server/vite-server.js index d3676fdc..fd36563e 100644 --- a/src/builders/dev-server/vite-server.js +++ b/src/builders/dev-server/vite-server.js @@ -86,6 +86,13 @@ async function* serveWithVite(serverOptions, builderName, builderAction, context browserOptions.security = { autoCsp: false, }; + // Disable JSON build stats. + // These are not accessible with the dev server and can cause HMR fallbacks. + if (browserOptions.statsJson === true) { + context.logger.warn('Build JSON statistics output (`statsJson` option) has been disabled.' + + ' The development server does not support this option.'); + } + browserOptions.statsJson = false; // Set all packages as external to support Vite's prebundle caching browserOptions.externalPackages = serverOptions.prebundle; // Disable generating a full manifest with routes. @@ -109,16 +116,13 @@ async function* serveWithVite(serverOptions, builderName, builderAction, context } // Enable to support link-based component style hot reloading (`NG_HMR_CSTYLES=1` can be used to enable) browserOptions.externalRuntimeStyles = - serverOptions.liveReload && serverOptions.hmr && environment_options_1.useComponentStyleHmr; + browserOptions.aot && serverOptions.liveReload && serverOptions.hmr && environment_options_1.useComponentStyleHmr; // Enable to support component template hot replacement (`NG_HMR_TEMPLATE=0` can be used to disable selectively) // This will also replace file-based/inline styles as code if external runtime styles are not enabled. browserOptions.templateUpdates = - serverOptions.liveReload && serverOptions.hmr && environment_options_1.useComponentTemplateHmr; + browserOptions.aot && serverOptions.liveReload && serverOptions.hmr && environment_options_1.useComponentTemplateHmr; if (browserOptions.templateUpdates) { - context.logger.warn('Component HMR has been enabled.\n' + - 'If you encounter application reload issues, you can manually reload the page to bypass HMR and/or disable this feature with the' + - ' `--no-hmr` command line option.\n' + - 'Please consider reporting any issues you encounter here: https://github.com/angular/angular-cli/issues\n'); + context.logger.warn('Component HMR has been enabled, see https://angular.dev/hmr for more info.'); } browserOptions.incrementalResults = true; // Setup the prebundling transformer that will be shared across Vite prebundling requests @@ -246,8 +250,9 @@ async function* serveWithVite(serverOptions, builderName, builderAction, context externalMetadata.explicitServer.length = 0; externalMetadata.implicitServer.length = 0; externalMetadata.implicitBrowser.length = 0; - externalMetadata.explicitBrowser.push(...explicit); - externalMetadata.explicitServer.push(...explicit, ...node_module_1.builtinModules); + const externalDeps = browserOptions.externalDependencies ?? []; + externalMetadata.explicitBrowser.push(...explicit, ...externalDeps); + externalMetadata.explicitServer.push(...explicit, ...externalDeps, ...node_module_1.builtinModules); externalMetadata.implicitServer.push(...implicitServerFiltered); externalMetadata.implicitBrowser.push(...implicitBrowserFiltered); // The below needs to be sorted as Vite uses these options are part of the hashing invalidation algorithm. @@ -306,7 +311,10 @@ async function* serveWithVite(serverOptions, builderName, builderAction, context }); } // Setup server and start listening - const serverConfiguration = await setupServer(serverOptions, generatedFiles, assetFiles, browserOptions.preserveSymlinks, externalMetadata, ssrMode, prebundleTransformer, target, (0, internal_1.isZonelessApp)(polyfills), componentStyles, templateUpdates, browserOptions.loader, browserOptions.define, extensions?.middleware, transformers?.indexHtml, thirdPartySourcemaps); + const serverConfiguration = await setupServer(serverOptions, generatedFiles, assetFiles, browserOptions.preserveSymlinks, externalMetadata, ssrMode, prebundleTransformer, target, (0, internal_1.isZonelessApp)(polyfills), componentStyles, templateUpdates, browserOptions.loader, { + ...browserOptions.define, + 'ngHmrMode': browserOptions.templateUpdates ? 'true' : 'false', + }, extensions?.middleware, transformers?.indexHtml, thirdPartySourcemaps); server = await createServer(serverConfiguration); await server.listen(); // Setup builder context logging for browser clients diff --git a/src/builders/extract-i18n/builder.js b/src/builders/extract-i18n/builder.js index 2babc523..19b0f374 100644 --- a/src/builders/extract-i18n/builder.js +++ b/src/builders/extract-i18n/builder.js @@ -102,7 +102,7 @@ async function execute(options, context, extensions) { }; const diagnostics = checkDuplicateMessages( // eslint-disable-next-line @typescript-eslint/no-explicit-any - checkFileSystem, extractionResult.messages, 'warning', + checkFileSystem, extractionResult.messages, normalizedOptions.i18nOptions.i18nDuplicateTranslation || 'warning', // eslint-disable-next-line @typescript-eslint/no-explicit-any extractionResult.basePath); if (diagnostics.messages.length > 0) { diff --git a/src/builders/karma/application_builder.js b/src/builders/karma/application_builder.js index 4e590436..bc35771c 100644 --- a/src/builders/karma/application_builder.js +++ b/src/builders/karma/application_builder.js @@ -55,6 +55,7 @@ const bundler_context_1 = require("../../tools/esbuild/bundler-context"); const schema_1 = require("../application/schema"); const find_tests_1 = require("./find-tests"); const localResolve = (0, node_module_1.createRequire)(__filename).resolve; +const isWindows = process.platform === 'win32'; class ApplicationBuildError extends Error { constructor(message) { super(message); @@ -75,9 +76,15 @@ class AngularAssetsMiddleware { let err = null; try { const url = new URL(`http://${req.headers['host']}${req.url}`); - const file = this.latestBuildFiles.files[url.pathname.slice(1)]; + // Remove the leading slash from the URL path and convert to platform specific. + // The latest build files will use the platform path separator. + let pathname = url.pathname.slice(1); + if (isWindows) { + pathname = pathname.replaceAll(path.posix.sep, path.win32.sep); + } + const file = this.latestBuildFiles.files[pathname]; if (file?.origin === 'disk') { - this.serveFile(file.inputPath, undefined, res); + this.serveFile(file.inputPath, undefined, res, undefined, undefined, /* doNotCache */ true); return; } else if (file?.origin === 'memory') { @@ -316,6 +323,7 @@ async function initializeApplication(options, context, karmaOptions, transforms entryPoints, tsConfig: options.tsConfig, outputPath, + preserveSymlinks: options.preserveSymlinks, aot: options.aot, index: false, outputHashing: schema_1.OutputHashing.None, @@ -351,7 +359,8 @@ async function initializeApplication(options, context, karmaOptions, transforms // Write test files await writeTestFiles(buildOutput.files, buildOptions.outputPath); // We need to add this to the beginning *after* the testing framework has - // prepended its files. + // prepended its files. The output path is required for each since they are + // added later in the test process via a plugin. const polyfillsFile = { pattern: `${outputPath}/polyfills.js`, included: true, @@ -366,28 +375,29 @@ async function initializeApplication(options, context, karmaOptions, transforms type: 'module', watched: false, }; + karmaOptions.basePath = outputPath; karmaOptions.files ??= []; if (options.scripts?.length) { // This should be more granular to support named bundles. // However, it replicates the behavior of the Karma Webpack-based builder. karmaOptions.files.push({ - pattern: `${outputPath}/scripts.js`, + pattern: `scripts.js`, watched: false, type: 'js', }); } karmaOptions.files.push( // Serve global setup script. - { pattern: `${outputPath}/${mainName}.js`, type: 'module', watched: false }, + { pattern: `${mainName}.js`, type: 'module', watched: false }, // Serve all source maps. - { pattern: `${outputPath}/*.map`, included: false, watched: false }, + { pattern: `*.map`, included: false, watched: false }, // These are the test entrypoints. - { pattern: `${outputPath}/spec-*.js`, type: 'module', watched: false }); + { pattern: `spec-*.js`, type: 'module', watched: false }); if (hasChunkOrWorkerFiles(buildOutput.files)) { karmaOptions.files.push( // Allow loading of chunk-* files but don't include them all on load. { - pattern: `${outputPath}/{chunk,worker}-*.js`, + pattern: `{chunk,worker}-*.js`, type: 'module', included: false, watched: false, @@ -395,7 +405,7 @@ async function initializeApplication(options, context, karmaOptions, transforms } if (options.styles?.length) { // Serve CSS outputs on page load, these are the global styles. - karmaOptions.files.push({ pattern: `${outputPath}/*.css`, type: 'css', watched: false }); + karmaOptions.files.push({ pattern: `*.css`, type: 'css', watched: false }); } const parsedKarmaConfig = await karma.config.parseConfig(options.karmaConfig && path.resolve(context.workspaceRoot, options.karmaConfig), transforms.karmaOptions ? transforms.karmaOptions(karmaOptions) : karmaOptions, { promiseConfig: true, throwErrors: true }); // Remove the webpack plugin/framework: diff --git a/src/tools/angular/angular-host.js b/src/tools/angular/angular-host.js index cb2ea324..4a47a9c2 100644 --- a/src/tools/angular/angular-host.js +++ b/src/tools/angular/angular-host.js @@ -118,6 +118,9 @@ function createAngularCompilerHost(typescript, compilerOptions, hostOptions, pac }; host.resourceNameToFileName = function (resourceName, containingFile) { const resolvedPath = node_path_1.default.join(node_path_1.default.dirname(containingFile), resourceName); + if (!this.fileExists(resolvedPath)) { + return null; + } // All resource names that have template file extensions are assumed to be templates // TODO: Update compiler to provide the resource type to avoid extension matching here. if (!hostOptions.externalStylesheets || hasTemplateExtension(resolvedPath)) { diff --git a/src/tools/babel/plugins/add-code-coverage.js b/src/tools/babel/plugins/add-code-coverage.js index 6b585bc1..6cb4cdf3 100644 --- a/src/tools/babel/plugins/add-code-coverage.js +++ b/src/tools/babel/plugins/add-code-coverage.js @@ -14,7 +14,6 @@ exports.default = default_1; const core_1 = require("@babel/core"); const istanbul_lib_instrument_1 = require("istanbul-lib-instrument"); const node_assert_1 = __importDefault(require("node:assert")); -const node_url_1 = require("node:url"); /** * A babel plugin factory function for adding istanbul instrumentation. * @@ -26,15 +25,9 @@ function default_1() { visitor: { Program: { enter(path, state) { - const inputSourceMap = // eslint-disable-next-line @typescript-eslint/no-explicit-any - state.file.inputMap?.toObject(); - // istanbul does not support URL as sources. - if (inputSourceMap?.sources) { - inputSourceMap.sources = inputSourceMap.sources.map((s) => s.startsWith('file://') ? (0, node_url_1.fileURLToPath)(s) : s); - } const visitor = (0, istanbul_lib_instrument_1.programVisitor)(core_1.types, state.filename, { // Babel returns a Converter object from the `convert-source-map` package - inputSourceMap, + inputSourceMap: state.file.inputMap?.toObject(), }); visitors.set(path, visitor); visitor.enter(path); diff --git a/src/tools/esbuild/angular/compiler-plugin.d.ts b/src/tools/esbuild/angular/compiler-plugin.d.ts index 7fb0bb9e..34efbb1a 100644 --- a/src/tools/esbuild/angular/compiler-plugin.d.ts +++ b/src/tools/esbuild/angular/compiler-plugin.d.ts @@ -14,6 +14,13 @@ export interface CompilerPluginOptions { sourcemap: boolean | 'external'; tsconfig: string; jit?: boolean; + /** + * Include class metadata and JIT information in built code. + * The Angular TestBed APIs require additional metadata for the Angular aspects of the application + * such as Components, Modules, Pipes, etc. + * TestBed may also leverage JIT capabilities during testing (e.g., overrideComponent). + */ + includeTestMetadata?: boolean; advancedOptimizations?: boolean; thirdPartySourcemaps?: boolean; fileReplacements?: Record; diff --git a/src/tools/esbuild/angular/compiler-plugin.js b/src/tools/esbuild/angular/compiler-plugin.js index 7e4c8cb0..2d32ea67 100644 --- a/src/tools/esbuild/angular/compiler-plugin.js +++ b/src/tools/esbuild/angular/compiler-plugin.js @@ -47,7 +47,6 @@ exports.createCompilerPlugin = createCompilerPlugin; const node_assert_1 = __importDefault(require("node:assert")); const node_crypto_1 = require("node:crypto"); const path = __importStar(require("node:path")); -const node_url_1 = require("node:url"); const environment_options_1 = require("../../../utils/environment-options"); const compilation_1 = require("../../angular/compilation"); const javascript_transformer_1 = require("../javascript-transformer"); @@ -90,7 +89,7 @@ function createCompilerPlugin(pluginOptions, compilationOrFactory, stylesheetBun sourcemap: !!pluginOptions.sourcemap, thirdPartySourcemaps: pluginOptions.thirdPartySourcemaps, advancedOptimizations: pluginOptions.advancedOptimizations, - jit: pluginOptions.jit, + jit: pluginOptions.jit || pluginOptions.includeTestMetadata, }, environment_options_1.maxWorkers, cacheStore?.createCache('jstransformer')); // Setup defines based on the values used by the Angular compiler-cli build.initialOptions.define ??= {}; @@ -234,7 +233,7 @@ function createCompilerPlugin(pluginOptions, compilationOrFactory, stylesheetBun let referencedFiles; let externalStylesheets; try { - const initializationResult = await compilation.initialize(pluginOptions.tsconfig, hostOptions, createCompilerOptionsTransformer(setupWarnings, pluginOptions, preserveSymlinks, build.initialOptions.conditions, build.initialOptions.absWorkingDir)); + const initializationResult = await compilation.initialize(pluginOptions.tsconfig, hostOptions, createCompilerOptionsTransformer(setupWarnings, pluginOptions, preserveSymlinks, build.initialOptions.conditions)); shouldTsIgnoreJs = !initializationResult.compilerOptions.allowJs; // Isolated modules option ensures safe non-TypeScript transpilation. // Typescript printing support for sourcemaps is not yet integrated. @@ -475,7 +474,7 @@ async function bundleExternalStylesheet(stylesheetBundler, stylesheetFile, exter }); } } -function createCompilerOptionsTransformer(setupWarnings, pluginOptions, preserveSymlinks, customConditions, absWorkingDir) { +function createCompilerOptionsTransformer(setupWarnings, pluginOptions, preserveSymlinks, customConditions) { return (compilerOptions) => { // target of 9 is ES2022 (using the number avoids an expensive import of typescript just for an enum) if (compilerOptions.target === undefined || compilerOptions.target < 9 /** ES2022 */) { @@ -537,16 +536,13 @@ function createCompilerOptionsTransformer(setupWarnings, pluginOptions, preserve } // Synchronize custom resolve conditions. // Set if using the supported bundler resolution mode (bundler is the default in new projects) - if (compilerOptions.moduleResolution === 100 /* ModuleResolutionKind.Bundler */) { + if (compilerOptions.moduleResolution === 100 /* ModuleResolutionKind.Bundler */ || + compilerOptions.module === 200 /** ModuleKind.Preserve */) { compilerOptions.customConditions = customConditions; } return { ...compilerOptions, noEmitOnError: false, - // Using the path as a URL is necessary here; otherwise, esbuild will not generate source maps correctly. - // https://github.com/evanw/esbuild/issues/4070 - // https://github.com/evanw/esbuild/issues/4075 - outDir: absWorkingDir ? (0, node_url_1.pathToFileURL)(absWorkingDir + '/').href : undefined, inlineSources: !!pluginOptions.sourcemap, inlineSourceMap: !!pluginOptions.sourcemap, sourceMap: undefined, @@ -555,6 +551,8 @@ function createCompilerOptionsTransformer(setupWarnings, pluginOptions, preserve preserveSymlinks, externalRuntimeStyles: pluginOptions.externalRuntimeStyles, _enableHmr: !!pluginOptions.templateUpdates, + supportTestBed: !!pluginOptions.includeTestMetadata, + supportJitMode: !!pluginOptions.includeTestMetadata, }; }; } diff --git a/src/tools/esbuild/angular/component-stylesheets.js b/src/tools/esbuild/angular/component-stylesheets.js index 883de8fd..2e31ab5c 100644 --- a/src/tools/esbuild/angular/component-stylesheets.js +++ b/src/tools/esbuild/angular/component-stylesheets.js @@ -58,6 +58,10 @@ class ComponentStylesheetBundler { else { buildOptions.entryPoints = [entry]; } + // Angular encapsulation does not support nesting + // See: https://github.com/angular/angular/issues/58996 + buildOptions.supported ??= {}; + buildOptions.supported['nesting'] = false; return buildOptions; }); }); @@ -89,6 +93,10 @@ class ComponentStylesheetBundler { else { buildOptions.entryPoints = [`${namespace};${entry}`]; } + // Angular encapsulation does not support nesting + // See: https://github.com/angular/angular/issues/58996 + buildOptions.supported ??= {}; + buildOptions.supported['nesting'] = false; buildOptions.plugins.push({ name: 'angular-component-styles', setup(build) { diff --git a/src/tools/esbuild/angular/jit-plugin-callbacks.js b/src/tools/esbuild/angular/jit-plugin-callbacks.js index 9323d948..23fae0d1 100644 --- a/src/tools/esbuild/angular/jit-plugin-callbacks.js +++ b/src/tools/esbuild/angular/jit-plugin-callbacks.js @@ -24,23 +24,18 @@ const load_result_cache_1 = require("../load-result-cache"); * For inline entries the contents will always be provided. */ async function loadEntry(entry, root, skipRead) { - if (entry.startsWith('file:')) { - const specifier = (0, node_path_1.join)(root, entry.slice(5)); - return { - path: specifier, - contents: skipRead ? undefined : await (0, promises_1.readFile)(specifier, 'utf-8'), - }; - } - else if (entry.startsWith('inline:')) { + if (entry.startsWith('inline:')) { const [importer, data] = entry.slice(7).split(';', 2); return { path: (0, node_path_1.join)(root, importer), contents: Buffer.from(data, 'base64').toString(), }; } - else { - throw new Error('Invalid data for Angular JIT entry.'); - } + const path = (0, node_path_1.join)(root, entry); + return { + path, + contents: skipRead ? undefined : await (0, promises_1.readFile)(path, 'utf-8'), + }; } /** * Sets up esbuild resolve and load callbacks to support Angular JIT mode processing @@ -66,7 +61,7 @@ function setupJitPluginCallbacks(build, stylesheetBundler, additionalResultFiles return { // Use a relative path to prevent fully resolved paths in the metafile (JSON stats file). // This is only necessary for custom namespaces. esbuild will handle the file namespace. - path: 'file:' + (0, node_path_1.relative)(root, (0, node_path_1.join)((0, node_path_1.dirname)(args.importer), specifier)), + path: (0, node_path_1.relative)(root, (0, node_path_1.join)((0, node_path_1.dirname)(args.importer), specifier)), namespace, }; } diff --git a/src/tools/esbuild/application-code-bundle.js b/src/tools/esbuild/application-code-bundle.js index dea33231..4b944861 100644 --- a/src/tools/esbuild/application-code-bundle.js +++ b/src/tools/esbuild/application-code-bundle.js @@ -430,6 +430,7 @@ function getEsBuildCommonOptions(options) { ...(optimizationOptions.scripts ? { 'ngDevMode': 'false' } : undefined), 'ngJitMode': jit ? 'true' : 'false', 'ngServerMode': 'false', + 'ngHmrMode': options.templateUpdates ? 'true' : 'false', }, loader: loaderExtensions, footer, diff --git a/src/tools/esbuild/bundler-context.js b/src/tools/esbuild/bundler-context.js index 55c2e877..b68e60de 100644 --- a/src/tools/esbuild/bundler-context.js +++ b/src/tools/esbuild/bundler-context.js @@ -13,6 +13,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.BundlerContext = exports.BuildOutputFileType = void 0; const esbuild_1 = require("esbuild"); const node_assert_1 = __importDefault(require("node:assert")); +const node_module_1 = require("node:module"); const node_path_1 = require("node:path"); const load_result_cache_1 = require("./load-result-cache"); const utils_1 = require("./utils"); @@ -198,10 +199,11 @@ class BundlerContext { if (this.incremental) { // Add input files except virtual angular files which do not exist on disk for (const input of Object.keys(result.metafile.inputs)) { - if (!isInternalAngularFile(input)) { - // input file paths are always relative to the workspace root - this.watchFiles.add((0, node_path_1.join)(this.workspaceRoot, input)); + if (isInternalAngularFile(input) || isInternalBundlerFile(input)) { + continue; } + // Input file paths are always relative to the workspace root + this.watchFiles.add((0, node_path_1.join)(this.workspaceRoot, input)); } } // Return if the build encountered any errors @@ -276,15 +278,13 @@ class BundlerContext { // Collect all external package names const externalImports = new Set(); for (const { imports } of Object.values(result.metafile.outputs)) { - for (const importData of imports) { - if (!importData.external || - utils_1.SERVER_GENERATED_EXTERNALS.has(importData.path) || - (importData.kind !== 'import-statement' && - importData.kind !== 'dynamic-import' && - importData.kind !== 'require-call')) { + for (const { external, kind, path } of imports) { + if (!external || + utils_1.SERVER_GENERATED_EXTERNALS.has(path) || + (kind !== 'import-statement' && kind !== 'dynamic-import' && kind !== 'require-call')) { continue; } - externalImports.add(importData.path); + externalImports.add(path); } } (0, node_assert_1.default)(this.#esbuildOptions, 'esbuild options cannot be undefined.'); @@ -382,3 +382,16 @@ exports.BundlerContext = BundlerContext; function isInternalAngularFile(file) { return file.startsWith('angular:'); } +function isInternalBundlerFile(file) { + // Bundler virtual files such as "" or "" + if (file[0] === '<' && file.at(-1) === '>') { + return true; + } + const DISABLED_BUILTIN = '(disabled):'; + // Disabled node builtins such as "/some/path/(disabled):fs" + const disabledIndex = file.indexOf(DISABLED_BUILTIN); + if (disabledIndex >= 0) { + return node_module_1.builtinModules.includes(file.slice(disabledIndex + DISABLED_BUILTIN.length)); + } + return false; +} diff --git a/src/tools/esbuild/bundler-execution-result.d.ts b/src/tools/esbuild/bundler-execution-result.d.ts index 00c5b200..b972c4fb 100644 --- a/src/tools/esbuild/bundler-execution-result.d.ts +++ b/src/tools/esbuild/bundler-execution-result.d.ts @@ -74,7 +74,7 @@ export declare class ExecutionResult { * @param implicitServer External dependencies for the server bundles due to the external packages option. * @param explicit External dependencies due to explicit project configuration. */ - setExternalMetadata(implicitBrowser: string[], implicitServer: string[], explicit: string[] | undefined): void; + setExternalMetadata(implicitBrowser: string[], implicitServer: string[], explicit: string[]): void; get output(): { success: boolean; }; diff --git a/src/tools/esbuild/bundler-execution-result.js b/src/tools/esbuild/bundler-execution-result.js index 09dcd268..03d26302 100644 --- a/src/tools/esbuild/bundler-execution-result.js +++ b/src/tools/esbuild/bundler-execution-result.js @@ -86,7 +86,7 @@ class ExecutionResult { * @param explicit External dependencies due to explicit project configuration. */ setExternalMetadata(implicitBrowser, implicitServer, explicit) { - this.externalMetadata = { implicitBrowser, implicitServer, explicit: explicit ?? [] }; + this.externalMetadata = { implicitBrowser, implicitServer, explicit }; } get output() { return { diff --git a/src/tools/esbuild/compiler-plugin-options.js b/src/tools/esbuild/compiler-plugin-options.js index 3bc2dbf1..22adab19 100644 --- a/src/tools/esbuild/compiler-plugin-options.js +++ b/src/tools/esbuild/compiler-plugin-options.js @@ -9,7 +9,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.createCompilerPluginOptions = createCompilerPluginOptions; function createCompilerPluginOptions(options, sourceFileCache, loadResultCache, templateUpdates) { - const { sourcemapOptions, tsconfig, fileReplacements, advancedOptimizations, jit, externalRuntimeStyles, instrumentForCoverage, } = options; + const { sourcemapOptions, tsconfig, fileReplacements, advancedOptimizations, jit, externalRuntimeStyles, instrumentForCoverage, optimizationOptions, } = options; const incremental = !!options.watch; return { sourcemap: !!sourcemapOptions.scripts && (sourcemapOptions.hidden ? 'external' : true), @@ -24,5 +24,6 @@ function createCompilerPluginOptions(options, sourceFileCache, loadResultCache, externalRuntimeStyles, instrumentForCoverage, templateUpdates, + includeTestMetadata: !optimizationOptions.scripts, }; } diff --git a/src/tools/esbuild/external-packages-plugin.js b/src/tools/esbuild/external-packages-plugin.js index 77254314..864b3155 100644 --- a/src/tools/esbuild/external-packages-plugin.js +++ b/src/tools/esbuild/external-packages-plugin.js @@ -18,7 +18,14 @@ const EXTERNAL_PACKAGE_RESOLUTION = Symbol('EXTERNAL_PACKAGE_RESOLUTION'); * @returns An esbuild plugin. */ function createExternalPackagesPlugin(options) { - const exclusions = options?.exclude?.length ? new Set(options.exclude) : undefined; + const exclusions = new Set(options?.exclude); + // Similar to esbuild, --external:@foo/bar automatically implies --external:@foo/bar/*, + // which matches import paths like @foo/bar/baz. + // This means all paths within the @foo/bar package are also marked as external. + const exclusionsPrefixes = options?.exclude?.map((exclusion) => exclusion + '/') ?? []; + const seenExclusions = new Set(); + const seenExternals = new Set(); + const seenNonExclusions = new Set(); return { name: 'angular-external-packages', setup(build) { @@ -29,7 +36,7 @@ function createExternalPackagesPlugin(options) { .filter(([, value]) => value !== 'file') .map(([key]) => key); // Safe to use native packages external option if no loader options or exclusions present - if (!exclusions && !loaderOptionKeys?.length) { + if (!exclusions.size && !loaderOptionKeys?.length) { build.initialOptions.packages = 'external'; return; } @@ -39,9 +46,21 @@ function createExternalPackagesPlugin(options) { if (args.pluginData?.[EXTERNAL_PACKAGE_RESOLUTION]) { return null; } - if (exclusions?.has(args.path)) { + if (seenExternals.has(args.path)) { + return { external: true }; + } + if (exclusions.has(args.path) || seenExclusions.has(args.path)) { return null; } + if (!seenNonExclusions.has(args.path)) { + for (const exclusion of exclusionsPrefixes) { + if (args.path.startsWith(exclusion)) { + seenExclusions.add(args.path); + return null; + } + } + seenNonExclusions.add(args.path); + } const { importer, kind, resolveDir, namespace, pluginData = {} } = args; pluginData[EXTERNAL_PACKAGE_RESOLUTION] = true; const result = await build.resolve(args.path, { @@ -51,20 +70,23 @@ function createExternalPackagesPlugin(options) { pluginData, resolveDir, }); - // Return result if unable to resolve or explicitly marked external (externalDependencies option) - if (!result.path || result.external) { + // Return result if unable to resolve + if (!result.path) { return result; } + // Return if explicitly marked external (externalDependencies option) + if (result.external) { + seenExternals.add(args.path); + return { external: true }; + } // Allow customized loaders to run against configured paths regardless of location if (loaderFileExtensions.has((0, node_path_1.extname)(result.path))) { return result; } // Mark paths from a node modules directory as external if (/[\\/]node_modules[\\/]/.test(result.path)) { - return { - path: args.path, - external: true, - }; + seenExternals.add(args.path); + return { external: true }; } // Otherwise return original result return result; diff --git a/src/tools/esbuild/javascript-transformer.js b/src/tools/esbuild/javascript-transformer.js index c74a7ebc..6e823d90 100644 --- a/src/tools/esbuild/javascript-transformer.js +++ b/src/tools/esbuild/javascript-transformer.js @@ -39,6 +39,9 @@ class JavaScriptTransformer { this.#fileCacheKeyBase = Buffer.from(JSON.stringify(this.#commonOptions), 'utf-8'); } #ensureWorkerPool() { + if (this.#workerPool) { + return this.#workerPool; + } const workerPoolOptions = { filename: require.resolve('./javascript-transformer-worker'), maxThreads: this.maxThreads, @@ -48,7 +51,7 @@ class JavaScriptTransformer { if (process.execArgv.length !== filteredExecArgv.length) { workerPoolOptions.execArgv = filteredExecArgv; } - this.#workerPool ??= new worker_pool_1.WorkerPool(workerPoolOptions); + this.#workerPool = new worker_pool_1.WorkerPool(workerPoolOptions); return this.#workerPool; } /** diff --git a/src/tools/esbuild/stylesheets/less-language.js b/src/tools/esbuild/stylesheets/less-language.js index b7160e7b..80f029c4 100644 --- a/src/tools/esbuild/stylesheets/less-language.js +++ b/src/tools/esbuild/stylesheets/less-language.js @@ -42,8 +42,6 @@ var __importStar = (this && this.__importStar) || (function () { Object.defineProperty(exports, "__esModule", { value: true }); exports.LessStylesheetLanguage = void 0; const promises_1 = require("node:fs/promises"); -const node_path_1 = require("node:path"); -const node_url_1 = require("node:url"); /** * The lazy-loaded instance of the less stylesheet preprocessor. * It is only imported and initialized if a less stylesheet is used. @@ -117,7 +115,7 @@ async function compileString(data, filename, options, resolver, unsafeInlineJava }, }; try { - const { imports, map, css } = await less.render(data, { + const { imports, css } = await less.render(data, { filename, paths: options.includePaths, plugins: [resolverPlugin], @@ -125,13 +123,13 @@ async function compileString(data, filename, options, resolver, unsafeInlineJava javascriptEnabled: unsafeInlineJavaScript, sourceMap: options.sourcemap ? { - sourceMapFileInline: false, + sourceMapFileInline: true, outputSourceFiles: true, } : undefined, }); return { - contents: options.sourcemap ? `${css}\n${sourceMapToUrlComment(map)}` : css, + contents: css, loader: 'css', watchFiles: [filename, ...imports], }; @@ -184,13 +182,3 @@ function convertExceptionLocation(exception) { lineText: exception.extract && exception.extract[Math.trunc(exception.extract.length / 2)], }; } -function sourceMapToUrlComment(sourceMap) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const map = JSON.parse(sourceMap); - // Using file URLs instead of paths ensures that esbuild correctly resolves the source map. - // https://github.com/evanw/esbuild/issues/4070 - // https://github.com/evanw/esbuild/issues/4075 - map.sources = map.sources.map((source) => source && (0, node_path_1.isAbsolute)(source) ? (0, node_url_1.pathToFileURL)(source).href : source); - const urlSourceMap = Buffer.from(JSON.stringify(map), 'utf-8').toString('base64'); - return `/*# sourceMappingURL=data:application/json;charset=utf-8;base64,${urlSourceMap} */`; -} diff --git a/src/tools/esbuild/stylesheets/stylesheet-plugin-factory.js b/src/tools/esbuild/stylesheets/stylesheet-plugin-factory.js index 4b5a7253..ac61a292 100644 --- a/src/tools/esbuild/stylesheets/stylesheet-plugin-factory.js +++ b/src/tools/esbuild/stylesheets/stylesheet-plugin-factory.js @@ -48,6 +48,7 @@ const fast_glob_1 = __importDefault(require("fast-glob")); const node_assert_1 = __importDefault(require("node:assert")); const promises_1 = require("node:fs/promises"); const node_path_1 = require("node:path"); +const error_1 = require("../../../utils/error"); const load_result_cache_1 = require("../load-result-cache"); /** * The lazy-loaded instance of the postcss stylesheet postprocessor. @@ -313,6 +314,15 @@ async function compileString(data, filename, postcssProcessor, options) { ], }; } - throw error; + else { + (0, error_1.assertIsError)(error); + return { + errors: [ + { + text: error.message, + }, + ], + }; + } } } diff --git a/src/tools/vite/middlewares/ssr-middleware.js b/src/tools/vite/middlewares/ssr-middleware.js index 40ab7573..270dfc6d 100644 --- a/src/tools/vite/middlewares/ssr-middleware.js +++ b/src/tools/vite/middlewares/ssr-middleware.js @@ -22,11 +22,12 @@ function createAngularSsrInternalMiddleware(server, indexHtmlTransformer) { // which must be processed by the runtime linker, even if they are not used. await (0, load_esm_1.loadEsmModule)('@angular/compiler'); const { writeResponseToNodeResponse, createWebRequestFromNodeRequest } = await (0, load_esm_1.loadEsmModule)('@angular/ssr/node'); - // The following is necessary because accessing the module after invalidation may result in an empty module, - // which can trigger a `TypeError: ɵgetOrCreateAngularServerApp is not a function` error. - // TODO: look into why. - await server.ssrLoadModule('/main.server.mjs'); const { ɵgetOrCreateAngularServerApp } = (await server.ssrLoadModule('/main.server.mjs')); + // `ɵgetOrCreateAngularServerApp` can be undefined right after an error. + // See: https://github.com/angular/angular-cli/issues/29907 + if (!ɵgetOrCreateAngularServerApp) { + return next(); + } const angularServerApp = ɵgetOrCreateAngularServerApp({ allowStaticRouteRender: true, }); diff --git a/src/tools/vite/plugins/id-prefix-plugin.js b/src/tools/vite/plugins/id-prefix-plugin.js index 3892da61..5ef53577 100644 --- a/src/tools/vite/plugins/id-prefix-plugin.js +++ b/src/tools/vite/plugins/id-prefix-plugin.js @@ -23,7 +23,7 @@ function createRemoveIdPrefixPlugin(externals) { if (externals.length === 0) { return; } - const escapedExternals = externals.map(escapeRegexSpecialChars); + const escapedExternals = externals.map((e) => escapeRegexSpecialChars(e) + '(?:/.+)?'); const prefixedExternalRegex = new RegExp(`${resolvedConfig.base}${VITE_ID_PREFIX}(${escapedExternals.join('|')})`, 'g'); // @ts-expect-error: Property 'push' does not exist on type 'readonly Plugin[]' // Reasoning: diff --git a/src/utils/bundle-calculator.js b/src/utils/bundle-calculator.js index aacc7bc5..f99ae750 100644 --- a/src/utils/bundle-calculator.js +++ b/src/utils/bundle-calculator.js @@ -183,7 +183,7 @@ class AllScriptCalculator extends Calculator { class AllCalculator extends Calculator { calculate() { const size = this.assets - .filter((asset) => !asset.name.endsWith('.map')) + .filter((asset) => !asset.name.endsWith('.map') && !asset.componentStyle) .map((asset) => this.getAssetSize(asset)) .reduce((total, size) => total + size, 0); return [{ size, label: 'total' }]; @@ -208,7 +208,7 @@ class AnyScriptCalculator extends Calculator { class AnyCalculator extends Calculator { calculate() { return this.assets - .filter((asset) => !asset.name.endsWith('.map')) + .filter((asset) => !asset.name.endsWith('.map') && !asset.componentStyle) .map((asset) => ({ size: this.getAssetSize(asset), label: asset.name, diff --git a/src/utils/i18n-options.d.ts b/src/utils/i18n-options.d.ts index 481a3221..364132b0 100644 --- a/src/utils/i18n-options.d.ts +++ b/src/utils/i18n-options.d.ts @@ -5,6 +5,7 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.dev/license */ +import { DiagnosticHandlingStrategy } from '@angular/localize/tools'; import type { TranslationLoader } from './load-translations'; export interface LocaleDescription { files: { @@ -24,6 +25,7 @@ export interface I18nOptions { flatOutput?: boolean; readonly shouldInline: boolean; hasDefinedSourceLocale?: boolean; + i18nDuplicateTranslation?: DiagnosticHandlingStrategy; } export declare function createI18nOptions(projectMetadata: { i18n?: unknown; diff --git a/src/utils/load-proxy-config.js b/src/utils/load-proxy-config.js index fbc440f3..6483d6de 100644 --- a/src/utils/load-proxy-config.js +++ b/src/utils/load-proxy-config.js @@ -93,7 +93,7 @@ async function loadProxyConfiguration(root, proxyConfig) { } catch (e) { (0, error_1.assertIsError)(e); - if (e.code === 'ERR_REQUIRE_ESM') { + if (e.code === 'ERR_REQUIRE_ESM' || e.code === 'ERR_REQUIRE_ASYNC_MODULE') { // Load the ESM configuration file using the TypeScript dynamic import workaround. // Once TypeScript provides support for keeping the dynamic import this workaround can be // changed to a direct dynamic import. diff --git a/src/utils/normalize-cache.js b/src/utils/normalize-cache.js index b4a8c17f..da0e6f07 100644 --- a/src/utils/normalize-cache.js +++ b/src/utils/normalize-cache.js @@ -10,7 +10,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.normalizeCacheOptions = normalizeCacheOptions; const node_path_1 = require("node:path"); /** Version placeholder is replaced during the build process with actual package version */ -const VERSION = '19.2.0-next.2+sha-8f73dad'; +const VERSION = '19.2.15+sha-c6eba4d'; function hasCacheMetadata(value) { return (!!value && typeof value === 'object' && diff --git a/src/utils/server-rendering/launch-server.js b/src/utils/server-rendering/launch-server.js index fcfdd47b..16c899c8 100644 --- a/src/utils/server-rendering/launch-server.js +++ b/src/utils/server-rendering/launch-server.js @@ -34,7 +34,7 @@ async function launchServer() { // handle request if ((0, utils_1.isSsrNodeRequestHandler)(reqHandler)) { await reqHandler(req, res, (e) => { - throw e; + throw e ?? new Error(`Unable to handle request: '${req.url}'.`); }); } else { diff --git a/src/utils/server-rendering/prerender.js b/src/utils/server-rendering/prerender.js index e51d7f3b..27270cc5 100644 --- a/src/utils/server-rendering/prerender.js +++ b/src/utils/server-rendering/prerender.js @@ -54,7 +54,7 @@ async function prerenderPages(workspaceRoot, baseHref, appShellOptions, prerende // Get routes to prerender const { errors: extractionErrors, serializedRouteTree: serializableRouteTreeNode, appShellRoute, } = await getAllRoutes(workspaceRoot, baseHref, outputFilesForWorker, assetsReversed, appShellOptions, prerenderOptions, sourcemap, outputMode).catch((err) => { return { - errors: [`An error occurred while extracting routes.\n\n${err.stack ?? err.message ?? err}`], + errors: [`An error occurred while extracting routes.\n\n${err.message ?? err.stack ?? err}`], serializedRouteTree: [], appShellRoute: undefined, }; @@ -150,7 +150,7 @@ async function renderPages(baseHref, sourcemap, serializableRouteTreeNode, maxTh } }) .catch((err) => { - errors.push(`An error occurred while prerendering route '${route}'.\n\n${err.stack ?? err.message ?? err.code ?? err}`); + errors.push(`An error occurred while prerendering route '${route}'.\n\n${err.message ?? err.stack ?? err.code ?? err}`); void renderWorker.destroy(); }); renderingPromises.push(renderResult); @@ -222,7 +222,7 @@ async function getAllRoutes(workspaceRoot, baseHref, outputFilesForWorker, asset (0, error_1.assertIsError)(err); return { errors: [ - `An error occurred while extracting routes.\n\n${err.stack ?? err.message ?? err.code ?? err}`, + `An error occurred while extracting routes.\n\n${err.message ?? err.stack ?? err.code ?? err}`, ], serializedRouteTree: [], }; diff --git a/uniqueId b/uniqueId index 779e5be7..0e4fea35 100644 --- a/uniqueId +++ b/uniqueId @@ -1 +1 @@ -Wed Feb 19 2025 21:37:19 GMT+0000 (Coordinated Universal Time) \ No newline at end of file +Wed Jun 11 2025 17:17:08 GMT+0000 (Coordinated Universal Time) \ No newline at end of file