8000 feature(universal): add support for Angular Universal · datatypevoid/angular-cli@1026670 · GitHub
[go: up one dir, main page]

Skip to content

Commit 1026670

Browse files
devCrossNetBrocco
authored andcommitted
feature(universal): add support for Angular Universal
add the possibility to scaffold a universal project with --universal
1 parent b8ad686 commit 1026670

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+763
-169
lines changed

.travis.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,24 @@ matrix:
4646
- node_js: "6"
4747
os: osx
4848
env: NODE_SCRIPT=tests/e2e_runner.js
49+
- node_js: "4"
50+
os: osx
51+
env: UNIVERSAL=true NODE_SCRIPT=tests/e2e_runner.js
52+
- node_js: "5"
53+
os: osx
54+
env: UNIVERSAL=true NODE_SCRIPT=tests/e2e_runner.js
55+
- node_js: "6"
56+
os: osx
57+
env: UNIVERSAL=true NODE_SCRIPT=tests/e2e_runner.js
58+
- node_js: "4"
59+
os: linux
60+
env: UNIVERSAL=true NODE_SCRIPT=tests/e2e_runner.js
61+
- node_js: "5"
62+
os: linux
63+
env: UNIVERSAL=true NODE_SCRIPT=tests/e2e_runner.js
64+
- node_js: "6"
65+
os: linux
66+
env: UNIVERSAL=true NODE_SCRIPT=tests/e2e_runner.js
4967

5068
before_install:
5169
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi

