diff --git a/aio/content/examples/universal/server.ts b/aio/content/examples/universal/server.ts index 32e2a08ec1..4b552b4b41 100644 --- a/aio/content/examples/universal/server.ts +++ b/aio/content/examples/universal/server.ts @@ -1,61 +1,66 @@ -// These are important and needed before anything else import 'zone.js/dist/zone-node'; -import 'reflect-metadata'; - -import { enableProdMode } from '@angular/core'; +import { ngExpressEngine } from '@nguniversal/express-engine'; import * as express from 'express'; import { join } from 'path'; -// Faster server renders w/ Prod mode (dev mode never needed) -enableProdMode(); +import { AppServerModule } from './src/main.server'; +import { APP_BASE_HREF } from '@angular/common'; -// Express server -const app = express(); +// The Express app is exported so that it can be used by serverless Functions. +export function app() { + const server = express(); + const distFolder = join(process.cwd(), 'dist/express-engine-ivy/browser'); -const PORT = process.env.PORT || 4000; -const DIST_FOLDER = join(process.cwd(), 'dist'); + // #docregion ngExpressEngine + server.engine('html', ngExpressEngine({ + bootstrap: AppServerModule, + })); + // #enddocregion ngExpressEngine + server.set('view engine', 'html'); + server.set('views', distFolder); -// * NOTE :: leave this as require() since this file is built Dynamically from webpack -const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main'); + // #docregion data-request + // TODO: implement data requests securely + server.get('/api/*', (req, res) => { + res.status(404).send('data requests are not supported'); + }); + // #enddocregion data-request -// Express Engine -import { ngExpressEngine } from '@nguniversal/express-engine'; -// Import module map for lazy loading -import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader'; + // #docregion static + // Serve static files from /browser + server.get('*.*', express.static(distFolder, { + maxAge: '1y' + })); + // #enddocregion static -// #docregion ngExpressEngine -app.engine('html', ngExpressEngine({ - bootstrap: AppServerModuleNgFactory, - providers: [ - provideModuleMap(LAZY_MODULE_MAP) - ] -})); -// #enddocregion ngExpressEngine + // #docregion navigation-request + // All regular routes use the Universal engine + server.get('*', (req, res) => { + res.render('index', { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] }); + }); + // #enddocregion navigation-request -app.set('view engine', 'html'); -app.set('views', join(DIST_FOLDER, 'browser')); + return server; +} -// #docregion data-request -// TODO: implement data requests securely -app.get('/api/*', (req, res) => { - res.status(404).send('data requests are not supported'); -}); -// #enddocregion data-request +function run() { + const port = process.env.PORT || 4000; -// #docregion static -// Server static files from /browser -app.get('*.*', express.static(join(DIST_FOLDER, 'browser'))); -// #enddocregion static + // Start up the Node server + const server = app(); + server.listen(port, () => { + console.log(`Node Express server listening on http://localhost:${port}`); + }); +} -// #docregion navigation-request -// All regular routes use the Universal engine -app.get('*', (req, res) => { - res.render('index', { req }); -}); -// #enddocregion navigation-request +// Webpack will replace 'require' with '__webpack_require__' +// '__non_webpack_require__' is a proxy to Node 'require' +// The below code is to ensure that the server is run only when not requiring the bundle. +declare const __non_webpack_require__: NodeRequire; +const mainModule = __non_webpack_require__.main; +if (mainModule && mainModule.filename === __filename) { + run(); +} -// Start up the Node server -app.listen(PORT, () => { - console.log(`Node server listening on http://localhost:${PORT}`); -}); +export * from './src/main.server'; diff --git a/aio/content/examples/universal/src/main.server.ts b/aio/content/examples/universal/src/main.server.ts index d7c01cde7b..10150a7181 100644 --- a/aio/content/examples/universal/src/main.server.ts +++ b/aio/content/examples/universal/src/main.server.ts @@ -1 +1,10 @@ +import { enableProdMode } from '@angular/core'; + +import { environment } from './environments/environment'; + +if (environment.production) { + enableProdMode(); +} + export { AppServerModule } from './app/app.server.module'; +export { renderModule, renderModuleFactory } from '@angular/platform-server'; diff --git a/aio/content/examples/universal/tsconfig.server.json b/aio/content/examples/universal/tsconfig.server.json index 5a3e958cb0..992b1b2a5d 100644 --- a/aio/content/examples/universal/tsconfig.server.json +++ b/aio/content/examples/universal/tsconfig.server.json @@ -1,16 +1,15 @@ { - "extends": "./tsconfig.json", + "extends": "./tsconfig.app.json", "compilerOptions": { - "outDir": "../out-tsc/app", - "baseUrl": "./", + "outDir": "./out-tsc/app-server", "module": "commonjs", - "types": [] + "types": ["node"] }, - "exclude": [ - "test.ts", - "**/*.spec.ts" + "files": [ + "src/main.server.ts", + "server.ts" ], "angularCompilerOptions": { - "entryModule": "src/app/app.server.module#AppServerModule" + "entryModule": "./src/app/app.server.module#AppServerModule" } } diff --git a/aio/content/guide/app-shell.md b/aio/content/guide/app-shell.md index cef5676605..8804980873 100644 --- a/aio/content/guide/app-shell.md +++ b/aio/content/guide/app-shell.md @@ -21,7 +21,7 @@ For an existing application, you have to manually add the `RouterModule` and def Use the CLI to automatically create the app shell. -ng generate app-shell --client-project my-app +ng generate app-shell * `client-project` takes the name of your client application. diff --git a/aio/content/guide/universal.md b/aio/content/guide/universal.md index 4521251706..b43337d8f5 100644 --- a/aio/content/guide/universal.md +++ b/aio/content/guide/universal.md @@ -33,7 +33,7 @@ To create the server-side app module, `app.server.module.ts`, run the following -ng add @nguniversal/express-engine --clientProject angular.io-example +ng add @nguniversal/express-engine @@ -53,7 +53,6 @@ tsconfig.app.json TypeScript client configuration tsconfig.server.json * TypeScript server configuration tsconfig.spec.json TypeScript spec configuration package.json npm configuration -webpack.server.config.js * webpack server configuration The files marked with `*` are new and not in the original tutorial sample. @@ -152,7 +151,7 @@ The sample web server for this guide is based on the popular [Express](https://e
- **Note:** _Any_ web server technology can serve a Universal app as long as it can call Universal's `renderModuleFactory()` function. + **Note:** _Any_ web server technology can serve a Universal app as long as it can call Universal's `renderModule()` function. The principles and decision points discussed here apply to any web server technology.
@@ -162,15 +161,15 @@ server implementations of the DOM, `XMLHttpRequest`, and other low-level feature The server ([Node Express](https://expressjs.com/) in this guide's example) passes client requests for application pages to the NgUniversal `ngExpressEngine`. Under the hood, this -calls Universal's `renderModuleFactory()` function, while providing caching and other helpful utilities. +calls Universal's `renderModule()` function, while providing caching and other helpful utilities. -The `renderModuleFactory()` function takes as inputs a *template* HTML page (usually `index.html`), +The `renderModule()` function takes as inputs a *template* HTML page (usually `index.html`), an Angular *module* containing components, and a *route* that determines which components to display. The route comes from the client's request to the server. Each request results in the appropriate view for the requested route. -The `renderModuleFactory()` function renders the view within the `` tag of the template, +The `renderModule()` function renders the view within the `` tag of the template, creating a finished HTML page for the client. Finally, the server returns the rendered page to the client. @@ -263,7 +262,7 @@ The important bit in the `server.ts` file is the `ngExpressEngine()` function. -The `ngExpressEngine()` function is a wrapper around Universal's `renderModuleFactory()` function which turns a client's +The `ngExpressEngine()` function is a wrapper around Universal's `renderModule()` function which turns a client's requests into server-rendered HTML pages. * The first parameter is `AppServerModule`. @@ -282,7 +281,7 @@ which then forwards it to the client in the HTTP response.
- **Note:** These wrappers help hide the complexity of the `renderModuleFactory()` function. There are more wrappers + **Note:** These wrappers help hide the complexity of the `renderModule()` function. There are more wrappers for different backend technologies at the [Universal repository](https://github.com/angular/universal).