diff --git a/packages/angular/build/src/builders/application/options.ts b/packages/angular/build/src/builders/application/options.ts index 44a70b1da5ab..4c78495cab99 100644 --- a/packages/angular/build/src/builders/application/options.ts +++ b/packages/angular/build/src/builders/application/options.ts @@ -216,10 +216,11 @@ export async function normalizeOptions( if (options.ssr === true) { ssrOptions = {}; } else if (typeof options.ssr === 'object') { - const { entry } = options.ssr; + const { entry, providers } = options.ssr; ssrOptions = { entry: entry && path.join(workspaceRoot, entry), + providers: providers && path.join(workspaceRoot, providers), }; } diff --git a/packages/angular/build/src/builders/application/schema.json b/packages/angular/build/src/builders/application/schema.json index 6df812386f01..62dff92b1227 100644 --- a/packages/angular/build/src/builders/application/schema.json +++ b/packages/angular/build/src/builders/application/schema.json @@ -518,6 +518,10 @@ "entry": { "type": "string", "description": "The server entry-point that when executed will spawn the web server." + }, + "providers": { + "type": "string", + "description": "Path to providers for server application" } }, "additionalProperties": false diff --git a/packages/angular/build/src/builders/dev-server/vite-server.ts b/packages/angular/build/src/builders/dev-server/vite-server.ts index a85da51561d9..112d219e756f 100644 --- a/packages/angular/build/src/builders/dev-server/vite-server.ts +++ b/packages/angular/build/src/builders/dev-server/vite-server.ts @@ -94,7 +94,6 @@ export async function* serveWithVite( browserOptions.prerender = false; // Avoid bundling and processing the ssr entry-point as this is not used by the dev-server. - browserOptions.ssr = true; // https://nodejs.org/api/process.html#processsetsourcemapsenabledval process.setSourceMapsEnabled(true); @@ -286,7 +285,7 @@ export async function* serveWithVite( assetFiles, browserOptions.preserveSymlinks, externalMetadata, - !!browserOptions.ssr, + browserOptions.ssr, prebundleTransformer, target, isZonelessApp(polyfills), @@ -465,7 +464,7 @@ export async function setupServer( assets: Map, preserveSymlinks: boolean | undefined, externalMetadata: ExternalResultMetadata, - ssr: boolean, + ssr: boolean | { entry: string; providers: string; }, prebundleTransformer: JavaScriptTransformer, target: string[], zoneless: boolean, diff --git a/packages/angular/build/src/tools/esbuild/application-code-bundle.ts b/packages/angular/build/src/tools/esbuild/application-code-bundle.ts index 696784fd3702..b3ae6419be9f 100644 --- a/packages/angular/build/src/tools/esbuild/application-code-bundle.ts +++ b/packages/angular/build/src/tools/esbuild/application-code-bundle.ts @@ -185,6 +185,10 @@ export function createServerCodeBundleOptions( if (ssrEntryPoint) { entryPoints['server'] = ssrEntryPoint; } + const providersEntryPoint = ssrOptions?.providers; + if (providersEntryPoint) { + entryPoints['providers'] = providersEntryPoint; + } const buildOptions: BuildOptions = { ...getEsBuildCommonOptions(options), diff --git a/packages/angular/build/src/tools/vite/angular-memory-plugin.ts b/packages/angular/build/src/tools/vite/angular-memory-plugin.ts index e991d0395406..befe1b8edad0 100644 --- a/packages/angular/build/src/tools/vite/angular-memory-plugin.ts +++ b/packages/angular/build/src/tools/vite/angular-memory-plugin.ts @@ -14,13 +14,14 @@ import { ServerResponse } from 'node:http'; import { dirname, extname, join, relative } from 'node:path'; import type { Connect, Plugin } from 'vite'; import { renderPage } from '../../utils/server-rendering/render-page'; +import { loadEsmModuleFromMemory } from '../../utils/server-rendering/load-esm-from-memory'; export interface AngularMemoryPluginOptions { workspaceRoot: string; virtualProjectRoot: string; outputFiles: Map; assets: Map; - ssr: boolean; + ssr: boolean | {entry: string, providers: string}; external?: string[]; extensionMiddleware?: Connect.NextHandleFunction[]; extraHeaders?: Record; @@ -221,6 +222,18 @@ export function createAngularMemoryPlugin(options: AngularMemoryPluginOptions): transformIndexHtmlAndAddHeaders(req.url, rawHtml, res, next, async (html) => { const resolvedUrls = server.resolvedUrls; const baseUrl = resolvedUrls?.local[0] ?? resolvedUrls?.network[0]; + const providers: [] = []; + if (ssr && typeof ssr === 'object' && ssr.providers){ + const providersModule = await server.ssrLoadModule('/providers.mjs') + if (providersModule && providersModule.default) { + try { + const result = providersModule.default(req, res) + if (result && Array.isArray(result)) providers.push(...result as []) + } catch (e) { + throw new Error('Should be export default function which return array of provider') + } + } + } const { content } = await renderPage({ document: html, @@ -233,6 +246,7 @@ export function createAngularMemoryPlugin(options: AngularMemoryPluginOptions): outputFiles: {}, // TODO: add support for critical css inlining. inlineCriticalCss: false, + providers }); return indexHtmlTransformer && content ? await indexHtmlTransformer(content) : content; diff --git a/packages/angular/build/src/utils/server-rendering/load-esm-from-memory.ts b/packages/angular/build/src/utils/server-rendering/load-esm-from-memory.ts index a3a3384545a4..150427357fe3 100644 --- a/packages/angular/build/src/utils/server-rendering/load-esm-from-memory.ts +++ b/packages/angular/build/src/utils/server-rendering/load-esm-from-memory.ts @@ -8,11 +8,18 @@ import { assertIsError } from '../error'; import { loadEsmModule } from '../load-esm'; -import { MainServerBundleExports, RenderUtilsServerBundleExports } from './main-bundle-exports'; +import { + MainProvidersBundleExports, + MainServerBundleExports, + RenderUtilsServerBundleExports +} from './main-bundle-exports'; export function loadEsmModuleFromMemory( path: './main.server.mjs', ): Promise; +export function loadEsmModuleFromMemory( + path: './providers.mjs', +): Promise; export function loadEsmModuleFromMemory( path: './render-utils.server.mjs', ): Promise; diff --git a/packages/angular/build/src/utils/server-rendering/main-bundle-exports.ts b/packages/angular/build/src/utils/server-rendering/main-bundle-exports.ts index eb6f0f0dfb8c..a29f4a8b9bbc 100644 --- a/packages/angular/build/src/utils/server-rendering/main-bundle-exports.ts +++ b/packages/angular/build/src/utils/server-rendering/main-bundle-exports.ts @@ -6,15 +6,20 @@ * found in the LICENSE file at https://angular.dev/license */ -import type { ApplicationRef, Type, ɵConsole } from '@angular/core'; +import type { ApplicationRef, Type, ɵConsole, ApplicationConfig } from '@angular/core'; import type { renderApplication, renderModule, ɵSERVER_CONTEXT } from '@angular/platform-server'; import type { extractRoutes } from '../routes-extractor/extractor'; +import type { ServerResponse } from 'node:http'; export interface MainServerBundleExports { /** Standalone application bootstrapping function. */ default: (() => Promise) | Type; } +export interface MainProvidersBundleExports { + default: (req: Req, res: Res) => ApplicationConfig['providers']; +} + export interface RenderUtilsServerBundleExports { /** An internal token that allows providing extra information about the server context. */ ɵSERVER_CONTEXT: typeof ɵSERVER_CONTEXT; diff --git a/packages/angular/build/src/utils/server-rendering/render-page.ts b/packages/angular/build/src/utils/server-rendering/render-page.ts index aaf4509c35a2..4ce15767f20d 100644 --- a/packages/angular/build/src/utils/server-rendering/render-page.ts +++ b/packages/angular/build/src/utils/server-rendering/render-page.ts @@ -20,6 +20,7 @@ export interface RenderOptions { inlineCriticalCss?: boolean; loadBundle?: ((path: './main.server.mjs') => Promise) & ((path: './render-utils.server.mjs') => Promise); + providers: StaticProvider[] } export interface RenderResult { @@ -40,6 +41,7 @@ export async function renderPage({ inlineCriticalCss, outputFiles, loadBundle = loadEsmModuleFromMemory, + providers }: RenderOptions): Promise { const { default: bootstrapAppFnOrModule } = await loadBundle('./main.server.mjs'); const { ɵSERVER_CONTEXT, renderModule, renderApplication, ɵresetCompiledComponents, ɵConsole } = @@ -71,6 +73,7 @@ export async function renderPage({ return new Console(); }, }, + ...providers ]; assert(