Skip to content

Commit af5b05b

Browse files
authored
Merge pull request #13 from jridgewell/scopes
Preliminary scopes support
2 parents 75eff9d + 6c86691 commit af5b05b

File tree

4 files changed

+176
-19
lines changed

4 files changed

+176
-19
lines changed

package-lock.json

+11-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@jridgewell/gen-mapping",
3-
"version": "0.3.5",
3+
"version": "0.3.6-beta.4",
44
"description": "Generate source maps",
55
"keywords": [
66
"source",
@@ -70,7 +70,7 @@
7070
},
7171
"dependencies": {
7272
"@jridgewell/set-array": "^1.2.1",
73-
"@jridgewell/sourcemap-codec": "^1.4.10",
73+
"@jridgewell/sourcemap-codec": "1.4.16-beta.0",
7474
"@jridgewell/trace-mapping": "^0.3.24"
7575
}
7676
}

src/gen-mapping.ts

+144-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { SetArray, put, remove } from '@jridgewell/set-array';
2-
import { encode } from '@jridgewell/sourcemap-codec';
2+
import { encode, encodeGeneratedRanges, encodeOriginalScopes } from '@jridgewell/sourcemap-codec';
33
import { TraceMap, decodedMappings } from '@jridgewell/trace-mapping';
44

