@@ -21,22 +21,24 @@ import {
2121 url ,
2222} from '@angular-devkit/schematics' ;
2323import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks' ;
24- import { getWorkspace , getWorkspacePath } from '@schematics/angular/utility/config' ;
24+ import { getWorkspace } from '@schematics/angular/utility/config' ;
2525import { Schema as UniversalOptions } from './schema' ;
2626import {
2727 addPackageJsonDependency ,
2828 NodeDependencyType ,
2929} from '@schematics/angular/utility/dependencies' ;
30- import { BrowserBuilderOptions } from '@schematics/angular/utility/workspace-models' ;
3130import { getProject } from '@schematics/angular/utility/project' ;
32- import {
33- getProjectTargets ,
34- targetBuildNotFoundError ,
35- } from '@schematics/angular/utility/project-targets' ;
31+ import { getProjectTargets } from '@schematics/angular/utility/project-targets' ;
3632import { InsertChange } from '@schematics/angular/utility/change' ;
37- import { addSymbolToNgModuleMetadata , insertImport } from '@schematics/angular/utility/ast-utils' ;
33+ import {
34+ addSymbolToNgModuleMetadata ,
35+ findNodes ,
36+ insertAfterLastOccurrence ,
37+ insertImport
38+ } from '@schematics/angular/utility/ast-utils' ;
3839import * as ts from 'typescript' ;
39- import { findAppServerModulePath } from './utils' ;
40+ import { findAppServerModulePath , generateExport , getTsSourceFile , getTsSourceText } from './utils' ;
41+ import { updateWorkspace } from '@schematics/angular/utility/workspace' ;
4042
4143// TODO(CaerusKaru): make these configurable
4244const BROWSER_DIST = 'dist/browser' ;
@@ -100,54 +102,50 @@ function addDependenciesAndScripts(options: UniversalOptions): Rule {
100102 pkg . scripts [ 'serve:ssr' ] = `node dist/${ serverFileName } ` ;
101103 pkg . scripts [ 'build:ssr' ] = 'npm run build:client-and-server-bundles && npm run compile:server' ;
102104 pkg . scripts [ 'build:client-and-server-bundles' ] =
103- `ng build --prod && ng run ${ options . clientProject } :server:production` ;
105+ // tslint:disable:max-line-length
106+ `ng build --prod && ng run ${ options . clientProject } :server:production --bundleDependencies all` ;
104107
105108 host . overwrite ( pkgPath , JSON . stringify ( pkg , null , 2 ) ) ;
106109
107110 return host ;
108111 } ;
109112}
110113
111- function updateConfigFile ( options : UniversalOptions ) : Rule {
112- return ( host : Tree ) => {
113- const workspace = getWorkspace ( host ) ;
114- if ( ! workspace . projects [ options . clientProject ] ) {
115- throw new SchematicsException ( `Client app ${ options . clientProject } not found.` ) ;
116- }
117-
118- const clientProject = workspace . projects [ options . clientProject ] ;
119- if ( ! clientProject . architect ) {
120- throw new Error ( 'Client project architect not found.' ) ;
121- }
122-
123- // We have to check if the project config has a server target, because
124- // if the Universal step in this schematic isn't run, it can't be guaranteed
125- // to exist
126- if ( ! clientProject . architect . server ) {
127- return ;
128- }
129-
130- clientProject . architect . server . configurations = {
131- production : {
132- fileReplacements : [
133- {
134- replace : 'src/environments/environment.ts' ,
135- with : 'src/environments/environment.prod.ts'
136- }
137- ]
114+ function updateConfigFile ( options : UniversalOptions ) {
115+ return updateWorkspace ( ( workspace => {
116+ const clientProject = workspace . projects . get ( options . clientProject ) ;
117+ if ( clientProject ) {
118+ const buildTarget = clientProject . targets . get ( 'build' ) ;
119+ const serverTarget = clientProject . targets . get ( 'build' ) ;
120+
121+ // We have to check if the project config has a server target, because
122+ // if the Universal step in this schematic isn't run, it can't be guaranteed
123+ // to exist
124+ if ( ! serverTarget || ! buildTarget ) {
125+ return ;
138126 }
139- } ;
140- // TODO(CaerusKaru): make this configurable
141- clientProject . architect . server . options . outputPath = SERVER_DIST ;
142- // TODO(CaerusKaru): make this configurable
143- ( clientProject . architect . build . options as BrowserBuilderOptions ) . outputPath = BROWSER_DIST ;
144-
145- const workspacePath = getWorkspacePath ( host ) ;
146127
147- host . overwrite ( workspacePath , JSON . stringify ( workspace , null , 2 ) ) ;
148-
149- return host ;
150- } ;
128+ serverTarget . configurations = {
129+ production : {
130+ fileReplacements : [
131+ {
132+ replace : 'src/environments/environment.ts' ,
133+ with : 'src/environments/environment.prod.ts'
134+ }
135+ ]
136+ }
137+ } ;
138+
139+ serverTarget . options = {
140+ ...serverTarget . options ,
141+ outputPath : SERVER_DIST ,
142+ } ;
143+
144+ buildTarget . options = {
145+ outputPath : BROWSER_DIST ,
146+ } ;
147+ }
148+ } ) ) ;
151149}
152150
153151function addModuleMapLoader ( options : UniversalOptions ) : Rule {
@@ -160,7 +158,6 @@ function addModuleMapLoader(options: UniversalOptions): Rule {
160158 return ;
161159 }
162160 const mainPath = normalize ( '/' + clientTargets . server . options . main ) ;
163-
164161 const appServerModuleRelativePath = findAppServerModulePath ( host , mainPath ) ;
165162 const modulePath = normalize (
166163 `/${ clientProject . root } /src/${ appServerModuleRelativePath } .ts` ) ;
@@ -192,15 +189,33 @@ function addModuleMapLoader(options: UniversalOptions): Rule {
192189 } ;
193190}
194191
195- function getTsSourceFile ( host : Tree , path : string ) : ts . SourceFile {
196- const buffer = host . read ( path ) ;
197- if ( ! buffer ) {
198- throw new SchematicsException ( `Could not read file (${ path } ).` ) ;
199- }
200- const content = buffer . toString ( ) ;
201- const source = ts . createSourceFile ( path , content , ts . ScriptTarget . Latest , true ) ;
192+ function addExports ( options : UniversalOptions ) : Rule {
193+ return ( host : Tree ) => {
194+ const clientProject = getProject ( host , options . clientProject ) ;
195+ const clientTargets = getProjectTargets ( clientProject ) ;
196+
197+ if ( ! clientTargets . server ) {
198+ // If they skipped Universal schematics and don't have a server target,
199+ // just get out
200+ return ;
201+ }
202202
203- return source ;
203+ const mainPath = normalize ( '/' + clientTargets . server . options . main ) ;
204+ const mainSourceFile = getTsSourceFile ( host , mainPath ) ;
205+ let mainText = getTsSourceText ( host , mainPath ) ;
206+ const mainRecorder = host . beginUpdate ( mainPath ) ;
207+ const expressEngineExport = generateExport ( mainSourceFile , [ 'ngExpressEngine' ] ,
208+ '@nguniversal/express-engine' ) ;
209+ const moduleMapExport = generateExport ( mainSourceFile , [ 'provideModuleMap' ] ,
210+ '@nguniversal/module-map-ngfactory-loader' ) ;
211+ const exports = findNodes ( mainSourceFile , ts . SyntaxKind . ExportDeclaration ) ;
212+ const addedExports = `\n${ expressEngineExport } \n${ moduleMapExport } \n` ;
213+ const exportChange = insertAfterLastOccurrence ( exports , addedExports , mainText ,
214+ 0 ) as InsertChange ;
215+
216+ mainRecorder . insertLeft ( exportChange . pos , exportChange . toAdd ) ;
217+ host . commitUpdate ( mainRecorder ) ;
218+ } ;
204219}
205220
206221export default function ( options : UniversalOptions ) : Rule {
@@ -234,6 +249,7 @@ export default function (options: UniversalOptions): Rule {
234249 mergeWith ( rootSource ) ,
235250 addDependenciesAndScripts ( options ) ,
236251 addModuleMapLoader ( options ) ,
252+ addExports ( options ) ,
237253 ] ) ;
238254 } ;
239255}
0 commit comments