@angular/ssr 19.0.5 → 19.1.0-next.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"node.mjs","sources":["../../../../../../darwin_arm64-fastbuild-ST-70f2edae98f4/bin/packages/angular/ssr/node/src/common-engine/inline-css-processor.mjs","../../../../../../darwin_arm64-fastbuild-ST-70f2edae98f4/bin/packages/angular/ssr/node/src/common-engine/peformance-profiler.mjs","../../../../../../darwin_arm64-fastbuild-ST-70f2edae98f4/bin/packages/angular/ssr/node/src/common-engine/common-engine.mjs","../../../../../../darwin_arm64-fastbuild-ST-70f2edae98f4/bin/packages/angular/ssr/node/src/request.mjs","../../../../../../darwin_arm64-fastbuild-ST-70f2edae98f4/bin/packages/angular/ssr/node/src/app-engine.mjs","../../../../../../darwin_arm64-fastbuild-ST-70f2edae98f4/bin/packages/angular/ssr/node/src/handler.mjs","../../../../../../darwin_arm64-fastbuild-ST-70f2edae98f4/bin/packages/angular/ssr/node/src/response.mjs","../../../../../../darwin_arm64-fastbuild-ST-70f2edae98f4/bin/packages/angular/ssr/node/src/module.mjs"],"sourcesContent":["/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\nimport { ɵInlineCriticalCssProcessor as InlineCriticalCssProcessor } from '@angular/ssr';\nimport { readFile } from 'node:fs/promises';\nexport class CommonEngineInlineCriticalCssProcessor {\n resourceCache = new Map();\n async process(html, outputPath) {\n const beasties = new InlineCriticalCssProcessor(async (path) => {\n let resourceContent = this.resourceCache.get(path);\n if (resourceContent === undefined) {\n resourceContent = await readFile(path, 'utf-8');\n this.resourceCache.set(path, resourceContent);\n }\n return resourceContent;\n }, outputPath);\n return beasties.process(html);\n }\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\nconst PERFORMANCE_MARK_PREFIX = '🅰️';\nexport function printPerformanceLogs() {\n let maxWordLength = 0;\n const benchmarks = [];\n for (const { name, duration } of performance.getEntriesByType('measure')) {\n if (!name.startsWith(PERFORMANCE_MARK_PREFIX)) {\n continue;\n }\n // `🅰️:Retrieve SSG Page` -> `Retrieve SSG Page:`\n const step = name.slice(PERFORMANCE_MARK_PREFIX.length + 1) + ':';\n if (step.length > maxWordLength) {\n maxWordLength = step.length;\n }\n benchmarks.push([step, `${duration.toFixed(1)}ms`]);\n performance.clearMeasures(name);\n }\n /* eslint-disable no-console */\n console.log('********** Performance results **********');\n for (const [step, value] of benchmarks) {\n const spaces = maxWordLength - step.length + 5;\n console.log(step + ' '.repeat(spaces) + value);\n }\n console.log('*****************************************');\n /* eslint-enable no-console */\n}\nexport async function runMethodAndMeasurePerf(label, asyncMethod) {\n const labelName = `${PERFORMANCE_MARK_PREFIX}:${label}`;\n const startLabel = `start:${labelName}`;\n const endLabel = `end:${labelName}`;\n try {\n performance.mark(startLabel);\n return await asyncMethod();\n }\n finally {\n performance.mark(endLabel);\n performance.measure(labelName, startLabel, endLabel);\n performance.clearMarks(startLabel);\n performance.clearMarks(endLabel);\n }\n}\nexport function noopRunMethodAndMeasurePerf(label, asyncMethod) {\n return asyncMethod();\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\nimport { renderApplication, renderModule, ɵSERVER_CONTEXT } from '@angular/platform-server';\nimport * as fs from 'node:fs';\nimport { dirname, join, normalize, resolve } from 'node:path';\nimport { URL } from 'node:url';\nimport { CommonEngineInlineCriticalCssProcessor } from './inline-css-processor';\nimport { noopRunMethodAndMeasurePerf, printPerformanceLogs, runMethodAndMeasurePerf, } from './peformance-profiler';\nconst SSG_MARKER_REGEXP = /ng-server-context=[\"']\\w*\\|?ssg\\|?\\w*[\"']/;\n/**\n * A common engine to use to server render an application.\n */\nexport class CommonEngine {\n options;\n templateCache = new Map();\n inlineCriticalCssProcessor = new CommonEngineInlineCriticalCssProcessor();\n pageIsSSG = new Map();\n constructor(options) {\n this.options = options;\n }\n /**\n * Render an HTML document for a specific URL with specified\n * render options\n */\n async render(opts) {\n const enablePerformanceProfiler = this.options?.enablePerformanceProfiler;\n const runMethod = enablePerformanceProfiler\n ? runMethodAndMeasurePerf\n : noopRunMethodAndMeasurePerf;\n let html = await runMethod('Retrieve SSG Page', () => this.retrieveSSGPage(opts));\n if (html === undefined) {\n html = await runMethod('Render Page', () => this.renderApplication(opts));\n if (opts.inlineCriticalCss !== false) {\n const content = await runMethod('Inline Critical CSS', () => \n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n this.inlineCriticalCss(html, opts));\n html = content;\n }\n }\n if (enablePerformanceProfiler) {\n printPerformanceLogs();\n }\n return html;\n }\n inlineCriticalCss(html, opts) {\n const outputPath = opts.publicPath ?? (opts.documentFilePath ? dirname(opts.documentFilePath) : '');\n return this.inlineCriticalCssProcessor.process(html, outputPath);\n }\n async retrieveSSGPage(opts) {\n const { publicPath, documentFilePath, url } = opts;\n if (!publicPath || !documentFilePath || url === undefined) {\n return undefined;\n }\n const { pathname } = new URL(url, 'resolve://');\n // Do not use `resolve` here as otherwise it can lead to path traversal vulnerability.\n // See: https://portswigger.net/web-security/file-path-traversal\n const pagePath = join(publicPath, pathname, 'index.html');\n if (this.pageIsSSG.get(pagePath)) {\n // Serve pre-rendered page.\n return fs.promises.readFile(pagePath, 'utf-8');\n }\n if (!pagePath.startsWith(normalize(publicPath))) {\n // Potential path traversal detected.\n return undefined;\n }\n if (pagePath === resolve(documentFilePath) || !(await exists(pagePath))) {\n // View matches with prerender path or file does not exist.\n this.pageIsSSG.set(pagePath, false);\n return undefined;\n }\n // Static file exists.\n const content = await fs.promises.readFile(pagePath, 'utf-8');\n const isSSG = SSG_MARKER_REGEXP.test(content);\n this.pageIsSSG.set(pagePath, isSSG);\n return isSSG ? content : undefined;\n }\n async renderApplication(opts) {\n const moduleOrFactory = this.options?.bootstrap ?? opts.bootstrap;\n if (!moduleOrFactory) {\n throw new Error('A module or bootstrap option must be provided.');\n }\n const extraProviders = [\n { provide: ɵSERVER_CONTEXT, useValue: 'ssr' },\n ...(opts.providers ?? []),\n ...(this.options?.providers ?? []),\n ];\n let document = opts.document;\n if (!document && opts.documentFilePath) {\n document = await this.getDocument(opts.documentFilePath);\n }\n const commonRenderingOptions = {\n url: opts.url,\n document,\n };\n return isBootstrapFn(moduleOrFactory)\n ? renderApplication(moduleOrFactory, {\n platformProviders: extraProviders,\n ...commonRenderingOptions,\n })\n : renderModule(moduleOrFactory, { extraProviders, ...commonRenderingOptions });\n }\n /** Retrieve the document from the cache or the filesystem */\n async getDocument(filePath) {\n let doc = this.templateCache.get(filePath);\n if (!doc) {\n doc = await fs.promises.readFile(filePath, 'utf-8');\n this.templateCache.set(filePath, doc);\n }\n return doc;\n }\n}\nasync function exists(path) {\n try {\n await fs.promises.access(path, fs.constants.F_OK);\n return true;\n }\n catch {\n return false;\n }\n}\nfunction isBootstrapFn(value) {\n // We can differentiate between a module and a bootstrap function by reading compiler-generated `ɵmod` static property:\n return typeof value === 'function' && !('ɵmod' in value);\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n/**\n * A set containing all the pseudo-headers defined in the HTTP/2 specification.\n *\n * This set can be used to filter out pseudo-headers from a list of headers,\n * as they are not allowed to be set directly using the `Node.js` Undici API or\n * the web `Headers` API.\n */\nconst HTTP2_PSEUDO_HEADERS = new Set([':method', ':scheme', ':authority', ':path', ':status']);\n/**\n * Converts a Node.js `IncomingMessage` or `Http2ServerRequest` into a\n * Web Standard `Request` object.\n *\n * This function adapts the Node.js request objects to a format that can\n * be used by web platform APIs.\n *\n * @param nodeRequest - The Node.js request object (`IncomingMessage` or `Http2ServerRequest`) to convert.\n * @returns A Web Standard `Request` object.\n * @developerPreview\n */\nexport function createWebRequestFromNodeRequest(nodeRequest) {\n const { headers, method = 'GET' } = nodeRequest;\n const withBody = method !== 'GET' && method !== 'HEAD';\n return new Request(createRequestUrl(nodeRequest), {\n method,\n headers: createRequestHeaders(headers),\n body: withBody ? nodeRequest : undefined,\n duplex: withBody ? 'half' : undefined,\n });\n}\n/**\n * Creates a `Headers` object from Node.js `IncomingHttpHeaders`.\n *\n * @param nodeHeaders - The Node.js `IncomingHttpHeaders` object to convert.\n * @returns A `Headers` object containing the converted headers.\n */\nfunction createRequestHeaders(nodeHeaders) {\n const headers = new Headers();\n for (const [name, value] of Object.entries(nodeHeaders)) {\n if (HTTP2_PSEUDO_HEADERS.has(name)) {\n continue;\n }\n if (typeof value === 'string') {\n headers.append(name, value);\n }\n else if (Array.isArray(value)) {\n for (const item of value) {\n headers.append(name, item);\n }\n }\n }\n return headers;\n}\n/**\n * Creates a `URL` object from a Node.js `IncomingMessage`, taking into account the protocol, host, and port.\n *\n * @param nodeRequest - The Node.js `IncomingMessage` or `Http2ServerRequest` object to extract URL information from.\n * @returns A `URL` object representing the request URL.\n */\nfunction createRequestUrl(nodeRequest) {\n const { headers, socket, url = '', originalUrl, } = nodeRequest;\n const protocol = headers['x-forwarded-proto'] ?? ('encrypted' in socket && socket.encrypted ? 'https' : 'http');\n const hostname = headers['x-forwarded-host'] ?? headers.host ?? headers[':authority'];\n const port = headers['x-forwarded-port'] ?? socket.localPort;\n if (Array.isArray(hostname)) {\n throw new Error('host value cannot be an array.');\n }\n let hostnameWithPort = hostname;\n if (port && !hostname?.includes(':')) {\n hostnameWithPort += `:${port}`;\n }\n return new URL(originalUrl ?? url, `${protocol}://${hostnameWithPort}`);\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\nimport { AngularAppEngine } from '@angular/ssr';\nimport { createWebRequestFromNodeRequest } from './request';\n/**\n * Angular server application engine.\n * Manages Angular server applications (including localized ones), handles rendering requests,\n * and optionally transforms index HTML before rendering.\n *\n * @remarks This class should be instantiated once and used as a singleton across the server-side\n * application to ensure consistent handling of rendering requests and resource management.\n *\n * @developerPreview\n */\nexport class AngularNodeAppEngine {\n angularAppEngine = new AngularAppEngine();\n /**\n * Handles an incoming HTTP request by serving prerendered content, performing server-side rendering,\n * or delivering a static file for client-side rendered routes based on the `RenderMode` setting.\n *\n * This method adapts Node.js's `IncomingMessage` or `Http2ServerRequest`\n * to a format compatible with the `AngularAppEngine` and delegates the handling logic to it.\n *\n * @param request - The incoming HTTP request (`IncomingMessage` or `Http2ServerRequest`).\n * @param requestContext - Optional context for rendering, such as metadata associated with the request.\n * @returns A promise that resolves to the resulting HTTP response object, or `null` if no matching Angular route is found.\n *\n * @remarks A request to `https://www.example.com/page/index.html` will serve or render the Angular route\n * corresponding to `https://www.example.com/page`.\n */\n async handle(request, requestContext) {\n const webRequest = createWebRequestFromNodeRequest(request);\n return this.angularAppEngine.handle(webRequest, requestContext);\n }\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n/**\n * Attaches metadata to the handler function to mark it as a special handler for Node.js environments.\n *\n * @typeParam T - The type of the handler function.\n * @param handler - The handler function to be defined and annotated.\n * @returns The same handler function passed as an argument, with metadata attached.\n *\n * @example\n * Usage in an Express application:\n * ```ts\n * const app = express();\n * export default createNodeRequestHandler(app);\n * ```\n *\n * @example\n * Usage in a Hono application:\n * ```ts\n * const app = new Hono();\n * export default createNodeRequestHandler(async (req, res, next) => {\n * try {\n * const webRes = await app.fetch(createWebRequestFromNodeRequest(req));\n * if (webRes) {\n * await writeResponseToNodeResponse(webRes, res);\n * } else {\n * next();\n * }\n * } catch (error) {\n * next(error);\n * }\n * }));\n * ```\n *\n * @example\n * Usage in a Fastify application:\n * ```ts\n * const app = Fastify();\n * export default createNodeRequestHandler(async (req, res) => {\n * await app.ready();\n * app.server.emit('request', req, res);\n * res.send('Hello from Fastify with Node Next Handler!');\n * }));\n * ```\n * @developerPreview\n */\nexport function createNodeRequestHandler(handler) {\n handler['__ng_node_request_handler__'] = true;\n return handler;\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n/**\n * Streams a web-standard `Response` into a Node.js `ServerResponse`\n * or `Http2ServerResponse`.\n *\n * This function adapts the web `Response` object to write its content\n * to a Node.js response object, handling both HTTP/1.1 and HTTP/2.\n *\n * @param source - The web-standard `Response` object to stream from.\n * @param destination - The Node.js response object (`ServerResponse` or `Http2ServerResponse`) to stream into.\n * @returns A promise that resolves once the streaming operation is complete.\n * @developerPreview\n */\nexport async function writeResponseToNodeResponse(source, destination) {\n const { status, headers, body } = source;\n destination.statusCode = status;\n let cookieHeaderSet = false;\n for (const [name, value] of headers.entries()) {\n if (name === 'set-cookie') {\n if (cookieHeaderSet) {\n continue;\n }\n // Sets the 'set-cookie' header only once to ensure it is correctly applied.\n // Concatenating 'set-cookie' values can lead to incorrect behavior, so we use a single value from `headers.getSetCookie()`.\n destination.setHeader(name, headers.getSetCookie());\n cookieHeaderSet = true;\n }\n else {\n destination.setHeader(name, value);\n }\n }\n if (!body) {\n destination.end();\n return;\n }\n try {\n const reader = body.getReader();\n destination.on('close', () => {\n reader.cancel().catch((error) => {\n // eslint-disable-next-line no-console\n console.error(`An error occurred while writing the response body for: ${destination.req.url}.`, error);\n });\n });\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n destination.end();\n break;\n }\n destination.write(value);\n }\n }\n catch {\n destination.end('Internal server error.');\n }\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\nimport { argv } from 'node:process';\nimport { fileURLToPath } from 'node:url';\n/**\n * Determines whether the provided URL represents the main entry point module.\n *\n * This function checks if the provided URL corresponds to the main ESM module being executed directly.\n * It's useful for conditionally executing code that should only run when a module is the entry point,\n * such as starting a server or initializing an application.\n *\n * It performs two key checks:\n * 1. Verifies if the URL starts with 'file:', ensuring it is a local file.\n * 2. Compares the URL's resolved file path with the first command-line argument (`process.argv[1]`),\n * which points to the file being executed.\n *\n * @param url The URL of the module to check. This should typically be `import.meta.url`.\n * @returns `true` if the provided URL represents the main entry point, otherwise `false`.\n * @developerPreview\n */\nexport function isMainModule(url) {\n return url.startsWith('file:') && argv[1] === fileURLToPath(url);\n}\n"],"names":["InlineCriticalCssProcessor","URL","ɵSERVER_CONTEXT"],"mappings":"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"}
1
+ {"version":3,"file":"node.mjs","sources":["../../../../../../k8-fastbuild-ST-70f2edae98f4/bin/packages/angular/ssr/node/src/common-engine/inline-css-processor.mjs","../../../../../../k8-fastbuild-ST-70f2edae98f4/bin/packages/angular/ssr/node/src/common-engine/peformance-profiler.mjs","../../../../../../k8-fastbuild-ST-70f2edae98f4/bin/packages/angular/ssr/node/src/common-engine/common-engine.mjs","../../../../../../k8-fastbuild-ST-70f2edae98f4/bin/packages/angular/ssr/node/src/request.mjs","../../../../../../k8-fastbuild-ST-70f2edae98f4/bin/packages/angular/ssr/node/src/app-engine.mjs","../../../../../../k8-fastbuild-ST-70f2edae98f4/bin/packages/angular/ssr/node/src/handler.mjs","../../../../../../k8-fastbuild-ST-70f2edae98f4/bin/packages/angular/ssr/node/src/response.mjs","../../../../../../k8-fastbuild-ST-70f2edae98f4/bin/packages/angular/ssr/node/src/module.mjs"],"sourcesContent":["/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\nimport { ɵInlineCriticalCssProcessor as InlineCriticalCssProcessor } from '@angular/ssr';\nimport { readFile } from 'node:fs/promises';\nexport class CommonEngineInlineCriticalCssProcessor {\n resourceCache = new Map();\n async process(html, outputPath) {\n const beasties = new InlineCriticalCssProcessor(async (path) => {\n let resourceContent = this.resourceCache.get(path);\n if (resourceContent === undefined) {\n resourceContent = await readFile(path, 'utf-8');\n this.resourceCache.set(path, resourceContent);\n }\n return resourceContent;\n }, outputPath);\n return beasties.process(html);\n }\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\nconst PERFORMANCE_MARK_PREFIX = '🅰️';\nexport function printPerformanceLogs() {\n let maxWordLength = 0;\n const benchmarks = [];\n for (const { name, duration } of performance.getEntriesByType('measure')) {\n if (!name.startsWith(PERFORMANCE_MARK_PREFIX)) {\n continue;\n }\n // `🅰️:Retrieve SSG Page` -> `Retrieve SSG Page:`\n const step = name.slice(PERFORMANCE_MARK_PREFIX.length + 1) + ':';\n if (step.length > maxWordLength) {\n maxWordLength = step.length;\n }\n benchmarks.push([step, `${duration.toFixed(1)}ms`]);\n performance.clearMeasures(name);\n }\n /* eslint-disable no-console */\n console.log('********** Performance results **********');\n for (const [step, value] of benchmarks) {\n const spaces = maxWordLength - step.length + 5;\n console.log(step + ' '.repeat(spaces) + value);\n }\n console.log('*****************************************');\n /* eslint-enable no-console */\n}\nexport async function runMethodAndMeasurePerf(label, asyncMethod) {\n const labelName = `${PERFORMANCE_MARK_PREFIX}:${label}`;\n const startLabel = `start:${labelName}`;\n const endLabel = `end:${labelName}`;\n try {\n performance.mark(startLabel);\n return await asyncMethod();\n }\n finally {\n performance.mark(endLabel);\n performance.measure(labelName, startLabel, endLabel);\n performance.clearMarks(startLabel);\n performance.clearMarks(endLabel);\n }\n}\nexport function noopRunMethodAndMeasurePerf(label, asyncMethod) {\n return asyncMethod();\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\nimport { renderApplication, renderModule, ɵSERVER_CONTEXT } from '@angular/platform-server';\nimport * as fs from 'node:fs';\nimport { dirname, join, normalize, resolve } from 'node:path';\nimport { URL } from 'node:url';\nimport { CommonEngineInlineCriticalCssProcessor } from './inline-css-processor';\nimport { noopRunMethodAndMeasurePerf, printPerformanceLogs, runMethodAndMeasurePerf, } from './peformance-profiler';\nconst SSG_MARKER_REGEXP = /ng-server-context=[\"']\\w*\\|?ssg\\|?\\w*[\"']/;\n/**\n * A common engine to use to server render an application.\n */\nexport class CommonEngine {\n options;\n templateCache = new Map();\n inlineCriticalCssProcessor = new CommonEngineInlineCriticalCssProcessor();\n pageIsSSG = new Map();\n constructor(options) {\n this.options = options;\n }\n /**\n * Render an HTML document for a specific URL with specified\n * render options\n */\n async render(opts) {\n const enablePerformanceProfiler = this.options?.enablePerformanceProfiler;\n const runMethod = enablePerformanceProfiler\n ? runMethodAndMeasurePerf\n : noopRunMethodAndMeasurePerf;\n let html = await runMethod('Retrieve SSG Page', () => this.retrieveSSGPage(opts));\n if (html === undefined) {\n html = await runMethod('Render Page', () => this.renderApplication(opts));\n if (opts.inlineCriticalCss !== false) {\n const content = await runMethod('Inline Critical CSS', () => \n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n this.inlineCriticalCss(html, opts));\n html = content;\n }\n }\n if (enablePerformanceProfiler) {\n printPerformanceLogs();\n }\n return html;\n }\n inlineCriticalCss(html, opts) {\n const outputPath = opts.publicPath ?? (opts.documentFilePath ? dirname(opts.documentFilePath) : '');\n return this.inlineCriticalCssProcessor.process(html, outputPath);\n }\n async retrieveSSGPage(opts) {\n const { publicPath, documentFilePath, url } = opts;\n if (!publicPath || !documentFilePath || url === undefined) {\n return undefined;\n }\n const { pathname } = new URL(url, 'resolve://');\n // Do not use `resolve` here as otherwise it can lead to path traversal vulnerability.\n // See: https://portswigger.net/web-security/file-path-traversal\n const pagePath = join(publicPath, pathname, 'index.html');\n if (this.pageIsSSG.get(pagePath)) {\n // Serve pre-rendered page.\n return fs.promises.readFile(pagePath, 'utf-8');\n }\n if (!pagePath.startsWith(normalize(publicPath))) {\n // Potential path traversal detected.\n return undefined;\n }\n if (pagePath === resolve(documentFilePath) || !(await exists(pagePath))) {\n // View matches with prerender path or file does not exist.\n this.pageIsSSG.set(pagePath, false);\n return undefined;\n }\n // Static file exists.\n const content = await fs.promises.readFile(pagePath, 'utf-8');\n const isSSG = SSG_MARKER_REGEXP.test(content);\n this.pageIsSSG.set(pagePath, isSSG);\n return isSSG ? content : undefined;\n }\n async renderApplication(opts) {\n const moduleOrFactory = this.options?.bootstrap ?? opts.bootstrap;\n if (!moduleOrFactory) {\n throw new Error('A module or bootstrap option must be provided.');\n }\n const extraProviders = [\n { provide: ɵSERVER_CONTEXT, useValue: 'ssr' },\n ...(opts.providers ?? []),\n ...(this.options?.providers ?? []),\n ];\n let document = opts.document;\n if (!document && opts.documentFilePath) {\n document = await this.getDocument(opts.documentFilePath);\n }\n const commonRenderingOptions = {\n url: opts.url,\n document,\n };\n return isBootstrapFn(moduleOrFactory)\n ? renderApplication(moduleOrFactory, {\n platformProviders: extraProviders,\n ...commonRenderingOptions,\n })\n : renderModule(moduleOrFactory, { extraProviders, ...commonRenderingOptions });\n }\n /** Retrieve the document from the cache or the filesystem */\n async getDocument(filePath) {\n let doc = this.templateCache.get(filePath);\n if (!doc) {\n doc = await fs.promises.readFile(filePath, 'utf-8');\n this.templateCache.set(filePath, doc);\n }\n return doc;\n }\n}\nasync function exists(path) {\n try {\n await fs.promises.access(path, fs.constants.F_OK);\n return true;\n }\n catch {\n return false;\n }\n}\nfunction isBootstrapFn(value) {\n // We can differentiate between a module and a bootstrap function by reading compiler-generated `ɵmod` static property:\n return typeof value === 'function' && !('ɵmod' in value);\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n/**\n * A set containing all the pseudo-headers defined in the HTTP/2 specification.\n *\n * This set can be used to filter out pseudo-headers from a list of headers,\n * as they are not allowed to be set directly using the `Node.js` Undici API or\n * the web `Headers` API.\n */\nconst HTTP2_PSEUDO_HEADERS = new Set([':method', ':scheme', ':authority', ':path', ':status']);\n/**\n * Converts a Node.js `IncomingMessage` or `Http2ServerRequest` into a\n * Web Standard `Request` object.\n *\n * This function adapts the Node.js request objects to a format that can\n * be used by web platform APIs.\n *\n * @param nodeRequest - The Node.js request object (`IncomingMessage` or `Http2ServerRequest`) to convert.\n * @returns A Web Standard `Request` object.\n * @developerPreview\n */\nexport function createWebRequestFromNodeRequest(nodeRequest) {\n const { headers, method = 'GET' } = nodeRequest;\n const withBody = method !== 'GET' && method !== 'HEAD';\n return new Request(createRequestUrl(nodeRequest), {\n method,\n headers: createRequestHeaders(headers),\n body: withBody ? nodeRequest : undefined,\n duplex: withBody ? 'half' : undefined,\n });\n}\n/**\n * Creates a `Headers` object from Node.js `IncomingHttpHeaders`.\n *\n * @param nodeHeaders - The Node.js `IncomingHttpHeaders` object to convert.\n * @returns A `Headers` object containing the converted headers.\n */\nfunction createRequestHeaders(nodeHeaders) {\n const headers = new Headers();\n for (const [name, value] of Object.entries(nodeHeaders)) {\n if (HTTP2_PSEUDO_HEADERS.has(name)) {\n continue;\n }\n if (typeof value === 'string') {\n headers.append(name, value);\n }\n else if (Array.isArray(value)) {\n for (const item of value) {\n headers.append(name, item);\n }\n }\n }\n return headers;\n}\n/**\n * Creates a `URL` object from a Node.js `IncomingMessage`, taking into account the protocol, host, and port.\n *\n * @param nodeRequest - The Node.js `IncomingMessage` or `Http2ServerRequest` object to extract URL information from.\n * @returns A `URL` object representing the request URL.\n */\nfunction createRequestUrl(nodeRequest) {\n const { headers, socket, url = '', originalUrl, } = nodeRequest;\n const protocol = headers['x-forwarded-proto'] ?? ('encrypted' in socket && socket.encrypted ? 'https' : 'http');\n const hostname = headers['x-forwarded-host'] ?? headers.host ?? headers[':authority'];\n const port = headers['x-forwarded-port'] ?? socket.localPort;\n if (Array.isArray(hostname)) {\n throw new Error('host value cannot be an array.');\n }\n let hostnameWithPort = hostname;\n if (port && !hostname?.includes(':')) {\n hostnameWithPort += `:${port}`;\n }\n return new URL(originalUrl ?? url, `${protocol}://${hostnameWithPort}`);\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\nimport { AngularAppEngine } from '@angular/ssr';\nimport { createWebRequestFromNodeRequest } from './request';\n/**\n * Angular server application engine.\n * Manages Angular server applications (including localized ones), handles rendering requests,\n * and optionally transforms index HTML before rendering.\n *\n * @remarks This class should be instantiated once and used as a singleton across the server-side\n * application to ensure consistent handling of rendering requests and resource management.\n *\n * @developerPreview\n */\nexport class AngularNodeAppEngine {\n angularAppEngine = new AngularAppEngine();\n /**\n * Handles an incoming HTTP request by serving prerendered content, performing server-side rendering,\n * or delivering a static file for client-side rendered routes based on the `RenderMode` setting.\n *\n * This method adapts Node.js's `IncomingMessage` or `Http2ServerRequest`\n * to a format compatible with the `AngularAppEngine` and delegates the handling logic to it.\n *\n * @param request - The incoming HTTP request (`IncomingMessage` or `Http2ServerRequest`).\n * @param requestContext - Optional context for rendering, such as metadata associated with the request.\n * @returns A promise that resolves to the resulting HTTP response object, or `null` if no matching Angular route is found.\n *\n * @remarks A request to `https://www.example.com/page/index.html` will serve or render the Angular route\n * corresponding to `https://www.example.com/page`.\n */\n async handle(request, requestContext) {\n const webRequest = createWebRequestFromNodeRequest(request);\n return this.angularAppEngine.handle(webRequest, requestContext);\n }\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n/**\n * Attaches metadata to the handler function to mark it as a special handler for Node.js environments.\n *\n * @typeParam T - The type of the handler function.\n * @param handler - The handler function to be defined and annotated.\n * @returns The same handler function passed as an argument, with metadata attached.\n *\n * @example\n * Usage in an Express application:\n * ```ts\n * const app = express();\n * export default createNodeRequestHandler(app);\n * ```\n *\n * @example\n * Usage in a Hono application:\n * ```ts\n * const app = new Hono();\n * export default createNodeRequestHandler(async (req, res, next) => {\n * try {\n * const webRes = await app.fetch(createWebRequestFromNodeRequest(req));\n * if (webRes) {\n * await writeResponseToNodeResponse(webRes, res);\n * } else {\n * next();\n * }\n * } catch (error) {\n * next(error);\n * }\n * }));\n * ```\n *\n * @example\n * Usage in a Fastify application:\n * ```ts\n * const app = Fastify();\n * export default createNodeRequestHandler(async (req, res) => {\n * await app.ready();\n * app.server.emit('request', req, res);\n * res.send('Hello from Fastify with Node Next Handler!');\n * }));\n * ```\n * @developerPreview\n */\nexport function createNodeRequestHandler(handler) {\n handler['__ng_node_request_handler__'] = true;\n return handler;\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n/**\n * Streams a web-standard `Response` into a Node.js `ServerResponse`\n * or `Http2ServerResponse`.\n *\n * This function adapts the web `Response` object to write its content\n * to a Node.js response object, handling both HTTP/1.1 and HTTP/2.\n *\n * @param source - The web-standard `Response` object to stream from.\n * @param destination - The Node.js response object (`ServerResponse` or `Http2ServerResponse`) to stream into.\n * @returns A promise that resolves once the streaming operation is complete.\n * @developerPreview\n */\nexport async function writeResponseToNodeResponse(source, destination) {\n const { status, headers, body } = source;\n destination.statusCode = status;\n let cookieHeaderSet = false;\n for (const [name, value] of headers.entries()) {\n if (name === 'set-cookie') {\n if (cookieHeaderSet) {\n continue;\n }\n // Sets the 'set-cookie' header only once to ensure it is correctly applied.\n // Concatenating 'set-cookie' values can lead to incorrect behavior, so we use a single value from `headers.getSetCookie()`.\n destination.setHeader(name, headers.getSetCookie());\n cookieHeaderSet = true;\n }\n else {\n destination.setHeader(name, value);\n }\n }\n if (!body) {\n destination.end();\n return;\n }\n try {\n const reader = body.getReader();\n destination.on('close', () => {\n reader.cancel().catch((error) => {\n // eslint-disable-next-line no-console\n console.error(`An error occurred while writing the response body for: ${destination.req.url}.`, error);\n });\n });\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n destination.end();\n break;\n }\n destination.write(value);\n }\n }\n catch {\n destination.end('Internal server error.');\n }\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\nimport { argv } from 'node:process';\nimport { fileURLToPath } from 'node:url';\n/**\n * Determines whether the provided URL represents the main entry point module.\n *\n * This function checks if the provided URL corresponds to the main ESM module being executed directly.\n * It's useful for conditionally executing code that should only run when a module is the entry point,\n * such as starting a server or initializing an application.\n *\n * It performs two key checks:\n * 1. Verifies if the URL starts with 'file:', ensuring it is a local file.\n * 2. Compares the URL's resolved file path with the first command-line argument (`process.argv[1]`),\n * which points to the file being executed.\n *\n * @param url The URL of the module to check. This should typically be `import.meta.url`.\n * @returns `true` if the provided URL represents the main entry point, otherwise `false`.\n * @developerPreview\n */\nexport function isMainModule(url) {\n return url.startsWith('file:') && argv[1] === fileURLToPath(url);\n}\n"],"names":["InlineCriticalCssProcessor","URL","ɵSERVER_CONTEXT"],"mappings":"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"}
package/fesm2022/ssr.mjs CHANGED
@@ -655,6 +655,11 @@ class RouteTree {
655
655
  }
