Skip to content
This repository was archived by the owner on Nov 22, 2024. It is now read-only.

feat(schematics): add prerendering scripts to express-schematic #1206

Closed
wants to merge 11 commits into from
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import 'zone.js/dist/zone-node';

import 'reflect-metadata';
import {readFileSync, writeFileSync, existsSync, mkdirSync} from 'fs';
import {join} from 'path';

import {enableProdMode} from '@angular/core';
// Faster server renders w/ Prod mode (dev mode never needed)
enableProdMode();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this still needed since it's already defined in main.server.ts?


// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const {AppServerModuleNgFactory, LAZY_MODULE_MAP, provideModuleMap, renderModuleFactory} = require('./<%= getServerDistDirectory() %>/main');

const BROWSER_FOLDER = join(process.cwd(), '<%= getBrowserDistDirectory() %>');

// Load the index.html file containing references to your application bundle.
const index = readFileSync(join('browser', 'index.html'), 'utf8');

let previousRender = Promise.resolve();

export const ROUTES = [
'/',
// Additional pre-rendered Routes go here
];

// Iterate each route path
ROUTES.forEach(route => {
const fullPath = join(BROWSER_FOLDER, route);

// Make sure the directory structure is there
if (!existsSync(fullPath)) {
mkdirSync(fullPath);
}

// Writes rendered HTML to index.html, replacing the file if it already exists.
previousRender = previousRender.then(_ => renderModuleFactory(AppServerModuleNgFactory, {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there is an error here, would this throw with unhandled promise rejection?

Copy link
Contributor Author

@MarkPieszak MarkPieszak Sep 3, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it actually throws it regardless, which is great! For example a (<any>window).test = '123'; thrown in an app components code (that will pass ng build, etc) will throw an error when trying to build the prerender like shown below.

image

document: index,
url: route,
extraProviders: [
provideModuleMap(LAZY_MODULE_MAP)
]
})).then(html => writeFileSync(join(fullPath, 'index.html'), html));
});
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,7 @@
"dom"
]
},
"include": ["<%= stripTsExtension(serverFileName) %>.ts"]
"include": [
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are missing the closing ]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, fixed!

"<%= stripTsExtension(serverFileName) %>.ts",
"<%= stripTsExtension(prerenderFileName) %>.ts"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be excluded when using skipPrerender?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cannot find anywhere were an existing the server.tsconfig is being modified, since when not using skipUniversal option the tsconfig will be created by the @schematics/angular universal schematic.

noop() : externalSchematic('@schematics/angular', 'universal', options),

https://github.com/angular/angular-cli/blob/master/packages/schematics/angular/universal/files/root/__tsconfigFileName__.json.template

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ module.exports = {
mode: 'none',
entry: {
// This is our Express server for Dynamic universal
server: './<%= stripTsExtension(serverFileName) %>.ts'
server: './<%= stripTsExtension(serverFileName) %>.ts',
// This is our script for Static Prerendering
prerender: './<%= stripTsExtension(prerenderFileName) %>.ts'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be excluded when using skipPrerender?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it should, let me know if I did the templating incorrectly, I couldn't find many good examples within other CLI schematics! :)

<%= skipPrerender ? '' : '"<%= stripTsExtension(prerenderFileName) %>.ts"' %>

},
externals: {
'./<%= getServerDistDirectory() %>/main': 'require("./server/main")'
Expand Down
10 changes: 9 additions & 1 deletion modules/express-engine/schematics/install/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,12 @@ function addDependenciesAndScripts(options: UniversalOptions): Rule {
pkg.scripts['compile:server'] = options.webpack ?
'webpack --config webpack.server.config.js --progress --colors' :
`tsc -p ${serverFileName}.tsconfig.json`;

pkg.scripts['serve:ssr'] = `node dist/${serverFileName}`;
pkg.scripts['build:prerender'] =
// tslint:disable-next-line: max-line-length
`npm run build:client-and-server-bundles && npm run compile:server && npm run generate:prerender`,
pkg.scripts['generate:prerender'] = `node dist/${options.prerenderFileName}`,
pkg.scripts['build:ssr'] = 'npm run build:client-and-server-bundles && npm run compile:server';
pkg.scripts['build:client-and-server-bundles'] =
// tslint:disable:max-line-length
Expand Down Expand Up @@ -194,12 +199,14 @@ function addExports(options: UniversalOptions): Rule {
const mainSourceFile = getTsSourceFile(host, mainPath);
let mainText = getTsSourceText(host, mainPath);
const mainRecorder = host.beginUpdate(mainPath);
const renderModuleFactoryExport = generateExport(mainSourceFile, ['renderModuleFactory'],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this target version 8 or 9? Is so probable we should change renderModuleFactory to renderModule

Some more context here: angular/angular-cli#15517

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually regarding the later two comments if we wait for version 9 you don’t need to add them because they will be adde by default in the cli version 9.

Also, for existing project I will do a migration to add these exports

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually regarding the later two comments if we wait for version 9 you don’t need to add them because they will be adde by default in the cli version 9.

Also, for existing project I will do a migration to add these exports

'@angular/platform-server');
const expressEngineExport = generateExport(mainSourceFile, ['ngExpressEngine'],
'@nguniversal/express-engine');
const moduleMapExport = generateExport(mainSourceFile, ['provideModuleMap'],
'@nguniversal/module-map-ngfactory-loader');
const exports = findNodes(mainSourceFile, ts.SyntaxKind.ExportDeclaration);
const addedExports = `\n${expressEngineExport}\n${moduleMapExport}\n`;
const addedExports = `\n${expressEngineExport}\n${moduleMapExport}\n${renderModuleFactoryExport}\n`;
const exportChange = insertAfterLastOccurrence(exports, addedExports, mainText,
0) as InsertChange;

Expand All @@ -221,6 +228,7 @@ export default function (options: UniversalOptions): Rule {

const rootSource = apply(url('./files/root'), [
options.skipServer ? filter(path => !path.startsWith('__serverFileName')) : noop(),
options.skipPrerender ? filter(path => !path.startsWith('__prerenderFileName')) : noop(),
options.webpack ?
filter(path => !path.includes('tsconfig')) : filter(path => !path.startsWith('webpack')),
template({
Expand Down
5 changes: 5 additions & 0 deletions modules/express-engine/schematics/install/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
"format": "path",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

skipPrerender doesn't seem to be defined in the schema.

"description": "The name of the test entry-point file."
},
"prerenderFileName": {
"type": "string",
"default": "prerender.ts",
"description": "The name of the Prerender server file."
},
"serverFileName": {
"type": "string",
"default": "server.ts",
Expand Down
8 changes: 8 additions & 0 deletions modules/express-engine/schematics/install/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ export interface Schema {
* The name of the test entry-point file.
*/
test?: string;
/**
* The name of the Prerender script file.
*/
prerenderFileName?: string;
/**
* The name of the Express server file.
*/
Expand Down Expand Up @@ -55,6 +59,10 @@ export interface Schema {
* Skip installing dependency packages.
*/
skipInstall?: boolean;
/**
* Skip adding Prerender script file.
*/
skipPrerender?: boolean;
/**
* Skip adding Express server file.
*/
Expand Down