diff --git a/.travis.yml b/.travis.yml index d60fd6f8b00..f29beadec9f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,6 +20,7 @@ jobs: - env: "MODE=e2e" - env: "MODE=aot" - env: "MODE=payload" + - env: "MODE=prerender" - env: "MODE=closure-compiler" - env: "MODE=saucelabs_required" - env: "MODE=browserstack_required" diff --git a/scripts/ci/build-and-test.sh b/scripts/ci/build-and-test.sh index bd6f3f0be00..4de825b5051 100755 --- a/scripts/ci/build-and-test.sh +++ b/scripts/ci/build-and-test.sh @@ -41,6 +41,8 @@ elif is_closure_compiler; then ./scripts/closure-compiler/build-devapp-bundle.sh elif is_unit; then $(npm bin)/gulp ci:test +elif is_prerender; then + ./scripts/ci/prerender.sh fi # Upload coverage results if those are present. diff --git a/scripts/ci/prerender.sh b/scripts/ci/prerender.sh new file mode 100755 index 00000000000..40ae3167000 --- /dev/null +++ b/scripts/ci/prerender.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +### Pre-renders the app under src/universal-app with platform-server and dumps the +### output to stdout. + +# Go to the project root directory +cd $(dirname $0)/../.. + +# Build the @angular/material package and copy it into node_modules. +# This is a workaround for https://github.com/angular/angular/issues/12249 +rm -rf ./node_modules/@angular/material +gulp material:build-release +cp -r ./dist/releases/material ./node_modules/@angular/ + +# Compile the kitchen sink app (the generated ngfactories are consumed by the prerender script) +$(npm bin)/ngc -p src/universal-app/tsconfig-ngc.json + +# Run the prerender script. +# This does not use ts-node because some of the generated ngfactory.ts files are written into a +# directory called "node_modules"; ts-node intentionally will not treat any file inside of a +# "node_modules" directory as TypeScript (with the opinion that node_modules should only contain JS) +$(npm bin)/tsc -p src/universal-app/tsconfig-prerender.json +node ./src/universal-app/dist/prerender.js diff --git a/scripts/ci/sources/mode.sh b/scripts/ci/sources/mode.sh index d37725919d9..b4090a2f202 100644 --- a/scripts/ci/sources/mode.sh +++ b/scripts/ci/sources/mode.sh @@ -24,3 +24,7 @@ is_payload() { is_unit() { [[ "$MODE" = saucelabs_required || "$MODE" = browserstack_required ]] } + +is_prerender() { + [[ "$MODE" = prerender ]] +} diff --git a/src/universal-app/index.html b/src/universal-app/index.html new file mode 100644 index 00000000000..91f2bbcc3fe --- /dev/null +++ b/src/universal-app/index.html @@ -0,0 +1,11 @@ + + + + + Angular Material Univeral Kitchen Sink Test + + + + Loading... + + diff --git a/src/universal-app/kitchen-sink/kitchen-sink.css b/src/universal-app/kitchen-sink/kitchen-sink.css new file mode 100644 index 00000000000..c160ab1e002 --- /dev/null +++ b/src/universal-app/kitchen-sink/kitchen-sink.css @@ -0,0 +1,3 @@ +.kitchen-sink { + color: lightsteelblue; +} diff --git a/src/universal-app/kitchen-sink/kitchen-sink.html b/src/universal-app/kitchen-sink/kitchen-sink.html new file mode 100644 index 00000000000..b0306d06ac6 --- /dev/null +++ b/src/universal-app/kitchen-sink/kitchen-sink.html @@ -0,0 +1,15 @@ +

Kitchen sink app

+ +

Buttons

+ + + + + + + +Google +Google +Google +Google +Google diff --git a/src/universal-app/kitchen-sink/kitchen-sink.ts b/src/universal-app/kitchen-sink/kitchen-sink.ts new file mode 100644 index 00000000000..31ad43339a4 --- /dev/null +++ b/src/universal-app/kitchen-sink/kitchen-sink.ts @@ -0,0 +1,30 @@ +import {Component, NgModule} from '@angular/core'; +import {ServerModule} from '@angular/platform-server'; +import {BrowserModule} from '@angular/platform-browser'; +import {MdButtonModule} from '@angular/material'; + + +@Component({ + selector: 'kitchen-sink', + templateUrl: './kitchen-sink.html', + styleUrls: ['./kitchen-sink.css'], +}) +export class KitchenSink { } + + +@NgModule({ + imports: [ + BrowserModule.withServerTransition({appId: 'kitchen-sink'}), + MdButtonModule, + ], + bootstrap: [KitchenSink], + declarations: [KitchenSink], +}) +export class KitchenSinkClientModule { } + + +@NgModule({ + imports: [KitchenSinkClientModule, ServerModule], + bootstrap: [KitchenSink], +}) +export class KitchenSinkServerModule { } diff --git a/src/universal-app/prerender.ts b/src/universal-app/prerender.ts new file mode 100644 index 00000000000..f3aceac66d0 --- /dev/null +++ b/src/universal-app/prerender.ts @@ -0,0 +1,15 @@ +import 'reflect-metadata'; +require('zone.js/dist/zone-node.js'); + +import {enableProdMode} from '@angular/core'; +import {renderModuleFactory} from '@angular/platform-server'; +import {KitchenSinkServerModuleNgFactory} from './dist/aot/kitchen-sink/kitchen-sink.ngfactory'; +import {readFileSync} from 'fs-extra'; + +enableProdMode(); + +const result = renderModuleFactory(KitchenSinkServerModuleNgFactory, { + document: readFileSync('src/universal-app/index.html', 'utf-8') +}); + +result.then(html => console.log(html)); diff --git a/src/universal-app/tsconfig-ngc.json b/src/universal-app/tsconfig-ngc.json new file mode 100644 index 00000000000..df92f94b317 --- /dev/null +++ b/src/universal-app/tsconfig-ngc.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "module": "es2015", + "moduleResolution": "node", + "target": "es6", + "noImplicitAny": false, + "sourceMap": false, + "experimentalDecorators": true, + "outDir": "dist", + "declaration": true, + "typeRoots": ["node_modules/@types"] + }, + "exclude": [ + "node_modules", + "dist", + "prerender.ts" + ], + "angularCompilerOptions": { + "annotationsAs": "static fields", + "annotateForClosureCompiler": true, + "genDir": "./dist/aot" + } +} diff --git a/src/universal-app/tsconfig-prerender.json b/src/universal-app/tsconfig-prerender.json new file mode 100644 index 00000000000..26d012ee682 --- /dev/null +++ b/src/universal-app/tsconfig-prerender.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "lib": ["es2015", "dom"], + "target": "es6", + "module": "commonjs", + "moduleResolution": "node", + "outDir": "dist", + "noEmitOnError": true, + "noImplicitAny": true, + "sourceMap": false, + "experimentalDecorators": true, + "types": ["node"] + }, + "files": ["prerender.ts"] +}