import { RawSourceMap } from 'source-map' ; import ts from 'typescript' ; import * as file_ from './file' ; export interface Result { readonly text : string ; readonly sourceMap : RawSourceMap ; } export const printBundle = ( programIn , files , substituteNode ) : Result => { const program = programIn ; const compiler = ts ; const host = { getPrependNodes : ( ) => [ ] , getCanonicalFileName : ( fileName ) => ( ts . sys . useCaseSensitiveFileNames ? fileName : fileName . toLowerCase ( ) ) , getCommonSourceDirectory : program . getCommonSourceDirectory , getCompilerOptions : program . getCompilerOptions , getCurrentDirectory : program . getCurrentDirectory , getNewLine : ( ) => ts . sys . newLine , getSourceFile : program . getSourceFile , getSourceFileByPath : program . getSourceFileByPath , getSourceFiles : program . getSourceFiles , isSourceFileFromExternalLibrary : program . isSourceFileFromExternalLibrary , writeFile : ts . sys . writeFile , readFile : ts . sys . readFile , fileExists : ts . sys . fileExists , directoryExists : ts . sys . directoryExists } ; const writer = compiler . createTextWriter ( ts . sys . newLine ) ; const sourceMapGenerator = compiler . createSourceMapGenerator ( host , 'foo.ts' , program . getCurrentDirectory ( ) , program . getCurrentDirectory ( ) , { } ) ; const printer = compiler . createPrinter ( { ... program . getCompilerOptions ( ) , outFile : 'foo.ts' } , { substituteNode } ) ; printer . writeBundle ( ts . createBundle ( files ) , writer , sourceMapGenerator ) ; const resolvedSourceMap = sourceMapGenerator . toJSON ( ) ; return { text : writer . getText ( ) , sourceMap : { ... resolvedSourceMap , sourcesContent : resolvedSourceMap . sources . map ( ( filePath ) => { const foundFile = files . find ( ( file ) => file_ . getFilePath ( file ) . endsWith ( filePath ) ) ; return foundFile === undefined ? '' : file_ . getText ( foundFile ) ; } ) } } ; } ; export const markOriginal = < T extends ts . Node > ( node ) : T => { ( node as any ) . __originalSet = true ; return ts . setSourceMapRange ( node , file_ . createSourceMapRange ( node ) ) ; } ; export const setOriginal = < T extends ts . Node > ( node , original ) : T => { if ( ! ( node as any ) . __originalSet ) { const transformedNode = ts . moveSyntheticComments ( ts . setSourceMapRange ( ts . setOriginalNode ( node , original ) , file_ . createSourceMapRange ( original ) ) , original ) ; ( node as any ) . __originalSet = true ; return transformedNode ; } return node ; } ; export const isOriginal = ( node ) : boolean => ! ( node as any ) . __originalSet ; const context = { getCompilerOptions : ( ) : ts . CompilerOptions => ( { } as any ) , startLexicalEnvironment : ( ) : void => { } , suspendLexicalEnvironment : ( ) : void => { } , resumeLexicalEnvironment : ( ) : void => { } , endLexicalEnvironment : ( ) => undefined , hoistFunctionDeclaration : ( ) : void => { } , hoistVariableDeclaration : ( ) : void => { } , requestEmitHelper : ( ) : void => { } , readEmitHelpers : ( ) => undefined , enableSubstitution : ( ) : void => { } , isSubstitutionEnabled : ( ) : boolean => false , onSubstituteNode : ( _hint , node ) => node , enableEmitNotification : ( ) : void => { } , isEmitNotificationEnabled : ( ) : boolean => false , onEmitNode : ( hint , node , emitCallback ) => { emitCallback ( hint , node ) ; } } ; export const setOriginalRecursive = < T extends ts . Node > ( start , original ) : T => { const seen = new Set ( ) ; function visit ( node ) { if ( seen . has ( node ) ) { return node ; } seen . add ( node ) ; const transformedNode = setOriginal ( node , original ) ; return ts . visitEachChild ( transformedNode , visit , context ) ; } return visit ( start ) as T ; } ;