/** * @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 */ import {GeneratedFile} from '@angular/compiler'; import * as path from 'path'; import * as ts from 'typescript'; import * as api from '../transformers/api'; import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, SelectorScopeRegistry} from './annotations'; import {CompilerHost} from './compiler_host'; import {TypeScriptReflectionHost} from './metadata'; import {IvyCompilation, ivyTransformFactory} from './transform'; export class NgtscProgram implements api.Program { private tsProgram: ts.Program; constructor( rootNames: ReadonlyArray, private options: api.CompilerOptions, private host: api.CompilerHost, oldProgram?: api.Program) { this.tsProgram = ts.createProgram(rootNames, options, host, oldProgram && oldProgram.getTsProgram()); } getTsProgram(): ts.Program { return this.tsProgram; } getTsOptionDiagnostics(cancellationToken?: ts.CancellationToken| undefined): ReadonlyArray { return this.tsProgram.getOptionsDiagnostics(cancellationToken); } getNgOptionDiagnostics(cancellationToken?: ts.CancellationToken| undefined): ReadonlyArray { return []; } getTsSyntacticDiagnostics( sourceFile?: ts.SourceFile|undefined, cancellationToken?: ts.CancellationToken|undefined): ReadonlyArray { return this.tsProgram.getSyntacticDiagnostics(sourceFile, cancellationToken); } getNgStructuralDiagnostics(cancellationToken?: ts.CancellationToken| undefined): ReadonlyArray { return []; } getTsSemanticDiagnostics( sourceFile?: ts.SourceFile|undefined, cancellationToken?: ts.CancellationToken|undefined): ReadonlyArray { return this.tsProgram.getSemanticDiagnostics(sourceFile, cancellationToken); } getNgSemanticDiagnostics( fileName?: string|undefined, cancellationToken?: ts.CancellationToken|undefined): ReadonlyArray { return []; } loadNgStructureAsync(): Promise { return Promise.resolve(); } listLazyRoutes(entryRoute?: string|undefined): api.LazyRoute[] { throw new Error('Method not implemented.'); } getLibrarySummaries(): Map { throw new Error('Method not implemented.'); } getEmittedGeneratedFiles(): Map { throw new Error('Method not implemented.'); } getEmittedSourceFiles(): Map { throw new Error('Method not implemented.'); } emit(opts?: { emitFlags?: api.EmitFlags, cancellationToken?: ts.CancellationToken, customTransformers?: api.CustomTransformers, emitCallback?: api.TsEmitCallback, mergeEmitResultsCallback?: api.TsMergeEmitResultsCallback }): ts.EmitResult { const emitCallback = opts && opts.emitCallback || defaultEmitCallback; const mergeEmitResultsCallback = opts && opts.mergeEmitResultsCallback || mergeEmitResults; const checker = this.tsProgram.getTypeChecker(); const reflector = new TypeScriptReflectionHost(checker); const scopeRegistry = new SelectorScopeRegistry(checker, reflector); // Set up the IvyCompilation, which manages state for the Ivy transformer. const handlers = [ new ComponentDecoratorHandler(checker, reflector, scopeRegistry), new DirectiveDecoratorHandler(checker, reflector, scopeRegistry), new InjectableDecoratorHandler(reflector), new NgModuleDecoratorHandler(checker, reflector, scopeRegistry), ]; const compilation = new IvyCompilation(handlers, checker, reflector); // Analyze every source file in the program. this.tsProgram.getSourceFiles() .filter(file => !file.fileName.endsWith('.d.ts')) .forEach(file => compilation.analyze(file)); // Since there is no .d.ts transformation API, .d.ts files are transformed during write. const writeFile: ts.WriteFileCallback = (fileName: string, data: string, writeByteOrderMark: boolean, onError: ((message: string) => void) | undefined, sourceFiles: ReadonlyArray) => { if (fileName.endsWith('.d.ts')) { data = sourceFiles.reduce( (data, sf) => compilation.transformedDtsFor(sf.fileName, data), data); } this.host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles); }; // Run the emit, including a custom transformer that will downlevel the Ivy decorators in code. const emitResult = emitCallback({ program: this.tsProgram, host: this.host, options: this.options, emitOnlyDtsFiles: false, writeFile, customTransformers: { before: [ivyTransformFactory(compilation)], }, }); return emitResult; } } const defaultEmitCallback: api.TsEmitCallback = ({program, targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers}) => program.emit( targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers); function mergeEmitResults(emitResults: ts.EmitResult[]): ts.EmitResult { const diagnostics: ts.Diagnostic[] = []; let emitSkipped = false; const emittedFiles: string[] = []; for (const er of emitResults) { diagnostics.push(...er.diagnostics); emitSkipped = emitSkipped || er.emitSkipped; emittedFiles.push(...(er.emittedFiles || [])); } return {diagnostics, emitSkipped, emittedFiles}; }