import * as path from 'path' ; import * as fs from 'fs' ; import * as ts from 'typescript' ; const templatePath = path . resolve ( __dirname , '../fixtures/template.html' ) ; const dataPath = path . resolve ( __dirname , '../fixtures/data.ts' ) ; const host = { getCompilationSettings : ( ) => ( { allowNonTsExtensions : true } ) , getScriptFileNames : ( ) => [ templatePath , dataPath ] , getScriptVersion : fileName => '1' , getScriptSnapshot : fileName => { if ( ! fs . existsSync ( fileName ) ) { return undefined ; } return ts . ScriptSnapshot . fromString ( fs . readFileSync ( fileName ) . toString ( ) ) ; } , getCurrentDirectory : ts . sys . getCurrentDirectory , getDefaultLibFileName : ts . getDefaultLibFileName } ; const service = ts . createLanguageService ( host ) ; const clssf = ts . createLanguageServiceSourceFile ; const ulssf = ts . updateLanguageServiceSourceFile ; ( ts as any ) . createLanguageServiceSourceFile = function createLanguageServiceSourceFile ( fileName , scriptSnapshot , scriptTarget , version , setNodeParents , scriptKind ? ) : ts . SourceFile { const source = clssf ( fileName , scriptSnapshot , scriptTarget , version , setNodeParents , scriptKind ) ; if ( fileName === templatePath ) { transformTemlate ( source , scriptSnapshot . getText ( 0 , scriptSnapshot . getLength ( ) ) , dataPath ) ; } return source ; } ; ( ts as any ) . updateLanguageServiceSourceFile = function updateLanguageServiceSourceFile ( sourceFile , scriptSnapshot , version , textChangeRange , aggressiveChecks ? ) : ts . SourceFile { const source = ulssf ( sourceFile , scriptSnapshot , version , textChangeRange , aggressiveChecks ) ; if ( source . fileName === templatePath ) { transformTemlate ( source , scriptSnapshot . getText ( 0 , scriptSnapshot . getLength ( ) ) , dataPath ) ; } return source ; } ; function transformTemlate ( source , html , dataPath ) { const regexp = /\{\{(.+)\}\}/g ; const setZeroPos = < T extends ts . Node > ( node ) : T => { return ts . setTextRange ( node , { pos : 0 , end : 0 } ) ; } ; const importNode = setZeroPos ( ts . createImportDeclaration ( undefined , undefined , setZeroPos ( ts . createImportClause ( setZeroPos ( ts . createIdentifier ( 'data' ) ) , undefined ) ) , setZeroPos ( ts . createLiteral ( dataPath . slice ( 0 , - 3 ) ) ) ) ) ; let match ; const expressions = [ ] ; while ( match = regexp . exec ( html ) ) { const offset = match . index + 2 ; const source = ts . createSourceFile ( '/tmp/test.ts' , match [ 1 ] , ts . ScriptTarget . Latest ) ; expressions . push ( ... source . statements ) ; ts . forEachChild ( source , function next ( node ) { ts . setTextRange ( node , { pos : offset + node . pos , end : offset + node . end } ) ; ts . forEachChild ( node , next ) ; } ) ; } source . statements = ts . createNodeArray ( [ importNode , ... expressions ] ) ; } service . getSemanticDiagnostics ( templatePath ) . forEach ( diag => { const pos = diag . start != null && diag . length != null ? `[${ diag . start }-${ diag . start + diag . length }] ` : '' ; console . log ( pos + diag . messageText ) ; } ) ;