From 24982cad96b8baff84c79c2a39e76a394dc6f708 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Thu, 1 Dec 2016 23:22:52 -0500 Subject: [PATCH 1/3] WIP towards unified sourcemap generation --- src/Bundle.js | 49 ++++++++++++- src/MagicString.js | 25 ++++++- src/utils/Mappings.js | 134 ++++++++++++++++++++++++++++++++++++ src/utils/encodeMappings.js | 1 + test/MagicString.Bundle.js | 44 +++++++++++- 5 files changed, 248 insertions(+), 5 deletions(-) create mode 100644 src/utils/Mappings.js diff --git a/src/Bundle.js b/src/Bundle.js index 0f3786a..f225006 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -4,6 +4,8 @@ import getSemis from './utils/getSemis.js'; import getRelativePath from './utils/getRelativePath.js'; import hasOwnProp from './utils/hasOwnProp.js'; import isObject from './utils/isObject.js'; +import getLocator from './utils/getLocator.js'; +import Mappings from './utils/Mappings.js'; export default function Bundle ( options = {} ) { this.intro = options.intro || ''; @@ -87,6 +89,50 @@ Bundle.prototype = { }); }); + const mappings = new Mappings( options.hires ); + + if ( this.intro ) { + mappings.addInsert( this.intro ); + } + + this.sources.forEach( ( source, i ) => { + if ( i > 0 ) { + mappings.addInsert( this.separator ); + } + + const sourceIndex = source.filename ? this.uniqueSourceIndexByFilename[ source.filename ] : -1; + const magicString = source.content; + const locate = getLocator( magicString.original ); + + if ( magicString.intro ) { + mappings.addInsert( magicString.intro ); + } + + magicString.firstChunk.eachNext( chunk => { + const loc = locate( chunk.start ); + + if ( chunk.intro.length ) mappings.addInsert( chunk.intro ); + + if ( chunk.edited ) { + mappings.addEdit( sourceIndex, chunk.content, chunk.original, loc, chunk.storeName ? names.indexOf( chunk.original ) : -1 ); + } else { + mappings.addUneditedChunk( sourceIndex, chunk, chunk.original, loc, magicString.sourcemapLocations ); + } + + if ( chunk.outro.length ) mappings.addInsert( chunk.outro ); + }); + + if ( magicString.outro ) { + mappings.addInsert( magicString.outro ); + } + }); + + const oldMappings = this.getMappings( options, names ); + // const newMappings = mappings.encode(); + + // console.log( `oldMappings`, oldMappings ) + // console.log( `newMappings`, newMappings ) + return new SourceMap({ file: ( options.file ? options.file.split( /[\/\\]/ ).pop() : null ), sources: this.uniqueSources.map( source => { @@ -96,7 +142,8 @@ Bundle.prototype = { return options.includeContent ? source.content : null; }), names, - mappings: this.getMappings( options, names ) + // mappings: this.getMappings( options, names ) + mappings: mappings.encode() }); }, diff --git a/src/MagicString.js b/src/MagicString.js index 7fc93d1..21f231b 100644 --- a/src/MagicString.js +++ b/src/MagicString.js @@ -5,6 +5,7 @@ import encodeMappings from './utils/encodeMappings.js'; import getRelativePath from './utils/getRelativePath.js'; import isObject from './utils/isObject.js'; import getLocator from './utils/getLocator.js'; +import Mappings from './utils/Mappings.js'; import Stats from './utils/Stats.js'; const warned = { @@ -130,7 +131,29 @@ MagicString.prototype = { generateMap ( options ) { options = options || {}; + const sourceIndex = 0; const names = Object.keys( this.storedNames ); + const mappings = new Mappings( options.hires ); + + const locate = getLocator( this.original ); + + if ( this.intro ) { + mappings.addEdit( -1, this.intro, '', { line: 0, column: 0 }, -1 ); + } + + this.firstChunk.eachNext( chunk => { + const loc = locate( chunk.start ); + + if ( chunk.intro.length ) mappings.addInsert( chunk.intro ); + + if ( chunk.edited ) { + mappings.addEdit( sourceIndex, chunk.content, chunk.original, loc, chunk.storeName ? names.indexOf( chunk.original ) : -1 ); + } else { + mappings.addUneditedChunk( sourceIndex, chunk, chunk.original, loc, this.sourcemapLocations ); + } + + if ( chunk.outro.length ) mappings.addInsert( chunk.outro ); + }); if ( DEBUG ) this.stats.time( 'generateMap' ); const map = new SourceMap({ @@ -138,7 +161,7 @@ MagicString.prototype = { sources: [ options.source ? getRelativePath( options.file || '', options.source ) : null ], sourcesContent: options.includeContent ? [ this.original ] : [ null ], names, - mappings: this.getMappings( options, 0, {}, names ) + mappings: mappings.encode() }); if ( DEBUG ) this.stats.timeEnd( 'generateMap' ); diff --git a/src/utils/Mappings.js b/src/utils/Mappings.js new file mode 100644 index 0000000..286e72c --- /dev/null +++ b/src/utils/Mappings.js @@ -0,0 +1,134 @@ +import { encode } from 'vlq'; + +const nonWhitespace = /\S/; + +export default function Mappings ( hires ) { + const offsets = { + generatedCodeColumn: 0, + sourceIndex: 0, + sourceCodeLine: 0, + sourceCodeColumn: 0, + sourceCodeName: 0 + }; + + let generatedCodeLine = 0; + let generatedCodeColumn = 0; + let hasContent = false; + + this.raw = []; + let rawSegments = this.raw[ generatedCodeLine ] = []; + + this.addEdit = ( sourceIndex, content, original, loc, nameIndex ) => { + if ( content.length ) { + if ( hasContent || ( content.length && nonWhitespace.test( content ) ) ) { + rawSegments.push({ + generatedCodeColumn, + sourceCodeLine: loc.line, + sourceCodeColumn: loc.column, + sourceCodeName: nameIndex, + sourceIndex + }); + } + } + + let lines = content.split( '\n' ); + let lastLine = lines.pop(); + + if ( lines.length ) { + generatedCodeLine += lines.length; + this.raw[ generatedCodeLine ] = rawSegments = []; + generatedCodeColumn = lastLine.length; + } else { + generatedCodeColumn += lastLine.length; + } + + lines = original.split( '\n' ); + lastLine = lines.pop(); + + if ( lines.length ) { + loc.line += lines.length; + loc.column = lastLine.length; + } else { + loc.column += lastLine.length; + } + + if ( content ) hasContent = true; + }; + + this.addInsert = str => { + if ( !str ) return; + + const lines = str.split( '\n' ); + const lastLine = lines.pop(); + + if ( lines.length ) { + generatedCodeLine += lines.length; + this.raw[ generatedCodeLine ] = rawSegments = []; + generatedCodeColumn = lastLine.length; + } else { + generatedCodeColumn += lastLine.length; + } + }; + + this.addUneditedChunk = ( sourceIndex, chunk, original, loc, sourcemapLocations ) => { + let originalCharIndex = chunk.start; + let first = true; + + while ( originalCharIndex < chunk.end ) { + if ( sourceIndex !== -1 ) { + if ( hires || first || sourcemapLocations[ originalCharIndex ] ) { + rawSegments.push({ + generatedCodeColumn, + sourceCodeLine: loc.line, + sourceCodeColumn: loc.column, + sourceCodeName: -1, + sourceIndex + }); + } + } + + if ( original[ originalCharIndex ] === '\n' ) { + loc.line += 1; + loc.column = 0; + generatedCodeLine += 1; + this.raw[ generatedCodeLine ] = rawSegments = []; + generatedCodeColumn = 0; + } else { + loc.column += 1; + generatedCodeColumn += 1; + } + + originalCharIndex += 1; + first = false; + } + + if ( chunk.content ) hasContent = true; + }; + + this.encode = () => { + return this.raw.map( segments => { + let generatedCodeColumn = 0; + + return segments.map( segment => { + const arr = [ + segment.generatedCodeColumn - generatedCodeColumn, + segment.sourceIndex - offsets.sourceIndex, + segment.sourceCodeLine - offsets.sourceCodeLine, + segment.sourceCodeColumn - offsets.sourceCodeColumn + ]; + + generatedCodeColumn = segment.generatedCodeColumn; + offsets.sourceIndex = segment.sourceIndex; + offsets.sourceCodeLine = segment.sourceCodeLine; + offsets.sourceCodeColumn = segment.sourceCodeColumn; + + if ( ~segment.sourceCodeName ) { + arr.push( segment.sourceCodeName - offsets.sourceCodeName ); + offsets.sourceCodeName = segment.sourceCodeName; + } + + return encode( arr ); + }).join( ',' ); + }).join( ';' ); + }; +} diff --git a/src/utils/encodeMappings.js b/src/utils/encodeMappings.js index de210e0..6a3d613 100644 --- a/src/utils/encodeMappings.js +++ b/src/utils/encodeMappings.js @@ -105,6 +105,7 @@ export default function encodeMappings ( original, intro, outro, chunk, hires, s chunk = nextChunk; } + offsets.generatedCodeColumn = offsets.generatedCodeColumn || 0; offsets.sourceIndex = offsets.sourceIndex || 0; offsets.sourceCodeLine = offsets.sourceCodeLine || 0; offsets.sourceCodeColumn = offsets.sourceCodeColumn || 0; diff --git a/test/MagicString.Bundle.js b/test/MagicString.Bundle.js index 9033450..b049e5d 100644 --- a/test/MagicString.Bundle.js +++ b/test/MagicString.Bundle.js @@ -111,9 +111,6 @@ describe( 'MagicString.Bundle', () => { assert.deepEqual( map.sources, [ 'foo.js', 'bar.js' ]); assert.deepEqual( map.sourcesContent, [ 'var answer = 42;', 'console.log( answer );' ]); - assert.equal( map.toString(), '{"version":3,"file":"bundle.js","sources":["foo.js","bar.js"],"sourcesContent":["var answer = 42;","console.log( answer );"],"names":[],"mappings":"AAAA,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;ACAf,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC"}' ); - assert.equal( map.toUrl(), 'data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnVuZGxlLmpzIiwic291cmNlcyI6WyJmb28uanMiLCJiYXIuanMiXSwic291cmNlc0NvbnRlbnQiOlsidmFyIGFuc3dlciA9IDQyOyIsImNvbnNvbGUubG9nKCBhbnN3ZXIgKTsiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7QUNBZixDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyJ9' ); - const smc = new SourceMapConsumer( map ); let loc; @@ -400,6 +397,47 @@ describe( 'MagicString.Bundle', () => { loc = smc.originalPositionFor({ line: 3, column: 9 }); assert.equal( loc.source, 'two.js' ); }); + + it( 'should handle empty separator', () => { + const b = new MagicString.Bundle({ + separator: '' + }); + + b.addSource({ + content: new MagicString( 'if ( foo ) { ' ) + }); + + const s = new MagicString( 'console.log( 42 );' ); + s.addSourcemapLocation( 8 ); + s.addSourcemapLocation( 15 ); + + b.addSource({ + filename: 'input.js', + content: s + }); + + b.addSource({ + content: new MagicString( ' }' ) + }); + + assert.equal( b.toString(), 'if ( foo ) { console.log( 42 ); }' ); + + const map = b.generateMap({ + file: 'output.js', + source: 'input.js', + includeContent: true + }); + + const smc = new SourceMapConsumer( map ); + const loc = smc.originalPositionFor({ line: 1, column: 21 }); + + assert.deepEqual( loc, { + source: 'input.js', + name: null, + line: 1, + column: 8 + }); + }); }); describe( 'indent', () => { From 1d7a9d6884920bc74389b9275c70ecd8c152311d Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Fri, 2 Dec 2016 00:00:28 -0500 Subject: [PATCH 2/3] unified approach to sourcemap generation - fixes bundles with non-newline separators --- src/Bundle.js | 42 ++--------- src/MagicString.js | 9 +-- src/utils/Mappings.js | 85 +++++++++------------- src/utils/encodeMappings.js | 138 ------------------------------------ src/utils/getSemis.js | 3 - 5 files changed, 43 insertions(+), 234 deletions(-) delete mode 100644 src/utils/encodeMappings.js delete mode 100644 src/utils/getSemis.js diff --git a/src/Bundle.js b/src/Bundle.js index f225006..9d26ed2 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -1,6 +1,5 @@ import MagicString from './MagicString.js'; import SourceMap from './utils/SourceMap.js'; -import getSemis from './utils/getSemis.js'; import getRelativePath from './utils/getRelativePath.js'; import hasOwnProp from './utils/hasOwnProp.js'; import isObject from './utils/isObject.js'; @@ -92,12 +91,12 @@ Bundle.prototype = { const mappings = new Mappings( options.hires ); if ( this.intro ) { - mappings.addInsert( this.intro ); + mappings.advance( this.intro ); } this.sources.forEach( ( source, i ) => { if ( i > 0 ) { - mappings.addInsert( this.separator ); + mappings.advance( this.separator ); } const sourceIndex = source.filename ? this.uniqueSourceIndexByFilename[ source.filename ] : -1; @@ -105,13 +104,13 @@ Bundle.prototype = { const locate = getLocator( magicString.original ); if ( magicString.intro ) { - mappings.addInsert( magicString.intro ); + mappings.advance( magicString.intro ); } magicString.firstChunk.eachNext( chunk => { const loc = locate( chunk.start ); - if ( chunk.intro.length ) mappings.addInsert( chunk.intro ); + if ( chunk.intro.length ) mappings.advance( chunk.intro ); if ( chunk.edited ) { mappings.addEdit( sourceIndex, chunk.content, chunk.original, loc, chunk.storeName ? names.indexOf( chunk.original ) : -1 ); @@ -119,20 +118,14 @@ Bundle.prototype = { mappings.addUneditedChunk( sourceIndex, chunk, chunk.original, loc, magicString.sourcemapLocations ); } - if ( chunk.outro.length ) mappings.addInsert( chunk.outro ); + if ( chunk.outro.length ) mappings.advance( chunk.outro ); }); if ( magicString.outro ) { - mappings.addInsert( magicString.outro ); + mappings.advance( magicString.outro ); } }); - const oldMappings = this.getMappings( options, names ); - // const newMappings = mappings.encode(); - - // console.log( `oldMappings`, oldMappings ) - // console.log( `newMappings`, newMappings ) - return new SourceMap({ file: ( options.file ? options.file.split( /[\/\\]/ ).pop() : null ), sources: this.uniqueSources.map( source => { @@ -142,33 +135,10 @@ Bundle.prototype = { return options.includeContent ? source.content : null; }), names, - // mappings: this.getMappings( options, names ) mappings: mappings.encode() }); }, - getMappings ( options, names ) { - const offsets = {}; - - return ( - getSemis( this.intro ) + - this.sources.map( ( source, i ) => { - const prefix = ( i > 0 ) ? ( getSemis( source.separator ) || ',' ) : ''; - let mappings; - - // we don't bother encoding sources without a filename - if ( !source.filename ) { - mappings = getSemis( source.content.toString() ); - } else { - const sourceIndex = this.uniqueSourceIndexByFilename[ source.filename ]; - mappings = source.content.getMappings( options, sourceIndex, offsets, names ); - } - - return prefix + mappings; - }).join( '' ) - ); - }, - getIndentString () { const indentStringCounts = {}; diff --git a/src/MagicString.js b/src/MagicString.js index 21f231b..3c44bb4 100644 --- a/src/MagicString.js +++ b/src/MagicString.js @@ -1,7 +1,6 @@ import Chunk from './Chunk.js'; import SourceMap from './utils/SourceMap.js'; import guessIndent from './utils/guessIndent.js'; -import encodeMappings from './utils/encodeMappings.js'; import getRelativePath from './utils/getRelativePath.js'; import isObject from './utils/isObject.js'; import getLocator from './utils/getLocator.js'; @@ -144,7 +143,7 @@ MagicString.prototype = { this.firstChunk.eachNext( chunk => { const loc = locate( chunk.start ); - if ( chunk.intro.length ) mappings.addInsert( chunk.intro ); + if ( chunk.intro.length ) mappings.advance( chunk.intro ); if ( chunk.edited ) { mappings.addEdit( sourceIndex, chunk.content, chunk.original, loc, chunk.storeName ? names.indexOf( chunk.original ) : -1 ); @@ -152,7 +151,7 @@ MagicString.prototype = { mappings.addUneditedChunk( sourceIndex, chunk, chunk.original, loc, this.sourcemapLocations ); } - if ( chunk.outro.length ) mappings.addInsert( chunk.outro ); + if ( chunk.outro.length ) mappings.advance( chunk.outro ); }); if ( DEBUG ) this.stats.time( 'generateMap' ); @@ -172,10 +171,6 @@ MagicString.prototype = { return this.indentStr === null ? '\t' : this.indentStr; }, - getMappings ( options, sourceIndex, offsets, names ) { - return encodeMappings( this.original, this.intro, this.outro, this.firstChunk, options.hires, this.sourcemapLocations, sourceIndex, offsets, names ); - }, - indent ( indentStr, options ) { const pattern = /^[^\r\n]/gm; diff --git a/src/utils/Mappings.js b/src/utils/Mappings.js index 286e72c..f318882 100644 --- a/src/utils/Mappings.js +++ b/src/utils/Mappings.js @@ -1,7 +1,5 @@ import { encode } from 'vlq'; -const nonWhitespace = /\S/; - export default function Mappings ( hires ) { const offsets = { generatedCodeColumn: 0, @@ -13,61 +11,27 @@ export default function Mappings ( hires ) { let generatedCodeLine = 0; let generatedCodeColumn = 0; - let hasContent = false; this.raw = []; let rawSegments = this.raw[ generatedCodeLine ] = []; + let pending = null; + this.addEdit = ( sourceIndex, content, original, loc, nameIndex ) => { if ( content.length ) { - if ( hasContent || ( content.length && nonWhitespace.test( content ) ) ) { - rawSegments.push({ - generatedCodeColumn, - sourceCodeLine: loc.line, - sourceCodeColumn: loc.column, - sourceCodeName: nameIndex, - sourceIndex - }); - } - } - - let lines = content.split( '\n' ); - let lastLine = lines.pop(); - - if ( lines.length ) { - generatedCodeLine += lines.length; - this.raw[ generatedCodeLine ] = rawSegments = []; - generatedCodeColumn = lastLine.length; - } else { - generatedCodeColumn += lastLine.length; + rawSegments.push({ + generatedCodeColumn, + sourceCodeLine: loc.line, + sourceCodeColumn: loc.column, + sourceCodeName: nameIndex, + sourceIndex + }); + } else if ( pending ) { + rawSegments.push( pending ); } - lines = original.split( '\n' ); - lastLine = lines.pop(); - - if ( lines.length ) { - loc.line += lines.length; - loc.column = lastLine.length; - } else { - loc.column += lastLine.length; - } - - if ( content ) hasContent = true; - }; - - this.addInsert = str => { - if ( !str ) return; - - const lines = str.split( '\n' ); - const lastLine = lines.pop(); - - if ( lines.length ) { - generatedCodeLine += lines.length; - this.raw[ generatedCodeLine ] = rawSegments = []; - generatedCodeColumn = lastLine.length; - } else { - generatedCodeColumn += lastLine.length; - } + this.advance( content ); + pending = null; }; this.addUneditedChunk = ( sourceIndex, chunk, original, loc, sourcemapLocations ) => { @@ -102,7 +66,28 @@ export default function Mappings ( hires ) { first = false; } - if ( chunk.content ) hasContent = true; + pending = { + generatedCodeColumn, + sourceCodeLine: loc.line, + sourceCodeColumn: loc.column, + sourceCodeName: -1, + sourceIndex + }; + }; + + this.advance = str => { + if ( !str ) return; + + const lines = str.split( '\n' ); + const lastLine = lines.pop(); + + if ( lines.length ) { + generatedCodeLine += lines.length; + this.raw[ generatedCodeLine ] = rawSegments = []; + generatedCodeColumn = lastLine.length; + } else { + generatedCodeColumn += lastLine.length; + } }; this.encode = () => { diff --git a/src/utils/encodeMappings.js b/src/utils/encodeMappings.js deleted file mode 100644 index 6a3d613..0000000 --- a/src/utils/encodeMappings.js +++ /dev/null @@ -1,138 +0,0 @@ -import { encode } from 'vlq'; -import getSemis from './getSemis.js'; -import getLocator from './getLocator.js'; - -const nonWhitespace = /\S/; - -export default function encodeMappings ( original, intro, outro, chunk, hires, sourcemapLocations, sourceIndex, offsets, names ) { - const rawLines = []; - - let generatedCodeLine = intro.split( '\n' ).length - 1; - let rawSegments = rawLines[ generatedCodeLine ] = []; - - let generatedCodeColumn = 0; - - const locate = getLocator( original ); - - function addEdit ( content, original, loc, nameIndex, i ) { - if ( i || ( content.length && nonWhitespace.test( content ) ) ) { - rawSegments.push({ - generatedCodeLine, - generatedCodeColumn, - sourceCodeLine: loc.line, - sourceCodeColumn: loc.column, - sourceCodeName: nameIndex, - sourceIndex - }); - } - - let lines = content.split( '\n' ); - let lastLine = lines.pop(); - - if ( lines.length ) { - generatedCodeLine += lines.length; - rawLines[ generatedCodeLine ] = rawSegments = []; - generatedCodeColumn = lastLine.length; - } else { - generatedCodeColumn += lastLine.length; - } - - lines = original.split( '\n' ); - lastLine = lines.pop(); - - if ( lines.length ) { - loc.line += lines.length; - loc.column = lastLine.length; - } else { - loc.column += lastLine.length; - } - } - - function addUneditedChunk ( chunk, loc ) { - let originalCharIndex = chunk.start; - let first = true; - - while ( originalCharIndex < chunk.end ) { - if ( hires || first || sourcemapLocations[ originalCharIndex ] ) { - rawSegments.push({ - generatedCodeLine, - generatedCodeColumn, - sourceCodeLine: loc.line, - sourceCodeColumn: loc.column, - sourceCodeName: -1, - sourceIndex - }); - } - - if ( original[ originalCharIndex ] === '\n' ) { - loc.line += 1; - loc.column = 0; - generatedCodeLine += 1; - rawLines[ generatedCodeLine ] = rawSegments = []; - generatedCodeColumn = 0; - } else { - loc.column += 1; - generatedCodeColumn += 1; - } - - originalCharIndex += 1; - first = false; - } - } - - let hasContent = false; - - while ( chunk ) { - const loc = locate( chunk.start ); - - if ( chunk.intro.length ) { - addEdit( chunk.intro, '', loc, -1, hasContent ); - } - - if ( chunk.edited ) { - addEdit( chunk.content, chunk.original, loc, chunk.storeName ? names.indexOf( chunk.original ) : -1, hasContent ); - } else { - addUneditedChunk( chunk, loc ); - } - - if ( chunk.outro.length ) { - addEdit( chunk.outro, '', loc, -1, hasContent ); - } - - if ( chunk.content || chunk.intro || chunk.outro ) hasContent = true; - - const nextChunk = chunk.next; - chunk = nextChunk; - } - - offsets.generatedCodeColumn = offsets.generatedCodeColumn || 0; - offsets.sourceIndex = offsets.sourceIndex || 0; - offsets.sourceCodeLine = offsets.sourceCodeLine || 0; - offsets.sourceCodeColumn = offsets.sourceCodeColumn || 0; - offsets.sourceCodeName = offsets.sourceCodeName || 0; - - return rawLines.map( segments => { - let generatedCodeColumn = 0; - - return segments.map( segment => { - const arr = [ - segment.generatedCodeColumn - generatedCodeColumn, - segment.sourceIndex - offsets.sourceIndex, - segment.sourceCodeLine - offsets.sourceCodeLine, - segment.sourceCodeColumn - offsets.sourceCodeColumn - ]; - - generatedCodeColumn = segment.generatedCodeColumn; - offsets.sourceIndex = segment.sourceIndex; - offsets.sourceCodeLine = segment.sourceCodeLine; - offsets.sourceCodeColumn = segment.sourceCodeColumn; - - if ( ~segment.sourceCodeName ) { - arr.push( segment.sourceCodeName - offsets.sourceCodeName ); - offsets.sourceCodeName = segment.sourceCodeName; - } - - return encode( arr ); - }).join( ',' ); - }).join( ';' ) + getSemis(outro); -} diff --git a/src/utils/getSemis.js b/src/utils/getSemis.js deleted file mode 100644 index 4ead76e..0000000 --- a/src/utils/getSemis.js +++ /dev/null @@ -1,3 +0,0 @@ -export default function getSemis ( str ) { - return new Array( str.split( '\n' ).length ).join( ';' ); -} From 96fcd623a7fbfcb972093406c7e191e0cd87fbda Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Fri, 2 Dec 2016 11:19:40 -0500 Subject: [PATCH 3/3] alright, it finally works. did i mention i hate sourcemaps --- src/Bundle.js | 14 ++++++--- src/MagicString.js | 4 +-- src/utils/Mappings.js | 64 ++++++++++++++++++-------------------- test/MagicString.Bundle.js | 58 ++++++++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 39 deletions(-) diff --git a/src/Bundle.js b/src/Bundle.js index 9d26ed2..8a11683 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -112,10 +112,16 @@ Bundle.prototype = { if ( chunk.intro.length ) mappings.advance( chunk.intro ); - if ( chunk.edited ) { - mappings.addEdit( sourceIndex, chunk.content, chunk.original, loc, chunk.storeName ? names.indexOf( chunk.original ) : -1 ); - } else { - mappings.addUneditedChunk( sourceIndex, chunk, chunk.original, loc, magicString.sourcemapLocations ); + if ( source.filename ) { + if ( chunk.edited ) { + mappings.addEdit( sourceIndex, chunk.content, chunk.original, loc, chunk.storeName ? names.indexOf( chunk.original ) : -1 ); + } else { + mappings.addUneditedChunk( sourceIndex, chunk, magicString.original, loc, magicString.sourcemapLocations ); + } + } + + else { + mappings.advance( chunk.content ); } if ( chunk.outro.length ) mappings.advance( chunk.outro ); diff --git a/src/MagicString.js b/src/MagicString.js index 3c44bb4..af851f2 100644 --- a/src/MagicString.js +++ b/src/MagicString.js @@ -137,7 +137,7 @@ MagicString.prototype = { const locate = getLocator( this.original ); if ( this.intro ) { - mappings.addEdit( -1, this.intro, '', { line: 0, column: 0 }, -1 ); + mappings.advance( this.intro ); } this.firstChunk.eachNext( chunk => { @@ -148,7 +148,7 @@ MagicString.prototype = { if ( chunk.edited ) { mappings.addEdit( sourceIndex, chunk.content, chunk.original, loc, chunk.storeName ? names.indexOf( chunk.original ) : -1 ); } else { - mappings.addUneditedChunk( sourceIndex, chunk, chunk.original, loc, this.sourcemapLocations ); + mappings.addUneditedChunk( sourceIndex, chunk, this.original, loc, this.sourcemapLocations ); } if ( chunk.outro.length ) mappings.advance( chunk.outro ); diff --git a/src/utils/Mappings.js b/src/utils/Mappings.js index f318882..b32cf0e 100644 --- a/src/utils/Mappings.js +++ b/src/utils/Mappings.js @@ -19,13 +19,13 @@ export default function Mappings ( hires ) { this.addEdit = ( sourceIndex, content, original, loc, nameIndex ) => { if ( content.length ) { - rawSegments.push({ + rawSegments.push([ generatedCodeColumn, - sourceCodeLine: loc.line, - sourceCodeColumn: loc.column, - sourceCodeName: nameIndex, - sourceIndex - }); + sourceIndex, + loc.line, + loc.column, + nameIndex, + ]); } else if ( pending ) { rawSegments.push( pending ); } @@ -39,16 +39,14 @@ export default function Mappings ( hires ) { let first = true; while ( originalCharIndex < chunk.end ) { - if ( sourceIndex !== -1 ) { - if ( hires || first || sourcemapLocations[ originalCharIndex ] ) { - rawSegments.push({ - generatedCodeColumn, - sourceCodeLine: loc.line, - sourceCodeColumn: loc.column, - sourceCodeName: -1, - sourceIndex - }); - } + if ( hires || first || sourcemapLocations[ originalCharIndex ] ) { + rawSegments.push([ + generatedCodeColumn, + sourceIndex, + loc.line, + loc.column, + -1 + ]); } if ( original[ originalCharIndex ] === '\n' ) { @@ -66,13 +64,13 @@ export default function Mappings ( hires ) { first = false; } - pending = { + pending = [ generatedCodeColumn, - sourceCodeLine: loc.line, - sourceCodeColumn: loc.column, - sourceCodeName: -1, - sourceIndex - }; + sourceIndex, + loc.line, + loc.column, + -1, + ]; }; this.advance = str => { @@ -96,20 +94,20 @@ export default function Mappings ( hires ) { return segments.map( segment => { const arr = [ - segment.generatedCodeColumn - generatedCodeColumn, - segment.sourceIndex - offsets.sourceIndex, - segment.sourceCodeLine - offsets.sourceCodeLine, - segment.sourceCodeColumn - offsets.sourceCodeColumn + segment[0] - generatedCodeColumn, + segment[1] - offsets.sourceIndex, + segment[2] - offsets.sourceCodeLine, + segment[3] - offsets.sourceCodeColumn ]; - generatedCodeColumn = segment.generatedCodeColumn; - offsets.sourceIndex = segment.sourceIndex; - offsets.sourceCodeLine = segment.sourceCodeLine; - offsets.sourceCodeColumn = segment.sourceCodeColumn; + generatedCodeColumn = segment[0]; + offsets.sourceIndex = segment[1]; + offsets.sourceCodeLine = segment[2]; + offsets.sourceCodeColumn = segment[3]; - if ( ~segment.sourceCodeName ) { - arr.push( segment.sourceCodeName - offsets.sourceCodeName ); - offsets.sourceCodeName = segment.sourceCodeName; + if ( ~segment[4] ) { + arr.push( segment[4] - offsets.sourceCodeName ); + offsets.sourceCodeName = segment[4]; } return encode( arr ); diff --git a/test/MagicString.Bundle.js b/test/MagicString.Bundle.js index b049e5d..f0ce009 100644 --- a/test/MagicString.Bundle.js +++ b/test/MagicString.Bundle.js @@ -438,6 +438,64 @@ describe( 'MagicString.Bundle', () => { column: 8 }); }); + + // TODO tidy this up. is a recreation of a bug in Svelte + it( 'generates a correct sourcemap for a Svelte component', () => { + const b = new MagicString.Bundle({ + separator: '' + }); + + const s = new MagicString( ` +
+ +`.trim() ); + + [ 21, 23, 38, 42, 50, 51, 54, 59, 66, 67, 70, 72, 74, 76, 77, 81, 84, 85 ].forEach( pos => { + s.addSourcemapLocation( pos ); + }); + + s.remove( 0, 21 ); + s.overwrite( 23, 38, 'return ' ); + s.prependRight( 21, 'var template = (function () {' ); + s.appendLeft( 85, '}());' ); + s.overwrite( 85, 94, '' ); + + b.addSource({ + content: s, + filename: 'input.js' + }); + + assert.equal( b.toString(), ` +var template = (function () { + return { + onrender () { + console.log( 42 ); + } + } +}());`.trim() ); + + const map = b.generateMap({ + file: 'output.js', + source: 'input.js', + includeContent: true + }); + + const smc = new SourceMapConsumer( map ); + const loc = smc.originalPositionFor({ line: 4, column: 16 }); + + assert.deepEqual( loc, { + source: 'input.js', + name: null, + line: 6, + column: 16 + }); + }); }); describe( 'indent', () => {