README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ with NPM 3 or higher.
4848
* [Deploying the App via GitHub Pages](#deploying-the-app-via-github-pages)
4949
* [Linting and formatting code](#linting-and-formatting-code)
5050
* [Support for offline applications](#support-for-offline-applications)
51+
* [Support for server side rendering](#support-for-server-side-rendering)
5152
* [Commands autocompletion](#commands-autocompletion)
5253
* [Project assets](#project-assets)
5354
* [Global styles](#global-styles)
@@ -268,7 +269,21 @@ You can modify the these scripts in `package.json` to run whatever tool you pref
268269

269270
**The `--mobile` flag has been disabled temporarily. Sorry for the inconvenience.**
270271

271-
~~Angular-CLI includes support for offline applications via the `--` flag on `ng new`. Support is experimental, please see the angular/mobile-toolkit project and https://mobile.angular.io/ for documentation on how to make use of this functionality.~~
272+
~~Angular-CLI includes support for offline applications via the `--mobile` flag on `ng new`. Support is experimental,
273+
please see the angular/mobile-toolkit project and https://mobile.angular.io/ for documentation on how to make use of this functionality.~~
274+
275+
### Support for server side rendering
276+
277+
**Angular-CLI includes Angular Universal via the `--universal` flag on `ng new` and `ng init`.**
278+
279+
**Angular Universal** helps you to seo optimize your application and offers a better user experience through server side rendering.
280+
Please see the **angular/universal** project and https://universal.angular.io/ for documentation on how to make use of this functionality.
281+
282+
#### Update an existing Project
283+
284+
`cd path/to/project` and init your project with the universal option `ng init --universal`. Take every Pipe, Directive, Component, Module and Routes from `./src/app/app.module.ts`
285+
and move them to `./src/app.browser.module.ts` and `./src/app.node.module.ts`. Try `ng serve`, if your application looks like before, then you can delete the files
286+
`./src/app/app.module.ts` and `./src/main.ts`. **Have fun with Angular Universal!**
272287

273288
### Commands autocompletion
274289

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"test:cli": "node tests/runner",
2121
"test:inspect": "node --inspect --debug-brk tests/runner",
2222
"test:packages": "node scripts/run-packages-spec.js",
23+
"test:universal": "UNIVERSAL=true node tests/e2e_runner.js",
2324
"build-config-interface": "dtsgen packages/angular-cli/lib/config/schema.json --out packages/angular-cli/lib/config/schema.d.ts",
2425
"eslint": "eslint .",
2526
"tslint": "tslint \"**/*.ts\" -c tslint.json -e \"**/blueprints/*/files/**/*.ts\" -e \"node_modules/**\" -e \"tmp/**\" -e \"dist/**\"",
@@ -81,6 +82,7 @@
8182
"less-loader": "^2.2.3",
8283
"lodash": "^4.11.1",
8384
"node-sass": "^3.10.1",
85+
"nodemon": "^1.11.0",
8486
"npm-run-all": "^3.0.0",
8587
"offline-plugin": "^3.4.1",
8688
"opn": "4.0.1",
@@ -104,6 +106,7 @@
104106
"stylus": "^0.54.5",
105107
"stylus-loader": "^2.1.0",
106108
"symlink-or-copy": "^1.0.3",
109+
"tiny-lr": "^1.0.2",
107110
"ts-loader": "^0.8.2",
108111
"tslint": "^3.15.1",
109112
"tslint-loader": "^2.1.4",

packages/angular-cli/blueprints/component/index.js

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ module.exports = {
1919
{ name: 'spec', type: Boolean }
2020
],
2121

22-
beforeInstall: function() {
22+
beforeInstall: function () {
2323
try {
24-
this.pathToModule = findParentModule(this.project, this.dynamicPath.dir);
24+
this.modulePaths = findParentModule(this.project, this.dynamicPath.dir);
2525
} catch(e) {
2626
throw `Error locating module for declaration\n\t${e}`;
2727
}
@@ -116,22 +116,25 @@ module.exports = {
116116
};
117117
},
118118

119-
afterInstall: function(options) {
119+
afterInstall: function (options) {
120120
if (options.dryRun) {
121121
return;
122122
}
123123

124124
const returns = [];
125125
const className = stringUtils.classify(`${options.entity.name}Component`);
126126
const fileName = stringUtils.dasherize(`${options.entity.name}.component`);
127-
const componentDir = path.relative(path.dirname(this.pathToModule), this.generatePath);
128-
const importPath = componentDir ? `./${componentDir}/${fileName}` : `./${fileName}`;
129127

130-
if (!options['skip-import']) {
131-
returns.push(
132-
astUtils.addDeclarationToModule(this.pathToModule, className, importPath)
133-
.then(change => change.apply(NodeHost)));
134-
}
128+
this.modulePaths.forEach((pathToModule) => {
129+
const componentDir = path.relative(path.dirname(pathToModule), this.generatePath);
130+
const importPath = componentDir ? `./${componentDir}/${fileName}` : `./${fileName}`;
131+
132+
if (!options['skip-import']) {
133+
returns.push(
134+
astUtils.addDeclarationToModule(pathToModule, className, importPath)
135+
.then(change => change.apply(NodeHost)));
136+
}
137+
});
135138

136139
return Promise.all(returns);
137140
}

packages/angular-cli/blueprints/directive/index.js

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ module.exports = {
1616
{ name: 'spec', type: Boolean }
1717
],
1818

19-
beforeInstall: function() {
19+
beforeInstall: function () {
2020
try {
21-
this.pathToModule = findParentModule(this.project, this.dynamicPath.dir);
21+
this.modulePaths = findParentModule(this.project, this.dynamicPath.dir);
2222
} catch(e) {
2323
throw `Error locating module for declaration\n\t${e}`;
2424
}
@@ -77,22 +77,25 @@ module.exports = {
7777
};
7878
},
7979

80-
afterInstall: function(options) {
80+
afterInstall: function (options) {
8181
if (options.dryRun) {
8282
return;
8383
}
8484

8585
const returns = [];
8686
const className = stringUtils.classify(`${options.entity.name}Directive`);
8787
const fileName = stringUtils.dasherize(`${options.entity.name}.directive`);
88-
const componentDir = path.relative(this.dynamicPath.appRoot, this.generatePath);
89-
const importPath = componentDir ? `./${componentDir}/${fileName}` : `./${fileName}`;
9088

91-
if (!options['skip-import']) {
92-
returns.push(
93-
astUtils.addDeclarationToModule(this.pathToModule, className, importPath)
94-
.then(change => change.apply(NodeHost)));
95-
}
89+
this.modulePaths.forEach((pathToModule) => {
90+
const componentDir = path.relative(this.dynamicPath.appRoot, this.generatePath);
91+
const importPath = componentDir ? `./${componentDir}/${fileName}` : `./${fileName}`;
92+
93+
if (!options['skip-import']) {
94+
returns.push(
95+
astUtils.addDeclarationToModule(pathToModule, className, importPath)
96+
.then(change => change.apply(NodeHost)));
97+
}
98+
});
9699

97100
return Promise.all(returns);
98101
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
export * from './app.component';
2-
export * from './app.module';
1+
export * from './app.component';<% if(!universal) { %>
2+
export * from './app.module';<% } %>

packages/angular-cli/blueprints/ng2/files/__path__/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
<link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-touch-icon-180x180.png">
2323
<link rel="apple-touch-startup-image" href="/icons/apple-touch-icon-180x180.png">
2424
<% } %>
25+
<% if (universal) { %><script src="http://localhost:35729/livereload.js?snipver=1"></script><% } %>
2526
</head>
2627
<body>
2728
<<%= prefix %>-root>Loading...</<%= prefix %>-root>

packages/angular-cli/blueprints/ng2/files/__path__/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"target": "es5",
1313
"typeRoots": [
1414
"<%= relativeRootPath %>/node_modules/@types"
15-
]
15+
]<% if(universal) { %>,
16+
"types": [ "node" ]<% } %>
1617
}
1718
}

packages/angular-cli/blueprints/ng2/files/angular-cli.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,15 @@
1111
"assets",
1212
"favicon.ico"
1313
],
14-
"index": "index.html",
15-
"main": "main.ts",
14+
"index": "index.html",<% if(!universal) { %>
15+
"main": "main.ts",<% } %><% if(universal) { %>
16+
"main": "client.ts",
17+
"nodeMain": "server.ts",<% } %>
1618
"test": "test.ts",
1719
"tsconfig": "tsconfig.json",
1820
"prefix": "<%= prefix %>",
1921
"mobile": <%= isMobile %>,
22+
"universal": <%= universal %>,
2023
"styles": [
2124
"styles.<%= styleExt %>"
2225
],

packages/angular-cli/blueprints/ng2/files/package.json

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,13 @@
1919
"@angular/http": "~2.1.0",
2020
"@angular/platform-browser": "~2.1.0",
2121
"@angular/platform-browser-dynamic": "~2.1.0",
22-
"@angular/router": "~3.1.0",
22+
"@angular/router": "~3.1.0",<% if(universal) { %>
23+
"@angular/platform-server": "~2.1.0",
24+
"angular2-platform-node": "~2.0.11",
25+
"angular2-universal": "~2.0.11",
26+
"angular2-universal-polyfills": "~2.0.11",
27+
"angular2-express-engine": "~2.0.11",
28+
"express": "^4.14.0",<% } %>
2329
"core-js": "^2.4.1",
2430
"rxjs": "5.0.0-beta.12",
2531
"ts-helpers": "^1.1.1",
@@ -34,7 +40,14 @@
3440
"preboot": "2.1.2",
3541
"parse5": "1.5.1",<% } %>
3642
"@types/jasmine": "^2.2.30",
37-
"@types/node": "^6.0.42",
43+
"@types/node": "^6.0.42",<% if(universal) { %>
44+
"@types/body-parser": "0.0.29",
45+
"@types/compression": "0.0.29",
46+
"@types/cookie-parser": "^1.3.29",
47+
"@types/express": "^4.0.29",
48+
"@types/express-serve-static-core": "^4.0.29",
49+
"@types/mime": "0.0.28",
50+
"@types/serve-static": "^1.7.27",<% } %>
3851
"angular-cli": "<%= version %>",
3952
"codelyzer": "1.0.0-beta.1",
4053
"jasmine-core": "2.4.1",

packages/angular-cli/blueprints/ng2/index.js

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
const Blueprint = require('ember-cli/lib/models/blueprint');
2-
const path = require('path');
1+
const Blueprint = require('ember-cli/lib/models/blueprint');
2+
const path = require('path');
33
const stringUtils = require('ember-cli-string-utils');
44
const getFiles = Blueprint.prototype.files;
55

@@ -11,6 +11,7 @@ module.exports = {
1111
{ name: 'prefix', type: String, default: 'app', aliases: ['p'] },
1212
{ name: 'style', type: String, default: 'css' },
1313
{ name: 'mobile', type: Boolean, default: false },
14+
{ name: 'universal', type: Boolean, default: false },
1415
{ name: 'routing', type: Boolean, default: false },
1516
{ name: 'inline-style', type: Boolean, default: false, aliases: ['is'] },
1617
{ name: 'inline-template', type: Boolean, default: false, aliases: ['it'] }
@@ -23,12 +24,20 @@ module.exports = {
2324
},
2425

2526
afterInstall: function (options) {
27+
var bluePrints = [];
28+
2629
if (options.mobile) {
27-
return Blueprint.load(path.join(__dirname, '../mobile')).install(options);
30+
bluePrints.push(Blueprint.load(path.join(__dirname, '../mobile')).install(options));
31+
}
32+
if (options.universal) {
33+
Blueprint.load(path.join(__dirname, '../ng2'));
34+
bluePrints.push(Blueprint.load(path.join(__dirname, '../universal')).install(options));
2835
}
36+
37+
return Promise.all(bluePrints);
2938
},
3039

31-
locals: function(options) {
40+
locals: function (options) {
3241
this.styleExt = options.style;
3342
this.version = require(path.resolve(__dirname, '../../package.json')).version;
3443

@@ -54,13 +63,14 @@ module.exports = {
5463
styleExt: this.styleExt,
5564
relativeRootPath: relativeRootPath,
5665
isMobile: options.mobile,
66+
universal: options.universal,
5767
routing: options.routing,
5868
inlineStyle: options.inlineStyle,
5969
inlineTemplate: options.inlineTemplate
6070
};
6171
},
6272

63-
files: function() {
73+
files: function () {
6474
var fileList = getFiles.call(this);
6575

6676
if (this.options && !this.options.routing) {
@@ -73,6 +83,11 @@ module.exports = {
7383
fileList = fileList.filter(p => p.indexOf('app.component.__styleext__') < 0);
7484
}
7585

86+
if (this.options && this.options.universal) {
87+
fileList = fileList.filter(p => p.indexOf('main.ts') < 0);
88+
fileList = fileList.filter(p => p.indexOf('app.module.ts') < 0);
89+
}
90+
7691
return fileList;
7792
},
7893

packages/angular-cli/blueprints/pipe/index.js

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ module.exports = {
1515
{ name: 'spec', type: Boolean }
1616
],
1717

18-
beforeInstall: function() {
18+
beforeInstall: function () {
1919
try {
20-
this.pathToModule = findParentModule(this.project, this.dynamicPath.dir);
20+
this.modulePaths = findParentModule(this.project, this.dynamicPath.dir);
2121
} catch(e) {
2222
throw `Error locating module for declaration\n\t${e}`;
2323
}
@@ -65,22 +65,25 @@ module.exports = {
6565
};
6666
},
6767

68-
afterInstall: function(options) {
68+
afterInstall: function (options) {
6969
if (options.dryRun) {
7070
return;
7171
}
7272

7373
const returns = [];
7474
const className = stringUtils.classify(`${options.entity.name}Pipe`);
7575
const fileName = stringUtils.dasherize(`${options.entity.name}.pipe`);
76-
const componentDir = path.relative(this.dynamicPath.appRoot, this.generatePath);
77-
cons 10000 t importPath = componentDir ? `./${componentDir}/${fileName}` : `./${fileName}`;
7876

79-
if (!options['skip-import']) {
80-
returns.push(
81-
astUtils.addDeclarationToModule(this.pathToModule, className, importPath)
82-
.then(change => change.apply(NodeHost)));
83-
}
77+
this.modulePaths.forEach((pathToModule) => {
78+
const componentDir = path.relative(this.dynamicPath.appRoot, this.generatePath);
79+
const importPath = componentDir ? `./${componentDir}/${fileName}` : `./${fileName}`;
80+
81+
if (!options['skip-import']) {
82+
returns.push(
83+
astUtils.addDeclarationToModule(pathToModule, className, importPath)
84+
.then(change => change.apply(NodeHost)));
85+
}
86+
});
8487

8588
return Promise.all(returns);
8689
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* This file and `main.node.ts` are identical, at the moment(!)
3+
* By splitting these, you're able to create logic, imports, etc that are "Platform" specific.
4+
* If you want your code to be completely Universal and don't need that
5+
* You can also just have 1 file, that is imported into both
6+
* client.ts and server.ts
7+
*/
8+
9+
import { NgModule } from '@angular/core';
10+
import { UniversalModule } from 'angular2-universal';
11+
import { FormsModule } from '@angular/forms';
12+
import { AppComponent } from './app/index';
13+
// import { RouterModule } from '@angular/router';
14+
// import { appRoutes } from './app/app.routing';
15+
16+
/**
17+
* Top-level NgModule "container"
18+
*/
19+
@NgModule({
20+
/** Root App Component */
21+
bootstrap: [ AppComponent ],
22+
/** Our Components */
23+
declarations: [ AppComponent ],
24+
imports: [
25+
/**
26+
* NOTE: Needs to be your first import (!)
27+
* BrowserModule, HttpModule, and JsonpModule are included
28+
*/
29+
UniversalModule,
30+
FormsModule
31+
/**
32+
* using routes
33+
*/
34+
// RouterModule.forRoot(appRoutes)
35+
]
36+
})
37+
export class AppModule {
38+
39+
}

0 commit comments

Comments
 (0)
0