├── .gitignore ├── .vscode └── tasks.json ├── LICENSE ├── README.md ├── bin ├── coverage.sh └── render.js ├── circle.yml ├── client └── package.json ├── examples ├── cli │ ├── .gitignore │ ├── README.md │ ├── angular-cli.json │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── app │ │ │ ├── app.component.css │ │ │ ├── app.component.html │ │ │ ├── app.component.ts │ │ │ ├── app.module.ts │ │ │ └── dialog-content.component.ts │ │ ├── assets │ │ │ └── .gitkeep │ │ ├── environments │ │ │ ├── environment.prod.ts │ │ │ └── environment.ts │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── main.ts │ │ ├── polyfills.ts │ │ ├── styles.scss │ │ ├── tsconfig.app.json │ │ └── tsconfig.spec.json │ ├── tsconfig.json │ └── tslint.json ├── demand-express │ ├── .gitignore │ ├── app │ │ ├── blog │ │ │ ├── blog.component.css │ │ │ ├── blog.component.html │ │ │ ├── blog.component.ts │ │ │ ├── index.ts │ │ │ ├── model.ts │ │ │ ├── module.ts │ │ │ ├── schema.ts │ │ │ └── service.ts │ │ ├── cookie │ │ │ ├── index.ts │ │ │ ├── module.ts │ │ │ └── service.ts │ │ ├── dialog │ │ │ ├── dialog-content.component.ts │ │ │ ├── index.ts │ │ │ └── module.ts │ │ ├── index.html │ │ ├── locale │ │ │ ├── index.ts │ │ │ ├── module.ts │ │ │ ├── selector.component.html │ │ │ ├── selector.component.ts │ │ │ └── service.ts │ │ ├── main.ts │ │ ├── root.component.css │ │ ├── root.component.html │ │ ├── root.component.ts │ │ └── root.module.ts │ ├── ci │ │ ├── README.md │ │ ├── client.ts │ │ ├── environment.ts │ │ ├── server.ts │ │ └── test-runner.ts │ ├── mock-content.json │ ├── package-lock.json │ ├── package.json │ ├── server │ │ ├── http.ts │ │ ├── index.ts │ │ └── paths.ts │ ├── tsconfig.json │ ├── webpack-server.config.js │ ├── webpack.config.js │ └── webpack │ │ └── loaders.js └── demand-koa │ ├── .gitignore │ └── package.json ├── package-lock.json ├── package.json ├── source ├── application │ ├── builder │ │ ├── application.ts │ │ ├── builder.ts │ │ ├── fork.ts │ │ ├── from-module-factory.ts │ │ ├── from-module.ts │ │ ├── from-source.ts │ │ ├── impl │ │ │ ├── application.ts │ │ │ ├── builder.ts │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── options.ts │ │ ├── prerenderer.ts │ │ └── tests │ │ │ ├── from-module-factory.ts │ │ │ ├── from-module.ts │ │ │ ├── from-source.ts │ │ │ └── prerenderer.ts │ ├── compiler │ │ ├── compiler.ts │ │ ├── factory.ts │ │ ├── index.ts │ │ ├── loader.ts │ │ ├── ngc │ │ │ ├── build.ts │ │ │ ├── compiler.ts │ │ │ ├── create.ts │ │ │ ├── diagnostics.ts │ │ │ ├── index.ts │ │ │ ├── loader.ts │ │ │ ├── resource-resolver.ts │ │ │ └── tests │ │ │ │ └── compiler.ts │ │ ├── options.ts │ │ └── webpack │ │ │ ├── compiler.ts │ │ │ ├── config │ │ │ ├── cli.ts │ │ │ ├── index.ts │ │ │ ├── loader.ts │ │ │ └── webpack.ts │ │ │ ├── index.ts │ │ │ ├── loader.ts │ │ │ └── source-map.ts │ ├── contracts.ts │ ├── index.ts │ ├── operation.ts │ ├── preboot │ │ ├── contract.ts │ │ ├── index.ts │ │ ├── schema.ts │ │ └── tests │ │ │ └── schema.ts │ ├── project.ts │ └── static │ │ ├── find.ts │ │ ├── index.ts │ │ ├── modules │ │ ├── collect.ts │ │ ├── index.ts │ │ ├── root.ts │ │ └── tests │ │ │ └── modules.ts │ │ ├── predicates.ts │ │ └── traverse.ts ├── bin │ ├── options │ │ ├── index.ts │ │ ├── options.ts │ │ └── parse.ts │ ├── render.ts │ └── vendor.ts ├── cache │ ├── cache.ts │ ├── index.ts │ ├── memory-cache.ts │ ├── memory-variant-cache.ts │ ├── pair.ts │ ├── tests │ │ ├── memory-cache.ts │ │ ├── memory-variant-cache.ts │ │ └── trie.ts │ └── trie.ts ├── dependencies.ts ├── disposable.ts ├── exception.ts ├── filesystem │ ├── contracts.ts │ ├── factories.ts │ ├── impl │ │ ├── base.ts │ │ ├── file.ts │ │ ├── index.ts │ │ └── path.ts │ ├── index.ts │ ├── tests │ │ └── filesystem.ts │ └── type.ts ├── index.ts ├── output │ ├── html.ts │ ├── index.ts │ ├── interprocess.ts │ ├── log.ts │ ├── options.ts │ ├── producer.ts │ ├── stylesheets.ts │ ├── svg.ts │ └── transform.ts ├── platform │ ├── application │ │ ├── bootstrap.ts │ │ ├── index.ts │ │ ├── router.ts │ │ └── stable.ts │ ├── collectors │ │ ├── console.ts │ │ ├── exceptions.ts │ │ ├── index.ts │ │ └── providers.ts │ ├── document │ │ ├── container.ts │ │ ├── index.ts │ │ ├── providers.ts │ │ └── tokens.ts │ ├── factory.ts │ ├── http │ │ ├── index.ts │ │ ├── pending-requests.ts │ │ ├── providers.ts │ │ └── xhr.ts │ ├── index.ts │ ├── injector.ts │ ├── location │ │ ├── index.ts │ │ ├── location.ts │ │ └── providers.ts │ ├── module │ │ ├── index.ts │ │ ├── metadata │ │ │ ├── decorators.ts │ │ │ ├── index.ts │ │ │ └── reflector.ts │ │ ├── runtime-loader.ts │ │ └── tokens.ts │ ├── platform.ts │ ├── render │ │ ├── index.ts │ │ └── providers.ts │ ├── resource-loader │ │ ├── index.ts │ │ ├── loader.ts │ │ └── providers.ts │ └── zone │ │ ├── assertions.ts │ │ ├── environment.ts │ │ ├── fork.ts │ │ ├── index.ts │ │ ├── injector-map.ts │ │ └── task.ts ├── predicate.ts ├── route │ ├── extract.ts │ ├── index.ts │ ├── route.ts │ ├── tests │ │ └── renderable.ts │ └── transform.ts ├── runtime │ ├── browser-emulation │ │ ├── animation.ts │ │ ├── base64.ts │ │ ├── control.ts │ │ ├── create.ts │ │ ├── events.ts │ │ ├── http.ts │ │ ├── index.ts │ │ ├── interaction.ts │ │ ├── mutation-observer.ts │ │ ├── navigator.ts │ │ ├── position.ts │ │ ├── promise.ts │ │ ├── range.ts │ │ ├── selection.ts │ │ ├── storage.ts │ │ ├── style.ts │ │ ├── tests │ │ │ ├── location.ts │ │ │ ├── navigator.ts │ │ │ ├── types.ts │ │ │ └── window.ts │ │ ├── types.ts │ │ ├── window.ts │ │ └── xhr.ts │ ├── index.ts │ └── zone.ts ├── snapshot │ ├── assert.ts │ ├── console.ts │ ├── creator │ │ ├── index.ts │ │ ├── inject.ts │ │ ├── preboot.ts │ │ ├── state.ts │ │ └── transform.ts │ ├── index.ts │ ├── orchestrate.ts │ └── snapshot.ts ├── static │ ├── bootstrap.ts │ ├── decorators.ts │ ├── files.ts │ ├── index.ts │ ├── random.ts │ └── uri.ts ├── test │ └── fixtures │ │ ├── application-basic-external-styled.scss │ │ ├── application-basic-external-styled.ts │ │ ├── application-basic-external.html │ │ ├── application-basic-external.ts │ │ ├── application-basic-inline.ts │ │ ├── application-lazy-module.ts │ │ ├── application-lazy-routed.ts │ │ ├── application-routed.ts │ │ ├── context.ts │ │ ├── dependencies.js │ │ ├── document.ts │ │ ├── index.ts │ │ ├── module.ts │ │ ├── project.ts │ │ └── styles.js ├── transformation │ ├── array.ts │ ├── flatten.ts │ ├── index.ts │ ├── json.ts │ ├── promise.ts │ ├── schema.ts │ ├── tests │ │ └── type-to-function.ts │ └── type-to-function.ts ├── typings │ └── angular.ts.d └── variants │ ├── compose.ts │ ├── index.ts │ ├── permutations.ts │ └── tests │ └── permutations.ts ├── tsconfig.json └── tslint.json /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | .vscode 4 | 5 | build 6 | coverage 7 | node_modules 8 | 9 | **/*.orig 10 | **/*.rej 11 | **/*.swp 12 | **/*~ 13 | 14 | .jest-cache 15 | 16 | source/**/*.ngfactory.ts 17 | source/**/*.ngfactory.js 18 | source/**/*.ngsummary.json 19 | source/**/*.ngstyle.ts 20 | source/**/*.shim.ngstyle.ts 21 | source/**/*.shim.ts 22 | source/**/*.shim.js 23 | source/**/*.metadata.json 24 | source/**/*.map 25 | 26 | yarn-error.log 27 | yarn.lock 28 | 29 | nohup.out 30 | 31 | npm-debug.* 32 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.0", 3 | "command": "npm", 4 | "isShellCommand": true, 5 | "showOutput": "always", 6 | "suppressTaskName": true, 7 | "tasks": [ 8 | { 9 | "taskName": "build", 10 | "isBuildCommand": true, 11 | "args": ["run", "build"] 12 | }, 13 | { 14 | "taskName": "lint", 15 | "args": ["run", "lint"] 16 | }, 17 | { 18 | "taskName": "install", 19 | "args": ["install"] 20 | }, 21 | { 22 | "taskName": "update", 23 | "args": ["update"] 24 | }, 25 | { 26 | "taskName": "test", 27 | "isTestCommand": true, 28 | "args": ["run", "test"] 29 | } 30 | ] 31 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Christopher L. Bond 2 | Copyright (c) 2017 Rangle.io 3 | 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /bin/coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if test `basename $PWD` = 'scripts'; 4 | then 5 | cd ..; 6 | fi; 7 | 8 | if test -z "$COVERALLS_REPO_TOKEN" -o ! -f ./coverage/lcov.info; 9 | then 10 | exit 0; 11 | fi; 12 | 13 | cat ./coverage/lcov.info | ./node_modules/.bin/coveralls; 14 | -------------------------------------------------------------------------------- /bin/render.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require('../build/bin/render'); 4 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | environment: 3 | YARN_VERSION: 0.23.2 4 | PATH: "${PATH}:${HOME}/.yarn/bin" 5 | node: 6 | version: 7.9.0 7 | 8 | dependencies: 9 | cache_directories: 10 | - ".jest-cache" 11 | pre: 12 | - npm install -g yarn@${YARN_VERSION} 13 | override: 14 | - yarn 15 | post: 16 | - yarn run build 17 | - yarn run build-examples 18 | 19 | test: 20 | override: 21 | - node --always_compact ./node_modules/jest/bin/jest.js -w 18 --coverage=false --logHeapUsage: 22 | timeout: 900 23 | post: 24 | - if test -d coverage; then cp -r coverage $CIRCLE_ARTIFACTS && ./bin/coverage.sh; fi 25 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@angular-ssr/client", 3 | "version": "0.1.98", 4 | "description": "Browser companion library for angular-ssr", 5 | "main": "build/index.js", 6 | "typings": "build/index.d.ts", 7 | "engines": { 8 | "node": ">=6.9", 9 | "npm": ">=3.10" 10 | }, 11 | "scripts": {}, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/clbond/angular-ssr.git" 15 | }, 16 | "author": { 17 | "name": "Christopher Bond", 18 | "email": "cb@clbond.org", 19 | "url": "https://clbond.org/" 20 | }, 21 | 22 | "contributors": [ 23 | { 24 | "name": "rangle.io", 25 | "url": "https://rangle.io/" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /examples/cli/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | yarn.lock 4 | 5 | src/**/*.js 6 | src/**/*.js.map 7 | src/**/*.ngfactory.ts 8 | src/**/*.shim.ngstyle.ts 9 | -------------------------------------------------------------------------------- /examples/cli/angular-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "project": { 3 | "version": "1.0.0-rc.2", 4 | "name": "angular-ssr-cli-example" 5 | }, 6 | "apps": [ 7 | { 8 | "root": "src", 9 | "outDir": "dist", 10 | "assets": [ 11 | "assets", 12 | "favicon.ico" 13 | ], 14 | "index": "index.html", 15 | "main": "main.ts", 16 | "polyfills": "polyfills.ts", 17 | "test": "test.ts", 18 | "tsconfig": "../tsconfig.json", 19 | "prefix": "app", 20 | "styles": [ 21 | "styles.scss" 22 | ], 23 | "scripts": [], 24 | "environmentSource": "environments/environment.ts", 25 | "environments": { 26 | "dev": "environments/environment.ts", 27 | "prod": "environments/environment.prod.ts" 28 | } 29 | } 30 | ], 31 | "e2e": {}, 32 | "lint": [ 33 | { 34 | "files": "src/**/*.ts", 35 | "project": "tsconfig.json" 36 | } 37 | ], 38 | "test": {}, 39 | "defaults": { 40 | "styleExt": "css", 41 | "prefixInterfaces": false, 42 | "inline": { 43 | "style": true, 44 | "template": false 45 | }, 46 | "spec": { 47 | "class": false, 48 | "component": true, 49 | "directive": true, 50 | "module": false, 51 | "pipe": true, 52 | "service": true 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /examples/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-ssr-cli-example", 3 | "version": "0.0.0", 4 | "license": "BSD-2-Clause", 5 | "scripts": { 6 | "ng": "ng", 7 | "start": "live-server dist", 8 | "build": "ng build", 9 | "postbuild": "ng-render --environment=prod --preboot=true --debug --inline", 10 | "lint": "ng lint" 11 | }, 12 | "private": true, 13 | "dependencies": { 14 | "@angular/animations": "4.2.2", 15 | "@angular/cli": ">=1.0.0", 16 | "@angular/common": "4.2.2", 17 | "@angular/compiler": "4.2.2", 18 | "@angular/compiler-cli": "4.2.2", 19 | "@angular/core": "4.2.2", 20 | "@angular/forms": "4.2.2", 21 | "@angular/http": "4.2.2", 22 | "@angular/material": "2.0.0-beta.3", 23 | "@angular/platform-browser": "4.2.2", 24 | "@angular/platform-browser-dynamic": "4.2.2", 25 | "@angular/router": "4.2.2", 26 | "@angular/tsc-wrapped": "4.2.2", 27 | "@types/node": "^6.0.60", 28 | "codelyzer": "^2.0.0", 29 | "angular-ssr": "latest", 30 | "classlist.js": "^1.1.20150312", 31 | "core-js": "^2.4.1", 32 | "hammerjs": "^2.0.8", 33 | "intl": "^1.2.5", 34 | "live-server": "^1.2.0", 35 | "preboot": "^4.5.2", 36 | "reflect-metadata": "^0.1.10", 37 | "rxjs": "^5.4.1", 38 | "ts-node": "^2.0.0", 39 | "tslint": "^4.5.0", 40 | "typescript": "2.3.2", 41 | "web-animations-js": "^2.2.2", 42 | "zone.js": "^0.8.4" 43 | }, 44 | "devDependencies": {} 45 | } 46 | -------------------------------------------------------------------------------- /examples/cli/src/app/app.component.css: -------------------------------------------------------------------------------- 1 | .m2app-dark { 2 | background: #444; 3 | color: #ddd; 4 | font-family: Ubuntu, Monaco, Open Sans, sans-serif; 5 | } 6 | 7 | .app-content { 8 | padding: 20px; 9 | } 10 | 11 | .app-content md-card { 12 | margin: 20px; 13 | } 14 | 15 | .app-content md-checkbox { 16 | margin: 10px; 17 | } 18 | 19 | .app-toolbar-filler { 20 | flex: 1 1 auto; 21 | text-align: right; 22 | } 23 | 24 | .app-toolbar-menu { 25 | padding: 0 14px 0 14px; 26 | color: white; 27 | } 28 | 29 | .app-icon-button { 30 | box-shadow: none; 31 | user-select: none; 32 | background: none; 33 | border: none; 34 | cursor: pointer; 35 | filter: none; 36 | font-weight: normal; 37 | height: auto; 38 | line-height: inherit; 39 | margin: 0; 40 | min-width: 0; 41 | padding: 0; 42 | text-align: left; 43 | text-decoration: none; 44 | } 45 | 46 | .app-action { 47 | display: inline-block; 48 | position: fixed; 49 | bottom: 20px; 50 | right: 20px; 51 | } 52 | 53 | .app-spinner { 54 | height: 30px; 55 | width: 30px; 56 | display: inline-block; 57 | } 58 | 59 | .app-input-icon { 60 | font-size: 16px; 61 | } 62 | 63 | .app-list { 64 | border: 1px solid rgba(0,0,0,0.12); 65 | width: 350px; 66 | margin: 20px; 67 | } 68 | 69 | .app-progress { 70 | margin: 20px; 71 | } 72 | -------------------------------------------------------------------------------- /examples/cli/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, Optional, OnInit, OnDestroy} from '@angular/core'; 2 | 3 | import {MdDialog, MdDialogRef, MdSnackBar} from '@angular/material'; 4 | 5 | import {DialogContent} from './dialog-content.component'; 6 | 7 | @Component({ 8 | selector: 'app-root', 9 | templateUrl: 'app.component.html', 10 | styleUrls: ['app.component.css'], 11 | }) 12 | export class AppComponent implements OnInit, OnDestroy { 13 | public isDarkTheme: boolean = false; 14 | 15 | public foods = [ 16 | {name: 'Pizza', rating: 'Excellent'}, 17 | {name: 'Burritos', rating: 'Great'}, 18 | {name: 'French fries', rating: 'Pretty good'}, 19 | ]; 20 | 21 | public progress: number = 0; 22 | 23 | private timer; 24 | 25 | constructor(private dialog: MdDialog, private snackbar: MdSnackBar) {} 26 | 27 | ngOnInit() { 28 | const update = () => this.progress = (this.progress + Math.floor(Math.random() * 4) + 1) % 100; 29 | 30 | this.timer = setInterval(() => update, 200); 31 | } 32 | 33 | ngOnDestroy() { 34 | clearInterval(this.timer); 35 | } 36 | 37 | openDialog() { 38 | this.dialog.open(DialogContent); 39 | } 40 | 41 | showSnackbar() { 42 | this.snackbar.open('Yum snacks', 'Chew'); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /examples/cli/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import {BrowserModule} from '@angular/platform-browser'; 2 | import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; 3 | import {CommonModule} from '@angular/common'; 4 | import {NgModule, NgZone} from '@angular/core'; 5 | import {FormsModule} from '@angular/forms'; 6 | import {HttpModule} from '@angular/http'; 7 | import {MaterialModule} from '@angular/material'; 8 | 9 | import {Subscription} from 'rxjs/Subscription'; 10 | 11 | import {AppComponent} from './app.component'; 12 | 13 | import {DialogContent} from './dialog-content.component'; 14 | 15 | @NgModule({ 16 | imports: [ 17 | BrowserAnimationsModule, 18 | BrowserModule, 19 | CommonModule, 20 | FormsModule, 21 | HttpModule, 22 | MaterialModule.forRoot(), 23 | ], 24 | declarations: [ 25 | AppComponent, 26 | DialogContent 27 | ], 28 | providers: [], 29 | bootstrap: [AppComponent] 30 | }) 31 | export class AppModule {} 32 | -------------------------------------------------------------------------------- /examples/cli/src/app/dialog-content.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, Optional} from '@angular/core'; 2 | 3 | import {MdDialogRef} from '@angular/material'; 4 | 5 | @Component({ 6 | template: ` 7 |

