├── .editorconfig ├── .gitignore ├── README.md ├── angular.json ├── package.json ├── server.ts ├── src ├── app │ ├── app.component.ts │ ├── app.module.ts │ ├── app.server.module.ts │ ├── list.component.ts │ ├── types.ts │ └── upvoter.component.ts ├── assets │ └── .gitkeep ├── browserslist ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── favicon.ico ├── index.html ├── main.server.ts ├── main.ts ├── polyfills.ts ├── styles.css ├── tsconfig.app.json └── tsconfig.server.json ├── tsconfig.json └── webpack.server.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | /node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | 20 | # IDE - VSCode 21 | .vscode/* 22 | !.vscode/settings.json 23 | !.vscode/tasks.json 24 | !.vscode/launch.json 25 | !.vscode/extensions.json 26 | 27 | # misc 28 | /.sass-cache 29 | /connect.lock 30 | /coverage 31 | /libpeerconnection.log 32 | npm-debug.log 33 | yarn-error.log 34 | testem.log 35 | /typings 36 | 37 | # System Files 38 | .DS_Store 39 | Thumbs.db 40 | 41 | package-lock.json 42 | yarn.lock 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Apollo-Angular with SSR and Store Rehydration 2 | 3 | Includes: 4 | 5 | - mutation 6 | - optimistic response 7 | - refetch 8 | 9 | ## Installation 10 | 11 | ```bash 12 | yarn 13 | ``` 14 | 15 | ## How to run 16 | 17 | ```bash 18 | yarn start 19 | ``` 20 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "apollo-angular-ssr": { 7 | "root": "", 8 | "sourceRoot": "src", 9 | "projectType": "application", 10 | "prefix": "app", 11 | "schematics": { 12 | "@schematics/angular:component": { 13 | "inlineTemplate": true, 14 | "inlineStyle": true, 15 | "spec": false 16 | }, 17 | "@schematics/angular:class": { 18 | "spec": false 19 | }, 20 | "@schematics/angular:directive": { 21 | "spec": false 22 | }, 23 | "@schematics/angular:guard": { 24 | "spec": false 25 | }, 26 | "@schematics/angular:module": { 27 | "spec": false 28 | }, 29 | "@schematics/angular:pipe": { 30 | "spec": false 31 | }, 32 | "@schematics/angular:service": { 33 | "spec": false 34 | } 35 | }, 36 | "architect": { 37 | "build": { 38 | "builder": "@angular-devkit/build-angular:browser", 39 | "options": { 40 | "outputPath": "dist/browser", 41 | "index": "src/index.html", 42 | "main": "src/main.ts", 43 | "polyfills": "src/polyfills.ts", 44 | "tsConfig": "src/tsconfig.app.json", 45 | "assets": [ 46 | "src/favicon.ico", 47 | "src/assets" 48 | ], 49 | "styles": [ 50 | "src/styles.css" 51 | ], 52 | "scripts": [] 53 | }, 54 | "configurations": { 55 | "production": { 56 | "fileReplacements": [ 57 | { 58 | "replace": "src/environments/environment.ts", 59 | "with": "src/environments/environment.prod.ts" 60 | } 61 | ], 62 | "optimization": true, 63 | "outputHashing": "all", 64 | "sourceMap": false, 65 | "extractCss": true, 66 | "namedChunks": false, 67 | "aot": true, 68 | "extractLicenses": true, 69 | "vendorChunk": false, 70 | "buildOptimizer": true 71 | } 72 | } 73 | }, 74 | "server": { 75 | "builder": "@angular-devkit/build-angular:server", 76 | "options": { 77 | "outputPath": "dist/server", 78 | "main": "src/main.server.ts", 79 | "tsConfig": "src/tsconfig.server.json" 80 | } 81 | }, 82 | "serve": { 83 | "builder": "@angular-devkit/build-angular:dev-server", 84 | "options": { 85 | "browserTarget": "apollo-angular-ssr:build" 86 | }, 87 | "configurations": { 88 | "production": { 89 | "browserTarget": "apollo-angular-ssr:build:production" 90 | } 91 | } 92 | } 93 | } 94 | } 95 | }, 96 | "defaultProject": "apollo-angular-ssr" 97 | } 98 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "apollo-angular-ssr", 3 | "version": "0.0.0", 4 | "author": "Kamil Kisiela ", 5 | "scripts": { 6 | "start": "npm run ssr", 7 | "ssr": "npm run build:ssr && npm run serve:ssr", 8 | "build:ssr": "npm run build:client-and-server-bundles && npm run webpack:server", 9 | "serve:ssr": "node dist/server", 10 | "build:client-and-server-bundles": "ng build --prod && ng run apollo-angular-ssr:server", 11 | "webpack:server": "webpack --config webpack.server.config.js --progress", 12 | "serve": "ng serve" 13 | }, 14 | "private": true, 15 | "dependencies": { 16 | "@angular/animations": "6.1.4", 17 | "@angular/common": "6.1.4", 18 | "@angular/compiler": "6.1.4", 19 | "@angular/core": "6.1.4", 20 | "@angular/forms": "6.1.4", 21 | "@angular/http": "6.1.4", 22 | "@angular/platform-browser": "6.1.4", 23 | "@angular/platform-browser-dynamic": "6.1.4", 24 | "@angular/router": "6.1.4", 25 | "apollo-angular": "1.4.1", 26 | "apollo-angular-link-http": "1.3.1", 27 | "apollo-cache-inmemory": "1.3.5", 28 | "apollo-client": "2.4.2", 29 | "core-js": "2.5.7", 30 | "graphql": "^0.13.2", 31 | "graphql-tag": "2.10.0", 32 | "rxjs": "6.3.3", 33 | "zone.js": "0.8.26" 34 | }, 35 | "devDependencies": { 36 | "@angular-devkit/build-angular": "0.7.5", 37 | "@angular/cli": "6.1.5", 38 | "@angular/compiler-cli": "6.1.4", 39 | "@angular/language-service": "6.1.4", 40 | "@angular/platform-server": "6.1.4", 41 | "@nguniversal/express-engine": "6.1.0", 42 | "@nguniversal/module-map-ngfactory-loader": "6.1.0", 43 | "@types/express": "4.16.0", 44 | "@types/node": "8.9.4", 45 | "ts-loader": "4.5.0", 46 | "ts-node": "5.0.1", 47 | "tslint": "5.9.1", 48 | "typescript": "2.9.2", 49 | "webpack-command": "0.4.1" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /server.ts: -------------------------------------------------------------------------------- 1 | // These are important and needed before anything else 2 | import 'zone.js/dist/zone-node'; 3 | import 'reflect-metadata'; 4 | 5 | import { enableProdMode } from '@angular/core'; 6 | 7 | import * as express from 'express'; 8 | import { join } from 'path'; 9 | 10 | // Faster server renders w/ Prod mode (dev mode never needed) 11 | enableProdMode(); 12 | 13 | // Express server 14 | const app = express(); 15 | 16 | const PORT = process.env.PORT || 4000; 17 | const DIST_FOLDER = join(process.cwd(), 'dist'); 18 | 19 | // * NOTE :: leave this as require() since this file is built Dynamically from webpack 20 | const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main'); 21 | 22 | // Express Engine 23 | import { ngExpressEngine } from '@nguniversal/express-engine'; 24 | // Import module map for lazy loading 25 | import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader'; 26 | 27 | app.engine('html', ngExpressEngine({ 28 | bootstrap: AppServerModuleNgFactory, 29 | providers: [ 30 | provideModuleMap(LAZY_MODULE_MAP) 31 | ] 32 | })); 33 | 34 | app.set('view engine', 'html'); 35 | app.set('views', join(DIST_FOLDER, 'browser')); 36 | 37 | // Server static files from /browser 38 | app.get('*.*', express.static(join(DIST_FOLDER, 'browser'))); 39 | 40 | // All regular routes use the Universal engine 41 | app.get('*', (req, res) => { 42 | res.render('index', { req }); 43 | }); 44 | 45 | // Start up the Node server 46 | app.listen(PORT, () => { 47 | console.log(`Node server listening on http://localhost:${PORT}`); 48 | }); 49 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | template: ` 6 | 7 | `, 8 | }) 9 | export class AppComponent {} 10 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule, PLATFORM_ID, Inject } from '@angular/core'; 2 | import { isPlatformBrowser } from '@angular/common'; 3 | import { HttpClientModule } from '@angular/common/http'; 4 | import { 5 | BrowserModule, 6 | BrowserTransferStateModule, 7 | TransferState, 8 | makeStateKey, 9 | } from '@angular/platform-browser'; 10 | // Apollo 11 | import { ApolloModule, Apollo } from 'apollo-angular'; 12 | import { 13 | HttpLinkModule, 14 | HttpLink, 15 | HttpLinkHandler, 16 | } from 'apollo-angular-link-http'; 17 | import { InMemoryCache, NormalizedCacheObject } from 'apollo-cache-inmemory'; 18 | 19 | import { AppComponent } from './app.component'; 20 | import { ListComponent } from './list.component'; 21 | import { UpvoterComponent } from './upvoter.component'; 22 | 23 | // GraphiQL: https://launchpad.graphql.com/1jzxrj179 24 | const uri = 'https://1jzxrj179.lp.gql.zone/graphql'; 25 | 26 | const STATE_KEY = makeStateKey('apollo.state'); 27 | 28 | @NgModule({ 29 | declarations: [AppComponent, ListComponent, UpvoterComponent], 30 | imports: [ 31 | BrowserModule.withServerTransition({ appId: 'example' }), 32 | HttpClientModule, 33 | BrowserTransferStateModule, 34 | HttpLinkModule, 35 | ApolloModule, 36 | ], 37 | providers: [], 38 | bootstrap: [AppComponent], 39 | }) 40 | export class AppModule { 41 | cache: InMemoryCache; 42 | link: HttpLinkHandler; 43 | 44 | constructor( 45 | private readonly apollo: Apollo, 46 | private readonly transferState: TransferState, 47 | private readonly httpLink: HttpLink, 48 | @Inject(PLATFORM_ID) readonly platformId: Object 49 | ) { 50 | // tells if it's browser or server 51 | const isBrowser = isPlatformBrowser(platformId); 52 | 53 | this.cache = new InMemoryCache(); 54 | this.link = this.httpLink.create({ uri }); 55 | 56 | this.apollo.create({ 57 | link: this.link, 58 | cache: this.cache, 59 | ...(isBrowser 60 | ? { 61 | // queries with `forceFetch` enabled will be delayed 62 | ssrForceFetchDelay: 200, 63 | } 64 | : { 65 | // avoid to run twice queries with `forceFetch` enabled 66 | ssrMode: true, 67 | }), 68 | }); 69 | 70 | if (isBrowser) { 71 | this.onBrowser(); 72 | } else { 73 | this.onServer(); 74 | } 75 | } 76 | 77 | onServer() { 78 | // serializes the cache and puts it under a key 79 | this.transferState.onSerialize(STATE_KEY, () => this.cache.extract()); 80 | } 81 | 82 | onBrowser() { 83 | // reads the serialized cache 84 | const state = this.transferState.get( 85 | STATE_KEY, 86 | null, 87 | ); 88 | // and puts it in the Apollo 89 | this.cache.restore(state); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/app/app.server.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { 3 | ServerModule, 4 | ServerTransferStateModule, 5 | } from '@angular/platform-server'; 6 | import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader'; 7 | 8 | import { AppModule } from './app.module'; 9 | import { AppComponent } from './app.component'; 10 | 11 | @NgModule({ 12 | imports: [ 13 | AppModule, 14 | ServerModule, 15 | ServerTransferStateModule, 16 | ModuleMapLoaderModule, 17 | ], 18 | bootstrap: [AppComponent], 19 | }) 20 | export class AppServerModule {} 21 | -------------------------------------------------------------------------------- /src/app/list.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { Apollo, QueryRef } from 'apollo-angular'; 3 | import { Observable } from 'rxjs'; 4 | import { map } from 'rxjs/operators'; 5 | import gql from 'graphql-tag'; 6 | 7 | import { Post, Query } from './types'; 8 | 9 | @Component({ 10 | selector: 'app-list', 11 | template: ` 12 |
    13 |
  • 14 | {{post.title}} by {{post.author.firstName}} {{post.author.lastName}} 15 | ({{post.votes}} votes) 16 | 17 |
  • 18 |