55
import {
@@ -11,8 +11,18 @@ import {
1111
} from './sourcemap-segment';
1212

1313
import type { SourceMapInput } from '@jridgewell/trace-mapping';
14+
import type { OriginalScope, GeneratedRange } from '@jridgewell/sourcemap-codec';
1415
import type { SourceMapSegment } from './sourcemap-segment';
15-
import type { DecodedSourceMap, EncodedSourceMap, Pos, Mapping } from './types';
16+
import type {
17+
DecodedSourceMap,
18+
EncodedSourceMap,
19+
Pos,
20+
Mapping,
21+
BindingExpressionRange,
22+
OriginalPos,
23+
OriginalScopeInfo,
24+
GeneratedRangeInfo,
25+
} from './types';
1626

1727
export type { DecodedSourceMap, EncodedSourceMap, Mapping };
1828

@@ -31,6 +41,8 @@ export class GenMapping {
3141
private declare _sources: SetArray<string>;
3242
private declare _sourcesContent: (string | null)[];
3343
private declare _mappings: SourceMapSegment[][];
44+
private declare _originalScopes: OriginalScope[][];
45+
private declare _generatedRanges: GeneratedRange[];
3446
private declare _ignoreList: SetArray<number>;
3547
declare file: string | null | undefined;
3648
declare sourceRoot: string | null | undefined;
@@ -40,6 +52,8 @@ export class GenMapping {
4052
this._sources = new SetArray();
4153
this._sourcesContent = [];
4254
this._mappings = [];
55+
this._originalScopes = [];
56+
this._generatedRanges = [];
4357
this.file = file;
4458
this.sourceRoot = sourceRoot;
4559
this._ignoreList = new SetArray();
@@ -51,6 +65,8 @@ interface PublicMap {
5165
_sources: GenMapping['_sources'];
5266
_sourcesContent: GenMapping['_sourcesContent'];
5367
_mappings: GenMapping['_mappings'];
68+
_originalScopes: GenMapping['_originalScopes'];
69+
_generatedRanges: GenMapping['_generatedRanges'];
5470
_ignoreList: GenMapping['_ignoreList'];
5571
}
5672

@@ -207,15 +223,26 @@ export const maybeAddMapping: typeof addMapping = (map, mapping) => {
207223
* Adds/removes the content of the source file to the source map.
208224
*/
209225
export function setSourceContent(map: GenMapping, source: string, content: string | null): void {
210-
const { _sources: sources, _sourcesContent: sourcesContent } = cast(map);
226+
const {
227+
_sources: sources,
228+
_sourcesContent: sourcesContent,
229+
_originalScopes: originalScopes,
230+
} = cast(map);
211231
const index = put(sources, source);
212232
sourcesContent[index] = content;
233+
if (index === originalScopes.length) originalScopes[index] = [];
213234
}
214235

215236
export function setIgnore(map: GenMapping, source: string, ignore = true) {
216-
const { _sources: sources, _sourcesContent: sourcesContent, _ignoreList: ignoreList } = cast(map);
237+
const {
238+
_sources: sources,
239+
_sourcesContent: sourcesContent,
240+
_ignoreList: ignoreList,
241+
_originalScopes: originalScopes,
242+
} = cast(map);
217243
const index = put(sources, source);
218244
if (index === sourcesContent.length) sourcesContent[index] = null;
245+
if (index === originalScopes.length) originalScopes[index] = [];
219246
if (ignore) put(ignoreList, index);
220247
else remove(ignoreList, index);
221248
}
@@ -231,6 +258,8 @@ export function toDecodedMap(map: GenMapping): DecodedSourceMap {
231258
_sourcesContent: sourcesContent,
232259
_names: names,
233260
_ignoreList: ignoreList,
261+
_originalScopes: originalScopes,
262+
_generatedRanges: generatedRanges,
234263
} = cast(map);
235264
removeEmptyFinalLines(mappings);
236265

@@ -242,6 +271,8 @@ export function toDecodedMap(map: GenMapping): DecodedSourceMap {
242271
sources: sources.array,
243272
sourcesContent,
244273
mappings,
274+
originalScopes,
275+
generatedRanges,
245276
ignoreList: ignoreList.array,
246277
};
247278
}
@@ -254,6 +285,8 @@ export function toEncodedMap(map: GenMapping): EncodedSourceMap {
254285
const decoded = toDecodedMap(map);
255286
return {
256287
...decoded,
288+
originalScopes: decoded.originalScopes.map((os) => encodeOriginalScopes(os)),
289+
generatedRanges: encodeGeneratedRanges(decoded.generatedRanges as GeneratedRange[]),
257290
mappings: encode(decoded.mappings as SourceMapSegment[][]),
258291
};
259292
}
@@ -269,6 +302,7 @@ export function fromMap(input: SourceMapInput): GenMapping {
269302
putAll(cast(gen)._sources, map.sources as string[]);
270303
cast(gen)._sourcesContent = map.sourcesContent || map.sources.map(() => null);
271304
cast(gen)._mappings = decodedMappings(map) as GenMapping['_mappings'];
305+
// TODO: implement originalScopes/generatedRanges
272306
if (map.ignoreList) putAll(cast(gen)._ignoreList, map.ignoreList);
273307

274308
return gen;
@@ -323,8 +357,9 @@ function addSegmentInternal<S extends string | null | undefined>(
323357
_sources: sources,
324358
_sourcesContent: sourcesContent,
325359
_names: names,
360+
_originalScopes: originalScopes,
326361
} = cast(map);
327-
const line = getLine(mappings, genLine);
362+
const line = getIndex(mappings, genLine);
328363
const index = getColumnIndex(line, genColumn);
329364

330365
if (!source) {
@@ -340,6 +375,7 @@ function addSegmentInternal<S extends string | null | undefined>(
340375
const sourcesIndex = put(sources, source);
341376
const namesIndex = name ? put(names, name) : NO_NAME;
342377
if (sourcesIndex === sourcesContent.length) sourcesContent[sourcesIndex] = content ?? null;
378+
if (sourcesIndex === originalScopes.length) originalScopes[sourcesIndex] = [];
343379

344380
if (skipable && skipSource(line, index, sourcesIndex, sourceLine, sourceColumn, namesIndex)) {
345381
return;
@@ -358,11 +394,11 @@ function assert<T>(_val: unknown): asserts _val is T {
358394
// noop.
359395
}
360396

361-
function getLine(mappings: SourceMapSegment[][], index: number): SourceMapSegment[] {
362-
for (let i = mappings.length; i <= index; i++) {
363-
mappings[i] = [];
397+
function getIndex<T>(arr: T[][], index: number): T[] {
398+
for (let i = arr.length; i <= index; i++) {
399+
arr[i] = [];
364400
}
365-
return mappings[index];
401+
return arr[index];
366402
}
367403

368404
function getColumnIndex(line: SourceMapSegment[], genColumn: number): number {
@@ -470,3 +506,102 @@ function addMappingInternal<S extends string | null | undefined>(
470506
content,
471507
);
472508
}
509+
510+
export function addOriginalScope(
511+
map: GenMapping,
512+
data: {
513+
start: Pos;
514+
end: Pos;
515+
source: string;
516+
kind: string;
517+
name?: string;
518+
variables?: string[];
519+
},
520+
): OriginalScopeInfo {
521+
const { start, end, source, kind, name, variables } = data;
522+
const {
523+
_sources: sources,
524+
_sourcesContent: sourcesContent,
525+
_originalScopes: originalScopes,
526+
_names: names,
527+
} = cast(map);
528+
const index = put(sources, source);
529+
if (index === sourcesContent.length) sourcesContent[index] = null;
530+
if (index === originalScopes.length) originalScopes[index] = [];
531+
532+
const kindIndex = put(names, kind);
533+
const scope: OriginalScope = name
534+
? [start.line - 1, start.column, end.line - 1, end.column, kindIndex, put(names, name)]
535+
: [start.line - 1, start.column, end.line - 1, end.column, kindIndex];
536+
if (variables) {
537+
scope.vars = variables.map((v) => put(names, v));
538+
}
539+
const len = originalScopes[index].push(scope);
540+
return [index, len - 1, variables];
541+
}
542+
543+
// Generated Ranges
544+
export function addGeneratedRange(
545+
map: GenMapping,
546+
data: {
547+
start: Pos;
548+
isScope: boolean;
549+
originalScope?: OriginalScopeInfo;
550+
callsite?: OriginalPos;
551+
},
552+
): GeneratedRangeInfo {
553+
const { start, isScope, originalScope, callsite } = data;
554+
const {
555+
_originalScopes: originalScopes,
556+
_sources: sources,
557+
_sourcesContent: sourcesContent,
558+
_generatedRanges: generatedRanges,
559+
} = cast(map);
560+
561+
const range: GeneratedRange = [
562+
start.line - 1,
563+
start.column,
564+
0,
565+
0,
566+
originalScope ? originalScope[0] : -1,
567+
originalScope ? originalScope[1] : -1,
568+
];
569+
if (originalScope?.[2]) {
570+
range.bindings = originalScope[2].map(() => [[-1]]);
571+
}
572+
if (callsite) {
573+
const index = put(sources, callsite.source);
574+
if (index === sourcesContent.length) sourcesContent[index] = null;
575+
if (index === originalScopes.length) originalScopes[index] = [];
576+
range.callsite = [index, callsite.line - 1, callsite.column];
577+
}
578+
if (isScope) range.isScope = true;
579+
generatedRanges.push(range);
580+
581+
return [range, originalScope?.[2]];
582+
}
583+
584+
export function setEndPosition(range: GeneratedRangeInfo, pos: Pos) {
585+
range[0][2] = pos.line - 1;
586+
range[0][3] = pos.column;
587+
}
588+
589+
export function addBinding(
590+
map: GenMapping,
591+
range: GeneratedRangeInfo,
592+
variable: string,
593+
expression: string | BindingExpressionRange,
594+
) {
595+
const { _names: names } = cast(map);
596+
const bindings = (range[0].bindings ||= []);
597+
const vars = range[1];
598+
599+
const index = vars!.indexOf(variable);
600+
const binding = getIndex(bindings, index);
601+
602+
if (typeof expression === 'string') binding[0] = [put(names, expression)];
603+
else {
604+
const { start } = expression;
605+
binding.push([put(names, expression.expression), start.line - 1, start.column]);
606+
}
607+
}

src/types.ts

+19-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { GeneratedRange, OriginalScope } from '@jridgewell/sourcemap-codec';
12
import type { SourceMapSegment } from './sourcemap-segment';
23

34
export interface SourceMapV3 {
@@ -12,17 +13,33 @@ export interface SourceMapV3 {
1213

1314
export interface EncodedSourceMap extends SourceMapV3 {
1415
mappings: string;
16+
originalScopes: string[];
17+
generatedRanges: string;
1518
}
1619

1720
export interface DecodedSourceMap extends SourceMapV3 {
1821
mappings: readonly SourceMapSegment[][];
22+
originalScopes: readonly OriginalScope[][];
23+
generatedRanges: readonly GeneratedRange[];
1924
}
2025

2126
export interface Pos {
22-
line: number;
23-
column: number;
27+
line: number; // 1-based
28+
column: number; // 0-based
2429
}
2530

31+
export interface OriginalPos extends Pos {
32+
source: string;
33+
}
34+
35+
export interface BindingExpressionRange {
36+
start: Pos;
37+
expression: string;
38+
}
39+
40+
export type OriginalScopeInfo = [number, number, string[] | undefined];
41+
export type GeneratedRangeInfo = [GeneratedRange, string[] | undefined];
42+
2643
export type Mapping =
2744
| {
2845
generated: Pos;

0 commit comments

Comments
 (0)