-
Notifications
You must be signed in to change notification settings - Fork 12k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(@angular/cli): Add ability to build AppShell
- Loading branch information
Showing
6 changed files
with
263 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { requireProjectModule } from '../utilities/require-project-module'; | ||
import { join } from 'path'; | ||
|
||
const fs = require('fs'); | ||
const Task = require('../ember-cli/lib/models/task'); | ||
|
||
export interface RenderUniversalTaskOptions { | ||
inputIndexPath: string; | ||
route: string; | ||
serverOutDir: string; | ||
outputIndexPath: string; | ||
} | ||
|
||
export default Task.extend({ | ||
run: function(options: RenderUniversalTaskOptions): Promise<any> { | ||
require('zone.js/dist/zone-node'); | ||
|
||
const renderModuleFactory = | ||
requireProjectModule(this.project.root, '@angular/platform-server').renderModuleFactory; | ||
|
||
// Get the main bundle from the server build's output directory. | ||
const serverDir = fs.readdirSync(options.serverOutDir); | ||
const serverMainBundle = serverDir | ||
.filter((file: string) => /main\.[a-zA-Z0-9]{20}.bundle\.js/.test(file))[0]; | ||
const serverBundlePath = join(options.serverOutDir, serverMainBundle); | ||
const AppServerModuleNgFactory = require(serverBundlePath).AppServerModuleNgFactory; | ||
|
||
const index = fs.readFileSync(options.inputIndexPath, 'utf8'); | ||
// Render to HTML and overwrite the client index file. | ||
return renderModuleFactory(AppServerModuleNgFactory, {document: index, url: options.route}) | ||
.then((html: string) => fs.writeFileSync(options.outputIndexPath, html)); | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { ng, npm } from '../../utils/process'; | ||
import { expectFileToMatch } from '../../utils/fs'; | ||
import { getGlobalVariable } from '../../utils/env'; | ||
import { expectToFail } from '../../utils/utils'; | ||
|
||
|
||
export default function () { | ||
// Skip this in ejected tests. | ||
if (getGlobalVariable('argv').eject) { | ||
return Promise.resolve(); | ||
} | ||
|
||
// Skip in nightly tests. | ||
if (getGlobalVariable('argv').nightly) { | ||
return Promise.resolve(); | ||
} | ||
|
||
return Promise.resolve() | ||
.then(() => ng('generate', 'appShell', 'name', '--universal-app', 'universal')) | ||
.then(() => npm('install')) | ||
.then(() => ng('build', '--prod')) | ||
.then(() => expectFileToMatch('dist/index.html', /app-shell works!/)) | ||
.then(() => ng('build', '--prod', '--skip-app-shell')) | ||
.then(() => expectToFail(() => expectFileToMatch('dist/index.html', /app-shell works!/))); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
import { ng, npm } from '../../utils/process'; | ||
import { expectFileToMatch, writeFile } from '../../utils/fs'; | ||
import { getGlobalVariable } from '../../utils/env'; | ||
import { expectToFail } from '../../utils/utils'; | ||
import { updateJsonFile } from '../../utils/project'; | ||
import { readNgVersion } from '../../utils/version'; | ||
import { stripIndent } from 'common-tags'; | ||
|
||
|
||
export default function () { | ||
// Skip this in ejected tests. | ||
if (getGlobalVariable('argv').eject) { | ||
return Promise.resolve(); | ||
} | ||
|
||
let platformServerVersion = readNgVersion(); | ||
|
||
if (getGlobalVariable('argv').nightly) { | ||
platformServerVersion = 'github:angular/platform-server-builds'; | ||
} | ||
|
||
return Promise.resolve() | ||
.then(() => updateJsonFile('.angular-cli.json', configJson => { | ||
const app = configJson['apps'][0]; | ||
app['appShell'] = { | ||
app: '1', | ||
route: 'shell' | ||
}; | ||
configJson['apps'].push({ | ||
platform: 'server', | ||
root: 'src', | ||
outDir: 'dist-server', | ||
assets: [ | ||
'assets', | ||
'favicon.ico' | ||
], | ||
index: 'index.html', | ||
main: 'main.server.ts', | ||
test: 'test.ts', | ||
tsconfig: 'tsconfig.server.json', | ||
testTsconfig: 'tsconfig.spec.json', | ||
prefix: 'app', | ||
styles: [ | ||
'styles.css' | ||
], | ||
scripts: [], | ||
environmentSource: 'environments/environment.ts', | ||
environments: { | ||
dev: 'environments/environment.ts', | ||
prod: 'environments/environment.prod.ts' | ||
} | ||
}); | ||
})) | ||
.then(() => writeFile('src/app/app.module.ts', stripIndent` | ||
import { BrowserModule } from '@angular/platform-browser'; | ||
import { NgModule } from '@angular/core'; | ||
import { RouterModule } from '@angular/router'; | ||
import { AppComponent } from './app.component'; | ||
@NgModule({ | ||
imports: [ | ||
BrowserModule.withServerTransition({ appId: 'appshell-play' }), | ||
RouterModule | ||
], | ||
declarations: [AppComponent], | ||
bootstrap: [AppComponent] | ||
}) | ||
export class AppModule { } | ||
`)) | ||
.then(() => writeFile('src/app/app.component.html', stripIndent` | ||
Hello World | ||
<router-outlet></router-outlet> | ||
`)) | ||
.then(() => writeFile('src/tsconfig.server.json', stripIndent` | ||
{ | ||
"extends": "../tsconfig.json", | ||
"compilerOptions": { | ||
"outDir": "../out-tsc/app", | ||
"baseUrl": "./", | ||
"module": "commonjs", | ||
"types": [] | ||
}, | ||
"exclude": [ | ||
"test.ts", | ||
"**/*.spec.ts" | ||
], | ||
"angularCompilerOptions": { | ||
"entryModule": "app/app.server.module#AppServerModule" | ||
} | ||
} | ||
`)) | ||
.then(() => writeFile('src/main.server.ts', stripIndent` | ||
export {AppServerModule} from './app/app.server.module'; | ||
`)) | ||
.then(() => writeFile('src/app/app.server.module.ts', stripIndent` | ||
import {NgModule} from '@angular/core'; | ||
import {ServerModule} from '@angular/platform-server'; | ||
import { Routes, RouterModule } from '@angular/router'; | ||
import { AppModule } from './app.module'; | ||
import { AppComponent } from './app.component'; | ||
import { ShellComponent } from './shell.component'; | ||
const routes: Routes = [ | ||
{ path: 'shell', component: ShellComponent } | ||
]; | ||
@NgModule({ | ||
imports: [ | ||
// The AppServerModule should import your AppModule followed | ||
// by the ServerModule from @angular/platform-server. | ||
AppModule, | ||
ServerModule, | ||
RouterModule.forRoot(routes), | ||
], | ||
// Since the bootstrapped component is not inherited from your | ||
// imported AppModule, it needs to be repeated here. | ||
bootstrap: [AppComponent], | ||
declarations: [ShellComponent], | ||
}) | ||
export class AppServerModule {} | ||
`)) | ||
.then(() => writeFile('src/app/shell.component.ts', stripIndent` | ||
import { Component } from '@angular/core'; | ||
@Component({ | ||
selector: 'app-shell', | ||
template: '<p>shell Works!</p>', | ||
styles: [] | ||
}) | ||
export class ShellComponent {} | ||
`)) | ||
.then(() => updateJsonFile('package.json', packageJson => { | ||
const dependencies = packageJson['dependencies']; | ||
dependencies['@angular/platform-server'] = platformServerVersion; | ||
}) | ||
.then(() => npm('install'))) | ||
.then(() => ng('build', '--prod')) | ||
.then(() => expectFileToMatch('dist/index.html', /shell Works!/)) | ||
.then(() => ng('build', '--prod', '--skip-app-shell')) | ||
.then(() => expectToFail(() => expectFileToMatch('dist/index.html', /shell Works!/))); | ||
} |