656
656
  }
657
657
 
658
+ /**
659
+ * The maximum number of module preload link elements that should be added for
660
+ * initial scripts.
661
+ */
662
+ const MODULE_PRELOAD_MAX = 10;
658
663
  /**
659
664
  * Regular expression to match segments preceded by a colon in a string.
660
665
  */
@@ -673,10 +678,10 @@ const VALID_REDIRECT_RESPONSE_CODES = new Set([301, 302, 303, 307, 308]);
673
678
  * @returns An async iterable iterator yielding either route tree node metadata or an error object with an error message.
674
679
  */
675
680
  async function* traverseRoutesConfig(options) {
676
- const { routes, compiler, parentInjector, parentRoute, serverConfigRouteTree, invokeGetPrerenderParams, includePrerenderFallbackRoutes, } = options;
681
+ const { routes, compiler, parentInjector, parentRoute, serverConfigRouteTree, entryPointToBrowserMapping, parentPreloads, invokeGetPrerenderParams, includePrerenderFallbackRoutes, } = options;
677
682
  for (const route of routes) {
678
683
  try {
679
- const { path = '', redirectTo, loadChildren, children } = route;
684
+ const { path = '', redirectTo, loadChildren, loadComponent, children, ɵentryName } = route;
680
685
  const currentRoutePath = joinUrlParts(parentRoute, path);
681
686
  // Get route metadata from the server config route tree, if available
682
687
  let matchedMetaData;
@@ -694,12 +699,16 @@ async function* traverseRoutesConfig(options) {
694
699
  const metadata = {
695
700
  renderMode: RenderMode.Prerender,
696
701
  ...matchedMetaData,
702
+ preload: parentPreloads,
697
703
  // Match Angular router behavior
698
704
  // ['one', 'two', ''] -> 'one/two/'
699
705
  // ['one', 'two', 'three'] -> 'one/two/three'
700
706
  route: path === '' ? addTrailingSlash(currentRoutePath) : currentRoutePath,
707
+ presentInClientRouter: undefined,
701
708
  };
702
- delete metadata.presentInClientRouter;
709
+ if (ɵentryName && loadComponent) {
710
+ appendPreloadToMetadata(ɵentryName, entryPointToBrowserMapping, metadata, true);
711
+ }
703
712
  if (metadata.renderMode === RenderMode.Prerender) {
704
713
  // Handle SSG routes
705
714
  yield* handleSSGRoute(typeof redirectTo === 'string' ? redirectTo : undefined, metadata, parentInjector, invokeGetPrerenderParams, includePrerenderFallbackRoutes);
@@ -724,10 +733,18 @@ async function* traverseRoutesConfig(options) {
724
733
  ...options,
725
734
  routes: children,
726
735
  parentRoute: currentRoutePath,
736
+ parentPreloads: metadata.preload,
727
737
  });
728
738
  }
729
739
  // Load and process lazy-loaded child routes
730
740
  if (loadChildren) {
741
+ if (ɵentryName) {
742
+ // When using `loadChildren`, the entire feature area (including multiple routes) is loaded.
743
+ // As a result, we do not want all dynamic-import dependencies to be preload, because it involves multiple dependencies
744
+ // across different child routes. In contrast, `loadComponent` only loads a single component, which allows
745
+ // for precise control over preloading, ensuring that the files preloaded are exactly those required for that specific route.
746
+ appendPreloadToMetadata(ɵentryName, entryPointToBrowserMapping, metadata, false);
747
+ }
731
748
  const loadedChildRoutes = await _loadChildren(route, compiler, parentInjector).toPromise();
732
749
  if (loadedChildRoutes) {
733
750
  const { routes: childRoutes, injector = parentInjector } = loadedChildRoutes;
@@ -736,6 +753,7 @@ async function* traverseRoutesConfig(options) {
736
753
  routes: childRoutes,
737
754
  parentInjector: injector,
738
755
  parentRoute: currentRoutePath,
756
+ parentPreloads: metadata.preload,
739
757
  });
740
758
  }
741
759
  }
@@ -747,6 +765,27 @@ async function* traverseRoutesConfig(options) {
747
765
  }
748
766
  }
749
767
  }
