Skip to content

Commit

Permalink
Merge pull request #171 from ampproject/gen-mapping
Browse files Browse the repository at this point in the history
Extract GenMapping
  • Loading branch information
jridgewell committed Apr 27, 2022
2 parents 5285f27 + b9f392f commit 3519872
Show file tree
Hide file tree
Showing 9 changed files with 1,151 additions and 1,258 deletions.
2,075 changes: 999 additions & 1,076 deletions package-lock.json

Large diffs are not rendered by default.

30 changes: 15 additions & 15 deletions package.json
Expand Up @@ -41,23 +41,23 @@
"test:watch": "jest --coverage --watch"
},
"devDependencies": {
"@rollup/plugin-typescript": "8.3.0",
"@types/jest": "27.4.0",
"@typescript-eslint/eslint-plugin": "5.10.2",
"@typescript-eslint/parser": "5.10.2",
"eslint": "8.8.0",
"eslint-config-prettier": "8.3.0",
"jest": "27.4.7",
"jest-config": "27.4.7",
"@rollup/plugin-typescript": "8.3.2",
"@types/jest": "27.4.1",
"@typescript-eslint/eslint-plugin": "5.20.0",
"@typescript-eslint/parser": "5.20.0",
"eslint": "8.14.0",
"eslint-config-prettier": "8.5.0",
"jest": "27.5.1",
"jest-config": "27.5.1",
"npm-run-all": "4.1.5",
"prettier": "2.5.1",
"rollup": "2.67.0",
"ts-jest": "27.1.3",
"tslib": "2.3.1",
"typescript": "4.5.5"
"prettier": "2.6.2",
"rollup": "2.70.2",
"ts-jest": "27.1.4",
"tslib": "2.4.0",
"typescript": "4.6.3"
},
"dependencies": {
"@jridgewell/set-array": "^1.0.0",
"@jridgewell/trace-mapping": "^0.3.0"
"@jridgewell/gen-mapping": "^0.1.0",
"@jridgewell/trace-mapping": "^0.3.9"
}
}
1 change: 1 addition & 0 deletions rollup.config.js
Expand Up @@ -12,6 +12,7 @@ function configure(esm) {
entryFileNames: '[name].umd.js',
sourcemap: true,
globals: {
'@jridgewell/gen-mapping': 'genMapping',
'@jridgewell/trace-mapping': 'traceMapping',
},
},
Expand Down
6 changes: 3 additions & 3 deletions src/build-source-map-tree.ts
Expand Up @@ -2,7 +2,7 @@ import { TraceMap } from '@jridgewell/trace-mapping';

import { OriginalSource, MapSource } from './source-map-tree';

import type { Sources } from './source-map-tree';
import type { Sources, MapSource as MapSourceType } from './source-map-tree';
import type { SourceMapInput, SourceMapLoader, LoaderContext } from './types';