19 | 20 | `, 21 | }) 22 | export class ListComponent implements OnInit { 23 | postsRef: QueryRef; 24 | posts: Observable; 25 | 26 | constructor(private readonly apollo: Apollo) {} 27 | 28 | ngOnInit() { 29 | this.postsRef = this.apollo.watchQuery({ 30 | query: gql` 31 | query allPosts { 32 | posts { 33 | id 34 | title 35 | votes 36 | author { 37 | id 38 | firstName 39 | lastName 40 | } 41 | } 42 | } 43 | `, 44 | }); 45 | 46 | this.posts = this.postsRef.valueChanges.pipe( 47 | map(result => result.data.posts), 48 | ); 49 | } 50 | 51 | refresh() { 52 | this.postsRef.refetch(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/app/types.ts: -------------------------------------------------------------------------------- 1 | export type Author = { 2 | id: number; 3 | firstName: string; 4 | lastName: string; 5 | } 6 | 7 | export type Post = { 8 | id: number; 9 | title: string; 10 | votes: number; 11 | } 12 | 13 | export type Query = { 14 | posts: Post[]; 15 | } 16 | -------------------------------------------------------------------------------- /src/app/upvoter.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | import { Apollo } from 'apollo-angular'; 3 | import gql from 'graphql-tag'; 4 | 5 | @Component({ 6 | selector: 'app-upvoter', 7 | template: ` 8 | 11 | ` 12 | }) 13 | export class UpvoterComponent { 14 | @Input() postId: number; 15 | @Input() votes: number; 16 | 17 | constructor(private readonly apollo: Apollo) {} 18 | 19 | upvote() { 20 | this.apollo.mutate({ 21 | mutation: gql` 22 | mutation upvotePost($postId: Int!) { 23 | upvotePost(postId: $postId) { 24 | id 25 | votes 26 | } 27 | } 28 | `, 29 | variables: { 30 | postId: this.postId, 31 | }, 32 | // Apollo treats the optimisticResponse as a result of the mutation 33 | // and updates the cache 34 | // when http call is done and we receive a response 35 | // Apollo uses the real response as a result of mutation 36 | // and updates the cache 37 | // 38 | // thanks to this mechanism, you see the result of mutation right after you make an action 39 | // without waiting for http request to finish 40 | // The app feels instant 41 | optimisticResponse: { 42 | upvotePost: { 43 | id: this.postId, 44 | votes: this.votes + 1, 45 | __typename: 'Post' 46 | } 47 | }, 48 | }).subscribe(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kamilkisiela/apollo-angular-ssr/4342e3021b261ed6d1f4ea089015a8fc2c5650bb/src/assets/.gitkeep -------------------------------------------------------------------------------- /src/browserslist: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # For IE 9-11 support, please uncomment the last line of the file and adjust as needed 5 | > 0.5% 6 | last 2 versions 7 | Firefox ESR 8 | not dead 9 | # IE 9-11 -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * In development mode, to ignore zone related error stack frames such as 11 | * `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can 12 | * import the following file, but please comment it out in production mode 13 | * because it will have performance impact when throw error 14 | */ 15 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 16 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kamilkisiela/apollo-angular-ssr/4342e3021b261ed6d1f4ea089015a8fc2c5650bb/src/favicon.ico -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ApolloAngularSsr 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/main.server.ts: -------------------------------------------------------------------------------- 1 | export { AppServerModule } from './app/app.server.module'; 2 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | document.addEventListener('DOMContentLoaded', () => { 12 | platformBrowserDynamic().bootstrapModule(AppModule) 13 | .catch(err => console.log(err)); 14 | }); 15 | -------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/weak-map'; 35 | // import 'core-js/es6/set'; 36 | 37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 38 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 39 | 40 | /** IE10 and IE11 requires the following for the Reflect API. */ 41 | // import 'core-js/es6/reflect'; 42 | 43 | 44 | /** Evergreen browsers require these. **/ 45 | // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. 46 | import 'core-js/es7/reflect'; 47 | 48 | 49 | /** 50 | * Web Animations `@angular/platform-browser/animations` 51 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 52 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 53 | **/ 54 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 55 | 56 | /** 57 | * By default, zone.js will patch all possible macroTask and DomEvents 58 | * user can disable parts of macroTask/DomEvents patch by setting following flags 59 | */ 60 | 61 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 62 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 63 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 64 | 65 | /* 66 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 67 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 68 | */ 69 | // (window as any).__Zone_enable_cross_context_check = true; 70 | 71 | /*************************************************************************************************** 72 | * Zone JS is required by default for Angular itself. 73 | */ 74 | import 'zone.js/dist/zone'; // Included with Angular CLI. 75 | 76 | 77 | 78 | /*************************************************************************************************** 79 | * APPLICATION IMPORTS 80 | */ 81 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "types": [ 6 | "node" 7 | ] 8 | }, 9 | "exclude": [ 10 | "src/test.ts", 11 | "**/*.spec.ts" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /src/tsconfig.server.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "baseUrl": "./", 6 | "module": "commonjs", 7 | "types": [ 8 | "node" 9 | ] 10 | }, 11 | "exclude": [ 12 | "test.ts", 13 | "**/*.spec.ts" 14 | ], 15 | "angularCompilerOptions": { 16 | "entryModule": "app/app.server.module#AppServerModule" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "module": "es2015", 9 | "moduleResolution": "node", 10 | "emitDecoratorMetadata": true, 11 | "experimentalDecorators": true, 12 | "target": "es5", 13 | "typeRoots": [ 14 | "node_modules/@types" 15 | ], 16 | "lib": [ 17 | "es2017", 18 | "esnext.asynciterable", 19 | "dom" 20 | ] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /webpack.server.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | 4 | module.exports = { 5 | entry: { server: './server.ts' }, 6 | resolve: { extensions: ['.js', '.ts'] }, 7 | target: 'node', 8 | mode: 'none', 9 | // this makes sure we include node_modules and other 3rd party libraries 10 | externals: [/node_modules/], 11 | output: { 12 | path: path.join(__dirname, 'dist'), 13 | filename: '[name].js' 14 | }, 15 | module: { 16 | rules: [{ test: /\.ts$/, loader: 'ts-loader' }] 17 | }, 18 | plugins: [ 19 | // Temporary Fix for issue: https://github.com/angular/angular/issues/11580 20 | // for 'WARNING Critical dependency: the request of a dependency is an expression' 21 | new webpack.ContextReplacementPlugin( 22 | /(.+)?angular(\\|\/)core(.+)?/, 23 | path.join(__dirname, 'src'), // location of your src 24 | {} // a map of your routes 25 | ), 26 | new webpack.ContextReplacementPlugin( 27 | /(.+)?express(\\|\/)(.+)?/, 28 | path.join(__dirname, 'src'), 29 | {} 30 | ) 31 | ] 32 | }; 33 | --------------------------------------------------------------------------------