768
+ /**
769
+ * Appends preload information to the metadata object based on the specified entry-point and chunk mappings.
770
+ *
771
+ * This function extracts preload data for a given entry-point from the provided chunk mappings. It adds the
772
+ * corresponding browser bundles to the metadata's preload list, ensuring no duplicates and limiting the total
773
+ * preloads to a predefined maximum.
774
+ */
775
+ function appendPreloadToMetadata(entryName, entryPointToBrowserMapping, metadata, includeDynamicImports) {
776
+ if (!entryPointToBrowserMapping) {
777
+ return;
778
+ }
779
+ const preload = entryPointToBrowserMapping[entryName];
780
+ if (preload?.length) {
781
+ // Merge existing preloads with new ones, ensuring uniqueness and limiting the total to the maximum allowed.
782
+ const preloadPaths = preload
783
+ .filter(({ dynamicImport }) => includeDynamicImports || !dynamicImport)
784
+ .map(({ path }) => path) ?? [];
785
+ const combinedPreloads = [...(metadata.preload ?? []), ...preloadPaths];
786
+ metadata.preload = Array.from(new Set(combinedPreloads)).slice(0, MODULE_PRELOAD_MAX);
787
+ }
788
+ }
750
789
  /**
751
790
  * Handles SSG (Static Site Generation) routes by invoking `getPrerenderParams` and yielding
752
791
  * all parameterized paths, returning any errors encountered.
@@ -891,10 +930,11 @@ function buildServerConfigRouteTree({ routes, appShellRoute }) {
891
930
  * @param invokeGetPrerenderParams - A boolean flag indicating whether to invoke `getPrerenderParams` for parameterized SSG routes
892
931
  * to handle prerendering paths. Defaults to `false`.
893
932
  * @param includePrerenderFallbackRoutes - A flag indicating whether to include fallback routes in the result. Defaults to `true`.
933
+ * @param entryPointToBrowserMapping - Maps the entry-point name to the associated JavaScript browser bundles.
894
934
  *
895
935
  * @returns A promise that resolves to an object of type `AngularRouterConfigResult` or errors.
896
936
  */
