diff --git a/src/main.server.aot.ts b/src/main.server.aot.ts new file mode 100644 index 0000000000..84374ccb78 --- /dev/null +++ b/src/main.server.aot.ts @@ -0,0 +1,4 @@ +import { startServer } from './server'; +import { ServerAppModuleNgFactory } from './modules/app/server-app.module.ngfactory'; + +startServer(ServerAppModuleNgFactory); diff --git a/src/main.server.ts b/src/main.server.ts index be2d89fbf3..b484be3be4 100644 --- a/src/main.server.ts +++ b/src/main.server.ts @@ -1,134 +1,4 @@ -import 'zone.js/dist/zone-node'; -import 'reflect-metadata'; -import 'rxjs/Rx'; - -import * as fs from 'fs'; -import * as pem from 'pem'; -import * as https from 'https'; -import * as morgan from 'morgan'; -import * as express from 'express'; -import * as bodyParser from 'body-parser'; -import * as compression from 'compression'; -import * as cookieParser from 'cookie-parser'; - -import { enableProdMode } from '@angular/core'; - -import { ngExpressEngine } from '@nguniversal/express-engine'; - +import { startServer } from './server'; import { ServerAppModule } from './modules/app/server-app.module'; -import { ROUTES } from './routes'; -import { ENV_CONFIG } from './config'; - -const app = express(); - -const port = ENV_CONFIG.ui.port ? ENV_CONFIG.ui.port : 80; - -if (ENV_CONFIG.production) { - enableProdMode(); - app.use(compression()); -} - -app.use(morgan('dev')); - -app.use(cookieParser()); -app.use(bodyParser.json()); - -app.engine('html', ngExpressEngine({ - bootstrap: ServerAppModule -})); - -app.set('view engine', 'html'); -app.set('views', 'src'); - -function cacheControl(req, res, next) { - // instruct browser to revalidate - res.header('Cache-Control', ENV_CONFIG.cache.control || 'max-age=60'); - next(); -} - -app.use('/', cacheControl, express.static('dist', { index: false })); - -// TODO: either remove or update mock backend -// app.get('/data.json', serverApi); -// app.use('/api', createMockApi()); - -function ngApp(req, res) { - - function onHandleError(parentZoneDelegate, currentZone, targetZone, error) { - console.warn('Error in SSR, serving for direct CSR'); - res.sendFile('index.csr.html', { root: './src' }); - } - - if (ENV_CONFIG.universal.preboot) { - Zone.current.fork({ name: 'CSR fallback', onHandleError }).run(() => { - res.render('../dist/index', { - req, - res, - preboot: ENV_CONFIG.universal.preboot, - async: ENV_CONFIG.universal.async, - time: ENV_CONFIG.universal.time, - baseUrl: ENV_CONFIG.ui.nameSpace, - originUrl: ENV_CONFIG.ui.baseUrl, - requestUrl: req.originalUrl - }); - }); - } else { - console.log('Universal off, serving for direct CSR'); - res.sendFile('index.csr.html', { root: './src' }); - } -} - -ROUTES.forEach((route: string) => { - app.get(route, ngApp); -}); - -function serverStarted() { - console.log(`[${new Date().toTimeString()}] Listening at ${ENV_CONFIG.ui.baseUrl}`); -} - -function createHttpsServer(keys) { - https.createServer({ - key: keys.serviceKey, - cert: keys.certificate - }, app).listen(port, ENV_CONFIG.ui.host, () => { - serverStarted(); - }); -} - -if (ENV_CONFIG.ui.ssl) { - let serviceKey; - try { - serviceKey = fs.readFileSync('./config/ssl/key.pem'); - } catch (e) { - console.warn('Service key not found at ./config/ssl/key.pem'); - } - - let certificate; - try { - certificate = fs.readFileSync('./config/ssl/cert.pem'); - } catch (e) { - console.warn('Certificate not found at ./config/ssl/key.pem'); - } - - if (serviceKey && certificate) { - createHttpsServer({ - serviceKey: serviceKey, - certificate: certificate - }); - } else { - - process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; - - pem.createCertificate({ - days: 1, - selfSigned: true - }, (error, keys) => { - createHttpsServer(keys); - }); - } -} else { - app.listen(port, ENV_CONFIG.ui.host, () => { - serverStarted(); - }); -} +startServer(ServerAppModule); diff --git a/src/server.ts b/src/server.ts new file mode 100644 index 0000000000..91229a7c78 --- /dev/null +++ b/src/server.ts @@ -0,0 +1,133 @@ +import 'zone.js/dist/zone-node'; +import 'reflect-metadata'; +import 'rxjs/Rx'; + +import * as fs from 'fs'; +import * as pem from 'pem'; +import * as https from 'https'; +import * as morgan from 'morgan'; +import * as express from 'express'; +import * as bodyParser from 'body-parser'; +import * as compression from 'compression'; +import * as cookieParser from 'cookie-parser'; + +import { enableProdMode, NgModuleFactory, Type } from '@angular/core'; + +import { ngExpressEngine } from '@nguniversal/express-engine'; + +import { ROUTES } from './routes'; +import { ENV_CONFIG } from './config'; + +export function startServer(bootstrap: Type<{}> | NgModuleFactory<{}>) { + const app = express(); + + const port = ENV_CONFIG.ui.port ? ENV_CONFIG.ui.port : 80; + + if (ENV_CONFIG.production) { + enableProdMode(); + app.use(compression()); + } + + app.use(morgan('dev')); + + app.use(cookieParser()); + app.use(bodyParser.json()); + + app.engine('html', ngExpressEngine({ + bootstrap: bootstrap + })); + + app.set('view engine', 'html'); + app.set('views', 'src'); + + function cacheControl(req, res, next) { + // instruct browser to revalidate + res.header('Cache-Control', ENV_CONFIG.cache.control || 'max-age=60'); + next(); + } + + app.use('/', cacheControl, express.static('dist', { index: false })); + +// TODO: either remove or update mock backend +// app.get('/data.json', serverApi); +// app.use('/api', createMockApi()); + + function ngApp(req, res) { + + function onHandleError(parentZoneDelegate, currentZone, targetZone, error) { + console.warn('Error in SSR, serving for direct CSR'); + res.sendFile('index.csr.html', { root: './src' }); + } + + if (ENV_CONFIG.universal.preboot) { + Zone.current.fork({ name: 'CSR fallback', onHandleError }).run(() => { + res.render('../dist/index', { + req, + res, + preboot: ENV_CONFIG.universal.preboot, + async: ENV_CONFIG.universal.async, + time: ENV_CONFIG.universal.time, + baseUrl: ENV_CONFIG.ui.nameSpace, + originUrl: ENV_CONFIG.ui.baseUrl, + requestUrl: req.originalUrl + }); + }); + } else { + console.log('Universal off, serving for direct CSR'); + res.sendFile('index.csr.html', { root: './src' }); + } + } + + ROUTES.forEach((route: string) => { + app.get(route, ngApp); + }); + + function serverStarted() { + console.log(`[${new Date().toTimeString()}] Listening at ${ENV_CONFIG.ui.baseUrl}`); + } + + function createHttpsServer(keys) { + https.createServer({ + key: keys.serviceKey, + cert: keys.certificate + }, app).listen(port, ENV_CONFIG.ui.host, () => { + serverStarted(); + }); + } + + if (ENV_CONFIG.ui.ssl) { + let serviceKey; + try { + serviceKey = fs.readFileSync('./config/ssl/key.pem'); + } catch (e) { + console.warn('Service key not found at ./config/ssl/key.pem'); + } + + let certificate; + try { + certificate = fs.readFileSync('./config/ssl/cert.pem'); + } catch (e) { + console.warn('Certificate not found at ./config/ssl/key.pem'); + } + + if (serviceKey && certificate) { + createHttpsServer({ + serviceKey: serviceKey, + certificate: certificate + }); + } else { + + process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; + + pem.createCertificate({ + days: 1, + selfSigned: true + }, (error, keys) => { + createHttpsServer(keys); + }); + } + } else { + app.listen(port, ENV_CONFIG.ui.host, () => { + serverStarted(); + }); + }} diff --git a/src/tsconfig.server.aot.json b/src/tsconfig.server.aot.json new file mode 100644 index 0000000000..de753624f3 --- /dev/null +++ b/src/tsconfig.server.aot.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.server.json", + "angularCompilerOptions": { + "entryModule": "./modules/app/server-app.module#ServerAppModule" + }, + "exclude": [] +} diff --git a/tsconfig.json b/tsconfig.json index 6c41b1dd67..8037c659ed 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,13 +7,13 @@ "experimentalDecorators": true, "allowSyntheticDefaultImports": true, "sourceMap": true, - "noEmit": true, "noEmitHelpers": true, "importHelpers": true, "noImplicitAny": false, "strictNullChecks": false, + "skipDefaultLibCheck": true, + "pretty": true, "baseUrl": ".", - "paths": {}, "typeRoots": [ "node_modules/@types" ], @@ -22,23 +22,23 @@ "node" ], "lib": [ + "dom", + "es6", + "es2015", "es2016", - "dom" + "es2017" ] }, "exclude": [ "node_modules", "dist", "src/**/*.spec.ts", - "src/**/*.e2e.ts" + "src/**/*.e2e.ts", + "src/main.server.aot.ts" ], "compileOnSave": false, "buildOnSave": false, "atom": { "rewriteTsconfig": false - }, - "angularCompilerOptions": { - "skipTemplateCodegen": true, - "preserveWhitespaces": false } } diff --git a/webpack.config.js b/webpack.config.js index 6ca8966591..1ba63a19cc 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,7 +1,7 @@ const webpackMerge = require('webpack-merge'); const commonPartial = require('./webpack/webpack.common'); const clientPartial = require('./webpack/webpack.client'); -const serverPartial = require('./webpack/webpack.server'); +const { getServerWebpackPartial } = require('./webpack/webpack.server'); const prodPartial = require('./webpack/webpack.prod'); const { @@ -15,6 +15,8 @@ module.exports = function(options, webpackOptions) { console.log(`Running build for ${options.client ? 'client' : 'server'} with AoT Compilation`) } + let serverPartial = getServerWebpackPartial(options.aot); + let serverConfig = webpackMerge({}, commonPartial, serverPartial, { plugins: [ getAotPlugin('server', !!options.aot) diff --git a/webpack/webpack.aot.js b/webpack/webpack.aot.js index b1d981a981..2c2366a23e 100644 --- a/webpack/webpack.aot.js +++ b/webpack/webpack.aot.js @@ -3,7 +3,7 @@ const { } = require('./helpers'); const { - AotPlugin + AngularCompilerPlugin } = require('@ngtools/webpack'); const tsconfigs = { @@ -11,6 +11,11 @@ const tsconfigs = { server: root('./src/tsconfig.server.json') }; +const aotTsconfigs = { + client: root('./src/tsconfig.browser.json'), + server: root('./src/tsconfig.server.aot.json') +}; + /** * Generates a AotPlugin for @ngtools/webpack * @@ -19,8 +24,8 @@ const tsconfigs = { * @returns {AotPlugin} Configuration of AotPlugin */ function getAotPlugin(platform, aot) { - return new AotPlugin({ - tsConfigPath: tsconfigs[platform], + return new AngularCompilerPlugin({ + tsConfigPath: aot ? aotTsconfigs[platform] : tsconfigs[platform], skipCodeGeneration: !aot }); } diff --git a/webpack/webpack.server.js b/webpack/webpack.server.js index 76d084120f..20f188dc79 100644 --- a/webpack/webpack.server.js +++ b/webpack/webpack.server.js @@ -5,15 +5,20 @@ const { } = require('./helpers'); module.exports = { - entry: root('./src/main.server.ts'), - output: { - filename: 'server.js' - }, - target: 'node', - externals: [nodeExternals({ - whitelist: [ - /@angular/, - /@ng/, - /ngx/] - })], + getServerWebpackPartial: function (aot) { + const entry = aot ? root('./src/main.server.aot.ts') : root('./src/main.server.ts'); + return { + entry: entry, + output: { + filename: 'server.js' + }, + target: 'node', + externals: [nodeExternals({ + whitelist: [ + /@angular/, + /@ng/, + /ngx/] + })], + } + } };