Skip to content

Commit dd3bbad

Browse files
author
John Vilk
committed
Support multiple source files per map. No section support AFAIK.
1 parent f83fcce commit dd3bbad

File tree

3 files changed

+94
-97
lines changed

3 files changed

+94
-97
lines changed

lib/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ const defaultOptions: ISourceMapMergerConfig = {
2222
function mergeFile(file: IFile, config: ISourceMapMergerConfig): void {
2323
const sourceFile = new SourceFile(file.src);
2424
if (sourceFile.getMap() !== null) {
25-
const map = sourceFile.getMap();
2625
// Merge all of the sources together.
26+
const map = sourceFile.getMap();
2727
map.merge();
2828

2929
if (config.inlineSources) {

lib/merge_utils.ts

Lines changed: 0 additions & 72 deletions
This file was deleted.

lib/source_file.ts

Lines changed: 93 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import fs = require('fs');
22
import path = require('path');
33
import SourceMapModule = require("source-map");
4-
import Merger = require('./merge_utils');
54
const mappingUrlPrefix = "# sourceMappingURL=";
65
const dataURLPrefix = "data:application/json;base64,";
76

@@ -52,7 +51,7 @@ export class SourceFile {
5251
mapPath = path.resolve(path.dirname(this._path), url);
5352
mapContents = fs.readFileSync(mapPath).toString();
5453
}
55-
return new SourceMap(this, mapContents, mapPath);
54+
return new SourceMap(this, JSON.parse(mapContents), mapPath);
5655
}
5756

5857
public getPath(): string { return this._path; }
@@ -68,6 +67,18 @@ export class SourceFile {
6867
return path.relative(path.dirname(this._path), aPath);
6968
}
7069

70+
public findOriginal(line: number, col: number, shouldIgnoreMissingRanges: boolean): SourceMapModule.MappedPosition {
71+
if (this._map) {
72+
return this._map.findOriginal(line, col, shouldIgnoreMissingRanges);
73+
} else {
74+
return {
75+
line: line,
76+
column: col,
77+
source: this._path
78+
};
79+
}
80+
}
81+
7182
/**
7283
* Flush changes to disk.
7384
*/
@@ -83,7 +94,6 @@ export class SourceFile {
8394

8495
/**
8596
* Represents a SourceMap.
86-
* @param generatedFilePath The path to a generated JavaScript file that contains a sourceMappingURL.
8797
*/
8898
export class SourceMap {
8999
// The source map's corresponding file.
@@ -92,15 +102,26 @@ export class SourceMap {
92102
private _map: SourceMapModule.RawSourceMap;
93103
// The path to the SourceMap.
94104
private _path: string;
95-
// The next file in the compilation chain.
96105
private _sourceFiles: SourceFile[];
106+
private _sourceFileMap: {[p: string]: SourceFile};
107+
private _consumer: SourceMapModule.SourceMapConsumer;
97108

98-
constructor(file: SourceFile, rawContents: string, mapPath: string) {
109+
constructor(file: SourceFile, map: SourceMapModule.RawSourceMap, mapPath: string) {
99110
this._file = file;
100-
this._map = JSON.parse(rawContents);
101111
this._path = mapPath;
102-
// Get the source files
103-
this._sourceFiles = this.getAbsoluteSourcePaths().map((sourcePath) => new SourceFile(sourcePath));
112+
this._updateMap(map);
113+
}
114+
115+
private _updateMap(map: SourceMapModule.RawSourceMap): void {
116+
this._map = map;
117+
this._consumer = new SourceMapModule.SourceMapConsumer(map);
118+
this._sourceFileMap = {};
119+
this._sourceFiles = this.getAbsoluteSourcePaths().map((sourcePath) => {
120+
const m = new SourceFile(sourcePath);
121+
// Map relative path to sourcefile.
122+
this._sourceFileMap[sourcePath] = m;
123+
return m;
124+
});
104125
}
105126

106127
public getFile(): SourceFile {
@@ -127,6 +148,10 @@ export class SourceMap {
127148
return path.resolve(path.dirname(this._path), aPath);
128149
}
129150

151+
public getRelativePath(p: string): string {
152+
return path.relative(path.dirname(this._path), p);
153+
}
154+
130155
/**
131156
* Retrieve an absolute path to the SourceMap's sourceRoot.
132157
*/
@@ -147,28 +172,72 @@ export class SourceMap {
147172
return this._map;
148173
}
149174

150-
protected getParentMap(): SourceMap {
151-
if (this._sourceFiles.length > 1) {
152-
throw new Error(`Error: Source map for ${this._file.getPath()} has multiple source files.`);
153-
} else if (this._sourceFiles.length === 0) {
175+
protected getParentMaps(): SourceMap[] {
176+
if (this._sourceFiles.length === 0) {
154177
return null;
155178
}
156-
return this._sourceFiles[0].getMap();
179+
return this._sourceFiles.map((f) => f.getMap());
180+
}
181+
182+
public findOriginal(line: number, col: number, shouldIgnoreMissingRanges: boolean): SourceMapModule.MappedPosition {
183+
const pos = this._consumer.originalPositionFor({ line: line, column: col });
184+
if (!pos || pos.line === null || pos.line === undefined) {
185+
if (shouldIgnoreMissingRanges) {
186+
return pos;
187+
} else {
188+
throw new Error(`Could not find original location of ${this._file.getPath()}:${line}:${col}`);
189+
}
190+
}
191+
// Normalize source file.
192+
pos.source = this.resolveRelativePath(pos.source);
193+
if (this._sourceFiles.length > 0) {
194+
const sf = this._sourceFileMap[pos.source];
195+
if (!sf) {
196+
if (shouldIgnoreMissingRanges) {
197+
return pos;
198+
} else {
199+
throw new Error(`Could not find original location of ${this._file.getPath()}:${line}:${col}`);
200+
}
201+
} else {
202+
return sf.findOriginal(pos.line, pos.column, shouldIgnoreMissingRanges);
203+
}
204+
} else {
205+
return pos;
206+
}
157207
}
158208

159209
/**
160-
* Merges all parents into this source map.
210+
* Merges all parents into a new source map.
161211
*/
162-
public merge(): void {
163-
let nextMap: SourceMap = this,
164-
maps: SourceMapModule.RawSourceMap[] = [];
165-
while (nextMap !== null) {
166-
maps.push(nextMap.getMap())
167-
nextMap = nextMap.getParentMap();
168-
}
169-
this._map = JSON.parse(Merger.createMergedSourceMap(maps.reverse(), true));
170-
// Update SourceFiles.
171-
this._sourceFiles = this.getAbsoluteSourcePaths().map((sourcePath) => new SourceFile(sourcePath));
212+
public merge(shouldIgnoreMissingRanges: boolean = true): void {
213+
let generator = new SourceMapModule.SourceMapGenerator({
214+
file: this._path
215+
});
216+
217+
this._consumer.eachMapping((mapping) => {
218+
const original = this.findOriginal(mapping.generatedLine, mapping.generatedColumn, shouldIgnoreMissingRanges);
219+
// source-map uses nulled fields to indicate that it did not find a match.
220+
if (original.line === null && shouldIgnoreMissingRanges) {
221+
return;
222+
}
223+
224+
generator.addMapping({
225+
generated: {
226+
line: mapping.generatedLine,
227+
column: mapping.generatedColumn
228+
},
229+
original: {
230+
line: original.line,
231+
column: original.column
232+
},
233+
source: original.source,
234+
name: original.name
235+
});
236+
});
237+
238+
const newMap = generator.toJSON();
239+
newMap.sources = newMap.sources.map((s) => this.getRelativePath(s));
240+
this._updateMap(newMap);
172241
}
173242

174243
public inlineSources(): void {

0 commit comments

Comments
 (0)