import * as ts from 'typescript' ; import { RawSourceMap , SourceMapConsumer , SourceMapGenerator , Position } from 'source-map' ; import { expect } from 'chai' ; import { MutableSourceCode , ReplaceAction , Action } from '../src/mutable-source-code' ; import { traverseAst } from '../src/traverse-ast' ; import { findCodeRange , findCodePosition } from '../test-kit/index' ; import { SingleFileHost } from '../src/hosts' ; import { defaultCompilerOptions } from '../src/configuration' ; function makeReplacement ( source , atStr , insStr ) { const textRange = findCodeRange ( source , atStr ) ; return new ReplaceAction ( textRange . pos , textRange . end , insStr ) ; } function makeLineInsersion ( source , atStr , insStr ) { const textRange = findCodeRange ( source , atStr ) ; return new ReplaceAction ( textRange . pos , textRange . pos , insStr + '\n' ) ; } function aSourceMapperFor ( source ) { const ast = ts . createSourceFile ( 'test.ts' , source , defaultCompilerOptions . target , true ) ; return new MutableSourceCode ( ast ) ; } function expectSourceMapToMatchChangeForSuppliedText ( source , target , sourceMap , text ) { const positionBeforeChange = findCodePosition ( source , text ) ; const positionAfterChange = findCodePosition ( target , text ) ; const mapConsumer = new SourceMapConsumer ( sourceMap ) ; const mappedPosition = mapConsumer . originalPositionFor ( positionAfterChange ) ; expect ( { line : mappedPosition . line , column : mappedPosition . column } ) . to . eql ( { line : positionBeforeChange . line , column : positionBeforeChange . column } ) ; } function transpile ( source ) { const ast = ts . createSourceFile ( 'test.ts' , source , defaultCompilerOptions . target , true ) ; const compilerHost = new SingleFileHost ( ast ) ; const program = ts . createProgram ( [ 'test.ts' ] , defaultCompilerOptions , compilerHost ) ; program . emit ( ) ; return { code : compilerHost . output , map : compilerHost . sourceMap } ; } describe ( 'given a source code and given a replacement command, sourcemapper should' , ( ) => { const source = `class A {}
PLACE_HOLDERclass B {}
fubar();` ; const target = `class A {}
@bar
@foo
class B {}
fubar();` ; const action1 = makeLineInsersion ( source , 'PLACE_HOLDER' , '@bar' ) ; const action2 = makeReplacement ( source , 'PLACE_HOLDER' , '@foo\n' ) ; var mutableCode ; beforeEach ( ( ) => { mutableCode = aSourceMapperFor ( source ) ; } ) ; it ( 'generate a new string that matches the expected target' , ( ) => { mutableCode . execute ( [ action1 , action2 ] ) ; expect ( mutableCode . code ) . to . equal ( target ) ; } ) ; it ( 'generate correct sourcemap that reflects the changes' , ( ) => { mutableCode . execute ( [ action1 , action2 ] ) ; expectSourceMapToMatchChangeForSuppliedText ( source , mutableCode . code , mutableCode . sourceMap , 'class B' ) ; } ) ; it ( 'map the changes onto a sourcemap generated by typescript' , ( ) => { mutableCode . execute ( [ action1 , action2 ] ) ; var result = transpile ( mutableCode . code ) ; const sourceMap = mutableCode . translateMap ( result . map ) ; expectSourceMapToMatchChangeForSuppliedText ( source , result . code , sourceMap , 'fubar()' ) ; } ) ; } ) ;