diff --git a/modules/builders/BUILD.bazel b/modules/builders/BUILD.bazel index 59747e2f6..49ce6d0c5 100644 --- a/modules/builders/BUILD.bazel +++ b/modules/builders/BUILD.bazel @@ -25,9 +25,12 @@ ts_library( deps = [ "//modules/common/clover/server", "//modules/common/engine", + "//modules/common/tools", "@npm//@angular-devkit/architect", "@npm//@angular-devkit/build-angular", "@npm//@angular-devkit/core", + "@npm//@angular/core", + "@npm//@angular/platform-server", "@npm//@types/browser-sync", "@npm//@types/express", "@npm//guess-parser", @@ -44,6 +47,9 @@ pkg_npm( package_name = "@nguniversal/builders", srcs = [":builders_assets"], tags = ["release"], + visibility = [ + "//integration:__subpackages__", + ], deps = [":builders"], ) diff --git a/modules/builders/src/prerender/index.ts b/modules/builders/src/prerender/index.ts index a797aaa68..dd8324b81 100644 --- a/modules/builders/src/prerender/index.ts +++ b/modules/builders/src/prerender/index.ts @@ -19,13 +19,10 @@ import * as fs from 'fs'; import ora from 'ora'; import * as path from 'path'; import Piscina from 'piscina'; -import { promisify } from 'util'; import { PrerenderBuilderOptions, PrerenderBuilderOutput } from './models'; import { getIndexOutputFile, getRoutes } from './utils'; import { RenderOptions, RenderResult } from './worker'; -export const readFile = promisify(fs.readFile); - type BuildBuilderOutput = BuilderOutput & { baseOutputPath: string; outputPaths: string[]; @@ -103,11 +100,13 @@ async function _renderUniversal( browserOptions.optimization, ); + const zonePackage = require.resolve('zone.js', { paths: [context.workspaceRoot] }); + const { baseOutputPath = '' } = serverResult; const worker = new Piscina({ filename: path.join(__dirname, 'worker.js'), - name: 'render', maxThreads: numProcesses, + workerData: { zonePackage }, }); try { @@ -134,7 +133,7 @@ async function _renderUniversal( serverBundlePath, }; - return worker.run(options, { name: 'render' }); + return worker.run(options); }), )) as RenderResult[]; let numErrors = 0; diff --git a/modules/builders/src/prerender/worker.ts b/modules/builders/src/prerender/worker.ts index de53f9da0..f2eb4ce00 100644 --- a/modules/builders/src/prerender/worker.ts +++ b/modules/builders/src/prerender/worker.ts @@ -6,8 +6,12 @@ * found in the LICENSE file at https://angular.io/license */ +import type { Type } from '@angular/core'; +import type * as platformServer from '@angular/platform-server'; +import type { ɵInlineCriticalCssProcessor } from '@nguniversal/common/tools'; import * as fs from 'fs'; import * as path from 'path'; +import { workerData } from 'worker_threads'; import { loadEsmModule } from '../utils/utils'; export interface RenderOptions { @@ -24,10 +28,18 @@ export interface RenderResult { warnings?: string[]; } +/** + * The fully resolved path to the zone.js package that will be loaded during worker initialization. + * This is passed as workerData when setting up the worker via the `piscina` package. + */ +const { zonePackage } = workerData as { + zonePackage: string; +}; + /** * Renders each route in routes and writes them to //index.html. */ -export async function render({ +async function render({ indexFile, deployUrl, minifyCss, @@ -41,31 +53,38 @@ export async function render({ const outputFolderPath = path.join(outputPath, route); const outputIndexPath = path.join(outputFolderPath, 'index.html'); - const { ɵInlineCriticalCssProcessor: InlineCriticalCssProcessor } = await loadEsmModule< - typeof import('@nguniversal/common/tools') - >('@nguniversal/common/tools'); + const { AppServerModule, renderModule } = (await import(serverBundlePath)) as { + renderModule: typeof platformServer.renderModule | undefined; + AppServerModule: Type | undefined; + }; - const { renderModule, AppServerModule } = await import(serverBundlePath); + if (!renderModule) { + throw new Error(`renderModule was not exported from: ${serverBundlePath}.`); + } + + if (!AppServerModule) { + throw new Error(`AppServerModule was not exported from: ${serverBundlePath}.`); + } const indexBaseName = fs.existsSync(path.join(outputPath, 'index.original.html')) ? 'index.original.html' : indexFile; const browserIndexInputPath = path.join(outputPath, indexBaseName); - let indexHtml = await fs.promises.readFile(browserIndexInputPath, 'utf8'); - indexHtml = indexHtml.replace( + let document = await fs.promises.readFile(browserIndexInputPath, 'utf8'); + document = document.replace( '', '\n', ); if (inlineCriticalCss) { // Workaround for https://github.com/GoogleChromeLabs/critters/issues/64 - indexHtml = indexHtml.replace( + document = document.replace( / media="print" onload="this\.media='all'">