function asArray<T>(value: T | T[]): T[] {
Expand All @@ -24,7 +24,7 @@ function asArray<T>(value: T | T[]): T[] {
export default function buildSourceMapTree(
input: SourceMapInput | SourceMapInput[],
loader: SourceMapLoader
): Sources {
): MapSourceType {
const maps = asArray(input).map((m) => new TraceMap(m, ''));
const map = maps.pop()!;

Expand All @@ -49,7 +49,7 @@ function build(
loader: SourceMapLoader,
importer: string,
importerDepth: number
): Sources {
): MapSourceType {
const { resolvedSources, sourcesContent } = map;

const depth = importerDepth + 1;
Expand Down
117 changes: 43 additions & 74 deletions src/source-map-tree.ts
@@ -1,23 +1,41 @@
import { SetArray, put } from '@jridgewell/set-array';
import { presortedDecodedMap, traceSegment, decodedMappings } from '@jridgewell/trace-mapping';
import { GenMapping, addSegment, setSourceContent } from '@jridgewell/gen-mapping';
import { traceSegment, decodedMappings } from '@jridgewell/trace-mapping';

import type { TraceMap } from '@jridgewell/trace-mapping';
import type { SourceMapSegment, SourceMapSegmentObject } from './types';

const INVALID_MAPPING = undefined;
const SOURCELESS_MAPPING = null;
export type SourceMapSegmentObject =
| {
column: number;
line: number;
name: string;
source: string;
content: string | null;
}
| {
column: null;
line: null;
name: null;
source: null;
content: null;
};

const SOURCELESS_MAPPING = {
source: null,
column: null,
line: null,
name: null,
content: null,
};
const EMPTY_SOURCES: Sources[] = [];

type MappingSource = SourceMapSegmentObject | typeof INVALID_MAPPING | typeof SOURCELESS_MAPPING;

type OriginalSource = {
export type OriginalSource = {
map: TraceMap;
sources: Sources[];
source: string;
content: string | null;
};

type MapSource = {
export type MapSource = {
map: TraceMap;
sources: Sources[];
source: string;
Expand Down Expand Up @@ -60,28 +78,24 @@ export function OriginalSource(source: string, content: string | null): Original
* traceMappings is only called on the root level SourceMapTree, and begins the process of
* resolving each mapping in terms of the original source files.
*/
export function traceMappings(tree: Sources): TraceMap {
const mappings: SourceMapSegment[][] = [];
const names = new SetArray();
const sources = new SetArray();
const sourcesContent: (string | null)[] = [];
export function traceMappings(tree: MapSource): GenMapping {
const gen = new GenMapping({ file: tree.map.file });
const { sources: rootSources, map } = tree;
const rootNames = map.names;
const rootMappings = decodedMappings(map);

let lastLineWithSegment = -1;
for (let i = 0; i < rootMappings.length; i++) {
const segments = rootMappings[i];
const tracedSegments: SourceMapSegment[] = [];

let lastSourcesIndex = -1;
let lastSourceLine = -1;
let lastSourceColumn = -1;
let lastSource = null;
let lastSourceLine = null;
let lastSourceColumn = null;

for (let j = 0; j < segments.length; j++) {
const segment = segments[j];
const genCol = segment[0];
let traced: SourceMapSegmentObject | null = SOURCELESS_MAPPING;

let traced: MappingSource = SOURCELESS_MAPPING;
// 1-length segments only move the current generated column, there's no source information
// to gather from it.
if (segment.length !== 1) {
Expand All @@ -95,71 +109,26 @@ export function traceMappings(tree: Sources): TraceMap {

// If the trace is invalid, then the trace ran into a sourcemap that doesn't contain a
// respective segment into an original source.
if (traced === INVALID_MAPPING) continue;
}

const genCol = segment[0];
if (traced === SOURCELESS_MAPPING) {
if (lastSourcesIndex === -1) {
// This is a consecutive source-less segment, which doesn't carry any new information.
continue;
}
lastSourcesIndex = lastSourceLine = lastSourceColumn = -1;
tracedSegments.push([genCol]);
continue;
if (traced == null) continue;
}

// So we traced a segment down into its original source file. Now push a
// new segment pointing to this location.
const { column, line, name, content, source } = traced;

// Store the source location, and ensure we keep sourcesContent up to
// date with the sources array.
const sourcesIndex = put(sources, source);
sourcesContent[sourcesIndex] = content;

if (
lastSourcesIndex === sourcesIndex &&
lastSourceLine === line &&
lastSourceColumn === column
) {
// This is a duplicate mapping pointing at the exact same starting point in the source
// file. It doesn't carry any new information, and only bloats the sourcemap.
if (line === lastSourceLine && column === lastSourceColumn && source === lastSource) {
continue;
}
lastLineWithSegment = i;
lastSourcesIndex = sourcesIndex;
lastSourceLine = line;
lastSourceColumn = column;
lastSource = source;

// This looks like unnecessary duplication, but it noticeably increases performance. If we
// were to push the nameIndex onto length-4 array, v8 would internally allocate 22 slots!
// That's 68 wasted bytes! Array literals have the same capacity as their length, saving
// memory.
tracedSegments.push(
name
? [genCol, sourcesIndex, line, column, put(names, name)]
: [genCol, sourcesIndex, line, column]
);
// Sigh, TypeScript can't figure out source/line/column are either all null, or all non-null...
(addSegment as any)(gen, i, genCol, source, line, column, name);
if (content != null) setSourceContent(gen, source, content);
}

mappings.push(tracedSegments);
}

if (mappings.length > lastLineWithSegment + 1) {
mappings.length = lastLineWithSegment + 1;
}

return presortedDecodedMap(
Object.assign({}, tree.map, {
mappings,
// TODO: Make all sources relative to the sourceRoot.
sourceRoot: undefined,
names: names.array,
sources: sources.array,
sourcesContent,
})
);
return gen;
}

/**
Expand All @@ -171,15 +140,15 @@ export function originalPositionFor(
line: number,
column: number,
name: string
): MappingSource {
): SourceMapSegmentObject | null {
if (!source.map) {
return { column, line, name, source: source.source, content: source.content };
}

const segment = traceSegment(source.map, line, column);

// If we couldn't find a segment, then this doesn't exist in the sourcemap.
if (segment == null) return INVALID_MAPPING;
if (segment == null) return null;
// 1-length segments only move the current generated column, there's no source information
// to gather from it.
if (segment.length === 1) return SOURCELESS_MAPPING;
Expand Down
25 changes: 12 additions & 13 deletions src/source-map.ts
@@ -1,6 +1,6 @@
import { encodedMappings, decodedMappings } from '@jridgewell/trace-mapping';
import { decodedMap, encodedMap } from '@jridgewell/gen-mapping';

import type { TraceMap } from '@jridgewell/trace-mapping';
import type { GenMapping } from '@jridgewell/gen-mapping';
import type { DecodedSourceMap, EncodedSourceMap, Options } from './types';

/**
Expand All @@ -16,19 +16,18 @@ export default class SourceMap {
declare sourcesContent?: (string | null)[];
declare version: 3;

constructor(map: TraceMap, options: Options) {
this.version = 3; // SourceMap spec says this should be first.
this.file = map.file;
this.mappings = options.decodedMappings
? (decodedMappings(map) as DecodedSourceMap['mappings'])
: encodedMappings(map);
this.names = map.names;
constructor(map: GenMapping, options: Options) {
const out = options.decodedMappings ? decodedMap(map) : encodedMap(map);
this.version = out.version; // SourceMap spec says this should be first.
this.file = out.file;
this.mappings = out.mappings as SourceMap['mappings'];
this.names = out.names as SourceMap['names'];

this.sourceRoot = map.sourceRoot;
this.sourceRoot = out.sourceRoot;

this.sources = map.sources;
if (!options.excludeContent && 'sourcesContent' in map) {
this.sourcesContent = map.sourcesContent;
this.sources = out.sources as SourceMap['sources'];
if (!options.excludeContent) {
this.sourcesContent = out.sourcesContent as SourceMap['sourcesContent'];
}
}

Expand Down
8 changes: 0 additions & 8 deletions src/types.ts
Expand Up @@ -8,14 +8,6 @@ export type {

export type { SourceMapInput };

export interface SourceMapSegmentObject {
column: number;
line: number;
name: string;
source: string;
content: string | null;
}

export type LoaderContext = {
readonly importer: string;
readonly depth: number;
Expand Down

0 comments on commit 3519872

Please sign in to comment.