897
- async function getRoutesFromAngularRouterConfig(bootstrap, document, url, invokeGetPrerenderParams = false, includePrerenderFallbackRoutes = true) {
937
+ async function getRoutesFromAngularRouterConfig(bootstrap, document, url, invokeGetPrerenderParams = false, includePrerenderFallbackRoutes = true, entryPointToBrowserMapping = undefined) {
898
938
  const { protocol, host } = url;
899
939
  // Create and initialize the Angular platform for server-side rendering.
900
940
  const platformRef = platformServer([
@@ -968,6 +1008,7 @@ async function getRoutesFromAngularRouterConfig(bootstrap, document, url, invoke
968
1008
  serverConfigRouteTree,
969
1009
  invokeGetPrerenderParams,
970
1010
  includePrerenderFallbackRoutes,
1011
+ entryPointToBrowserMapping,
971
1012
  });
972
1013
  for await (const result of traverseRoutes) {
973
1014
  if ('error' in result) {
@@ -1040,7 +1081,7 @@ function extractRoutesAndCreateRouteTree(options) {
1040
1081
  const routeTree = new RouteTree();
1041
1082
  const document = await new ServerAssets(manifest).getIndexServerHtml().text();
1042
1083
  const bootstrap = await manifest.bootstrap();
1043
- const { baseHref, appShellRoute, routes, errors } = await getRoutesFromAngularRouterConfig(bootstrap, document, url, invokeGetPrerenderParams, includePrerenderFallbackRoutes);
1084
+ const { baseHref, appShellRoute, routes, errors } = await getRoutesFromAngularRouterConfig(bootstrap, document, url, invokeGetPrerenderParams, includePrerenderFallbackRoutes, manifest.entryPointToBrowserMapping);
1044
1085
  for (const { route, ...metadata } of routes) {
1045
1086
  if (metadata.redirectTo !== undefined) {
1046
1087
  metadata.redirectTo = joinUrlParts(baseHref, metadata.redirectTo);
@@ -1692,7 +1733,7 @@ class AngularServerApp {
1692
1733
  * @returns A promise that resolves to the rendered response, or null if no matching route is found.
1693
1734
  */
1694
1735
  async handleRendering(request, matchedRoute, requestContext) {
1695
- const { renderMode, headers, status } = matchedRoute;
1736
+ const { renderMode, headers, status, preload } = matchedRoute;
1696
1737
  if (!this.allowStaticRouteRender && renderMode === RenderMode.Prerender) {
1697
1738
  return null;
1698
1739
  }
@@ -1723,8 +1764,8 @@ class AngularServerApp {
1723
1764
  }
1724
1765
  else if (renderMode === RenderMode.Client) {
1725
1766
  // Serve the client-side rendered version if the route is configured for CSR.
1726
- let html = await assets.getServerAsset('index.csr.html').text();
1727
- html = await this.runTransformsOnHtml(html, url);
1767
+ let html = await this.assets.getServerAsset('index.csr.html').text();
1768
+ html = await this.runTransformsOnHtml(html, url, preload);
1728
1769
  return new Response(html, responseInit);
1729
1770
  }
1730
1771
  if (locale !== undefined) {
@@ -1735,7 +1776,7 @@ class AngularServerApp {
1735
1776
  }
1736
1777
  this.boostrap ??= await bootstrap();
1737
1778
  let html = await assets.getIndexServerHtml().text();
1738
- html = await this.runTransformsOnHtml(html, url);
1779
+ html = await this.runTransformsOnHtml(html, url, preload);
1739
1780
  html = await renderAngular(html, this.boostrap, url, platformProviders, SERVER_CONTEXT_VALUE[renderMode]);
1740
1781
  if (inlineCriticalCss) {
1741
1782
  // Optionally inline critical CSS.
@@ -1795,12 +1836,16 @@ class AngularServerApp {
1795
1836
  *
1796
1837
  * @param html - The raw HTML content to be transformed.
1797
1838
  * @param url - The URL associated with the HTML content, used for context during transformations.
1839
+ * @param preload - An array of URLs representing the JavaScript resources to preload.
1798
1840
  * @returns A promise that resolves to the transformed HTML string.
1799
1841
  */
1800
- async runTransformsOnHtml(html, url) {
1842
+ async runTransformsOnHtml(html, url, preload) {
1801
1843
  if (this.hooks.has('html:transform:pre')) {
1802
1844
  html = await this.hooks.run('html:transform:pre', { html, url });
1803
1845
  }
1846
+ if (preload?.length) {
1847
+ html = appendPreloadHintsToHtml(html, preload);
1848
+ }
1804
1849
  return html;
1805
1850
  }
1806
1851
  }
@@ -1833,6 +1878,31 @@ function destroyAngularServerApp() {
1833
1878
  }
1834
1879
  angularServerApp = undefined;
1835
1880
  }
1881
+ /**
1882
+ * Appends module preload hints to an HTML string for specified JavaScript resources.
1883
+ * This function enhances the HTML by injecting `<link rel="modulepreload">` elements
1884
+ * for each provided resource, allowing browsers to preload the specified JavaScript
1885
+ * modules for better performance.
1886
+ *
1887
+ * @param html - The original HTML string to which preload hints will be added.
1888
+ * @param preload - An array of URLs representing the JavaScript resources to preload.
1889
+ * @returns The modified HTML string with the preload hints injected before the closing `</body>` tag.
1890
+ * If `</body>` is not found, the links are not added.
1891
+ */
1892
+ function appendPreloadHintsToHtml(html, preload) {
1893
+ const bodyCloseIdx = html.lastIndexOf('</body>');
1894
+ if (bodyCloseIdx === -1) {
1895
+ return html;
1896
+ }
1897
+ // Note: Module preloads should be placed at the end before the closing body tag to avoid a performance penalty.
1898
+ // Placing them earlier can cause the browser to prioritize downloading these modules
1899
+ // over other critical page resources like images, CSS, and fonts.
1900
+ return [
1901
+ html.slice(0, bodyCloseIdx),
1902
+ ...preload.map((val) => `<link rel="modulepreload" href="${val}">`),
1903
+ html.slice(bodyCloseIdx),
1904
+ ].join('\n');
1905
+ }
1836
1906
 
1837
1907
  // ɵgetRoutesFromAngularRouterConfig is only used by the Webpack based server builder.
1838
1908
 
@@ -1870,6 +1940,144 @@ function getPotentialLocaleIdFromUrl(url, basePath) {
1870
1940
  // Extract the potential locale id.
1871
1941
  return pathname.slice(start, end);
1872
1942
  }
1943
+ /**
1944
+ * Parses the `Accept-Language` header and returns a list of locale preferences with their respective quality values.
1945
+ *
1946
+ * The `Accept-Language` header is typically a comma-separated list of locales, with optional quality values
1947
+ * in the form of `q=<value>`. If no quality value is specified, a default quality of `1` is assumed.
1948
+ * Special case: if the header is `*`, it returns the default locale with a quality of `1`.
1949
+ *
1950
+ * @param header - The value of the `Accept-Language` header, typically a comma-separated list of locales
1951
+ * with optional quality values (e.g., `en-US;q=0.8,fr-FR;q=0.9`). If the header is `*`,
1952
+ * it represents a wildcard for any language, returning the default locale.
1953
+ *
1954
+ * @returns A `ReadonlyMap` where the key is the locale (e.g., `en-US`, `fr-FR`), and the value is
1955
+ * the associated quality value (a number between 0 and 1). If no quality value is provided,
1956
+ * a default of `1` is used.
1957
+ *
1958
+ * @example
1959
+ * ```js
1960
+ * parseLanguageHeader('en-US;q=0.8,fr-FR;q=0.9')
1961
+ * // returns new Map([['en-US', 0.8], ['fr-FR', 0.9]])
1962
+
1963
+ * parseLanguageHeader('*')
1964
+ * // returns new Map([['*', 1]])
1965
+ * ```
1966
+ */
1967
+ function parseLanguageHeader(header) {
1968
+ if (header === '*') {
1969
+ return new Map([['*', 1]]);
1970
+ }
1971
+ const parsedValues = header
1972
+ .split(',')
1973
+ .map((item) => {
1974
+ const [locale, qualityValue] = item.split(';', 2).map((v) => v.trim());
1975
+ let quality = qualityValue?.startsWith('q=') ? parseFloat(qualityValue.slice(2)) : undefined;
1976
+ if (typeof quality !== 'number' || isNaN(quality) || quality < 0 || quality > 1) {
1977
+ quality = 1; // Invalid quality value defaults to 1
1978
+ }
1979
+ return [locale, quality];
1980
+ })
1981
+ .sort(([_localeA, qualityA], [_localeB, qualityB]) => qualityB - qualityA);
1982
+ return new Map(parsedValues);
1983
+ }
1984
+ /**
1985
+ * Gets the preferred locale based on the highest quality value from the provided `Accept-Language` header
1986
+ * and the set of available locales.
1987
+ *
1988
+ * This function adheres to the HTTP `Accept-Language` header specification as defined in
1989
+ * [RFC 7231](https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.5), including:
1990
+ * - Case-insensitive matching of language tags.
1991
+ * - Quality value handling (e.g., `q=1`, `q=0.8`). If no quality value is provided, it defaults to `q=1`.
1992
+ * - Prefix matching (e.g., `en` matching `en-US` or `en-GB`).
1993
+ *
1994
+ * @param header - The `Accept-Language` header string to parse and evaluate. It may contain multiple
1995
+ * locales with optional quality values, for example: `'en-US;q=0.8,fr-FR;q=0.9'`.
1996
+ * @param supportedLocales - An array of supported locales (e.g., `['en-US', 'fr-FR']`),
1997
+ * representing the locales available in the application.
1998
+ * @returns The best matching locale from the supported languages, or `undefined` if no match is found.
1999
+ *
2000
+ * @example
2001
+ * ```js
2002
+ * getPreferredLocale('en-US;q=0.8,fr-FR;q=0.9', ['en-US', 'fr-FR', 'de-DE'])
2003
+ * // returns 'fr-FR'
2004
+ *
2005
+ * getPreferredLocale('en;q=0.9,fr-FR;q=0.8', ['en-US', 'fr-FR', 'de-DE'])
2006
+ * // returns 'en-US'
2007
+ *
2008
+ * getPreferredLocale('es-ES;q=0.7', ['en-US', 'fr-FR', 'de-DE'])
2009
+ * // returns undefined
2010
+ * ```
2011
+ */
2012
+ function getPreferredLocale(header, supportedLocales) {
2013
+ if (supportedLocales.length < 2) {
2014
+ return supportedLocales[0];
2015
+ }
2016
+ const parsedLocales = parseLanguageHeader(header);
2017
+ // Handle edge cases:
2018
+ // - No preferred locales provided.
2019
+ // - Only one supported locale.
2020
+ // - Wildcard preference.
2021
+ if (parsedLocales.size === 0 || (parsedLocales.size === 1 && parsedLocales.has('*'))) {
2022
+ return supportedLocales[0];
2023
+ }
2024
+ // Create a map for case-insensitive lookup of supported locales.
2025
+ // Keys are normalized (lowercase) locale values, values are original casing.
2026
+ const normalizedSupportedLocales = new Map();
2027
+ for (const locale of supportedLocales) {
2028
+ normalizedSupportedLocales.set(normalizeLocale(locale), locale);
2029
+ }
2030
+ // Iterate through parsed locales in descending order of quality.
2031
+ let bestMatch;
2032
+ const qualityZeroNormalizedLocales = new Set();
2033
+ for (const [locale, quality] of parsedLocales) {
2034
+ const normalizedLocale = normalizeLocale(locale);
2035
+ if (quality === 0) {
2036
+ qualityZeroNormalizedLocales.add(normalizedLocale);
2037
+ continue; // Skip locales with quality value of 0.
2038
+ }
2039
+ // Exact match found.
2040
+ if (normalizedSupportedLocales.has(normalizedLocale)) {
2041
+ return normalizedSupportedLocales.get(normalizedLocale);
2042
+ }
2043
+ // If an exact match is not found, try prefix matching (e.g., "en" matches "en-US").
2044
+ // Store the first prefix match encountered, as it has the highest quality value.
2045
+ if (bestMatch !== undefined) {
2046
+ continue;
2047
+ }
2048
+ const [languagePrefix] = normalizedLocale.split('-', 1);
2049
+ for (const supportedLocale of normalizedSupportedLocales.keys()) {
2050
+ if (supportedLocale.startsWith(languagePrefix)) {
2051
+ bestMatch = normalizedSupportedLocales.get(supportedLocale);
2052
+ break; // No need to continue searching for this locale.
2053
+ }
2054
+ }
2055
+ }
2056
+ if (bestMatch !== undefined) {
2057
+ return bestMatch;
2058
+ }
2059
+ // Return the first locale that is not quality zero.
2060
+ for (const [normalizedLocale, locale] of normalizedSupportedLocales) {
2061
+ if (!qualityZeroNormalizedLocales.has(normalizedLocale)) {
2062
+ return locale;
2063
+ }
2064
+ }
2065
+ }
2066
+ /**
2067
+ * Normalizes a locale string by converting it to lowercase.
2068
+ *
2069
+ * @param locale - The locale string to normalize.
2070
+ * @returns The normalized locale string in lowercase.
2071
+ *
2072
+ * @example
2073
+ * ```ts
2074
+ * const normalized = normalizeLocale('EN-US');
2075
+ * console.log(normalized); // Output: "en-us"
2076
+ * ```
2077
+ */
2078
+ function normalizeLocale(locale) {
2079
+ return locale.toLowerCase();
2080
+ }
1873
2081
 
1874
2082
  /**
1875
2083
  * Angular server application engine.
@@ -1904,9 +2112,9 @@ class AngularAppEngine {
1904
2112
  */
1905
2113
  manifest = getAngularAppEngineManifest();
1906
2114
  /**
1907
- * The number of entry points available in the server application's manifest.
2115
+ * A map of supported locales from the server application's manifest.
1908
2116
  */
1909
- entryPointsCount = Object.keys(this.manifest.entryPoints).length;
2117
+ supportedLocales = Object.keys(this.manifest.supportedLocales);
1910
2118
  /**
1911
2119
  * A cache that holds entry points, keyed by their potential locale string.
1912
2120
  */
@@ -1924,7 +2132,47 @@ class AngularAppEngine {
1924
2132
  */
1925
2133
  async handle(request, requestContext) {
1926
2134
  const serverApp = await this.getAngularServerAppForRequest(request);
1927
- return serverApp ? serverApp.handle(request, requestContext) : null;
2135
+ if (serverApp) {
2136
+ return serverApp.handle(request, requestContext);
2137
+ }
2138
+ if (this.supportedLocales.length > 1) {
2139
+ // Redirect to the preferred language if i18n is enabled.
2140
+ return this.redirectBasedOnAcceptLanguage(request);
2141
+ }
2142
+ return null;
2143
+ }
2144
+ /**
2145
+ * Handles requests for the base path when i18n is enabled.
2146
+ * Redirects the user to a locale-specific path based on the `Accept-Language` header.
2147
+ *
2148
+ * @param request The incoming request.
2149
+ * @returns A `Response` object with a 302 redirect, or `null` if i18n is not enabled
2150
+ * or the request is not for the base path.
2151
+ */
2152
+ redirectBasedOnAcceptLanguage(request) {
2153
+ const { basePath, supportedLocales } = this.manifest;
2154
+ // If the request is not for the base path, it's not our responsibility to handle it.
2155
+ const url = new URL(request.url);
2156
+ if (url.pathname !== basePath) {
2157
+ return null;
2158
+ }
2159
+ // For requests to the base path (typically '/'), attempt to extract the preferred locale
2160
+ // from the 'Accept-Language' header.
2161
+ const preferredLocale = getPreferredLocale(request.headers.get('Accept-Language') || '*', this.supportedLocales);
2162
+ if (preferredLocale) {
2163
+ const subPath = supportedLocales[preferredLocale];
2164
+ if (subPath !== undefined) {
2165
+ url.pathname = joinUrlParts(url.pathname, subPath);
2166
+ return new Response(null, {
2167
+ status: 302, // Use a 302 redirect as language preference may change.
2168
+ headers: {
2169
+ 'Location': url.toString(),
2170
+ 'Vary': 'Accept-Language',
2171
+ },
2172
+ });
2173
+ }
2174
+ }
2175
+ return null;
1928
2176
  }
1929
2177
  /**
1930
2178
  * Retrieves the Angular server application instance for a given request.
@@ -1986,11 +2234,11 @@ class AngularAppEngine {
1986
2234
  */
1987
2235
  getEntryPointExportsForUrl(url) {
1988
2236
  const { basePath } = this.manifest;
1989
- if (this.entryPointsCount === 1) {
2237
+ if (this.supportedLocales.length === 1) {
1990
2238
  return this.getEntryPointExports('');
1991
2239
  }
1992
2240
  const potentialLocale = getPotentialLocaleIdFromUrl(url, basePath);
1993
- return this.getEntryPointExports(potentialLocale);
2241
+ return this.getEntryPointExports(potentialLocale) ?? this.getEntryPointExports('');
1994
2242
  }
1995
2243
  }
1996
2244