import ts = require ( 'typescript' ) ; import { SingleFileHost , MultipleFilesHost } from './hosts' ; import { traverseAst } from './traverse-ast' ; import { MutableSourceCode } from './mutable-source-code' ; import { RawSourceMap } from 'source-map' ; import { Visitor } from './visitor' ; import { TranspilerContext } from './transpiler-context' ; import { defaultCompilerOptions } from './configuration' ; import { SemanticHost } from './chainable-hosts' ; import { TransformationHost } from './chainable-hosts' ; import { chainHosts } from './hosts-base' ; import { AstCacheHost } from './chainable-hosts' ; import _ = require ( 'lodash' ) ; /**
 * result of transpilation action
 */ export interface TranspilerOutput { /**
     * the transpiled code, if transpilation was not halted
     */ code : string ; /**
     * a raw sourcemap object representing all changes made from the supplied source to the transpiled code (visitors and typescript alike)
     */ sourceMap : RawSourceMap ; /**
     * diagnostics produced by Typescript or the visitors
     */ diags : ts . Diagnostic [ ] ; /**
     * did the transpilation fail
     */ halted : boolean ; /**
     * Generated .d.ts file content
     */ declaration ? : string ; } export interface TranspilerConfig { sourceFileName : string ; compilerOptions ? : ts . CompilerOptions ; visitors : Visitor [ ] ; } export interface ValidatorConfig { resolutionHosts ? : ts . ModuleResolutionHost [ ] ; visitors ? : Visitor [ ] ; mutators ? : Visitor [ ] ; } function getParserErrors ( sourceFile ) { return sourceFile [ 'parseDiagnostics' ] ; } export function transpile ( content , config ) { const compilerOptions = _ . assign ( { } , defaultCompilerOptions , config . compilerOptions ) ; const fileName = config . sourceFileName ; const ast = ts . createSourceFile ( fileName , content , compilerOptions . target , true ) ; const parserErrors = getParserErrors ( ast ) ; if ( parserErrors . length > 0 ) { return { code : null , diags : parserErrors , halted : true , sourceMap : null } ; } let context = new TranspilerContext ( ast . fileName ) ; config . visitors . some ( ( visitor ) => { traverseAst ( ast , visitor , context ) ; return context . halted ; } ) ; if ( context . halted ) { return { code : null , sourceMap : null , diags : context . diags , halted : true } ; } const mutable = new MutableSourceCode ( ast ) ; mutable . execute ( context . actions ) ; const compilerHost = new SingleFileHost ( mutable . ast ) ; const program = ts . createProgram ( [ fileName ] , compilerOptions , compilerHost ) ; const emitResult = program . emit ( ) ; emitResult . diagnostics . forEach ( ( d ) => { context . pushDiag ( mutable . translateDiagnostic ( d ) ) ; } ) ; if ( emitResult . emitSkipped ) { return { code : null , sourceMap : null , diags : context . diags , halted : true } ; } const finalCode = compilerHost . output ; const intermediateSourceMap = compilerHost . sourceMap ; const declaration = compilerHost . declaration ; const finalSourceMap = intermediateSourceMap ? mutable . translateMap ( intermediateSourceMap ) : null ; return { code : finalCode , sourceMap : finalSourceMap , diags : context . diags , halted : false , declaration } ; } export function validateAll ( files , config ) { let langService ; const sourceHost = new MultipleFilesHost ( config . resolutionHosts , defaultCompilerOptions ) ; const astCache = new AstCacheHost ( ) ; const cachedSource = chainHosts ( sourceHost , astCache ) ; const semanticHost = < SemanticHost > chainHosts ( cachedSource , new SemanticHost ( files , defaultCompilerOptions ) ) ; const langServiceProvider = ( ) => { return langService ? langService : langService = ts . createLanguageService ( semanticHost , ts . createDocumentRegistry ( ) ) ; } ; const transformHost = new TransformationHost ( config . mutators || [ ] , langServiceProvider ) ; const program = ts . createProgram ( files , defaultCompilerOptions , chainHosts ( cachedSource , transformHost ) ) ; const diags = [ ] . concat ( sourceHost . getSyntacticErrors ( ) , program . getSemanticDiagnostics ( ) ) ; return diags . map ( diagnostic => transformHost . translateDiagnostic ( diagnostic ) ) ; }