in modules/common/clover/server/src/server-engine.ts [38:141]
async render(options: RenderOptions): Promise<string> {
const { pathname, origin } = new URL(options.url);
const prerenderedSnapshot = await this.getPrerenderedSnapshot(options.publicPath, pathname);
if (prerenderedSnapshot) {
return prerenderedSnapshot;
}
let htmlContent = await this.getHtmlTemplate(
options.publicPath,
pathname,
options.htmlFilename,
);
const inlineCriticalCss = options.inlineCriticalCss !== false;
const customResourceLoader = new CustomResourceLoader(
origin,
options.publicPath,
this.resourceLoaderCache,
);
let dom: JSDOM | undefined;
if (inlineCriticalCss) {
// Workaround for https://github.com/GoogleChromeLabs/critters/issues/64
htmlContent = htmlContent.replace(
/ media="print" onload="this\.media='all'"><noscript><link .+?><\/noscript>/g,
'>',
);
}
// JSDOM doesn't support type=module
// https://github.com/jsdom/jsdom/issues/2475
htmlContent = htmlContent.replace(/ type="module"/g, '');
try {
dom = new JSDOM(htmlContent, {
runScripts: 'dangerously',
resources: customResourceLoader,
url: options.url,
referrer: options.headers?.referrer as string | undefined,
userAgent: options.headers?.['user-agent'] as string | undefined,
beforeParse: (window) => {
augmentWindowWithStubs(window);
window.ngRenderMode = true;
},
});
const doc = dom.window.document;
// 60s timeout.
const stablizationTimeout = setTimeout(() => {
throw new Error('Angular application failed to stablize after in time.');
}, 60000);
const ngRenderMode = await new Promise<NGRenderModeAPI>((resolve) => {
const interval = setInterval(() => {
const ngDOMMode = dom?.window.ngRenderMode as NGRenderMode;
if (ngDOMMode && typeof ngDOMMode === 'object') {
// Poll until ngDOMMode is an object.
clearTimeout(stablizationTimeout);
clearInterval(interval);
resolve(ngDOMMode);
}
}, 30);
});
await ngRenderMode.getWhenStable();
doc.querySelector('[ng-version]')?.setAttribute('ng-clover', '');
// Add Angular state
const state = ngRenderMode.getSerializedState();
if (state) {
const script = doc.createElement('script');
script.id = `${ngRenderMode.appId}-state`;
script.setAttribute('type', 'application/json');
script.textContent = state;
doc.body.appendChild(script);
}
const content = dom.serialize();
if (!inlineCriticalCss) {
return content;
}
const baseHref = doc.querySelector('base[href]')?.getAttribute('href') ?? '';
const {
content: contentWithInlineCSS,
warnings,
errors,
} = await this.inlineCriticalCssProcessor.process(content, {
outputPath: path.join(options.publicPath, baseHref),
});
// eslint-disable-next-line no-console
warnings?.forEach((m) => console.warn(m));
// eslint-disable-next-line no-console
errors?.forEach((m) => console.error(m));
return contentWithInlineCSS;
} finally {
dom?.window.close();
}
}