This is a dialog

8 |

9 | 13 |

14 |

15 | `, 16 | }) 17 | export class DialogContent { 18 | constructor(@Optional() public dialogRef: MdDialogRef) { } 19 | } -------------------------------------------------------------------------------- /examples/cli/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rangle/angular-ssr/18a8e40a541b3cb840ae7dfaa0abf95e5fab1256/examples/cli/src/assets/.gitkeep -------------------------------------------------------------------------------- /examples/cli/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /examples/cli/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // The file contents for the current environment will overwrite these during build. 2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do 3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead. 4 | // The list of which env maps to which file can be found in `.angular-cli.json`. 5 | 6 | export const environment = { 7 | production: false 8 | }; 9 | -------------------------------------------------------------------------------- /examples/cli/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rangle/angular-ssr/18a8e40a541b3cb840ae7dfaa0abf95e5fab1256/examples/cli/src/favicon.ico -------------------------------------------------------------------------------- /examples/cli/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Cli2 6 | 7 | 8 | 9 | 10 | " 11 | 12 | 13 | Loading... 14 | 15 | 16 | -------------------------------------------------------------------------------- /examples/cli/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 | import {prebootClient} from 'preboot/__build/src/browser/preboot_browser'; 8 | 9 | import 'hammerjs'; 10 | 11 | if (environment.production) { 12 | enableProdMode(); 13 | } 14 | 15 | platformBrowserDynamic().bootstrapModule(AppModule).then(() => prebootClient().complete()); -------------------------------------------------------------------------------- /examples/cli/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | import 'core-js/es6/symbol'; 2 | import 'core-js/es6/object'; 3 | import 'core-js/es6/function'; 4 | import 'core-js/es6/parse-int'; 5 | import 'core-js/es6/parse-float'; 6 | import 'core-js/es6/number'; 7 | import 'core-js/es6/math'; 8 | import 'core-js/es6/string'; 9 | import 'core-js/es6/date'; 10 | import 'core-js/es6/array'; 11 | import 'core-js/es6/regexp'; 12 | import 'core-js/es6/map'; 13 | import 'core-js/es6/set'; 14 | 15 | import 'classlist.js'; 16 | 17 | import 'web-animations-js'; 18 | 19 | import 'core-js/es6/reflect'; 20 | import 'core-js/es7/reflect'; 21 | 22 | import 'zone.js/dist/zone'; 23 | 24 | import 'intl'; 25 | -------------------------------------------------------------------------------- /examples/cli/src/styles.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rangle/angular-ssr/18a8e40a541b3cb840ae7dfaa0abf95e5fab1256/examples/cli/src/styles.scss -------------------------------------------------------------------------------- /examples/cli/src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "sourceMap": true, 4 | "declaration": false, 5 | "moduleResolution": "node", 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "target": "es5", 9 | "lib": [ 10 | "es2016", 11 | "dom" 12 | ], 13 | "outDir": "../out-tsc/app", 14 | "module": "es2015", 15 | "baseUrl": "", 16 | "types": [] 17 | }, 18 | "exclude": [ 19 | "test.ts", 20 | "**/*.spec.ts" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /examples/cli/src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "sourceMap": true, 4 | "declaration": false, 5 | "moduleResolution": "node", 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "lib": [ 9 | "es2016" 10 | ], 11 | "outDir": "../out-tsc/spec", 12 | "module": "commonjs", 13 | "target": "es5", 14 | "baseUrl": "", 15 | "types": [ 16 | "jasmine", 17 | "node" 18 | ] 19 | }, 20 | "files": [ 21 | "test.ts" 22 | ], 23 | "include": [ 24 | "**/*.spec.ts" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /examples/cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "outDir": "./dist/out-tsc", 5 | "baseUrl": "src", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "moduleResolution": "node", 9 | "emitDecoratorMetadata": true, 10 | "experimentalDecorators": true, 11 | "target": "es5", 12 | "typeRoots": [ 13 | "node_modules/@types" 14 | ], 15 | "lib": [ 16 | "es2016", 17 | "dom" 18 | ] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/demand-express/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | dist-server 3 | app/**/*.js 4 | app/**/*.map 5 | node_modules 6 | yarn.lock 7 | -------------------------------------------------------------------------------- /examples/demand-express/app/blog/blog.component.css: -------------------------------------------------------------------------------- 1 | div { 2 | margin: 2em; 3 | } 4 | 5 | .content { 6 | font-size: 1.2em; 7 | } 8 | 9 | .author, .id { 10 | color: #555; 11 | font-weight: bold; 12 | } 13 | 14 | .id { 15 | font-size: small; 16 | } -------------------------------------------------------------------------------- /examples/demand-express/app/blog/blog.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | {{blog.author.name}} 4 | 5 |
6 |
7 |
{{blog.content}}
8 |
9 |
-------------------------------------------------------------------------------- /examples/demand-express/app/blog/blog.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | 3 | import {ActivatedRoute} from '@angular/router'; 4 | 5 | import {Observable} from 'rxjs'; 6 | 7 | import 'rxjs/add/operator/map'; 8 | 9 | import {Blog} from './model'; 10 | import {BlogService} from './service'; 11 | import {LocaleService} from '../locale'; 12 | 13 | @Component({ 14 | selector: 'blog-component', 15 | templateUrl: './blog.component.html', 16 | styleUrls: ['./blog.component.css'] 17 | }) 18 | export class BlogComponent { 19 | private locale: Observable; 20 | 21 | private blogs: Observable>; 22 | 23 | constructor( 24 | private blogService: BlogService, 25 | localeService: LocaleService, 26 | route: ActivatedRoute 27 | ) { 28 | this.blogs = this.blogService.load('en-US'); 29 | 30 | this.locale = localeService.locale(); 31 | 32 | this.locale.subscribe(locale => this.update(locale)); 33 | } 34 | 35 | private update(locale: string) { 36 | this.blogs = this.blogService.load(locale || 'en-US'); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /examples/demand-express/app/blog/index.ts: -------------------------------------------------------------------------------- 1 | export * from './blog.component'; 2 | export * from './model'; 3 | export * from './module'; 4 | export * from './service'; 5 | export * from './schema'; -------------------------------------------------------------------------------- /examples/demand-express/app/blog/model.ts: -------------------------------------------------------------------------------- 1 | export interface Blog { 2 | title: string; 3 | content: string; 4 | author: { 5 | name: string; 6 | email: string; 7 | } 8 | } -------------------------------------------------------------------------------- /examples/demand-express/app/blog/module.ts: -------------------------------------------------------------------------------- 1 | import {RouterModule} from '@angular/router'; 2 | import {CommonModule} from '@angular/common'; 3 | import {NgModule} from '@angular/core'; 4 | 5 | import {HttpModule} from '@angular/http'; 6 | 7 | import {BlogService} from './service'; 8 | import {BlogComponent} from './blog.component'; 9 | 10 | @NgModule({ 11 | imports: [ 12 | CommonModule, 13 | HttpModule, 14 | RouterModule.forChild([ 15 | {path: ':id', component: BlogComponent} 16 | ]) 17 | ], 18 | declarations: [BlogComponent], 19 | providers: [{provide: BlogService, useClass: BlogService}] 20 | }) 21 | export class BlogModule {} 22 | -------------------------------------------------------------------------------- /examples/demand-express/app/blog/schema.ts: -------------------------------------------------------------------------------- 1 | import {Validator, ValidatorResult, Schema} from 'jsonschema'; 2 | 3 | const blogsSchema: Schema = { 4 | $schema: 'http://json-schema.org/draft-04/schema#', 5 | type: 'object', 6 | properties: { 7 | ".*$": { 8 | type: 'array', 9 | properties: { 10 | title: {type: 'string'}, 11 | content: {type: 'string'}, 12 | author: {type: 'object', 13 | properties: { 14 | name: {type: 'string'}, 15 | email: {type: 'string'} 16 | } 17 | } 18 | } 19 | }, 20 | } 21 | }; 22 | 23 | export const validateBlogsAgainstSchema = (blogs): ValidatorResult => 24 | new Validator().validate(blogs, blogsSchema); -------------------------------------------------------------------------------- /examples/demand-express/app/blog/service.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '@angular/core'; 2 | 3 | import {Http} from '@angular/http'; 4 | 5 | import {Observable} from 'rxjs'; 6 | 7 | import {Blog} from './model'; 8 | 9 | @Injectable() 10 | export class BlogService { 11 | constructor(private http: Http) {} 12 | 13 | load(locale: string): Observable> { 14 | return this.http.get('https://raw.githubusercontent.com/clbond/angular-ssr/master/examples/demand-express/mock-content.json').map(r => r.json()).map(c => c[locale]); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/demand-express/app/cookie/index.ts: -------------------------------------------------------------------------------- 1 | export * from './module'; 2 | export * from './service'; -------------------------------------------------------------------------------- /examples/demand-express/app/cookie/module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | 3 | import {CookieService} from './service'; 4 | 5 | @NgModule({ 6 | providers: [CookieService] 7 | }) 8 | export class CookieModule {} -------------------------------------------------------------------------------- /examples/demand-express/app/cookie/service.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '@angular/core'; 2 | 3 | export type CookieValue = string | number; 4 | 5 | @Injectable() 6 | export class CookieService { 7 | get map(): Map { 8 | if (!document.cookie) { 9 | return new Map(); 10 | } 11 | 12 | const components = document.cookie.split(/(;\s?)/g); 13 | 14 | const tuples = components.map( 15 | (pair: string): [string, CookieValue] => { 16 | const [key, value] = pair.split(/=/); 17 | 18 | if (/^([\d+])$/.test(value)) { 19 | return [key, parseInt(value, 10)]; 20 | } 21 | 22 | return [key, value]; 23 | }); 24 | 25 | return new Map(tuples); 26 | } 27 | 28 | get(key: string): T { 29 | return this.map.get(key) as any; 30 | } 31 | 32 | set(key: string, value: CookieValue) { 33 | this.delete(key); 34 | 35 | document.cookie = `${key}=${value.toString()}; path=/; domain=${location.hostname};`; 36 | } 37 | 38 | delete(key: string) { 39 | const criterion: Array<[string, string | number]> = [ 40 | ['expires', 'Thu, 01 Jan 1970 00:00:01 GMT'], 41 | ['path', '/'], 42 | ['domain', location.hostname], 43 | ['max-age', 0] 44 | ]; 45 | 46 | while (criterion.length > 0) { 47 | const serialized = criterion.map(([k, v]) => `${k}=${v}`).join('; '); 48 | 49 | document.cookie = `${key}=;${serialized ? ' ' + serialized : String()}`.trim(); 50 | 51 | criterion.pop(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /examples/demand-express/app/dialog/dialog-content.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, Optional} from '@angular/core'; 2 | 3 | import {MdDialogRef} from '@angular/material'; 4 | 5 | @Component({ 6 | template: ` 7 |

