77 */
88import { experimental , strings , normalize } from '@angular-devkit/core' ;
99import {
10- Rule ,
11- SchematicContext ,
12- SchematicsException ,
13- Tree ,
1410 apply ,
1511 chain ,
12+ externalSchematic ,
13+ filter ,
1614 mergeWith ,
15+ noop ,
16+ Rule ,
17+ SchematicContext ,
18+ SchematicsException ,
1719 template ,
20+ Tree ,
1821 url ,
19- noop ,
20- filter ,
21- externalSchematic ,
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' ;
26- import { BrowserBuilderOptions } from '@schematics/angular/utility/workspace-models' ;
2726import {
2827 addPackageJsonDependency ,
2928 NodeDependencyType ,
3029} from '@schematics/angular/utility/dependencies' ;
3130import { getProject } from '@schematics/angular/utility/project' ;
3231import { getProjectTargets } from '@schematics/angular/utility/project-targets' ;
33- import * as ts from 'typescript' ;
34- import { findAppServerModulePath } from './utils' ;
35- import { addSymbolToNgModuleMetadata , insertImport } from '@schematics/angular/utility/ast-utils' ;
3632import { InsertChange } from '@schematics/angular/utility/change' ;
33+ import {
34+ addSymbolToNgModuleMetadata ,
35+ findNodes ,
36+ insertAfterLastOccurrence ,
37+ insertImport
38+ } from '@schematics/angular/utility/ast-utils' ;
39+ import * as ts from 'typescript' ;
40+ import { findAppServerModulePath , generateExport , getTsSourceFile , getTsSourceText } from './utils' ;
41+ import { updateWorkspace } from '@schematics/angular/utility/workspace' ;
3742
3843// TODO(CaerusKaru): make these configurable
3944const BROWSER_DIST = 'dist/browser' ;
@@ -102,54 +107,50 @@ function addDependenciesAndScripts(options: UniversalOptions): Rule {
102107 pkg . scripts [ 'serve:ssr' ] = `node dist/${ serverFileName } ` ;
103108 pkg . scripts [ 'build:ssr' ] = 'npm run build:client-and-server-bundles && npm run compile:server' ;
104109 pkg . scripts [ 'build:client-and-server-bundles' ] =
105- `ng build --prod && ng run ${ options . clientProject } :server:production` ;
110+ // tslint:disable:max-line-length
111+ `ng build --prod && ng run ${ options . clientProject } :server:production --bundleDependencies all` ;
106112
107113 host . overwrite ( pkgPath , JSON . stringify ( pkg , null , 2 ) ) ;
108114
109115 return host ;
110116 } ;
111117}
112118
113- function updateConfigFile ( options : UniversalOptions ) : Rule {
114- return ( host : Tree ) => {
115- const workspace = getWorkspace ( host ) ;
116- if ( ! workspace . projects [ options . clientProject ] ) {
117- throw new SchematicsException ( `Client app ${ options . clientProject } not found.` ) ;
118- }
119-
120- const clientProject = workspace . projects [ options . clientProject ] ;
121- if ( ! clientProject . architect ) {
122- throw new Error ( 'Client project architect not found.' ) ;
123- }
119+ function updateConfigFile ( options : UniversalOptions ) {
120+ return updateWorkspace ( ( workspace => {
121+ const clientProject = workspace . projects . get ( options . clientProject ) ;
122+ if ( clientProject ) {
123+ const buildTarget = clientProject . targets . get ( 'build' ) ;
124+ const serverTarget = clientProject . targets . get ( 'build' ) ;
124125
125- // We have to check if the project config has a server target, because
126- // if the Universal step in this schematic isn't run, it can't be guaranteed
127- // to exist
128- if ( ! clientProject . architect . server ) {
129- return ;
130- }
131-
132- clientProject . architect . server . configurations = {
133- production : {
134- fileReplacements : [
135- {
136- replace : 'src/environments/environment.ts' ,
137- with : 'src/environments/environment.prod.ts'
138- }
139- ]
126+ // We have to check if the project config has a server target, because
127+ // if the Universal step in this schematic isn't run, it can't be guaranteed
128+ // to exist
129+ if ( ! serverTarget || ! buildTarget ) {
130+ return ;
140131 }
141- } ;
142- // TODO(CaerusKaru): make this configurable
143- clientProject . architect . server . options . outputPath = SERVER_DIST ;
144- // TODO(CaerusKaru): make this configurable
145- ( clientProject . architect . build . options as BrowserBuilderOptions ) . outputPath = BROWSER_DIST ;
146132
147- const workspacePath = getWorkspacePath ( host ) ;
133+ serverTarget . configurations = {
134+ production : {
135+ fileReplacements : [
136+ {
137+ replace : 'src/environments/environment.ts' ,
138+ with : 'src/environments/environment.prod.ts'
139+ }
140+ ]
141+ }
142+ } ;
148143
149- host . overwrite ( workspacePath , JSON . stringify ( workspace , null , 2 ) ) ;
144+ serverTarget . options = {
145+ ...serverTarget . options ,
146+ outputPath : SERVER_DIST ,
147+ } ;
150148
151- return host ;
152- } ;
149+ buildTarget . options = {
150+ outputPath : BROWSER_DIST ,
151+ } ;
152+ }
153+ } ) ) ;
153154}
154155
155156function addModuleMapLoader ( options : UniversalOptions ) : Rule {
@@ -162,7 +163,6 @@ function addModuleMapLoader(options: UniversalOptions): Rule {
162163 return ;
163164 }
164165 const mainPath = normalize ( '/' + clientTargets . server . options . main ) ;
165-
166166 const appServerModuleRelativePath = findAppServerModulePath ( host , mainPath ) ;
167167 const modulePath = normalize (
168168 `/${ clientProject . root } /src/${ appServerModuleRelativePath } .ts` ) ;
@@ -194,15 +194,33 @@ function addModuleMapLoader(options: UniversalOptions): Rule {
194194 } ;
195195}
196196
197- function getTsSourceFile ( host : Tree , path : string ) : ts . SourceFile {
198- const buffer = host . read ( path ) ;
199- if ( ! buffer ) {
200- throw new SchematicsException ( `Could not read file (${ path } ).` ) ;
201- }
202- const content = buffer . toString ( ) ;
203- const source = ts . createSourceFile ( path , content , ts . ScriptTarget . Latest , true ) ;
197+ function addExports ( options : UniversalOptions ) : Rule {
198+ return ( host : Tree ) => {
199+ const clientProject = getProject ( host , options . clientProject ) ;
200+ const clientTargets = getProjectTargets ( clientProject ) ;
201+
202+ if ( ! clientTargets . server ) {
203+ // If they skipped Universal schematics and don't have a server target,
204+ // just get out
205+ return ;
206+ }
204207
205- return source ;
208+ const mainPath = normalize ( '/' + clientTargets . server . options . main ) ;
209+ const mainSourceFile = getTsSourceFile ( host , mainPath ) ;
210+ let mainText = getTsSourceText ( host , mainPath ) ;
211+ const mainRecorder = host . beginUpdate ( mainPath ) ;
212+ const hapiEngineExport = generateExport ( mainSourceFile , [ 'ngHapiEngine' ] ,
213+ '@nguniversal/hapi-engine' ) ;
214+ const moduleMapExport = generateExport ( mainSourceFile , [ 'provideModuleMap' ] ,
215+ '@nguniversal/module-map-ngfactory-loader' ) ;
216+ const exports = findNodes ( mainSourceFile , ts . SyntaxKind . ExportDeclaration ) ;
217+ const addedExports = `\n${ hapiEngineExport } \n${ moduleMapExport } \n` ;
218+ const exportChange = insertAfterLastOccurrence ( exports , addedExports , mainText ,
219+ 0 ) as InsertChange ;
220+
221+ mainRecorder . insertLeft ( exportChange . pos , exportChange . toAdd ) ;
222+ host . commitUpdate ( mainRecorder ) ;
223+ } ;
206224}
207225
208226export default function ( options : UniversalOptions ) : Rule {
@@ -236,6 +254,7 @@ export default function (options: UniversalOptions): Rule {
236254 mergeWith ( rootSource ) ,
237255 addDependenciesAndScripts ( options ) ,
238256 addModuleMapLoader ( options ) ,
257+ addExports ( options ) ,
239258 ] ) ;
240259 } ;
241260}
0 commit comments