| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @license | 
					
						
							|  |  |  |  * Copyright Google Inc. All Rights Reserved. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Use of this source code is governed by an MIT-style license that can be | 
					
						
							|  |  |  |  * found in the LICENSE file at https://angular.io/license
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:38 -07:00
										 |  |  | import {AotCompilerOptions, AotSummaryResolver, CompileMetadataResolver, CompilerConfig, DEFAULT_INTERPOLATION_CONFIG, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, HtmlParser, Lexer, NgModuleResolver, ParseError, Parser, PipeResolver, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, TemplateParser, TypeScriptEmitter, analyzeNgModules, createAotUrlResolver} from '@angular/compiler'; | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  | import {ViewEncapsulation} from '@angular/core'; | 
					
						
							|  |  |  | import * as ts from 'typescript'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-28 14:56:41 -08:00
										 |  |  | import {NgAnalyzedModules} from '../../src/aot/compiler'; | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  | import {ConstantPool} from '../../src/constant_pool'; | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:38 -07:00
										 |  |  | import * as html from '../../src/ml_parser/ast'; | 
					
						
							|  |  |  | import {removeWhitespaces} from '../../src/ml_parser/html_whitespaces'; | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  | import * as o from '../../src/output/output_ast'; | 
					
						
							| 
									
										
										
										
											2018-02-05 17:31:12 -08:00
										 |  |  | import {compilePipe} from '../../src/render3/r3_pipe_compiler'; | 
					
						
							| 
									
										
										
										
											2018-04-27 14:39:07 -07:00
										 |  |  | import {htmlAstToRender3Ast} from '../../src/render3/r3_template_transform'; | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  | import {compileComponentFromRender2, compileDirectiveFromRender2} from '../../src/render3/view/compiler'; | 
					
						
							| 
									
										
										
										
											2018-02-15 16:43:16 -08:00
										 |  |  | import {BindingParser} from '../../src/template_parser/binding_parser'; | 
					
						
							| 
									
										
										
										
											2018-03-16 12:06:45 -07:00
										 |  |  | import {OutputContext, escapeRegExp} from '../../src/util'; | 
					
						
							| 
									
										
										
										
											2018-02-14 10:54:00 -08:00
										 |  |  | import {MockAotCompilerHost, MockCompilerHost, MockData, MockDirectory, arrayToMockDir, expectNoDiagnostics, settings, toMockFileArray} from '../aot/test_util'; | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  | const IDENTIFIER = /[A-Za-z_$ɵ][A-Za-z0-9_$]*/; | 
					
						
							|  |  |  | const OPERATOR = | 
					
						
							| 
									
										
										
										
											2018-03-16 17:16:12 -07:00
										 |  |  |     /!|%|\*|\/|\^|&&?|\|\|?|\(|\)|\{|\}|\[|\]|:|;|<=?|>=?|={1,3}|!==?|=>|\+\+?|--?|@|,|\.|\.\.\./; | 
					
						
							| 
									
										
										
										
											2018-03-16 12:06:45 -07:00
										 |  |  | const STRING = /'[^']*'|"[^"]*"|`[\s\S]*?`/; | 
					
						
							|  |  |  | const NUMBER = /\d+/; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  | const ELLIPSIS = '…'; | 
					
						
							|  |  |  | const TOKEN = new RegExp( | 
					
						
							| 
									
										
										
										
											2018-03-16 12:06:45 -07:00
										 |  |  |     `\\s*((${IDENTIFIER.source})|(${OPERATOR.source})|(${STRING.source})|${NUMBER.source}|${ELLIPSIS})`, | 
					
						
							|  |  |  |     'y'); | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | type Piece = string | RegExp; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const SKIP = /(?:.|\n|\r)*/; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-16 17:16:12 -07:00
										 |  |  | const ERROR_CONTEXT_WIDTH = 30; | 
					
						
							| 
									
										
										
										
											2018-03-16 12:06:45 -07:00
										 |  |  | // Transform the expected output to set of tokens
 | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  | function tokenize(text: string): Piece[] { | 
					
						
							| 
									
										
										
										
											2018-03-16 12:06:45 -07:00
										 |  |  |   TOKEN.lastIndex = 0; | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-16 12:06:45 -07:00
										 |  |  |   let match: RegExpMatchArray|null; | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  |   const pieces: Piece[] = []; | 
					
						
							| 
									
										
										
										
											2018-03-16 12:06:45 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   while ((match = TOKEN.exec(text)) !== null) { | 
					
						
							|  |  |  |     const token = match[1]; | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  |     if (token === 'IDENT') { | 
					
						
							| 
									
										
										
										
											2018-03-16 12:06:45 -07:00
										 |  |  |       pieces.push(IDENTIFIER); | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  |     } else if (token === ELLIPSIS) { | 
					
						
							|  |  |  |       pieces.push(SKIP); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       pieces.push(token); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-03-16 12:06:45 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-16 17:16:12 -07:00
										 |  |  |   if (pieces.length === 0 || TOKEN.lastIndex !== 0) { | 
					
						
							| 
									
										
										
										
											2018-03-16 12:06:45 -07:00
										 |  |  |     const from = TOKEN.lastIndex; | 
					
						
							| 
									
										
										
										
											2018-03-16 17:16:12 -07:00
										 |  |  |     const to = from + ERROR_CONTEXT_WIDTH; | 
					
						
							|  |  |  |     throw Error(`Invalid test, no token found for '${text.substr(from, to)}...'`); | 
					
						
							| 
									
										
										
										
											2018-03-16 12:06:45 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  |   return pieces; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-16 17:16:12 -07:00
										 |  |  | export function expectEmit( | 
					
						
							|  |  |  |     source: string, expected: string, description: string, | 
					
						
							|  |  |  |     assertIdentifiers?: {[name: string]: RegExp}) { | 
					
						
							| 
									
										
										
										
											2018-04-16 00:15:12 -06:00
										 |  |  |   // turns `// ...` into `…`
 | 
					
						
							|  |  |  |   // remove `// TODO` comment lines
 | 
					
						
							|  |  |  |   expected = expected.replace(/\/\/\s*\.\.\./g, ELLIPSIS).replace(/\/\/\s*TODO.*?\n/g, ''); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-16 12:06:45 -07:00
										 |  |  |   const pieces = tokenize(expected); | 
					
						
							| 
									
										
										
										
											2018-03-16 17:16:12 -07:00
										 |  |  |   const {regexp, groups} = buildMatcher(pieces); | 
					
						
							|  |  |  |   const matches = source.match(regexp); | 
					
						
							|  |  |  |   if (matches === null) { | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  |     let last: number = 0; | 
					
						
							| 
									
										
										
										
											2018-03-16 12:06:45 -07:00
										 |  |  |     for (let i = 1; i < pieces.length; i++) { | 
					
						
							| 
									
										
										
										
											2018-03-16 17:16:12 -07:00
										 |  |  |       const {regexp} = buildMatcher(pieces.slice(0, i)); | 
					
						
							|  |  |  |       const m = source.match(regexp); | 
					
						
							| 
									
										
										
										
											2018-03-16 12:06:45 -07:00
										 |  |  |       const expectedPiece = pieces[i - 1] == IDENTIFIER ? '<IDENT>' : pieces[i - 1]; | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  |       if (!m) { | 
					
						
							|  |  |  |         fail( | 
					
						
							| 
									
										
										
										
											2018-03-16 12:06:45 -07:00
										 |  |  |             `${description}: Expected to find ${expectedPiece} '${source.substr(0,last)}[<---HERE expected "${expectedPiece}"]${source.substr(last)}'`); | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  |         return; | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         last = (m.index || 0) + m[0].length; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     fail( | 
					
						
							|  |  |  |         `Test helper failure: Expected expression failed but the reporting logic could not find where it failed in: ${source}`); | 
					
						
							| 
									
										
										
										
											2018-03-16 17:16:12 -07:00
										 |  |  |   } else { | 
					
						
							|  |  |  |     if (assertIdentifiers) { | 
					
						
							|  |  |  |       // It might be possible to add the constraints in the original regexp (see `buildMatcher`)
 | 
					
						
							|  |  |  |       // by transforming the assertion regexps when using anchoring, grouping, back references,
 | 
					
						
							|  |  |  |       // flags, ...
 | 
					
						
							|  |  |  |       //
 | 
					
						
							|  |  |  |       // Checking identifiers after they have matched allows for a simple and flexible
 | 
					
						
							|  |  |  |       // implementation.
 | 
					
						
							|  |  |  |       // The overall performance are not impacted when `assertIdentifiers` is empty.
 | 
					
						
							|  |  |  |       const ids = Object.keys(assertIdentifiers); | 
					
						
							|  |  |  |       for (let i = 0; i < ids.length; i++) { | 
					
						
							|  |  |  |         const id = ids[i]; | 
					
						
							|  |  |  |         if (groups.has(id)) { | 
					
						
							|  |  |  |           const name = matches[groups.get(id) as number]; | 
					
						
							|  |  |  |           const regexp = assertIdentifiers[id]; | 
					
						
							|  |  |  |           if (!regexp.test(name)) { | 
					
						
							|  |  |  |             throw Error( | 
					
						
							|  |  |  |                 `${description}: The matching identifier "${id}" is "${name}" which doesn't match ${regexp}`); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const IDENT_LIKE = /^[a-z][A-Z]/; | 
					
						
							| 
									
										
										
										
											2018-03-16 17:16:12 -07:00
										 |  |  | const MATCHING_IDENT = /^\$.*\$$/; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* | 
					
						
							|  |  |  |  * Builds a regexp that matches the given `pieces` | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * It returns: | 
					
						
							|  |  |  |  * - the `regexp` to be used to match the generated code, | 
					
						
							|  |  |  |  * - the `groups` which maps `$...$` identifier to their position in the regexp matches. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | function buildMatcher(pieces: (string | RegExp)[]): {regexp: RegExp, groups: Map<string, number>} { | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  |   const results: string[] = []; | 
					
						
							|  |  |  |   let first = true; | 
					
						
							|  |  |  |   let group = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const groups = new Map<string, number>(); | 
					
						
							|  |  |  |   for (const piece of pieces) { | 
					
						
							|  |  |  |     if (!first) | 
					
						
							|  |  |  |       results.push(`\\s${typeof piece === 'string' && IDENT_LIKE.test(piece) ? '+' : '*'}`); | 
					
						
							|  |  |  |     first = false; | 
					
						
							|  |  |  |     if (typeof piece === 'string') { | 
					
						
							|  |  |  |       if (MATCHING_IDENT.test(piece)) { | 
					
						
							|  |  |  |         const matchGroup = groups.get(piece); | 
					
						
							|  |  |  |         if (!matchGroup) { | 
					
						
							| 
									
										
										
										
											2018-03-16 12:06:45 -07:00
										 |  |  |           results.push('(' + IDENTIFIER.source + ')'); | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  |           const newGroup = ++group; | 
					
						
							|  |  |  |           groups.set(piece, newGroup); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           results.push(`\\${matchGroup}`); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } else { | 
					
						
							| 
									
										
										
										
											2018-03-16 12:06:45 -07:00
										 |  |  |         results.push(escapeRegExp(piece)); | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  |       } | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       results.push('(?:' + piece.source + ')'); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-03-16 17:16:12 -07:00
										 |  |  |   return { | 
					
						
							|  |  |  |     regexp: new RegExp(results.join('')), | 
					
						
							|  |  |  |     groups, | 
					
						
							|  |  |  |   }; | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-28 14:56:41 -08:00
										 |  |  | function doCompile( | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  |     data: MockDirectory, angularFiles: MockData, options: AotCompilerOptions = {}, | 
					
						
							| 
									
										
										
										
											2018-02-28 14:56:41 -08:00
										 |  |  |     errorCollector: (error: any, fileName?: string) => void = error => { throw error; }, | 
					
						
							|  |  |  |     compileAction: ( | 
					
						
							|  |  |  |         outputCtx: OutputContext, analyzedModules: NgAnalyzedModules, | 
					
						
							|  |  |  |         resolver: CompileMetadataResolver, htmlParser: HtmlParser, templateParser: TemplateParser, | 
					
						
							|  |  |  |         hostBindingParser: BindingParser, reflector: StaticReflector) => void) { | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  |   const testFiles = toMockFileArray(data); | 
					
						
							|  |  |  |   const scripts = testFiles.map(entry => entry.fileName); | 
					
						
							|  |  |  |   const angularFilesArray = toMockFileArray(angularFiles); | 
					
						
							|  |  |  |   const files = arrayToMockDir([...testFiles, ...angularFilesArray]); | 
					
						
							|  |  |  |   const mockCompilerHost = new MockCompilerHost(scripts, files); | 
					
						
							|  |  |  |   const compilerHost = new MockAotCompilerHost(mockCompilerHost); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const program = ts.createProgram(scripts, {...settings}, mockCompilerHost); | 
					
						
							|  |  |  |   expectNoDiagnostics(program); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // TODO(chuckj): Replace with a variant of createAotCompiler() when the r3_view_compiler is
 | 
					
						
							|  |  |  |   // integrated
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const urlResolver = createAotUrlResolver(compilerHost); | 
					
						
							|  |  |  |   const symbolCache = new StaticSymbolCache(); | 
					
						
							|  |  |  |   const summaryResolver = new AotSummaryResolver(compilerHost, symbolCache); | 
					
						
							|  |  |  |   const symbolResolver = new StaticSymbolResolver(compilerHost, symbolCache, summaryResolver); | 
					
						
							|  |  |  |   const staticReflector = | 
					
						
							|  |  |  |       new StaticReflector(summaryResolver, symbolResolver, [], [], errorCollector); | 
					
						
							| 
									
										
										
										
											2018-02-14 10:54:00 -08:00
										 |  |  |   const htmlParser = new HtmlParser(); | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  |   const config = new CompilerConfig({ | 
					
						
							|  |  |  |     defaultEncapsulation: ViewEncapsulation.Emulated, | 
					
						
							|  |  |  |     useJit: false, | 
					
						
							|  |  |  |     missingTranslation: options.missingTranslation, | 
					
						
							|  |  |  |     preserveWhitespaces: options.preserveWhitespaces, | 
					
						
							|  |  |  |     strictInjectionParameters: options.strictInjectionParameters, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   const normalizer = new DirectiveNormalizer( | 
					
						
							|  |  |  |       {get: (url: string) => compilerHost.loadResource(url)}, urlResolver, htmlParser, config); | 
					
						
							|  |  |  |   const expressionParser = new Parser(new Lexer()); | 
					
						
							|  |  |  |   const elementSchemaRegistry = new DomElementSchemaRegistry(); | 
					
						
							|  |  |  |   const templateParser = new TemplateParser( | 
					
						
							|  |  |  |       config, staticReflector, expressionParser, elementSchemaRegistry, htmlParser, console, []); | 
					
						
							|  |  |  |   const resolver = new CompileMetadataResolver( | 
					
						
							|  |  |  |       config, htmlParser, new NgModuleResolver(staticReflector), | 
					
						
							|  |  |  |       new DirectiveResolver(staticReflector), new PipeResolver(staticReflector), summaryResolver, | 
					
						
							|  |  |  |       elementSchemaRegistry, normalizer, console, symbolCache, staticReflector, errorCollector); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Create the TypeScript program
 | 
					
						
							|  |  |  |   const sourceFiles = program.getSourceFiles().map(sf => sf.fileName); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Analyze the modules
 | 
					
						
							|  |  |  |   // TODO(chuckj): Eventually this should not be necessary as the ts.SourceFile should be sufficient
 | 
					
						
							|  |  |  |   // to generate a template definition.
 | 
					
						
							|  |  |  |   const analyzedModules = analyzeNgModules(sourceFiles, compilerHost, symbolResolver, resolver); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-05 17:31:12 -08:00
										 |  |  |   const pipesOrDirectives = Array.from(analyzedModules.ngModuleByPipeOrDirective.keys()); | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   const fakeOutputContext: OutputContext = { | 
					
						
							|  |  |  |     genFilePath: 'fakeFactory.ts', | 
					
						
							|  |  |  |     statements: [], | 
					
						
							|  |  |  |     importExpr(symbol: StaticSymbol, typeParams: o.Type[]) { | 
					
						
							|  |  |  |       if (!(symbol instanceof StaticSymbol)) { | 
					
						
							|  |  |  |         if (!symbol) { | 
					
						
							|  |  |  |           throw new Error('Invalid: undefined passed to as a symbol'); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         throw new Error(`Invalid: ${(symbol as any).constructor.name} is not a symbol`); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       return (symbol.members || []) | 
					
						
							|  |  |  |           .reduce( | 
					
						
							|  |  |  |               (expr, member) => expr.prop(member), | 
					
						
							|  |  |  |               <o.Expression>o.importExpr(new o.ExternalReference(symbol.filePath, symbol.name))); | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     constantPool: new ConstantPool() | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-15 16:43:16 -08:00
										 |  |  |   const errors: ParseError[] = []; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const hostBindingParser = new BindingParser( | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |       expressionParser, DEFAULT_INTERPOLATION_CONFIG, elementSchemaRegistry, null, errors); | 
					
						
							| 
									
										
										
										
											2018-02-15 16:43:16 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-05 17:31:12 -08:00
										 |  |  |   // Load all directives and pipes
 | 
					
						
							|  |  |  |   for (const pipeOrDirective of pipesOrDirectives) { | 
					
						
							|  |  |  |     const module = analyzedModules.ngModuleByPipeOrDirective.get(pipeOrDirective) !; | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  |     resolver.loadNgModuleDirectiveAndPipeMetadata(module.type.reference, true); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-28 14:56:41 -08:00
										 |  |  |   compileAction( | 
					
						
							|  |  |  |       fakeOutputContext, analyzedModules, resolver, htmlParser, templateParser, hostBindingParser, | 
					
						
							|  |  |  |       staticReflector); | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   fakeOutputContext.statements.unshift(...fakeOutputContext.constantPool.statements); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const emitter = new TypeScriptEmitter(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const result = emitter.emitStatementsAndContext( | 
					
						
							|  |  |  |       fakeOutputContext.genFilePath, fakeOutputContext.statements, '', false, | 
					
						
							|  |  |  |       /* referenceFilter */ undefined, | 
					
						
							|  |  |  |       /* importFilter */ e => e.moduleName != null && e.moduleName.startsWith('/app')); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-15 16:43:16 -08:00
										 |  |  |   if (errors.length) { | 
					
						
							|  |  |  |     throw new Error('Unexpected errors:' + errors.map(e => e.toString()).join(', ')); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-25 08:52:10 -08:00
										 |  |  |   return {source: result.sourceText, outputContext: fakeOutputContext}; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-02-28 14:56:41 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | export function compile( | 
					
						
							|  |  |  |     data: MockDirectory, angularFiles: MockData, options: AotCompilerOptions = {}, | 
					
						
							|  |  |  |     errorCollector: (error: any, fileName?: string) => void = error => { throw error;}) { | 
					
						
							|  |  |  |   return doCompile( | 
					
						
							|  |  |  |       data, angularFiles, options, errorCollector, | 
					
						
							|  |  |  |       (outputCtx: OutputContext, analyzedModules: NgAnalyzedModules, | 
					
						
							|  |  |  |        resolver: CompileMetadataResolver, htmlParser: HtmlParser, templateParser: TemplateParser, | 
					
						
							|  |  |  |        hostBindingParser: BindingParser, reflector: StaticReflector) => { | 
					
						
							|  |  |  |         const pipesOrDirectives = Array.from(analyzedModules.ngModuleByPipeOrDirective.keys()); | 
					
						
							|  |  |  |         for (const pipeOrDirective of pipesOrDirectives) { | 
					
						
							|  |  |  |           const module = analyzedModules.ngModuleByPipeOrDirective.get(pipeOrDirective); | 
					
						
							|  |  |  |           if (!module || !module.type.reference.filePath.startsWith('/app')) { | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           if (resolver.isDirective(pipeOrDirective)) { | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |             const directive = resolver.getDirectiveMetadata(pipeOrDirective); | 
					
						
							|  |  |  |             if (directive.isComponent) { | 
					
						
							| 
									
										
										
										
											2018-02-28 14:56:41 -08:00
										 |  |  |               const fakeUrl = 'ng://fake-template-url.html'; | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  |               let htmlAst = htmlParser.parse(directive.template !.template !, fakeUrl); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               // Map of StaticType by directive selectors
 | 
					
						
							|  |  |  |               const directiveTypeBySel = new Map<string, any>(); | 
					
						
							| 
									
										
										
										
											2018-02-28 14:56:41 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |               const directives = module.transitiveModule.directives.map( | 
					
						
							|  |  |  |                   dir => resolver.getDirectiveSummary(dir.reference)); | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |               directives.forEach(directive => { | 
					
						
							|  |  |  |                 if (directive.selector) { | 
					
						
							|  |  |  |                   directiveTypeBySel.set(directive.selector, directive.type.reference); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |               }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               // Map of StaticType by pipe names
 | 
					
						
							|  |  |  |               const pipeTypeByName = new Map<string, any>(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-28 14:56:41 -08:00
										 |  |  |               const pipes = module.transitiveModule.pipes.map( | 
					
						
							|  |  |  |                   pipe => resolver.getPipeSummary(pipe.reference)); | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |               pipes.forEach(pipe => { pipeTypeByName.set(pipe.name, pipe.type.reference); }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               const preserveWhitespaces = directive.template !.preserveWhitespaces; | 
					
						
							|  |  |  |               if (!preserveWhitespaces) { | 
					
						
							|  |  |  |                 htmlAst = removeWhitespaces(htmlAst); | 
					
						
							|  |  |  |               } | 
					
						
							| 
									
										
										
										
											2018-04-27 14:39:07 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |               const render3Ast = htmlAstToRender3Ast(htmlAst.rootNodes, hostBindingParser); | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:49 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  |               compileComponentFromRender2( | 
					
						
							| 
									
										
										
										
											2018-04-27 14:39:07 -07:00
										 |  |  |                   outputCtx, directive, render3Ast, reflector, hostBindingParser, | 
					
						
							|  |  |  |                   directiveTypeBySel, pipeTypeByName); | 
					
						
							| 
									
										
										
										
											2018-02-28 14:56:41 -08:00
										 |  |  |             } else { | 
					
						
							| 
									
										
										
										
											2018-04-24 11:34:11 -07:00
										 |  |  |               compileDirectiveFromRender2(outputCtx, directive, reflector, hostBindingParser); | 
					
						
							| 
									
										
										
										
											2018-02-28 14:56:41 -08:00
										 |  |  |             } | 
					
						
							|  |  |  |           } else if (resolver.isPipe(pipeOrDirective)) { | 
					
						
							|  |  |  |             const metadata = resolver.getPipeMetadata(pipeOrDirective); | 
					
						
							|  |  |  |             if (metadata) { | 
					
						
							| 
									
										
										
										
											2018-04-18 16:23:38 -07:00
										 |  |  |               compilePipe(outputCtx, metadata, reflector); | 
					
						
							| 
									
										
										
										
											2018-02-28 14:56:41 -08:00
										 |  |  |             } | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | } |