Skip to content

Commit dfff046

Browse files
committed
avoid generating invalid mappings when source maps contains mapping for the final position
1 parent 37233e3 commit dfff046

File tree

5 files changed

+278
-159
lines changed

5 files changed

+278
-159
lines changed

lib/ConcatSource.js

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -221,17 +221,6 @@ class ConcatSource extends Source {
221221
(finalSource && lastMappingLine === generatedLine);
222222
currentLineOffset += generatedLine - 1;
223223
}
224-
if (needToCloseMapping) {
225-
onChunk(
226-
undefined,
227-
currentLineOffset + 1,
228-
currentColumnOffset,
229-
-1,
230-
-1,
231-
-1,
232-
-1
233-
);
234-
}
235224
return {
236225
generatedLine: currentLineOffset + 1,
237226
generatedColumn: currentColumnOffset,

lib/PrefixSource.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ class PrefixSource extends Source {
7171
// for performance reasons)
7272
if (linesOnly || sourceIndex < 0) {
7373
chunk = prefix + chunk;
74-
} else {
74+
} else if (prefixOffset > 0) {
7575
onChunk(prefix, generatedLine, generatedColumn, -1, -1, -1, -1);
7676
generatedColumn += prefixOffset;
7777
}

lib/helpers/streamChunksOfSourceMap.js

Lines changed: 47 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ const streamChunksOfSourceMapFull = (
3939
}
4040
}
4141

42+
const lastLine = lines[lines.length - 1];
43+
const lastNewLine = lastLine.endsWith("\n");
44+
const finalLine = lastNewLine ? lines.length + 1 : lines.length;
45+
const finalColumn = lastNewLine ? 0 : lastLine.length;
46+
4247
let currentGeneratedLine = 1;
4348
let currentGeneratedColumn = 0;
4449

@@ -132,7 +137,11 @@ const streamChunksOfSourceMapFull = (
132137
}
133138
currentGeneratedColumn = generatedColumn;
134139
}
135-
if (sourceIndex >= 0) {
140+
if (
141+
sourceIndex >= 0 &&
142+
(generatedLine < finalLine ||
143+
(generatedLine === finalLine && generatedColumn < finalColumn))
144+
) {
136145
mappingActive = true;
137146
activeMappingSourceIndex = sourceIndex;
138147
activeMappingOriginalLine = originalLine;
@@ -141,18 +150,11 @@ const streamChunksOfSourceMapFull = (
141150
}
142151
};
143152
readMappings(mappings, onMapping);
144-
onMapping(lines.length + 1, 0, -1, -1, -1, -1);
145-
const lastLine = lines[lines.length - 1];
146-
const lastNewLine = lastLine.endsWith("\n");
147-
return lastNewLine
148-
? {
149-
generatedLine: lines.length + 1,
150-
generatedColumn: 0
151-
}
152-
: {
153-
generatedLine: lines.length,
154-
generatedColumn: lastLine.length
155-
};
153+
onMapping(finalLine, finalColumn, -1, -1, -1, -1);
154+
return {
155+
generatedLine: finalLine,
156+
generatedColumn: finalColumn
157+
};
156158
};
157159

