I noticed this issue when using gulp-typescript and gulp-terser together.
When compiling ts.ts to ts.js and compress it, the source map ends up with sources: [ 'ts.js', 'ts.ts' ], sourcesContent: [ null, '...' ]. But it should be only one source ts.ts.
The null ts.js content is due to the 2nd source map (by terser) has some mapping cannot be found in the first source map (by typescript), because typescript compiler injected few lines of header in compiled code.
This issue can be demonstrated by following code snippet.
var {SourceMapGenerator, SourceMapConsumer} = require('source-map');
async function applySourceMap(first, second) {
var generator = SourceMapGenerator.fromSourceMap(await new SourceMapConsumer(second));
generator.applySourceMap(await new SourceMapConsumer(first));
return JSON.parse(generator.toString());
};
var first = {
version: 3,
sources: [ 'ts.ts' ],
names: [],
mappings: ';;AAAA', // [ [ [ 0, 0, 0, 0 ] ] ]
file: 'ts.js',
sourcesContent: ['export const 1;']
};
var second = {
version: 3,
sources: [ 'ts.js' ],
names: [],
mappings: 'AAAA,AAEA', // [ [ [ 0, 0, 0, 0 ], [ 0, 0, 2, 0 ] ] ]
file: 'ts.js'
};
applySourceMap(first, second).then(merged => {
console.log(merged);
});
/* prints following merged map:
{
version: 3,
sources: [ 'ts.js', 'ts.ts' ],
names: [],
mappings: 'AAAA,ACAA', // [ [ [ 0, 0, 0, 0 ], [ 0, 1, 0, 0 ] ] ]
file: 'ts.js',
sourcesContent: [ null, 'export const 1;' ]
}
*/
The resulting source map retained a mapping [0, 0, 0, 0] which points to ts.js.
The retained mapping is due to implementation here. When original mapping could not be found, the new mapping is untouched.
|
if (mapping.source === sourceFile && mapping.originalLine != null) { |
|
// Check if it can be mapped by the source map, then update the mapping. |
|
const original = aSourceMapConsumer.originalPositionFor({ |
|
line: mapping.originalLine, |
|
column: mapping.originalColumn |
|
}); |
|
if (original.source != null) { |
|
// Copy mapping |
|
mapping.source = original.source; |
|
if (aSourceMapPath != null) { |
|
mapping.source = util.join(aSourceMapPath, mapping.source); |
|
} |
|
if (sourceRoot != null) { |
|
mapping.source = util.relative(sourceRoot, mapping.source); |
|
} |
|
mapping.originalLine = original.line; |
|
mapping.originalColumn = original.column; |
|
if (original.name != null) { |
|
mapping.name = original.name; |
|
} |
|
} |
|
} |
This is probably wrong.
The whole block is behind condition if (mapping.source === sourceFile ..., that means the current mapping is referencing a chained source from previous source map, not referencing an additional source.
I think the proper logic should be:
if (mapping.source === sourceFile && mapping.originalLine != null) {
...
if (original.source != null) {
...
} else {
// Add else branch here
remove_this_mapping_from_merged_result
}
}
Let me know whether this makes sense.
I noticed this issue when using
gulp-typescriptandgulp-tersertogether.When compiling
ts.tstots.jsand compress it, the source map ends up withsources: [ 'ts.js', 'ts.ts' ], sourcesContent: [ null, '...' ]. But it should be only one sourcets.ts.The null
ts.jscontent is due to the 2nd source map (by terser) has some mapping cannot be found in the first source map (by typescript), because typescript compiler injected few lines of header in compiled code.This issue can be demonstrated by following code snippet.
The resulting source map retained a mapping
[0, 0, 0, 0]which points tots.js.The retained mapping is due to implementation here. When original mapping could not be found, the new mapping is untouched.
source-map/lib/source-map-generator.js
Lines 202 to 223 in 8cb3ee5
This is probably wrong.
The whole block is behind condition
if (mapping.source === sourceFile ..., that means the current mapping is referencing a chained source from previous source map, not referencing an additional source.I think the proper logic should be:
Let me know whether this makes sense.