11import {
2+ NgModuleFactory ,
3+ NgModule
4+ } from '@angular/core' ;
5+
6+ import {
7+ CompilerOptions ,
28 Diagnostic ,
3- FormatDiagnosticsHost ,
9+ ModuleKind ,
10+ ModuleResolutionKind ,
411 Program ,
5- SourceFile ,
612 WriteFileCallback ,
713 createCompilerHost ,
814 createProgram ,
9- formatDiagnostics ,
1015 getPreEmitDiagnostics ,
1116} from 'typescript' ;
1217
13- import { EOL } from 'os' ;
14- import { cwd } from 'process' ;
15- import { relative } from 'path' ;
18+ import { dirname } from 'path' ;
1619
17- import { ApplicationBundle } from './bundle' ;
18- import { CompilerOptions , loadProjectOptions } from './options' ;
20+ import { CompileOptions , loadProjectOptions } from './options' ;
1921import { CompilerException } from 'exception' ;
2022import { Project } from '../project' ;
21-
23+ import { Reflector } from 'platform' ;
24+ import { VirtualMachine } from './vm' ;
25+ import { diagnosticsToException } from './diagnostics' ;
2226import { flatten } from 'transformation' ;
2327
2428export class Compiler {
25- private options : CompilerOptions ;
29+ private options : CompileOptions ;
2630
27- constructor ( project : Project ) {
31+ constructor ( private project : Project ) {
2832 this . options = loadProjectOptions ( project ) ;
33+
34+ if ( project . ngModule == null ||
35+ project . ngModule . length < 2 ) {
36+ throw new CompilerException ( 'Compiler requires a module ID and an export name in ngModule' ) ;
37+ }
38+ }
39+
40+ compile ( ) : NgModuleFactory < any > {
41+ const vm = new VirtualMachine ( ) ;
42+ try {
43+ this . compileToVm ( vm ) ;
44+
45+ const [ moduleId , exported ] = this . project . ngModule ;
46+
47+ const requiredModule = vm . require ( moduleId ) ;
48+ if ( requiredModule == null ) {
49+ throw new CompilerException ( `Attempted to require ${ moduleId } but received a null or undefined object` ) ;
50+ }
51+
52+ const rootModule =
53+ ! exported
54+ ? requiredModule
55+ : requiredModule [ exported ] ;
56+
57+ if ( Reflector . decorated ( rootModule , NgModule ) === false ) {
58+ throw new CompilerException ( `Root module type ${ rootModule . name } is not decorated with @NgModule` ) ;
59+ }
60+
61+ return rootModule ;
62+ }
63+ finally {
64+ vm . dispose ( ) ;
65+ }
2966 }
3067
31- async compile ( ) : Promise < ApplicationBundle > {
68+ private compileToVm ( vm : VirtualMachine ) {
3269 const program = this . createProgram ( null ) ;
3370
34- return new Promise ( resolve => {
35- const writer : WriteFileCallback =
36- ( fileName : string , data : string , writeByteOrderMark : boolean , onError ?: ( message : string ) => void , sourceFiles ?: SourceFile [ ] ) => {
37- throw new CompilerException ( 'Not implemented' ) ;
38- } ;
71+ const compilerOptions = program . getCompilerOptions ( ) ;
3972
40- program . emit ( undefined , writer , null , false ) ;
41- } ) ;
73+ const writer : WriteFileCallback =
74+ ( fileName , data , writeByteOrderMark , onError ?, sourceFiles ?) => {
75+ try {
76+ const moduleId = this . moduleIdFromFilename ( fileName , compilerOptions ) ;
77+
78+ vm . define ( fileName , moduleId , data ) ;
79+ }
80+ catch ( exception ) {
81+ if ( onError == null ) {
82+ throw exception ;
83+ }
84+ onError ( exception . stack ) ;
85+ }
86+ } ;
87+
88+ program . emit ( undefined , writer , null , false ) ;
4289 }
4390
4491 private createProgram ( previousProgram ?: Program ) : Program {
4592 const { typescriptOptions} = this . options ;
4693
47- const compilerHost = createCompilerHost ( typescriptOptions . options , true ) ;
94+ const options = Object . assign ( { } , typescriptOptions . options , {
95+ declaration : false ,
96+ sourceMap : false ,
97+ sourceRoot : null ,
98+ inlineSourceMap : false ,
99+ module : ModuleKind . CommonJS ,
100+ moduleResolution : ModuleResolutionKind . NodeJs ,
101+ } ) ;
102+
103+ const compilerHost = createCompilerHost ( options , true ) ;
48104
49105 const program = createProgram (
50106 typescriptOptions . fileNames ,
51- typescriptOptions . options ,
107+ options ,
52108 compilerHost ,
53109 previousProgram ) ;
54110
@@ -64,18 +120,37 @@ export class Compiler {
64120 }
65121
66122 private conditionalException ( diagnostics : Array < Diagnostic > ) {
67- if ( diagnostics == null || diagnostics . length === 0 ) {
123+ if ( diagnostics == null ||
124+ diagnostics . length === 0 ) {
68125 return ;
69126 }
127+ throw new CompilerException ( diagnosticsToException ( diagnostics ) ) ;
128+ }
70129
71- const host : FormatDiagnosticsHost = {
72- getCurrentDirectory : ( ) : string => cwd ( ) ,
73- getCanonicalFileName : ( filename : string ) : string => relative ( cwd ( ) , filename ) ,
74- getNewLine : ( ) : string => EOL ,
75- } ;
76-
77- const formatted = formatDiagnostics ( diagnostics , host ) ;
130+ private moduleIdFromFilename ( filename : string , compilerOptions : CompilerOptions ) : string {
131+ const projectPath = ( path : string ) : string =>
132+ path . toLowerCase ( ) . endsWith ( '.json' )
133+ ? dirname ( path )
134+ : path ;
135+
136+ const candidates = [
137+ compilerOptions . baseUrl ,
138+ compilerOptions . outDir ,
139+ compilerOptions . rootDir ,
140+ compilerOptions . project
141+ ? projectPath ( compilerOptions . project )
142+ : null ,
143+ ] . concat ( compilerOptions . rootDirs || [ ] ) . filter ( v => v ) ;
144+
145+ const lowerfile = filename . toLowerCase ( ) ;
146+
147+ const matches = candidates . map ( v => v . toLowerCase ( ) ) . filter ( p => lowerfile . startsWith ( p ) ) ;
148+ if ( matches . length > 0 ) {
149+ return filename . substring ( matches [ 0 ] . length )
150+ . replace ( / \. j s $ / , String ( ) )
151+ . replace ( / ^ \/ / , String ( ) ) ;
152+ }
78153
79- throw new CompilerException ( formatted ) ;
154+ throw new CompilerException ( `Cannot determine module ID of file ${ filename } ` ) ;
80155 }
81156}
0 commit comments