Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decode mappings #107

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,16 @@
"eslint": "^2.11.1",
"istanbul": "^0.4.3",
"mocha": "^3.0.1",
"object-assign": "^4.1.0",
"remap-istanbul": "^0.6.4",
"resolve": "^1.1.7",
"rollup": "^0.34.5",
"rollup-plugin-buble": "^0.12.1",
"rollup-plugin-node-resolve": "^2.0.0",
"rollup-plugin-replace": "^1.1.0",
"source-map": "^0.5.6",
"source-map-support": "^0.4.0"
"source-map-support": "^0.4.0",
"sourcemap-codec": "^1.3.0"
},
"keywords": [
"string",
Expand Down
45 changes: 29 additions & 16 deletions src/Bundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,23 +103,36 @@ Bundle.prototype = {
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 {
if (options.encodeMappings === false) {
return Array.prototype.concat.apply(
this.intro.split( '\n' ).map( () => [] ).slice( 1 ),
this.sources.map( ( source, i ) => {
if ( !source.filename ) {
return source.content.toString().split( '\n' ).map( () => [] ).slice( 1 );
}
const sourceIndex = this.uniqueSourceIndexByFilename[ source.filename ];
mappings = source.content.getMappings( options, sourceIndex, offsets, names );
}

return prefix + mappings;
}).join( '' )
);
return source.content.getMappings( options, sourceIndex, offsets, names );
})
);
} else {
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 () {
Expand Down
11 changes: 7 additions & 4 deletions src/MagicString.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Chunk from './Chunk.js';
import SourceMap from './utils/SourceMap.js';
import guessIndent from './utils/guessIndent.js';
import generateMappings from './utils/generateMappings.js';
import encodeMappings from './utils/encodeMappings.js';
import getRelativePath from './utils/getRelativePath.js';
import isObject from './utils/isObject.js';
Expand Down Expand Up @@ -84,9 +85,7 @@ MagicString.prototype = {
return cloned;
},

generateMap ( options ) {
options = options || {};

generateMap ( options = {} ) {
const names = Object.keys( this.storedNames );

if ( DEBUG ) this.stats.time( 'generateMap' );
Expand All @@ -107,7 +106,11 @@ MagicString.prototype = {
},

getMappings ( options, sourceIndex, offsets, names ) {
return encodeMappings( this.original, this.intro, this.outro, this.firstChunk, options.hires, this.sourcemapLocations, sourceIndex, offsets, names );
if ( options.encodeMappings === false ) {
return generateMappings( this.original, this.intro, this.outro, this.firstChunk, options.hires, this.sourcemapLocations, sourceIndex, names );
} else {
return encodeMappings( this.original, this.intro, this.outro, this.firstChunk, options.hires, this.sourcemapLocations, sourceIndex, offsets, names );
}
},

indent ( indentStr, options ) {
Expand Down
124 changes: 124 additions & 0 deletions src/utils/generateMappings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { encode } from 'vlq';
import getSemis from './getSemis.js';
import getLocator from './getLocator.js';

const nonWhitespace = /\S/;

export default function generateMappings ( original, intro, outro, chunk, hires, sourcemapLocations, sourceIndex, names ) {
const rawLines = intro.split( '\n' ).map( () => [] );
let generatedCodeLine = rawLines.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;
}

return rawLines.map( segments => {
return segments.map( segment => {
const arr = [
segment.generatedCodeColumn,
segment.sourceIndex,
segment.sourceCodeLine,
segment.sourceCodeColumn
];

if ( ~segment.sourceCodeName ) {
arr.push( segment.sourceCodeName );
}

return arr;
});
}).concat( outro.split( '\n' ).map( () => [] ).slice( 1 ) );
}
38 changes: 24 additions & 14 deletions test/MagicString.Bundle.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
const assert = require( 'assert' );
const assign = require('object-assign');
const decodeMappings = require( 'sourcemap-codec' ).decode;
const SourceMapConsumer = require( 'source-map' ).SourceMapConsumer;
const MagicString = require( '../' );

require( 'source-map-support' ).install();
require( 'console-group' ).install();

function generateMap( b, options ) {
const map = b.generateMap( options );
const decodedMap = b.generateMap( assign( {}, options || {}, { encodeMappings: false } ) );
assert.deepEqual( decodedMap.mappings, decodeMappings( map.mappings ) );
return map;
}

describe( 'MagicString.Bundle', () => {
describe( 'addSource', () => {
it( 'should return this', () => {
Expand Down Expand Up @@ -87,7 +96,7 @@ describe( 'MagicString.Bundle', () => {
});
});

describe( 'generateMap', () => {
describe.only( 'generateMap', () => {
it( 'should generate a sourcemap', () => {
const b = new MagicString.Bundle()
.addSource({
Expand All @@ -100,7 +109,7 @@ describe( 'MagicString.Bundle', () => {
});


const map = b.generateMap({
const map = generateMap(b, {
file: 'bundle.js',
includeContent: true,
hires: true
Expand Down Expand Up @@ -149,7 +158,7 @@ describe( 'MagicString.Bundle', () => {
content: new MagicString( 'console.log( answer );' )
});

const map = b.generateMap({
const map = generateMap(b, {
file: 'bundle.js',
includeContent: true,
hires: true
Expand Down Expand Up @@ -198,7 +207,7 @@ describe( 'MagicString.Bundle', () => {
})
.indent().prepend( '(function () {\n' ).append( '\n}());' );

const map = b.generateMap({
const map = generateMap(b, {
file: 'bundle.js',
includeContent: true,
hires: true
Expand Down Expand Up @@ -229,15 +238,16 @@ describe( 'MagicString.Bundle', () => {
});

it( 'should allow missing file option when generating map', () => {
new MagicString.Bundle()
const m = new MagicString.Bundle()
.addSource({
filename: 'foo.js',
content: new MagicString( 'var answer = 42;' )
})
.generateMap({
includeContent: true,
hires: true
});

generateMap(m, {
includeContent: true,
hires: true
});
});

it( 'should handle repeated sources', () => {
Expand All @@ -259,7 +269,7 @@ describe( 'MagicString.Bundle', () => {
const code = b.toString();
assert.equal( code, 'var one = 1;\nvar two = 2;\nvar three = 3;\nvar four = 4;' );

const map = b.generateMap({
const map = generateMap(b, {
includeContent: true,
hires: true
});
Expand Down Expand Up @@ -303,7 +313,7 @@ describe( 'MagicString.Bundle', () => {
b.addSource( one );
b.addSource( two );

const map = b.generateMap({
const map = generateMap(b, {
file: 'output.js',
source: 'input.js',
includeContent: true
Expand All @@ -330,7 +340,7 @@ describe( 'MagicString.Bundle', () => {
b.addSource( two );
b.addSource( three );

const map = b.generateMap({
const map = generateMap(b, {
file: 'output.js',
source: 'input.js',
includeContent: true
Expand Down Expand Up @@ -359,7 +369,7 @@ describe( 'MagicString.Bundle', () => {
b.addSource( one );
b.addSource( two );

const map = b.generateMap({
const map = generateMap(b, {
file: 'output.js',
source: 'input.js',
includeContent: true
Expand All @@ -385,7 +395,7 @@ describe( 'MagicString.Bundle', () => {
b.addSource( one );
b.addSource( two );

const map = b.generateMap({
const map = generateMap(b, {
file: 'output.js',
source: 'input.js',
includeContent: true
Expand Down