Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/angular/build/src/builders/application/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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),
};
}

Expand Down
4 changes: 4 additions & 0 deletions packages/angular/build/src/builders/application/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 2 additions & 3 deletions packages/angular/build/src/builders/dev-server/vite-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -286,7 +285,7 @@ export async function* serveWithVite(
assetFiles,
browserOptions.preserveSymlinks,
externalMetadata,
!!browserOptions.ssr,
browserOptions.ssr,
prebundleTransformer,
target,
isZonelessApp(polyfills),
Expand Down Expand Up @@ -465,7 +464,7 @@ export async function setupServer(
assets: Map<string, string>,
preserveSymlinks: boolean | undefined,
externalMetadata: ExternalResultMetadata,
ssr: boolean,
ssr: boolean | { entry: string; providers: string; },
prebundleTransformer: JavaScriptTransformer,
target: string[],
zoneless: boolean,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
16 changes: 15 additions & 1 deletion packages/angular/build/src/tools/vite/angular-memory-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, { contents: Uint8Array; servable: boolean }>;
assets: Map<string, string>;
ssr: boolean;
ssr: boolean | {entry: string, providers: string};
external?: string[];
extensionMiddleware?: Connect.NextHandleFunction[];
extraHeaders?: Record<string, string>;
Expand Down Expand Up @@ -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,
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<MainServerBundleExports>;
export function loadEsmModuleFromMemory(
path: './providers.mjs',
): Promise<MainProvidersBundleExports>;
export function loadEsmModuleFromMemory(
path: './render-utils.server.mjs',
): Promise<RenderUtilsServerBundleExports>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<ApplicationRef>) | Type<unknown>;
}

export interface MainProvidersBundleExports {
default: <Req, Res>(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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface RenderOptions {
inlineCriticalCss?: boolean;
loadBundle?: ((path: './main.server.mjs') => Promise<MainServerBundleExports>) &
((path: './render-utils.server.mjs') => Promise<RenderUtilsServerBundleExports>);
providers: StaticProvider[]
}

export interface RenderResult {
Expand All @@ -40,6 +41,7 @@ export async function renderPage({
inlineCriticalCss,
outputFiles,
loadBundle = loadEsmModuleFromMemory,
providers
}: RenderOptions): Promise<RenderResult> {
const { default: bootstrapAppFnOrModule } = await loadBundle('./main.server.mjs');
const { ɵSERVER_CONTEXT, renderModule, renderApplication, ɵresetCompiledComponents, ɵConsole } =
Expand Down Expand Up @@ -71,6 +73,7 @@ export async function renderPage({
return new Console();
},
},
...providers
];

assert(
Expand Down