158160
const streamChunksOfSourceMapLinesFull = (
@@ -188,7 +190,13 @@ const streamChunksOfSourceMapLinesFull = (
188190
originalColumn,
189191
_nameIndex
190192
) => {
191-
if (sourceIndex < 0 || generatedLine < currentGeneratedLine) return;
193+
if (
194+
sourceIndex < 0 ||
195+
generatedLine < currentGeneratedLine ||
196+
generatedLine > lines.length
197+
) {
198+
return;
199+
}
192200
while (generatedLine > currentGeneratedLine) {
193201
if (currentGeneratedLine <= lines.length) {
194202
onChunk(
@@ -228,17 +236,17 @@ const streamChunksOfSourceMapLinesFull = (
228236
-1
229237
);
230238
}
239+
231240
const lastLine = lines[lines.length - 1];
232241
const lastNewLine = lastLine.endsWith("\n");
233-
return lastNewLine
234-
? {
235-
generatedLine: lines.length + 1,
236-
generatedColumn: 0
237-
}
238-
: {
239-
generatedLine: lines.length,
240-
generatedColumn: lastLine.length
241-
};
242+
243+
const finalLine = lastNewLine ? lines.length + 1 : lines.length;
244+
const finalColumn = lastNewLine ? 0 : lastLine.length;
245+
246+
return {
247+
generatedLine: finalLine,
248+
generatedColumn: finalColumn
249+
};
242250
};
243251

244252
const streamChunksOfSourceMapFinal = (
@@ -248,6 +256,10 @@ const streamChunksOfSourceMapFinal = (
248256
onSource,
249257
onName
250258
) => {
259+
const result = getGeneratedSourceInfo(source);
260+
const { generatedLine: finalLine, generatedColumn: finalColumn } = result;
261+
262+
if (finalLine === 1 && finalColumn === 0) return result;
251263
const { sources, sourcesContent, names, mappings } = sourceMap;
252264
for (let i = 0; i < sources.length; i++) {
253265
onSource(
@@ -272,6 +284,12 @@ const streamChunksOfSourceMapFinal = (
272284
originalColumn,
273285
nameIndex
274286
) => {
287+
if (
288+
generatedLine >= finalLine &&
289+
(generatedColumn >= finalColumn || generatedLine > finalLine)
290+
) {
291+
return;
292+
}
275293
if (sourceIndex >= 0) {
276294
onChunk(
277295
undefined,
@@ -289,7 +307,7 @@ const streamChunksOfSourceMapFinal = (
289307
}
290308
};
291309
readMappings(mappings, onMapping);
292-
return getGeneratedSourceInfo(source);
310+
return result;
293311
};
294312

295313
const streamChunksOfSourceMapLinesFinal = (
@@ -300,8 +318,8 @@ const streamChunksOfSourceMapLinesFinal = (
300318
_onName
301319
) => {
302320
const result = getGeneratedSourceInfo(source);
303-
const { generatedLine: numberOfLines, generatedColumn } = result;
304-
if (numberOfLines === 1 && generatedColumn === 0) {
321+
const { generatedLine, generatedColumn } = result;
322+
if (generatedLine === 1 && generatedColumn === 0) {
305323
return {
306324
generatedLine: 1,
307325
generatedColumn: 0
@@ -317,6 +335,8 @@ const streamChunksOfSourceMapLinesFinal = (
317335
);
318336
}
319337

338+
const finalLine = generatedColumn === 0 ? generatedLine - 1 : generatedLine;
339+
320340
let currentGeneratedLine = 1;
321341

322342
const onMapping = (
@@ -330,7 +350,7 @@ const streamChunksOfSourceMapLinesFinal = (
330350
if (
331351
sourceIndex >= 0 &&
332352
currentGeneratedLine <= generatedLine &&
333-
generatedLine <= numberOfLines
353+
generatedLine <= finalLine
334354
) {
335355
onChunk(
336356
undefined,

test/SourceMapSource.js

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ jest.mock("../lib/helpers/createMappingsSerializer");
22
const SourceMapSource = require("../").SourceMapSource;
33
const OriginalSource = require("../").OriginalSource;
44
const ConcatSource = require("../").ConcatSource;
5+
const PrefixSource = require("../").PrefixSource;
56
const ReplaceSource = require("../").ReplaceSource;
7+
const CachedSource = require("../").CachedSource;
68
const SourceNode = require("source-map").SourceNode;
79
const fs = require("fs");
810
const path = require("path");
@@ -226,4 +228,112 @@ describe("SourceMapSource", () => {
226228
expect(source.source()).toBe(code + code);
227229
expect(source.sourceAndMap().source).toBe(code + code);
228230
});
231+
232+
it("should not emit zero sizes mappings when ending with empty mapping", () => {
233+
const a = new SourceMapSource("hello\n", "a", {
234+
version: 3,
235+
mappings: "AAAA;AACA",
236+
sources: ["hello1"]
237+
});
238+
const b = new SourceMapSource("hi", "b", {
239+
version: 3,
240+
mappings: "AAAA,EAAE",
241+
sources: ["hello2"]
242+
});
243+
const b2 = new SourceMapSource("hi", "b", {
244+
version: 3,
245+
mappings: "AAAA,EAAE",
246+
sources: ["hello3"]
247+
});
248+
const c = new SourceMapSource("", "c", {
249+
version: 3,
250+
mappings: "AAAA",
251+
sources: ["hello4"]
252+
});
253+
const source = new ConcatSource(
254+
a,
255+
a,
256+
b,
257+
b,
258+
b2,
259+
b,
260+
c,
261+
c,
262+
b2,
263+
a,
264+
b2,
265+
c,
266+
a,
267+
b
268+
);
269+
source.sourceAndMap();
270+
expect(withReadableMappings(source.map(), source.source()))
271+
.toMatchInlineSnapshot(`
272+
Object {
273+
"_mappings": "1:0 -> [hello1] 1:0
274+
hello
275+
^____
276+
2:0 -> [hello1] 1:0
277+
hello
278+
^____
279+
3:0 -> [hello2] 1:0, :4 -> [hello3] 1:0, :6 -> [hello2] 1:0, :8 -> [hello3] 1:0, :10 -> [hello1] 1:0
280+
hihihihihihello
281+
^___^_^_^_^____
282+
4:0 -> [hello3] 1:0, :2 -> [hello1] 1:0
283+
hihello
284+
^_^____
285+
5:0 -> [hello2] 1:0
286+
hi
287+
^_
288+
",
289+
"file": "x",
290+
"mappings": "AAAA;AAAA;ACAA,ICAA,EDAA,ECAA,EFAA;AEAA,EFAA;ACAA",
291+
"names": Array [],
292+
"sources": Array [
293+
"hello1",
294+
"hello2",
295+
"hello3",
296+
],
297+
"sourcesContent": undefined,
298+
"version": 3,
299+
}
300+
`);
301+
source.sourceAndMap({ columns: true });
302+
source.map({ columns: true });
303+
const withReplacements = s => {
304+
const r = new ReplaceSource(s);
305+
r.insert(0, "");
306+
return r;
307+
};
308+
withReplacements(source).sourceAndMap();
309+
withReplacements(source).map();
310+
withReplacements(source).sourceAndMap({ columns: false });
311+
withReplacements(source).map({ columns: false });
312+
const withPrefix = s => new PrefixSource("test", s);
313+
withPrefix(source).sourceAndMap();
314+
withPrefix(source).map();
315+
withPrefix(source).sourceAndMap({ columns: false });
316+
withPrefix(source).map({ columns: false });
317+
const testCached = (s, fn) => {
318+
const c = new CachedSource(s);
319+
const o = fn(s);
320+
const a = fn(c);
321+
expect(a).toEqual(o);
322+
const b = fn(c);
323+
expect(b).toEqual(o);
324+
return b;
325+
};
326+
testCached(source, s => s.sourceAndMap());
327+
testCached(source, s => s.map());
328+
testCached(source, s => s.sourceAndMap({ columns: false }));
329+
testCached(source, s => s.map({ columns: false }));
330+
testCached(withPrefix(source), s => s.sourceAndMap());
331+
testCached(withPrefix(source), s => s.map());
332+
testCached(withPrefix(source), s => s.sourceAndMap({ columns: false }));
333+
testCached(withPrefix(source), s => s.map({ columns: false }));
334+
testCached(source, s => withPrefix(s).sourceAndMap());
335+
testCached(source, s => withPrefix(s).map());
336+
testCached(source, s => withPrefix(s).sourceAndMap({ columns: false }));
337+
testCached(source, s => withPrefix(s).map({ columns: false }));
338+
});
229339
});

0 commit comments

Comments
 (0)