Browse Source

collapse sourcemaps from bundle transformers

gh-438-b
Rich-Harris 9 years ago
parent
commit
fc0c253101
  1. 22
      src/Bundle.js
  2. 2
      src/Module.js
  3. 2
      src/rollup.js
  4. 72
      src/utils/collapseSourcemaps.js
  5. 87
      src/utils/sourcemap/collapseSourcemaps.js
  6. 3
      src/utils/sourcemap/removeSourceMappingURL.js
  7. 0
      src/utils/sourcemap/sourceMappingURL.js
  8. 36
      src/utils/transformBundle.js
  9. 20
      test/sourcemaps/transform-bundle/_config.js

22
src/Bundle.js

@ -12,7 +12,8 @@ import getIndentString from './utils/getIndentString.js';
import { unixizePath } from './utils/normalizePlatform.js';
import transform from './utils/transform.js';
import transformBundle from './utils/transformBundle.js';
import collapseSourcemaps from './utils/collapseSourcemaps.js';
import collapseSourcemaps from './utils/sourcemap/collapseSourcemaps.js';
import SOURCEMAPPING_URL from './utils/sourcemap/sourceMappingURL.js';
import callIfFunction from './utils/callIfFunction.js';
import { isRelative } from './utils/path.js';
@ -247,22 +248,25 @@ export default class Bundle {
if ( banner ) magicString.prepend( banner + '\n' );
if ( footer ) magicString.append( '\n' + footer );
const code = magicString.toString();
let code = magicString.toString();
let map = null;
let bundleSourcemapChain = [];
code = transformBundle( code, this.bundleTransformers, bundleSourcemapChain )
.replace( new RegExp( `\\/\\/#\\s+${SOURCEMAPPING_URL}=.+\\n?`, 'g' ), '' );
if ( options.sourceMap ) {
const file = options.sourceMapFile || options.dest;
map = magicString.generateMap({
includeContent: true,
file
// TODO
});
map = magicString.generateMap({ file, includeContent: true });
if ( this.transformers.length || this.bundleTransformers.length ) {
map = collapseSourcemaps( map, usedModules, bundleSourcemapChain );
}
if ( this.transformers.length ) map = collapseSourcemaps( map, usedModules );
map.sources = map.sources.map( unixizePath );
}
return transformBundle( { code, map }, this.bundleTransformers );
return { code, map };
}
sort () {

2
src/Module.js

@ -6,7 +6,7 @@ import { blank, keys } from './utils/object.js';
import { basename, extname } from './utils/path.js';
import getLocation from './utils/getLocation.js';
import makeLegalIdentifier from './utils/makeLegalIdentifier.js';
import SOURCEMAPPING_URL from './utils/sourceMappingURL.js';
import SOURCEMAPPING_URL from './utils/sourcemap/sourceMappingURL.js';
import { SyntheticDefaultDeclaration, SyntheticNamespaceDeclaration } from './Declaration.js';
import { isFalsy, isTruthy } from './ast/conditions.js';
import { emptyBlockStatement } from './ast/create.js';

2
src/rollup.js

@ -3,7 +3,7 @@ import { basename } from './utils/path.js';
import { writeFile } from './utils/fs.js';
import { keys } from './utils/object.js';
import validateKeys from './utils/validateKeys.js';
import SOURCEMAPPING_URL from './utils/sourceMappingURL.js';
import SOURCEMAPPING_URL from './utils/sourcemap/sourceMappingURL.js';
import Bundle from './Bundle.js';
export const VERSION = '<@VERSION@>';

72
src/utils/collapseSourcemaps.js

@ -1,72 +0,0 @@
import { encode, decode } from 'sourcemap-codec';
function traceSegment ( loc, mappings ) {
const line = loc[0];
const column = loc[1];
const segments = mappings[ line ];
if ( !segments ) return null;
for ( let i = 0; i < segments.length; i += 1 ) {
const segment = segments[i];
if ( segment[0] > column ) return null;
if ( segment[0] === column ) {
if ( segment[1] !== 0 ) {
throw new Error( 'Bad sourcemap' );
}
return [ segment[2], segment[3] ];
}
}
return null;
}
export default function collapseSourcemaps ( map, modules ) {
const chains = modules.map( module => {
return module.sourceMapChain.map( map => {
if ( !map ) throw new Error( 'Cannot generate a sourcemap if non-sourcemap-generating transformers are used' );
return decode( map.mappings );
});
});
const decodedMappings = decode( map.mappings );
const tracedMappings = decodedMappings.map( line => {
let tracedLine = [];
line.forEach( segment => {
const sourceIndex = segment[1];
const sourceCodeLine = segment[2];
const sourceCodeColumn = segment[3];
const chain = chains[ sourceIndex ];
let i = chain.length;
let traced = [ sourceCodeLine, sourceCodeColumn ];
while ( i-- && traced ) {
traced = traceSegment( traced, chain[i] );
}
if ( traced ) {
tracedLine.push([
segment[0],
segment[1],
traced[0],
traced[1]
// TODO name?
]);
}
});
return tracedLine;
});
map.sourcesContent = modules.map( module => module.originalCode );
map.mappings = encode( tracedMappings );
return map;
}

87
src/utils/sourcemap/collapseSourcemaps.js

@ -0,0 +1,87 @@
import { encode, decode } from 'sourcemap-codec';
function Source ( map, sources ) {
if ( !map ) throw new Error( 'Cannot generate a sourcemap if non-sourcemap-generating transformers are used' );
this.sources = sources;
this.names = map.names;
this.mappings = decode( map.mappings );
}
Source.prototype = { // TODO bring into line with others post-https://github.com/rollup/rollup/pull/386
traceMappings () {
return this.mappings.map( line => {
let tracedLine = [];
line.forEach( segment => {
const source = this.sources[ segment[1] ];
const sourceCodeLine = segment[2];
const sourceCodeColumn = segment[3];
const traced = source.traceSegment( sourceCodeLine, sourceCodeColumn );
if ( traced ) {
tracedLine.push([
segment[0],
traced.index,
traced.line,
traced.column
// TODO name?
]);
}
});
return tracedLine;
});
},
traceSegment ( line, column ) {
const segments = this.mappings[ line ];
if ( !segments ) return null;
for ( let i = 0; i < segments.length; i += 1 ) {
const segment = segments[i];
if ( segment[0] > column ) return null;
if ( segment[0] === column ) {
const source = this.sources[ segment[1] ];
if ( !source ) throw new Error( 'Bad sourcemap' );
if ( source.isOriginal ) {
return { index: source.index, line: segment[2], column: segment[3] };
}
return source.traceSegment( segment[2], segment[3] );
}
}
return null;
}
};
export default function collapseSourcemaps ( map, modules, bundleSourcemapChain ) {
const sources = modules.map( ( module, i ) => {
let source = { isOriginal: true, index: i };
module.sourceMapChain.forEach( map => {
source = new Source( map, [ source ]);
});
return source;
});
let source = new Source( map, sources );
bundleSourcemapChain.forEach( map => {
source = new Source( map, [ source ] );
});
// we re-use the `map` object because it has convenient toString/toURL methods
map.sourcesContent = modules.map( module => module.originalCode );
map.mappings = encode( source.traceMappings() );
return map;
}

3
src/utils/sourcemap/removeSourceMappingURL.js

@ -0,0 +1,3 @@
export default function removeSourceMappingURL ( code ) {
}

0
src/utils/sourceMappingURL.js → src/utils/sourcemap/sourceMappingURL.js

36
src/utils/transformBundle.js

@ -1,17 +1,8 @@
import MagicString from 'magic-string';
export default function transformBundle ( code, transformers, sourceMapChain ) {
return transformers.reduce( ( code, transformer ) => {
let result = transformer( code );
export default function transformBundle ( source, transformers ) {
if ( typeof source === 'string' ) {
source = {
code: source,
map: null
};
}
return transformers.reduce( ( previous, transformer ) => {
let result = transformer( previous.code, previous.map );
if ( result == null ) return previous;
if ( result == null ) return code;
if ( typeof result === 'string' ) {
result = {
@ -19,21 +10,10 @@ export default function transformBundle ( source, transformers ) {
map: null
};
}
// `result.map` can only be a string if `result` isn't
else if ( typeof result.map === 'string' ) {
result.map = JSON.parse( result.map );
}
if (result.map != null) {
let map = new MagicString.Bundle().generateMap({});
map.file = result.map.file;
map.sources = result.map.sources;
map.sourcesContent = result.map.sourcesContent;
map.names = result.map.names;
map.mappings = result.map.mappings;
result.map = map;
}
const map = typeof result.map === 'string' ? JSON.parse( result.map ) : map;
sourceMapChain.push( map );
return result;
}, source );
return result.code;
}, code );
}

20
test/sourcemaps/transform-bundle/_config.js

@ -9,21 +9,13 @@ module.exports = {
options: {
plugins: [
{
transformBundle: function ( code, map ) {
var options = { fromString: true };
transformBundle: function ( code ) {
var options = {
fromString: true,
outSourceMap: 'x' // trigger sourcemap generation
};
if ( map != null ) {
options.inSourceMap = map;
options.outSourceMap = "out";
}
var result = uglify.minify( code, options );
if ( map != null ) {
result.code = result.code.slice( 0, -25 );
}
return result;
return uglify.minify( code, options );
}
}
]

Loading…
Cancel
Save