fix(ngcc): handle mappings outside the content when flattening source-maps (#35718)

Previously when rendering flattened source-maps, it was assumed that no
mapping would come from a line that is outside the lines of the actual
source content. It turns out this is not a valid assumption.

Now the code that renders flattened source-maps will handle such
mappings, with the additional benefit that the rendered source-map
will only contain mapping lines up to the last mapping, rather than a
mapping line for every content line.

Fixes #35709

PR Close #35718
This commit is contained in:
Pete Bacon Darwin 2020-02-27 19:50:14 +00:00 committed by Matias Niemelä
parent 72c4fda613
commit 73cf7d5cb4
3 changed files with 37 additions and 10 deletions

View File

@ -49,11 +49,9 @@ export class SourceFile {
const sources: SourceFile[] = []; const sources: SourceFile[] = [];
const names: string[] = []; const names: string[] = [];
// Ensure a mapping line array for each line in the generated source. const mappings: SourceMapMappings = [];
const mappings: SourceMapMappings = this.lineLengths.map(() => []);
for (const mapping of this.flattenedMappings) { for (const mapping of this.flattenedMappings) {
const mappingLine = mappings[mapping.generatedSegment.line];
const sourceIndex = findIndexOrAdd(sources, mapping.originalSource); const sourceIndex = findIndexOrAdd(sources, mapping.originalSource);
const mappingArray: SourceMapSegment = [ const mappingArray: SourceMapSegment = [
mapping.generatedSegment.column, mapping.generatedSegment.column,
@ -65,7 +63,14 @@ export class SourceFile {
const nameIndex = findIndexOrAdd(names, mapping.name); const nameIndex = findIndexOrAdd(names, mapping.name);
mappingArray.push(nameIndex); mappingArray.push(nameIndex);
} }
mappingLine.push(mappingArray);
// Ensure a mapping line array for this mapping.
const line = mapping.generatedSegment.line;
while (line >= mappings.length) {
mappings.push([]);
}
// Add this mapping to the line
mappings[line].push(mappingArray);
} }
const sourcePathDir = dirname(this.sourcePath); const sourcePathDir = dirname(this.sourcePath);

View File

@ -174,7 +174,6 @@ runInEachFileSystem(() => {
[5, 0, 1, 20], [7, 0, 1, 22], [12, 0, 1, 27], [14, 0, 1, 28], [15, 0, 1, 29], [5, 0, 1, 20], [7, 0, 1, 22], [12, 0, 1, 27], [14, 0, 1, 28], [15, 0, 1, 29],
[9, 0, 2, 13], [10, 0, 2, 14] [9, 0, 2, 13], [10, 0, 2, 14]
], ],
[],
]; ];
JS_CONTENT_MAP = fromObject({ JS_CONTENT_MAP = fromObject({
@ -196,15 +195,12 @@ runInEachFileSystem(() => {
'file': 'file.js', 'file': 'file.js',
'sources': ['file.js'], 'sources': ['file.js'],
'names': [], 'names': [],
'mappings': encode([ 'mappings': encode([[], [], [], [], [], [], [], [], [], [], [], [], [[0, 0, 0, 0]]]),
[], [], [], [], [], [], [], [], [], [], [], [], [[0, 0, 0, 0]],
[], [], [], [], [], [], [], [], []
]),
'sourcesContent': [JS_CONTENT.contents], 'sourcesContent': [JS_CONTENT.contents],
}); });
const MERGED_OUTPUT_PROGRAM_MAPPINGS: SourceMapMappings = const MERGED_OUTPUT_PROGRAM_MAPPINGS: SourceMapMappings =
[[], [], [], [], [], [], [], [], [], [], [], [], ...JS_CONTENT_MAPPINGS, []]; [[], [], [], [], [], [], [], [], [], [], [], [], ...JS_CONTENT_MAPPINGS];
MERGED_OUTPUT_PROGRAM_MAP = fromObject({ MERGED_OUTPUT_PROGRAM_MAP = fromObject({
'version': 3, 'version': 3,

View File

@ -229,6 +229,32 @@ runInEachFileSystem(() => {
[[1, 0, 0, 0], [2, 0, 0, 2], [3, 0, 0, 3], [3, 0, 0, 6], [4, 0, 0, 1], [5, 0, 0, 7]] [[1, 0, 0, 0], [2, 0, 0, 2], [3, 0, 0, 3], [3, 0, 0, 6], [4, 0, 0, 1], [5, 0, 0, 7]]
])); ]));
}); });
it('should handle mappings that map from lines outside of the actual content lines', () => {
const bSource = new SourceFile(_('/foo/src/b.js'), 'abcdef', null, false, []);
const aToBSourceMap: RawSourceMap = {
mappings: encode([
[[0, 0, 0, 0], [2, 0, 0, 3], [4, 0, 0, 2], [5, 0, 0, 5]],
[
[0, 0, 0, 0], // Extra mapping from a non-existent line
]
]),
names: [],
sources: ['b.js'],
version: 3
};
const aSource =
new SourceFile(_('/foo/src/a.js'), 'abdecf', aToBSourceMap, false, [bSource]);
const aTocSourceMap = aSource.renderFlattenedSourceMap();
expect(aTocSourceMap.version).toEqual(3);
expect(aTocSourceMap.file).toEqual('a.js');
expect(aTocSourceMap.names).toEqual([]);
expect(aTocSourceMap.sourceRoot).toBeUndefined();
expect(aTocSourceMap.sources).toEqual(['b.js']);
expect(aTocSourceMap.sourcesContent).toEqual(['abcdef']);
expect(aTocSourceMap.mappings).toEqual(aToBSourceMap.mappings);
});
}); });
}); });