This is a dialog

8 |

9 | 13 |

14 |

15 | `, 16 | }) 17 | export class DialogContent { 18 | constructor(@Optional() public dialogRef: MdDialogRef) { } 19 | } -------------------------------------------------------------------------------- /examples/demand-express/app/dialog/index.ts: -------------------------------------------------------------------------------- 1 | export * from './dialog-content.component'; 2 | export * from './module'; -------------------------------------------------------------------------------- /examples/demand-express/app/dialog/module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule} from '@angular/core'; 2 | 3 | import {DialogContent} from './dialog-content.component'; 4 | 5 | @NgModule({ 6 | declarations: [DialogContent], 7 | exports: [DialogContent] 8 | }) 9 | export class DialogModule {} -------------------------------------------------------------------------------- /examples/demand-express/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /examples/demand-express/app/locale/index.ts: -------------------------------------------------------------------------------- 1 | export * from './selector.component'; 2 | export * from './module'; 3 | export * from './service'; -------------------------------------------------------------------------------- /examples/demand-express/app/locale/module.ts: -------------------------------------------------------------------------------- 1 | import {CommonModule} from '@angular/common'; 2 | import {FormsModule} from '@angular/forms'; 3 | import {NgModule} from '@angular/core'; 4 | import {MdSelectModule} from '@angular/material'; 5 | 6 | import {LocaleSelectorComponent} from './selector.component'; 7 | 8 | import {LocaleService} from './service'; 9 | 10 | @NgModule({ 11 | declarations: [LocaleSelectorComponent], 12 | exports: [LocaleSelectorComponent], 13 | imports: [CommonModule, FormsModule, MdSelectModule], 14 | providers: [LocaleService] 15 | }) 16 | export class LocaleModule {} -------------------------------------------------------------------------------- /examples/demand-express/app/locale/selector.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | English 4 | 5 | 6 | French 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/demand-express/app/locale/selector.component.ts: -------------------------------------------------------------------------------- 1 | import {Component} from '@angular/core'; 2 | 3 | import {Observable} from 'rxjs'; 4 | 5 | import {LocaleService} from './service'; 6 | 7 | @Component({ 8 | selector: 'locale-selector', 9 | templateUrl: './selector.component.html', 10 | styles: [` 11 | :host { 12 | margin: 2em; 13 | } 14 | locale-selector { 15 | padding-top: 1em; 16 | }` 17 | ] 18 | }) 19 | export class LocaleSelectorComponent { 20 | public locale: Observable; 21 | 22 | constructor(public service: LocaleService) { 23 | this.locale = service.locale(); 24 | } 25 | } -------------------------------------------------------------------------------- /examples/demand-express/app/locale/service.ts: -------------------------------------------------------------------------------- 1 | import {Injectable} from '@angular/core'; 2 | 3 | import {Observable, ReplaySubject} from 'rxjs'; 4 | 5 | import {CookieService} from '../cookie'; 6 | 7 | @Injectable() 8 | export class LocaleService { 9 | subject = new ReplaySubject(); 10 | 11 | constructor(private cookies: CookieService) { 12 | this.update(cookies.get('locale') || navigator.language || 'en-US'); 13 | } 14 | 15 | locale(locale?: string): Observable { 16 | if (locale) { 17 | this.update(locale); 18 | } 19 | return Observable.from(this.subject); 20 | } 21 | 22 | private update(value: string) { 23 | this.subject.next(value); 24 | 25 | this.cookies.set('locale', value); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/demand-express/app/main.ts: -------------------------------------------------------------------------------- 1 | import 'reflect-metadata'; 2 | 3 | import 'zone.js'; 4 | 5 | import 'hammerjs'; 6 | 7 | import {platformBrowserDynamic} from '@angular/platform-browser-dynamic' 8 | 9 | import {RootModule} from './root.module'; 10 | 11 | platformBrowserDynamic().bootstrapModule(RootModule); 12 | -------------------------------------------------------------------------------- /examples/demand-express/app/root.component.css: -------------------------------------------------------------------------------- 1 | .app-right { 2 | float: right; 3 | padding-top: 1.5em; 4 | } -------------------------------------------------------------------------------- /examples/demand-express/app/root.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | Example Blog Application using SSR 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | -------------------------------------------------------------------------------- /examples/demand-express/app/root.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, NgZone, ViewEncapsulation} from '@angular/core'; 2 | 3 | import {MdDialog, MdSnackBar} from '@angular/material'; 4 | 5 | import {DialogContent} from './dialog/dialog-content.component'; 6 | 7 | @Component({ 8 | selector: 'application', 9 | templateUrl: 'root.component.html', 10 | styleUrls: [ 11 | '../node_modules/@angular/material/prebuilt-themes/indigo-pink.css', 12 | './root.component.css' 13 | ], 14 | encapsulation: ViewEncapsulation.None 15 | }) 16 | export class RootComponent { 17 | public foods = [ 18 | {name: 'Pizza', rating: 'Excellent'}, 19 | {name: 'Burritos', rating: 'Great'}, 20 | {name: 'French fries', rating: 'Pretty good'}, 21 | ]; 22 | 23 | public progress: number = 0; 24 | 25 | private timer; 26 | 27 | constructor( 28 | private dialog: MdDialog, 29 | private snackbar: MdSnackBar, 30 | private zone: NgZone 31 | ) {} 32 | 33 | ngOnInit() { 34 | const update = () => this.zone.run(() => { 35 | this.progress = (this.progress + Math.floor(Math.random() * 4) + 1) % 100; 36 | }); 37 | 38 | this.timer = setInterval(() => update, 200); 39 | } 40 | 41 | ngOnDestroy() { 42 | clearInterval(this.timer); 43 | } 44 | 45 | openDialog() { 46 | this.dialog.open(DialogContent); 47 | } 48 | 49 | showSnackbar() { 50 | this.snackbar.open('Yum snacks', 'Chew'); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /examples/demand-express/app/root.module.ts: -------------------------------------------------------------------------------- 1 | import {NgModule, NgZone} from '@angular/core'; 2 | import {FormsModule} from '@angular/forms'; 3 | import {BrowserModule} from '@angular/platform-browser'; 4 | import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; 5 | import {NavigationEnd, NavigationError, RouterModule, Router} from '@angular/router'; 6 | import {MaterialModule} from '@angular/material'; 7 | 8 | import {Observable} from 'rxjs'; 9 | 10 | import {prebootClient} from 'preboot/__build/src/browser/preboot_browser'; 11 | 12 | import {BlogModule} from './blog'; 13 | import {CookieModule} from './cookie'; 14 | import {LocaleModule} from './locale'; 15 | import {RootComponent} from './root.component'; 16 | 17 | @NgModule({ 18 | bootstrap: [RootComponent], 19 | imports: [ 20 | BlogModule, 21 | BrowserModule, 22 | BrowserAnimationsModule, 23 | FormsModule, 24 | CookieModule, 25 | LocaleModule, 26 | RouterModule.forRoot([ 27 | {path: '', pathMatch: 'full', 'redirectTo': 'blog/1'}, 28 | {path: 'blog', loadChildren: './blog/module.ts#BlogModule'}, 29 | ]), 30 | MaterialModule.forRoot() 31 | ], 32 | declarations: [RootComponent] 33 | }) 34 | export class RootModule { 35 | constructor(router: Router, zone: NgZone) { 36 | if (typeof prebootstrap === 'undefined') { 37 | return; 38 | } 39 | 40 | const finished = Observable.combineLatest(router.events, zone.onMicrotaskEmpty, zone.onStable); 41 | 42 | const subscription = finished.subscribe(([event, stable]) => { 43 | if (stable === false) { 44 | return; 45 | } 46 | 47 | switch (true) { 48 | case event instanceof NavigationError: 49 | case event instanceof NavigationEnd: 50 | prebootClient().complete(); 51 | subscription.unsubscribe(); 52 | break; 53 | default: 54 | break; 55 | } 56 | }); 57 | } 58 | } 59 | 60 | declare const prebootstrap; 61 | -------------------------------------------------------------------------------- /examples/demand-express/ci/README.md: -------------------------------------------------------------------------------- 1 | This code is not intended for use in your project. This is just a very simple set of end-to-end 2 | tests to verify that the server is producing the kind of documents that are expected. I just 3 | wanted to include some basic e2e tests as part of demand-express so that I can verify that the 4 | example server continues to work as part of the CircleCI process. I did this because there have 5 | been several instances of the server breaking due to changes in the main library, and I want to 6 | catch those kinds of problems as part of the CI builds. But do not copy this code to your project 7 | as it is not intended for that. -------------------------------------------------------------------------------- /examples/demand-express/ci/client.ts: -------------------------------------------------------------------------------- 1 | const fetch = require('node-fetch'); 2 | 3 | const domino = require('domino-modernized'); 4 | 5 | export class HttpTestClient { 6 | constructor(private serverUri: string) {} 7 | 8 | async run(): Promise { 9 | const response = await fetch(this.serverUri); 10 | 11 | if (response.ok === false) { 12 | throw new Error(`Server returned an HTTP response status code indicating failure: ${response.status}`); 13 | } 14 | 15 | const text = await response.text(); 16 | 17 | assertions(text); 18 | 19 | console.log(`Test assertions all passed!`); 20 | } 21 | } 22 | 23 | const assertions = (text: string) => { 24 | const window = domino.createWindow(text, this.serverUri); 25 | 26 | const {document} = window; 27 | 28 | if (document.head == null || document.body == null) { 29 | throw new Error(`Unable to parse HTML response: ${text}`); 30 | } 31 | 32 | const application = document.querySelector('application'); 33 | if (application == null) { 34 | throw new Error(`Cannot find root element in document: ${text}`); 35 | } 36 | 37 | const scripts = Array.from(document.querySelectorAll('script[type="text/javascript"]')); 38 | 39 | if (scripts.some(s => /prebootstrap\(\).init\(/.test(s.textContent)) === false) { 40 | throw new Error(`HTML document is missing preboot script: ${text}`); 41 | } 42 | 43 | if (scripts.some(s => /\/app.js/.test(s.src)) === false) { 44 | throw new Error(`HTML document is missing client-side script bundle in