Skip to content

Commit ccd8f87

Browse files
committed
Implement scopes generator
1 parent 75eff9d commit ccd8f87

File tree

4 files changed

+167
-16
lines changed

4 files changed

+167
-16
lines changed

package-lock.json

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

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -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

+139-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,17 @@ 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+
} from './types';
1625

1726
export type { DecodedSourceMap, EncodedSourceMap, Mapping };
1827

@@ -31,6 +40,8 @@ export class GenMapping {
3140
private declare _sources: SetArray<string>;
3241
private declare _sourcesContent: (string | null)[];
3342
private declare _mappings: SourceMapSegment[][];
43+
private declare _originalScopes: OriginalScope[][];
44+
private declare _generatedRanges: GeneratedRange[];
3445
private declare _ignoreList: SetArray<number>;
3546
declare file: string | null | undefined;
3647
declare sourceRoot: string | null | undefined;
@@ -40,6 +51,8 @@ export class GenMapping {
4051
this._sources = new SetArray();
4152
this._sourcesContent = [];
4253
this._mappings = [];
54+
this._originalScopes = [];
55+
this._generatedRanges = [];
4356
this.file = file;
4457
this.sourceRoot = sourceRoot;
4558
this._ignoreList = new SetArray();
@@ -51,6 +64,8 @@ interface PublicMap {
5164
_sources: GenMapping['_sources'];
5265
_sourcesContent: GenMapping['_sourcesContent'];
5366
_mappings: GenMapping['_mappings'];
67+
_originalScopes: GenMapping['_originalScopes'];
68+
_generatedRanges: GenMapping['_generatedRanges'];
5469
_ignoreList: GenMapping['_ignoreList'];
5570
}
5671

@@ -207,15 +222,26 @@ export const maybeAddMapping: typeof addMapping = (map, mapping) => {
207222
* Adds/removes the content of the source file to the source map.
208223
*/
209224
export function setSourceContent(map: GenMapping, source: string, content: string | null): void {
210-
const { _sources: sources, _sourcesContent: sourcesContent } = cast(map);
225+
const {
226+
_sources: sources,
227+
_sourcesContent: sourcesContent,
228+
_originalScopes: originalScopes,
229+
} = cast(map);
211230
const index = put(sources, source);
212231
sourcesContent[index] = content;
232+
if (index === originalScopes.length) originalScopes[index] = [];
213233
}
214234

215235
export function setIgnore(map: GenMapping, source: string, ignore = true) {
216-
const { _sources: sources, _sourcesContent: sourcesContent, _ignoreList: ignoreList } = cast(map);
236+
const {
237+
_sources: sources,
238+
_sourcesContent: sourcesContent,
239+
_ignoreList: ignoreList,
240+
_originalScopes: originalScopes,
241+
} = cast(map);
217242
const index = put(sources, source);
218243
if (index === sourcesContent.length) sourcesContent[index] = null;
244+
if (index === originalScopes.length) originalScopes[index] = [];
219245
if (ignore) put(ignoreList, index);
220246
else remove(ignoreList, index);
221247
}
@@ -231,6 +257,8 @@ export function toDecodedMap(map: GenMapping): DecodedSourceMap {
231257
_sourcesContent: sourcesContent,
232258
_names: names,
233259
_ignoreList: ignoreList,
260+
_originalScopes: originalScopes,
261+
_generatedRanges: generatedRanges,
234262
} = cast(map);
235263
removeEmptyFinalLines(mappings);
236264

@@ -242,6 +270,8 @@ export function toDecodedMap(map: GenMapping): DecodedSourceMap {
242270
sources: sources.array,
243271
sourcesContent,
244272
mappings,
273+
originalScopes,
274+
generatedRanges,
245275
ignoreList: ignoreList.array,
246276
};
247277
}
@@ -254,6 +284,8 @@ export function toEncodedMap(map: GenMapping): EncodedSourceMap {
254284
const decoded = toDecodedMap(map);
255285
return {
256286
...decoded,
287+
originalScopes: decoded.originalScopes.map((os) => encodeOriginalScopes(os)),
288+
generatedRanges: encodeGeneratedRanges(decoded.generatedRanges as GeneratedRange[]),
257289
mappings: encode(decoded.mappings as SourceMapSegment[][]),
258290
};
259291
}
@@ -269,6 +301,7 @@ export function fromMap(input: SourceMapInput): GenMapping {
269301
putAll(cast(gen)._sources, map.sources as string[]);
270302
cast(gen)._sourcesContent = map.sourcesContent || map.sources.map(() => null);
271303
cast(gen)._mappings = decodedMappings(map) as GenMapping['_mappings'];
304+
// TODO: implement originalScopes/generatedRanges
272305
if (map.ignoreList) putAll(cast(gen)._ignoreList, map.ignoreList);
273306

274307
return gen;
@@ -323,8 +356,9 @@ function addSegmentInternal<S extends string | null | undefined>(
323356
_sources: sources,
324357
_sourcesContent: sourcesContent,
325358
_names: names,
359+
_originalScopes: originalScopes,
326360
} = cast(map);
327-
const line = getLine(mappings, genLine);
361+
const line = getIndex(mappings, genLine);
328362
const index = getColumnIndex(line, genColumn);
329363

330364
if (!source) {
@@ -340,6 +374,7 @@ function addSegmentInternal<S extends string | null | undefined>(
340374
const sourcesIndex = put(sources, source);
341375
const namesIndex = name ? put(names, name) : NO_NAME;
342376
if (sourcesIndex === sourcesContent.length) sourcesContent[sourcesIndex] = content ?? null;
377+
if (sourcesIndex === originalScopes.length) originalScopes[sourcesIndex] = [];
343378

344379
if (skipable && skipSource(line, index, sourcesIndex, sourceLine, sourceColumn, namesIndex)) {
345380
return;
@@ -358,11 +393,11 @@ function assert<T>(_val: unknown): asserts _val is T {
358393
// noop.
359394
}
360395

361-
function getLine(mappings: SourceMapSegment[][], index: number): SourceMapSegment[] {
362-
for (let i = mappings.length; i <= index; i++) {
363-
mappings[i] = [];
396+
function getIndex<T>(arr: T[][], index: number): T[] {
397+
for (let i = arr.length; i <= index; i++) {
398+
arr[i] = [];
364399
}
365-
return mappings[index];
400+
return arr[index];
366401
}
367402

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

src/types.ts

+18-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,32 @@ 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+
2642
export type Mapping =
2743
| {
2844
generated: Pos;

0 commit comments

Comments
 (0)