namespace Harness . SourceMapRecorder { interface SourceMapSpanWithDecodeErrors { sourceMapSpan : ts . Mapping ; decodeErrors : string [ ] | undefined ; } namespace SourceMapDecoder { let sourceMapMappings ; let decodingIndex ; let mappings ; export interface DecodedMapping { sourceMapSpan : ts . Mapping ; error ? : string ; } export function initializeSourceMapDecoding ( sourceMap ) { decodingIndex = 0 ; sourceMapMappings = sourceMap . mappings ; mappings = ts . decodeMappings ( sourceMap . mappings ) ; } export function decodeNextEncodedSourceMapSpan ( ) { if ( ! mappings ) return ts . Debug . fail ( "not initialized" ) ; const result = mappings . next ( ) ; if ( result . done ) return { error : mappings . error || "No encoded entry found" , sourceMapSpan : mappings . state } ; return { sourceMapSpan : result . value } ; } export function hasCompletedDecoding ( ) { if ( ! mappings ) return ts . Debug . fail ( "not initialized" ) ; return mappings . pos === sourceMapMappings . length ; } export function getRemainingDecodeString ( ) { return sourceMapMappings . substr ( decodingIndex ) ; } } namespace SourceMapSpanWriter { let sourceMapRecorder ; let sourceMapSources ; let sourceMapNames ; let jsFile ; let jsLineMap ; let tsCode ; let tsLineMap ; let spansOnSingleLine ; let prevWrittenSourcePos ; let nextJsLineToWrite ; let spanMarkerContinues ; export function initializeSourceMapSpanWriter ( sourceMapRecordWriter , sourceMap , currentJsFile ) { sourceMapRecorder = sourceMapRecordWriter ; sourceMapSources = sourceMap . sources ; sourceMapNames = sourceMap . names ; jsFile = currentJsFile ; jsLineMap = jsFile . lineStarts ; spansOnSingleLine = [ ] ; prevWrittenSourcePos = 0 ; nextJsLineToWrite = 0 ; spanMarkerContinues = false ; SourceMapDecoder . initializeSourceMapDecoding ( sourceMap ) ; sourceMapRecorder . WriteLine ( "===================================================================" ) ; sourceMapRecorder . WriteLine ( "JsFile: " + sourceMap . file ) ; sourceMapRecorder . WriteLine ( "mapUrl: " + ts . tryGetSourceMappingURL ( ts . getLineInfo ( jsFile . text , jsLineMap ) ) ) ; sourceMapRecorder . WriteLine ( "sourceRoot: " + sourceMap . sourceRoot ) ; sourceMapRecorder . WriteLine ( "sources: " + sourceMap . sources ) ; if ( sourceMap . sourcesContent ) { sourceMapRecorder . WriteLine ( "sourcesContent: " + JSON . stringify ( sourceMap . sourcesContent ) ) ; } sourceMapRecorder . WriteLine ( "===================================================================" ) ; } function getSourceMapSpanString ( mapEntry , getAbsentNameIndex ? ) { let mapString = "Emitted(" + ( mapEntry . generatedLine + 1 ) + ", " + ( mapEntry . generatedCharacter + 1 ) + ")" ; if ( ts . isSourceMapping ( mapEntry ) ) { mapString += " Source(" + ( mapEntry . sourceLine + 1 ) + ", " + ( mapEntry . sourceCharacter + 1 ) + ") + SourceIndex(" + mapEntry . sourceIndex + ")" ; if ( mapEntry . nameIndex ! >= 0 && mapEntry . nameIndex ! < sourceMapNames ! . length ) { mapString += " name (" + sourceMapNames ! [ mapEntry . nameIndex ! ] + ")" ; } else { if ( ( mapEntry . nameIndex && mapEntry . nameIndex !== - 1 ) || getAbsentNameIndex ) { mapString += " nameIndex (" + mapEntry . nameIndex + ")" ; } } } return mapString ; } export function recordSourceMapSpan ( sourceMapSpan ) { const decodeResult = SourceMapDecoder . decodeNextEncodedSourceMapSpan ( ) ; let decodeErrors ; if ( typeof decodeResult . error === "string" || ! ts . sameMapping ( decodeResult . sourceMapSpan , sourceMapSpan ) ) { if ( decodeResult . error ) { decodeErrors = [ "!!^^ !!^^ There was decoding error in the sourcemap at this location: " + decodeResult . error ] ; } else { decodeErrors = [ "!!^^ !!^^ The decoded span from sourcemap's mapping entry does not match what was encoded for this span:" ] ; } decodeErrors . push ( "!!^^ !!^^ Decoded span from sourcemap's mappings entry: " + getSourceMapSpanString ( decodeResult . sourceMapSpan , true ) + " Span encoded by the emitter:" + getSourceMapSpanString ( sourceMapSpan , true ) ) ; } if ( spansOnSingleLine . length && spansOnSingleLine [ 0 ] . sourceMapSpan . generatedLine !== sourceMapSpan . generatedLine ) { writeRecordedSpans ( ) ; spansOnSingleLine = [ ] ; } spansOnSingleLine . push ( { sourceMapSpan , decodeErrors } ) ; } export function recordNewSourceFileSpan ( sourceMapSpan , newSourceFileCode ) { let continuesLine = false ; if ( spansOnSingleLine . length > 0 && spansOnSingleLine [ 0 ] . sourceMapSpan . generatedCharacter === sourceMapSpan . generatedLine ) { writeRecordedSpans ( ) ; spansOnSingleLine = [ ] ; nextJsLineToWrite -- ; continuesLine = true ; } recordSourceMapSpan ( sourceMapSpan ) ; assert . isTrue ( spansOnSingleLine . length === 1 ) ; sourceMapRecorder . WriteLine ( "-------------------------------------------------------------------" ) ; sourceMapRecorder . WriteLine ( "emittedFile:" + jsFile . file + ( continuesLine ? ` (${ sourceMapSpan . generatedLine + 1 }, ${ sourceMapSpan . generatedCharacter + 1 })` : "" ) ) ; sourceMapRecorder . WriteLine ( "sourceFile:" + sourceMapSources [ spansOnSingleLine [ 0 ] . sourceMapSpan . sourceIndex ! ] ) ; sourceMapRecorder . WriteLine ( "-------------------------------------------------------------------" ) ; tsLineMap = ts . computeLineStarts ( newSourceFileCode ) ; tsCode = newSourceFileCode ; prevWrittenSourcePos = 0 ; } export function close ( ) { writeRecordedSpans ( ) ; if ( ! SourceMapDecoder . hasCompletedDecoding ( ) ) { sourceMapRecorder . WriteLine ( "!!!! **** There are more source map entries in the sourceMap's mapping than what was encoded" ) ; sourceMapRecorder . WriteLine ( "!!!! **** Remaining decoded string: " + SourceMapDecoder . getRemainingDecodeString ( ) ) ; } writeJsFileLines ( jsLineMap . length ) ; } function getTextOfLine ( line , lineMap , code ) { const startPos = lineMap [ line ] ; const endPos = lineMap [ line + 1 ] ; const text = code . substring ( startPos , endPos ) ; return line === 0 ? Utils . removeByteOrderMark ( text ) : text ; } function writeJsFileLines ( endJsLine ) { for ( ; nextJsLineToWrite < endJsLine ; nextJsLineToWrite ++ ) { sourceMapRecorder . Write ( ">>>" + getTextOfLine ( nextJsLineToWrite , jsLineMap , jsFile . text ) ) ; } } function writeRecordedSpans ( ) { const markerIds = [ ] ; function getMarkerId ( markerIndex ) { let markerId = "" ; if ( spanMarkerContinues ) { assert . isTrue ( markerIndex === 0 ) ; markerId = "1->" ; } else { markerId = "" + ( markerIndex + 1 ) ; if ( markerId . length < 2 ) { markerId = markerId + " " ; } markerId += ">" ; } return markerId ; } let prevEmittedCol ! ; function iterateSpans ( fn ) { prevEmittedCol = 0 ; for ( let i = 0 ; i < spansOnSingleLine . length ; i ++ ) { fn ( spansOnSingleLine [ i ] , i ) ; prevEmittedCol = spansOnSingleLine [ i ] . sourceMapSpan . generatedCharacter ; } } function writeSourceMapIndent ( indentLength , indentPrefix ) { sourceMapRecorder . Write ( indentPrefix ) ; for ( let i = 0 ; i < indentLength ; i ++ ) { sourceMapRecorder . Write ( " " ) ; } } function writeSourceMapMarker ( currentSpan , index , endColumn = currentSpan . sourceMapSpan . generatedCharacter , endContinues = false ) { const markerId = getMarkerId ( index ) ; markerIds . push ( markerId ) ; writeSourceMapIndent ( prevEmittedCol , markerId ) ; for ( let i = prevEmittedCol ; i < endColumn ; i ++ ) { sourceMapRecorder . Write ( "^" ) ; } if ( endContinues ) { sourceMapRecorder . Write ( "->" ) ; } sourceMapRecorder . WriteLine ( "" ) ; spanMarkerContinues = endContinues ; } function writeSourceMapSourceText ( currentSpan , index ) { const sourcePos = tsLineMap [ currentSpan . sourceMapSpan . sourceLine ! ] + ( currentSpan . sourceMapSpan . sourceCharacter ! ) ; let sourceText = "" ; if ( prevWrittenSourcePos < sourcePos ) { sourceText = tsCode . substring ( prevWrittenSourcePos , sourcePos ) ; } if ( currentSpan . decodeErrors ) { for ( const decodeError of currentSpan . decodeErrors ) { writeSourceMapIndent ( prevEmittedCol , markerIds [ index ] ) ; sourceMapRecorder . WriteLine ( decodeError ) ; } } const tsCodeLineMap = ts . computeLineStarts ( sourceText ) ; for ( let i = 0 ; i < tsCodeLineMap . length ; i ++ ) { writeSourceMapIndent ( prevEmittedCol , i === 0 ? markerIds [ index ] : "  >" ) ; sourceMapRecorder . Write ( getTextOfLine ( i , tsCodeLineMap , sourceText ) ) ; if ( i === tsCodeLineMap . length - 1 ) { sourceMapRecorder . WriteLine ( "" ) ; } } prevWrittenSourcePos = sourcePos ; } function writeSpanDetails ( currentSpan , index ) { sourceMapRecorder . WriteLine ( markerIds [ index ] + getSourceMapSpanString ( currentSpan . sourceMapSpan ) ) ; } if ( spansOnSingleLine . length ) { const currentJsLine = spansOnSingleLine [ 0 ] . sourceMapSpan . generatedLine ; writeJsFileLines ( currentJsLine + 1 ) ; iterateSpans ( writeSourceMapMarker ) ; const jsFileText = getTextOfLine ( currentJsLine + 1 , jsLineMap , jsFile . text ) ; if ( prevEmittedCol < jsFileText . length - 1 ) { writeSourceMapMarker ( undefined ! , spansOnSingleLine . length , jsFileText . length - 1 , true ) ; } iterateSpans ( writeSourceMapSourceText ) ; iterateSpans ( writeSpanDetails ) ; sourceMapRecorder . WriteLine ( "---" ) ; } } } export function getSourceMapRecord ( sourceMapDataList , program , jsFiles , declarationFiles ) { const sourceMapRecorder = new Compiler . WriterAggregator ( ) ; for ( let i = 0 ; i < sourceMapDataList . length ; i ++ ) { const sourceMapData = sourceMapDataList [ i ] ; let prevSourceFile ; let currentFile ; if ( ts . endsWith ( sourceMapData . sourceMap . file , ts . Extension . Dts ) ) { if ( sourceMapDataList . length > jsFiles . length ) { currentFile = declarationFiles [ Math . floor ( i / 2 ) ] ; } else { currentFile = declarationFiles [ i ] ; } } else { if ( sourceMapDataList . length > jsFiles . length ) { currentFile = jsFiles [ Math . floor ( i / 2 ) ] ; } else { currentFile = jsFiles [ i ] ; } } SourceMapSpanWriter . initializeSourceMapSpanWriter ( sourceMapRecorder , sourceMapData . sourceMap , currentFile ) ; const mapper = ts . decodeMappings ( sourceMapData . sourceMap . mappings ) ; for ( let iterResult = mapper . next ( ) ; ! iterResult . done ; iterResult = mapper . next ( ) ) { const decodedSourceMapping = iterResult . value ; const currentSourceFile = ts . isSourceMapping ( decodedSourceMapping ) ? program . getSourceFile ( sourceMapData . inputSourceFileNames [ decodedSourceMapping . sourceIndex ] ) : undefined ; if ( currentSourceFile !== prevSourceFile ) { if ( currentSourceFile ) { SourceMapSpanWriter . recordNewSourceFileSpan ( decodedSourceMapping , currentSourceFile . text ) ; } prevSourceFile = currentSourceFile ; } else { SourceMapSpanWriter . recordSourceMapSpan ( decodedSourceMapping ) ; } } SourceMapSpanWriter . close ( ) ; } sourceMapRecorder . Close ( ) ; return sourceMapRecorder . lines . join ( "\r\n" ) ; } export function getSourceMapRecordWithSystem ( sys , sourceMapFile ) { const sourceMapRecorder = new Compiler . WriterAggregator ( ) ; let prevSourceFile ; const files = new ts . Map < string , documents . TextDocument > ( ) ; const sourceMap = ts . tryParseRawSourceMap ( sys . readFile ( sourceMapFile , "utf8" ) ! ) ; if ( sourceMap ) { const mapDirectory = ts . getDirectoryPath ( sourceMapFile ) ; const sourceRoot = sourceMap . sourceRoot ? ts . getNormalizedAbsolutePath ( sourceMap . sourceRoot , mapDirectory ) : mapDirectory ; const generatedAbsoluteFilePath = ts . getNormalizedAbsolutePath ( sourceMap . file , mapDirectory ) ; const sourceFileAbsolutePaths = sourceMap . sources . map ( source => ts . getNormalizedAbsolutePath ( source , sourceRoot ) ) ; const currentFile = getFile ( generatedAbsoluteFilePath ) ; SourceMapSpanWriter . initializeSourceMapSpanWriter ( sourceMapRecorder , sourceMap , currentFile ) ; const mapper = ts . decodeMappings ( sourceMap . mappings ) ; for ( let iterResult = mapper . next ( ) ; ! iterResult . done ; iterResult = mapper . next ( ) ) { const decodedSourceMapping = iterResult . value ; const currentSourceFile = ts . isSourceMapping ( decodedSourceMapping ) ? getFile ( sourceFileAbsolutePaths [ decodedSourceMapping . sourceIndex ] ) : undefined ; if ( currentSourceFile !== prevSourceFile ) { if ( currentSourceFile ) { SourceMapSpanWriter . recordNewSourceFileSpan ( decodedSourceMapping , currentSourceFile . text ) ; } prevSourceFile = currentSourceFile ; } else { SourceMapSpanWriter . recordSourceMapSpan ( decodedSourceMapping ) ; } } SourceMapSpanWriter . close ( ) ; } sourceMapRecorder . Close ( ) ; return sourceMapRecorder . lines . join ( "\r\n" ) ; function getFile ( path ) { const existing = files . get ( path ) ; if ( existing ) return existing ; const value = new documents . TextDocument ( path , sys . readFile ( path , "utf8" ) ! ) ; files . set ( path , value ) ; return value ; } } }