This repository has been archived by the owner on Oct 2, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 119
/
sourceMap.ts
181 lines (152 loc) · 7.32 KB
/
sourceMap.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/*---------------------------------------------------------
* Copyright (C) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------*/
import {SourceMapConsumer, MappedPosition} from 'source-map';
import * as path from 'path';
import * as sourceMapUtils from './sourceMapUtils';
import * as utils from '../utils';
import {Maybe} from '../utils';
import * as logger from '../logger';
import {ISourceMapPathOverrides} from '../debugAdapterInterfaces';
export type MappedPosition = MappedPosition;
export class SourceMap {
private _generatedPath: string; // the generated file for this sourcemap (absolute path)
private _sources: string[]; // list of authored files (absolute paths)
private _smc: SourceMapConsumer; // the source map
private _authoredPathCaseMap = new Map<string, string>(); // Maintain pathCase map because VSCode is case sensitive
/**
* pathToGenerated - an absolute local path or a URL
* json - sourcemap contents
* webRoot - an absolute path
*/
public constructor(generatedPath: string, json: string, webRoot?: string, sourceMapPathOverrides?: ISourceMapPathOverrides) {
this._generatedPath = generatedPath;
const sm = JSON.parse(json);
logger.log(`SourceMap: creating for ${generatedPath}`);
logger.log(`SourceMap: sourceRoot: ${sm.sourceRoot}`);
if (sm.sourceRoot && sm.sourceRoot.toLowerCase() === '/source/') {
logger.log('Warning: if you are using gulp-sourcemaps < 2.0 directly or indirectly, you may need to set sourceRoot manually in your build config, if your files are not actually under a directory called /source');
}
logger.log(`SourceMap: sources: ${JSON.stringify(sm.sources)}`);
if (sm.sourcesContent && sm.sourcesContent.length) {
logger.log(`Warning: SourceMap sources are inlined. This extension ignores inlined sources. If the paths are not correct, sourcemap support won't work.`);
}
logger.log(`SourceMap: webRoot: ${webRoot}`);
// Absolute path
const computedSourceRoot = sourceMapUtils.getComputedSourceRoot(sm.sourceRoot, this._generatedPath, webRoot);
// Overwrite the sourcemap's sourceRoot with the version that's resolved to an absolute path,
// so the work above only has to be done once
const origSourceRoot = sm.sourceRoot;
sm.sourceRoot = null;
// sm.sources are initially relative paths, file:/// urls, made-up urls like webpack:///./app.js, or paths that start with /.
// resolve them to file:/// urls, using computedSourceRoot, to be simpler and unambiguous, since
// it needs to look them up later in exactly the same format.
this._sources = sm.sources.map(sourcePath => {
if (sourceMapPathOverrides) {
const fullSourceEntry = origSourceRoot ? (origSourceRoot + sourcePath) : sourcePath;
const mappedFullSourceEntry = sourceMapUtils.applySourceMapPathOverrides(fullSourceEntry, sourceMapPathOverrides);
if (fullSourceEntry !== mappedFullSourceEntry) {
return utils.canonicalizeUrl(mappedFullSourceEntry);
}
}
if (sourcePath.startsWith('file://')) {
// strip file://
return utils.canonicalizeUrl(sourcePath);
}
if (!path.isAbsolute(sourcePath)) {
// Overrides not applied, use the computed sourceRoot
sourcePath = path.resolve(computedSourceRoot, sourcePath);
}
return utils.canonicalizeUrl(sourcePath);
});
// Rewrite sm.sources to same as this._sources but file url with forward slashes
sm.sources = this._sources.map(sourceAbsPath => {
// Convert to file:/// url. After this, it's a file URL for an absolute path to a file on disk with forward slashes.
// We lowercase so authored <-> generated mapping is not case sensitive.
const lowerCaseSourceAbsPath = sourceAbsPath.toLowerCase();
this._authoredPathCaseMap.set(lowerCaseSourceAbsPath, sourceAbsPath);
return utils.pathToFileURL(lowerCaseSourceAbsPath);
});
this._smc = new SourceMapConsumer(sm);
}
/*
* Return all mapped sources as absolute paths
*/
public get authoredSources(): string[] {
return this._sources;
}
/*
* The generated file of this source map.
*/
public generatedPath(): string {
return this._generatedPath;
}
/*
* Returns true if this source map originates from the given source.
*/
public doesOriginateFrom(absPath: string): boolean {
return this.authoredSources.some(path => path === absPath);
}
/*
* Finds the nearest source location for the given location in the generated file.
* Will return null instead of a mapping on the next line (different from generatedPositionFor).
*/
public authoredPositionFor(line: number, column: number): Maybe<MappedPosition> {
// source-map lib uses 1-indexed lines.
line++;
const lookupArgs = {
line,
column,
bias: SourceMapConsumer.LEAST_UPPER_BOUND
};
let position = this._smc.originalPositionFor(lookupArgs);
if (!position.source) {
// If it can't find a match, it returns a mapping with null props. Try looking the other direction.
lookupArgs.bias = SourceMapConsumer.GREATEST_LOWER_BOUND;
position = this._smc.originalPositionFor(lookupArgs);
}
if (position.source) {
// file:/// -> absolute path
position.source = utils.canonicalizeUrl(position.source);
// Convert back to original case
position.source = this._authoredPathCaseMap.get(position.source) || position.source;
// Back to 0-indexed lines
position.line--;
return position;
} else {
return null;
}
}
/*
* Finds the nearest location in the generated file for the given source location.
* Will return a mapping on the next line, if there is no subsequent mapping on the expected line.
*/
public generatedPositionFor(source: string, line: number, column: number): Maybe<MappedPosition> {
// source-map lib uses 1-indexed lines.
line++;
// sources in the sourcemap have been forced to file:///
// Convert to lowerCase so search is case insensitive
source = utils.pathToFileURL(source.toLowerCase());
const lookupArgs = {
line,
column,
source,
bias: SourceMapConsumer.LEAST_UPPER_BOUND
};
let position = this._smc.generatedPositionFor(lookupArgs);
if (position.line === null) {
// If it can't find a match, it returns a mapping with null props. Try looking the other direction.
lookupArgs.bias = SourceMapConsumer.GREATEST_LOWER_BOUND;
position = this._smc.generatedPositionFor(lookupArgs);
}
if (position.line === null) {
return null;
} else {
return {
line: position.line - 1, // Back to 0-indexed lines
column: position.column,
source: this._generatedPath
};
}
}
}