5
5
* Use of this source code is governed by an MIT-style license that can be
6
6
* found in the LICENSE file at https://angular.io/license
7
7
*/
8
- import { experimental , strings } from '@angular-devkit/core' ;
8
+ import { experimental , strings , normalize } from '@angular-devkit/core' ;
9
9
import {
10
10
Rule ,
11
11
SchematicContext ,
@@ -28,6 +28,12 @@ import {
28
28
addPackageJsonDependency ,
29
29
NodeDependencyType ,
30
30
} from '@schematics/angular/utility/dependencies' ;
31
+ import { getProject } from '@schematics/angular/utility/project' ;
32
+ import { 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' ;
36
+ import { InsertChange } from '@schematics/angular/utility/change' ;
31
37
32
38
// TODO(CaerusKaru): make these configurable
33
39
const BROWSER_DIST = 'dist/browser' ;
@@ -146,6 +152,59 @@ function updateConfigFile(options: UniversalOptions): Rule {
146
152
} ;
147
153
}
148
154
155
+ function addModuleMapLoader ( options : UniversalOptions ) : Rule {
156
+ return ( host : Tree ) => {
157
+ const clientProject = getProject ( host , options . clientProject ) ;
158
+ const clientTargets = getProjectTargets ( clientProject ) ;
159
+ if ( ! clientTargets . server ) {
160
+ // If they skipped Universal schematics and don't have a server target,
161
+ // just get out
162
+ return ;
163
+ }
164
+ const mainPath = normalize ( '/' + clientTargets . server . options . main ) ;
165
+
166
+ const appServerModuleRelativePath = findAppServerModulePath ( host , mainPath ) ;
167
+ const modulePath = normalize (
168
+ `/${ clientProject . root } /src/${ appServerModuleRelativePath } .ts` ) ;
169
+
170
+ // Add the module map loader import
171
+ let moduleSource = getTsSourceFile ( host , modulePath ) ;
172
+ const importModule = 'ModuleMapLoaderModule' ;
173
+ const importPath = '@nguniversal/module-map-ngfactory-loader' ;
174
+ const moduleMapImportChange = insertImport ( moduleSource , modulePath , importModule ,
175
+ importPath ) as InsertChange ;
176
+ if ( moduleMapImportChange ) {
177
+ const recorder = host . beginUpdate ( modulePath ) ;
178
+ recorder . insertLeft ( moduleMapImportChange . pos , moduleMapImportChange . toAdd ) ;
179
+ host . commitUpdate ( recorder ) ;
180
+ }
181
+
182
+ // Add the module map loader module to the imports
183
+ const importText = 'ModuleMapLoaderModule' ;
184
+ moduleSource = getTsSourceFile ( host , modulePath ) ;
185
+ const metadataChanges = addSymbolToNgModuleMetadata (
186
+ moduleSource , modulePath , 'imports' , importText ) ;
187
+ if ( metadataChanges ) {
188
+ const recorder = host . beginUpdate ( modulePath ) ;
189
+ metadataChanges . forEach ( ( change : InsertChange ) => {
190
+ recorder . insertRight ( change . pos , change . toAdd ) ;
191
+ } ) ;
192
+ host . commitUpdate ( recorder ) ;
193
+ }
194
+ } ;
195
+ }
196
+
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 ) ;
204
+
205
+ return source ;
206
+ }
207
+
149
208
export default function ( options : UniversalOptions ) : Rule {
150
209
return ( host : Tree , context : SchematicContext ) => {
151
210
const clientProject = getClientProject ( host , options ) ;
@@ -176,6 +235,7 @@ export default function (options: UniversalOptions): Rule {
176
235
updateConfigFile ( options ) ,
177
236
mergeWith ( rootSource ) ,
178
237
addDependenciesAndScripts ( options ) ,
238
+ addModuleMapLoader ( options ) ,
179
239
] ) ;
180
240
} ;
181
241
}
0 commit comments