Skip to content

Commit

Permalink
fix(@angular/build): Providing a DI token to dev server for SSR
Browse files Browse the repository at this point in the history
Add configuration for add custom DI token.

issue: angular#26323
  • Loading branch information
klerick committed Jun 8, 2024
1 parent fbc6eb3 commit 26f13b4
Show file tree
Hide file tree
Showing 8 changed files with 44 additions and 7 deletions.
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

0 comments on commit 26f13b4

Please sign in to comment.