@ttsc/metro 0.30.4 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -6
- package/lib/core/SnapshotReadOperations.d.ts +119 -0
- package/lib/core/SnapshotReadOperations.js +3 -0
- package/lib/core/SnapshotReadOperations.js.map +1 -0
- package/lib/core/SnapshotReadOperations.mjs +2 -0
- package/lib/core/SnapshotReadOperations.mjs.map +1 -0
- package/lib/core/TtscMetroOptions.d.ts +119 -0
- package/lib/core/TtscMetroOptions.js +3 -0
- package/lib/core/TtscMetroOptions.js.map +1 -0
- package/lib/core/TtscMetroOptions.mjs +2 -0
- package/lib/core/TtscMetroOptions.mjs.map +1 -0
- package/lib/core/TtscMetroProjectView.d.ts +64 -0
- package/lib/core/TtscMetroProjectView.js +3 -0
- package/lib/core/TtscMetroProjectView.js.map +1 -0
- package/lib/core/TtscMetroProjectView.mjs +2 -0
- package/lib/core/TtscMetroProjectView.mjs.map +1 -0
- package/lib/core/UpstreamTransformer.d.ts +127 -0
- package/lib/core/UpstreamTransformer.js +3 -0
- package/lib/core/UpstreamTransformer.js.map +1 -0
- package/lib/core/UpstreamTransformer.mjs +2 -0
- package/lib/core/UpstreamTransformer.mjs.map +1 -0
- package/lib/core/fingerprint.d.ts +442 -97
- package/lib/core/fingerprint.js +838 -83
- package/lib/core/fingerprint.js.map +1 -1
- package/lib/core/fingerprint.mjs +839 -84
- package/lib/core/fingerprint.mjs.map +1 -1
- package/lib/core/options.d.ts +81 -58
- package/lib/core/options.js +81 -4
- package/lib/core/options.js.map +1 -1
- package/lib/core/options.mjs +81 -4
- package/lib/core/options.mjs.map +1 -1
- package/lib/core/remapAstLocations.d.ts +76 -0
- package/lib/core/remapAstLocations.js +205 -0
- package/lib/core/remapAstLocations.js.map +1 -0
- package/lib/core/remapAstLocations.mjs +203 -0
- package/lib/core/remapAstLocations.mjs.map +1 -0
- package/lib/core/upstream.d.ts +97 -27
- package/lib/core/upstream.js +109 -8
- package/lib/core/upstream.js.map +1 -1
- package/lib/core/upstream.mjs +109 -9
- package/lib/core/upstream.mjs.map +1 -1
- package/lib/index.d.ts +50 -2
- package/lib/index.js +88 -24
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +89 -25
- package/lib/index.mjs.map +1 -1
- package/lib/transformer.d.ts +183 -6
- package/lib/transformer.js +209 -14
- package/lib/transformer.js.map +1 -1
- package/lib/transformer.mjs +211 -16
- package/lib/transformer.mjs.map +1 -1
- package/package.json +7 -18
- package/src/core/SnapshotReadOperations.ts +121 -0
- package/src/core/TtscMetroOptions.ts +127 -0
- package/src/core/TtscMetroProjectView.ts +74 -0
- package/src/core/UpstreamTransformer.ts +126 -0
- package/src/core/fingerprint.ts +987 -109
- package/src/core/options.ts +85 -62
- package/src/core/remapAstLocations.ts +219 -0
- package/src/core/upstream.ts +110 -26
- package/src/index.ts +99 -28
- package/src/transformer.ts +214 -17
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Move every source location in an upstream transformer's AST from the ttsc
|
|
3
|
+
* transformed text back to the text the module's author wrote
|
|
4
|
+
* (samchon/ttsc#1392).
|
|
5
|
+
*
|
|
6
|
+
* Metro's Babel transformer contract returns an AST, never a map, and Metro
|
|
7
|
+
* builds the module's source map from that AST's `loc` positions against the
|
|
8
|
+
* file it read. The upstream transformer parsed the ttsc transformed text, so
|
|
9
|
+
* without this pass every position after the first change points at the
|
|
10
|
+
* reprinted lines: breakpoints, stack traces, and the error overlay would all
|
|
11
|
+
* be off. Each node's `loc` start and end are looked up in `map` with the usual
|
|
12
|
+
* greatest-lower-bound rule. A node whose start has no mapping into `file`,
|
|
13
|
+
* such as generated code, loses its `loc`, which Babel treats as a synthesized
|
|
14
|
+
* node, so it takes the mapping of the code around it.
|
|
15
|
+
*
|
|
16
|
+
* Babel locations have one-based lines and zero-based UTF-16 columns; source
|
|
17
|
+
* map segments have zero-based lines and columns. The map points from generated
|
|
18
|
+
* text to the authored file. Missing or reversed mapped ends clamp to the
|
|
19
|
+
* mapped start. Traversal tracks visited objects and mutates only AST
|
|
20
|
+
* locations. Absolute sources are compared by filesystem identity within this
|
|
21
|
+
* call, so links and actual directory case capabilities determine the owning
|
|
22
|
+
* source.
|
|
23
|
+
*
|
|
24
|
+
* @param ast The upstream transformer's AST, rewritten in place.
|
|
25
|
+
* @param map The adapter's map from the transformed text to `file`, with
|
|
26
|
+
* absolute `sources`.
|
|
27
|
+
* @param file Absolute path of the module.
|
|
28
|
+
* @evidence contracts/common.md#principled-implementation
|
|
29
|
+
* The adapter consumes its own source-map VLQ segments using
|
|
30
|
+
* greatest-lower-bound lookup, then updates the upstream-owned AST through
|
|
31
|
+
* Metro's returned-AST extension boundary. Iterative traversal tracks
|
|
32
|
+
* visited objects to handle shared/cyclic AST references. Unmapped starts
|
|
33
|
+
* lose loc and invalid ends clamp to the mapped start rather than
|
|
34
|
+
* manufacturing authored positions.
|
|
35
|
+
*
|
|
36
|
+
* @evidence contracts/common.md#clear-and-simple-design
|
|
37
|
+
* Mapping decoding, position lookup and object traversal remain distinct
|
|
38
|
+
* steps; the traversal owns AST mutation and the local lookup owns coordinate
|
|
39
|
+
* conversion without adding a second Babel parser.
|
|
40
|
+
*
|
|
41
|
+
* @evidence contracts/performance.md#efficient-algorithms
|
|
42
|
+
* Mapping decoding is linear in encoded bytes. A visited Set walks each
|
|
43
|
+
* AST object once; each endpoint uses binary search on its generated line's
|
|
44
|
+
* segments. Temporary storage follows decoded segments and reachable AST
|
|
45
|
+
* objects, and the iterative stack avoids call-depth dependence on the AST.
|
|
46
|
+
* Source identity resolution is shared within this call; missing sources can
|
|
47
|
+
* additionally require ancestor and directory-capability observations.
|
|
48
|
+
*
|
|
49
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work
|
|
50
|
+
* This call mutates one returned AST using that delivery's map; it owns no
|
|
51
|
+
* shared cache and repeated calls would remap already moved coordinates.
|
|
52
|
+
*
|
|
53
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources
|
|
54
|
+
* The caller owns the AST. Local decoded lines and traversal state end with
|
|
55
|
+
* the call; no handle or cross-delivery state is retained.
|
|
56
|
+
*
|
|
57
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts
|
|
58
|
+
* No Babel methods or compiler internals are patched.
|
|
59
|
+
*
|
|
60
|
+
* @evidence contracts/portability.md#os-neutral-implementation
|
|
61
|
+
* The shared filesystem-identity context compares absolute map sources with
|
|
62
|
+
* the delivered file using physical spelling and actual directory case
|
|
63
|
+
* capabilities. Source coordinates remain line/column values independent
|
|
64
|
+
* of native newline spelling; OS names do not fold distinct filenames.
|
|
65
|
+
*
|
|
66
|
+
* @evidence contracts/common.md#meaningful-documentation
|
|
67
|
+
* The native JSDoc explains map direction, in-place mutation, coordinate
|
|
68
|
+
* indexing and generated/unmapped locations. Checked against the
|
|
69
|
+
* documentation skill: separate paragraphs state the contract and why its
|
|
70
|
+
* nonobvious boundary matters; field comments retain their own useful
|
|
71
|
+
* facts.
|
|
72
|
+
*/
|
|
73
|
+
export declare function remapAstLocations(ast: unknown, map: {
|
|
74
|
+
mappings: string;
|
|
75
|
+
sources: readonly string[];
|
|
76
|
+
}, file: string): void;
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var pathIdentity = require('ttsc/path-identity');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Move every source location in an upstream transformer's AST from the ttsc
|
|
7
|
+
* transformed text back to the text the module's author wrote
|
|
8
|
+
* (samchon/ttsc#1392).
|
|
9
|
+
*
|
|
10
|
+
* Metro's Babel transformer contract returns an AST, never a map, and Metro
|
|
11
|
+
* builds the module's source map from that AST's `loc` positions against the
|
|
12
|
+
* file it read. The upstream transformer parsed the ttsc transformed text, so
|
|
13
|
+
* without this pass every position after the first change points at the
|
|
14
|
+
* reprinted lines: breakpoints, stack traces, and the error overlay would all
|
|
15
|
+
* be off. Each node's `loc` start and end are looked up in `map` with the usual
|
|
16
|
+
* greatest-lower-bound rule. A node whose start has no mapping into `file`,
|
|
17
|
+
* such as generated code, loses its `loc`, which Babel treats as a synthesized
|
|
18
|
+
* node, so it takes the mapping of the code around it.
|
|
19
|
+
*
|
|
20
|
+
* Babel locations have one-based lines and zero-based UTF-16 columns; source
|
|
21
|
+
* map segments have zero-based lines and columns. The map points from generated
|
|
22
|
+
* text to the authored file. Missing or reversed mapped ends clamp to the
|
|
23
|
+
* mapped start. Traversal tracks visited objects and mutates only AST
|
|
24
|
+
* locations. Absolute sources are compared by filesystem identity within this
|
|
25
|
+
* call, so links and actual directory case capabilities determine the owning
|
|
26
|
+
* source.
|
|
27
|
+
*
|
|
28
|
+
* @param ast The upstream transformer's AST, rewritten in place.
|
|
29
|
+
* @param map The adapter's map from the transformed text to `file`, with
|
|
30
|
+
* absolute `sources`.
|
|
31
|
+
* @param file Absolute path of the module.
|
|
32
|
+
* @evidence contracts/common.md#principled-implementation
|
|
33
|
+
* The adapter consumes its own source-map VLQ segments using
|
|
34
|
+
* greatest-lower-bound lookup, then updates the upstream-owned AST through
|
|
35
|
+
* Metro's returned-AST extension boundary. Iterative traversal tracks
|
|
36
|
+
* visited objects to handle shared/cyclic AST references. Unmapped starts
|
|
37
|
+
* lose loc and invalid ends clamp to the mapped start rather than
|
|
38
|
+
* manufacturing authored positions.
|
|
39
|
+
*
|
|
40
|
+
* @evidence contracts/common.md#clear-and-simple-design
|
|
41
|
+
* Mapping decoding, position lookup and object traversal remain distinct
|
|
42
|
+
* steps; the traversal owns AST mutation and the local lookup owns coordinate
|
|
43
|
+
* conversion without adding a second Babel parser.
|
|
44
|
+
*
|
|
45
|
+
* @evidence contracts/performance.md#efficient-algorithms
|
|
46
|
+
* Mapping decoding is linear in encoded bytes. A visited Set walks each
|
|
47
|
+
* AST object once; each endpoint uses binary search on its generated line's
|
|
48
|
+
* segments. Temporary storage follows decoded segments and reachable AST
|
|
49
|
+
* objects, and the iterative stack avoids call-depth dependence on the AST.
|
|
50
|
+
* Source identity resolution is shared within this call; missing sources can
|
|
51
|
+
* additionally require ancestor and directory-capability observations.
|
|
52
|
+
*
|
|
53
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work
|
|
54
|
+
* This call mutates one returned AST using that delivery's map; it owns no
|
|
55
|
+
* shared cache and repeated calls would remap already moved coordinates.
|
|
56
|
+
*
|
|
57
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources
|
|
58
|
+
* The caller owns the AST. Local decoded lines and traversal state end with
|
|
59
|
+
* the call; no handle or cross-delivery state is retained.
|
|
60
|
+
*
|
|
61
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts
|
|
62
|
+
* No Babel methods or compiler internals are patched.
|
|
63
|
+
*
|
|
64
|
+
* @evidence contracts/portability.md#os-neutral-implementation
|
|
65
|
+
* The shared filesystem-identity context compares absolute map sources with
|
|
66
|
+
* the delivered file using physical spelling and actual directory case
|
|
67
|
+
* capabilities. Source coordinates remain line/column values independent
|
|
68
|
+
* of native newline spelling; OS names do not fold distinct filenames.
|
|
69
|
+
*
|
|
70
|
+
* @evidence contracts/common.md#meaningful-documentation
|
|
71
|
+
* The native JSDoc explains map direction, in-place mutation, coordinate
|
|
72
|
+
* indexing and generated/unmapped locations. Checked against the
|
|
73
|
+
* documentation skill: separate paragraphs state the contract and why its
|
|
74
|
+
* nonobvious boundary matters; field comments retain their own useful
|
|
75
|
+
* facts.
|
|
76
|
+
*/
|
|
77
|
+
function remapAstLocations(ast, map, file) {
|
|
78
|
+
const identities = pathIdentity.createFilesystemPathIdentityContext({
|
|
79
|
+
throwOnRealpathError: false,
|
|
80
|
+
});
|
|
81
|
+
const fileKey = identities.resolve(file).key;
|
|
82
|
+
const own = map.sources.findIndex((source) => identities.resolve(source).key === fileKey);
|
|
83
|
+
if (own < 0) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const lines = decodeSegments(map.mappings);
|
|
87
|
+
const locate = (position) => {
|
|
88
|
+
if (typeof position !== "object" || position === null)
|
|
89
|
+
return undefined;
|
|
90
|
+
const { column, line } = position;
|
|
91
|
+
if (typeof line !== "number" || typeof column !== "number") {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
const segments = lines[line - 1];
|
|
95
|
+
if (segments === undefined)
|
|
96
|
+
return undefined;
|
|
97
|
+
let low = 0;
|
|
98
|
+
let high = segments.length - 1;
|
|
99
|
+
let found;
|
|
100
|
+
while (low <= high) {
|
|
101
|
+
const middle = (low + high) >> 1;
|
|
102
|
+
const segment = segments[middle];
|
|
103
|
+
if (segment.generatedColumn <= column) {
|
|
104
|
+
found = segment;
|
|
105
|
+
low = middle + 1;
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
high = middle - 1;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return found === undefined || found.source !== own
|
|
112
|
+
? undefined
|
|
113
|
+
: { column: found.column, line: found.line + 1 };
|
|
114
|
+
};
|
|
115
|
+
const visited = new Set();
|
|
116
|
+
const pending = [ast];
|
|
117
|
+
while (pending.length !== 0) {
|
|
118
|
+
const value = pending.pop();
|
|
119
|
+
if (typeof value !== "object" || value === null || visited.has(value)) {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
visited.add(value);
|
|
123
|
+
if (Array.isArray(value)) {
|
|
124
|
+
for (const entryToAppend of value)
|
|
125
|
+
pending.push(entryToAppend);
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const node = value;
|
|
129
|
+
for (const [key, child] of Object.entries(node)) {
|
|
130
|
+
if (key !== "loc")
|
|
131
|
+
pending.push(child);
|
|
132
|
+
}
|
|
133
|
+
const loc = node.loc;
|
|
134
|
+
if (typeof loc !== "object" || loc === null)
|
|
135
|
+
continue;
|
|
136
|
+
const { end, start } = loc;
|
|
137
|
+
const mappedStart = locate(start);
|
|
138
|
+
if (mappedStart === undefined) {
|
|
139
|
+
node.loc = null;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
const mappedEnd = locate(end);
|
|
143
|
+
node.loc = {
|
|
144
|
+
...loc,
|
|
145
|
+
end: mappedEnd === undefined ||
|
|
146
|
+
mappedEnd.line < mappedStart.line ||
|
|
147
|
+
(mappedEnd.line === mappedStart.line &&
|
|
148
|
+
mappedEnd.column < mappedStart.column)
|
|
149
|
+
? mappedStart
|
|
150
|
+
: mappedEnd,
|
|
151
|
+
start: mappedStart,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Decode `mappings` into its segments per generated line, in column order.
|
|
157
|
+
* Segments of every source are kept, and a segment with no source position is
|
|
158
|
+
* kept with source `-1`, so a lookup that lands on either reports no position
|
|
159
|
+
* instead of reaching past it to an earlier mapping.
|
|
160
|
+
*/
|
|
161
|
+
function decodeSegments(mappings) {
|
|
162
|
+
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
163
|
+
const lines = [];
|
|
164
|
+
let source = 0;
|
|
165
|
+
let line = 0;
|
|
166
|
+
let column = 0;
|
|
167
|
+
for (const text of mappings.split(";")) {
|
|
168
|
+
const segments = [];
|
|
169
|
+
let generatedColumn = 0;
|
|
170
|
+
for (const encoded of text.split(",")) {
|
|
171
|
+
if (encoded === "")
|
|
172
|
+
continue;
|
|
173
|
+
const fields = [];
|
|
174
|
+
let value = 0;
|
|
175
|
+
let shift = 0;
|
|
176
|
+
for (const char of encoded) {
|
|
177
|
+
const digit = alphabet.indexOf(char);
|
|
178
|
+
if (digit < 0)
|
|
179
|
+
return lines;
|
|
180
|
+
value += (digit & 31) << shift;
|
|
181
|
+
if ((digit & 32) !== 0) {
|
|
182
|
+
shift += 5;
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
fields.push((value & 1) !== 0 ? -(value >>> 1) : value >>> 1);
|
|
186
|
+
value = 0;
|
|
187
|
+
shift = 0;
|
|
188
|
+
}
|
|
189
|
+
generatedColumn += fields[0] ?? 0;
|
|
190
|
+
if (fields.length < 4) {
|
|
191
|
+
segments.push({ column: 0, generatedColumn, line: 0, source: -1 });
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
source += fields[1];
|
|
195
|
+
line += fields[2];
|
|
196
|
+
column += fields[3];
|
|
197
|
+
segments.push({ column, generatedColumn, line, source });
|
|
198
|
+
}
|
|
199
|
+
lines.push(segments);
|
|
200
|
+
}
|
|
201
|
+
return lines;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
exports.remapAstLocations = remapAstLocations;
|
|
205
|
+
//# sourceMappingURL=remapAstLocations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remapAstLocations.js","sources":["../../src/core/remapAstLocations.ts"],"sourcesContent":[null],"names":["createFilesystemPathIdentityContext"],"mappings":"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"}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { createFilesystemPathIdentityContext } from 'ttsc/path-identity';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Move every source location in an upstream transformer's AST from the ttsc
|
|
5
|
+
* transformed text back to the text the module's author wrote
|
|
6
|
+
* (samchon/ttsc#1392).
|
|
7
|
+
*
|
|
8
|
+
* Metro's Babel transformer contract returns an AST, never a map, and Metro
|
|
9
|
+
* builds the module's source map from that AST's `loc` positions against the
|
|
10
|
+
* file it read. The upstream transformer parsed the ttsc transformed text, so
|
|
11
|
+
* without this pass every position after the first change points at the
|
|
12
|
+
* reprinted lines: breakpoints, stack traces, and the error overlay would all
|
|
13
|
+
* be off. Each node's `loc` start and end are looked up in `map` with the usual
|
|
14
|
+
* greatest-lower-bound rule. A node whose start has no mapping into `file`,
|
|
15
|
+
* such as generated code, loses its `loc`, which Babel treats as a synthesized
|
|
16
|
+
* node, so it takes the mapping of the code around it.
|
|
17
|
+
*
|
|
18
|
+
* Babel locations have one-based lines and zero-based UTF-16 columns; source
|
|
19
|
+
* map segments have zero-based lines and columns. The map points from generated
|
|
20
|
+
* text to the authored file. Missing or reversed mapped ends clamp to the
|
|
21
|
+
* mapped start. Traversal tracks visited objects and mutates only AST
|
|
22
|
+
* locations. Absolute sources are compared by filesystem identity within this
|
|
23
|
+
* call, so links and actual directory case capabilities determine the owning
|
|
24
|
+
* source.
|
|
25
|
+
*
|
|
26
|
+
* @param ast The upstream transformer's AST, rewritten in place.
|
|
27
|
+
* @param map The adapter's map from the transformed text to `file`, with
|
|
28
|
+
* absolute `sources`.
|
|
29
|
+
* @param file Absolute path of the module.
|
|
30
|
+
* @evidence contracts/common.md#principled-implementation
|
|
31
|
+
* The adapter consumes its own source-map VLQ segments using
|
|
32
|
+
* greatest-lower-bound lookup, then updates the upstream-owned AST through
|
|
33
|
+
* Metro's returned-AST extension boundary. Iterative traversal tracks
|
|
34
|
+
* visited objects to handle shared/cyclic AST references. Unmapped starts
|
|
35
|
+
* lose loc and invalid ends clamp to the mapped start rather than
|
|
36
|
+
* manufacturing authored positions.
|
|
37
|
+
*
|
|
38
|
+
* @evidence contracts/common.md#clear-and-simple-design
|
|
39
|
+
* Mapping decoding, position lookup and object traversal remain distinct
|
|
40
|
+
* steps; the traversal owns AST mutation and the local lookup owns coordinate
|
|
41
|
+
* conversion without adding a second Babel parser.
|
|
42
|
+
*
|
|
43
|
+
* @evidence contracts/performance.md#efficient-algorithms
|
|
44
|
+
* Mapping decoding is linear in encoded bytes. A visited Set walks each
|
|
45
|
+
* AST object once; each endpoint uses binary search on its generated line's
|
|
46
|
+
* segments. Temporary storage follows decoded segments and reachable AST
|
|
47
|
+
* objects, and the iterative stack avoids call-depth dependence on the AST.
|
|
48
|
+
* Source identity resolution is shared within this call; missing sources can
|
|
49
|
+
* additionally require ancestor and directory-capability observations.
|
|
50
|
+
*
|
|
51
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work
|
|
52
|
+
* This call mutates one returned AST using that delivery's map; it owns no
|
|
53
|
+
* shared cache and repeated calls would remap already moved coordinates.
|
|
54
|
+
*
|
|
55
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources
|
|
56
|
+
* The caller owns the AST. Local decoded lines and traversal state end with
|
|
57
|
+
* the call; no handle or cross-delivery state is retained.
|
|
58
|
+
*
|
|
59
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts
|
|
60
|
+
* No Babel methods or compiler internals are patched.
|
|
61
|
+
*
|
|
62
|
+
* @evidence contracts/portability.md#os-neutral-implementation
|
|
63
|
+
* The shared filesystem-identity context compares absolute map sources with
|
|
64
|
+
* the delivered file using physical spelling and actual directory case
|
|
65
|
+
* capabilities. Source coordinates remain line/column values independent
|
|
66
|
+
* of native newline spelling; OS names do not fold distinct filenames.
|
|
67
|
+
*
|
|
68
|
+
* @evidence contracts/common.md#meaningful-documentation
|
|
69
|
+
* The native JSDoc explains map direction, in-place mutation, coordinate
|
|
70
|
+
* indexing and generated/unmapped locations. Checked against the
|
|
71
|
+
* documentation skill: separate paragraphs state the contract and why its
|
|
72
|
+
* nonobvious boundary matters; field comments retain their own useful
|
|
73
|
+
* facts.
|
|
74
|
+
*/
|
|
75
|
+
function remapAstLocations(ast, map, file) {
|
|
76
|
+
const identities = createFilesystemPathIdentityContext({
|
|
77
|
+
throwOnRealpathError: false,
|
|
78
|
+
});
|
|
79
|
+
const fileKey = identities.resolve(file).key;
|
|
80
|
+
const own = map.sources.findIndex((source) => identities.resolve(source).key === fileKey);
|
|
81
|
+
if (own < 0) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const lines = decodeSegments(map.mappings);
|
|
85
|
+
const locate = (position) => {
|
|
86
|
+
if (typeof position !== "object" || position === null)
|
|
87
|
+
return undefined;
|
|
88
|
+
const { column, line } = position;
|
|
89
|
+
if (typeof line !== "number" || typeof column !== "number") {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
const segments = lines[line - 1];
|
|
93
|
+
if (segments === undefined)
|
|
94
|
+
return undefined;
|
|
95
|
+
let low = 0;
|
|
96
|
+
let high = segments.length - 1;
|
|
97
|
+
let found;
|
|
98
|
+
while (low <= high) {
|
|
99
|
+
const middle = (low + high) >> 1;
|
|
100
|
+
const segment = segments[middle];
|
|
101
|
+
if (segment.generatedColumn <= column) {
|
|
102
|
+
found = segment;
|
|
103
|
+
low = middle + 1;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
high = middle - 1;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return found === undefined || found.source !== own
|
|
110
|
+
? undefined
|
|
111
|
+
: { column: found.column, line: found.line + 1 };
|
|
112
|
+
};
|
|
113
|
+
const visited = new Set();
|
|
114
|
+
const pending = [ast];
|
|
115
|
+
while (pending.length !== 0) {
|
|
116
|
+
const value = pending.pop();
|
|
117
|
+
if (typeof value !== "object" || value === null || visited.has(value)) {
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
visited.add(value);
|
|
121
|
+
if (Array.isArray(value)) {
|
|
122
|
+
for (const entryToAppend of value)
|
|
123
|
+
pending.push(entryToAppend);
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
const node = value;
|
|
127
|
+
for (const [key, child] of Object.entries(node)) {
|
|
128
|
+
if (key !== "loc")
|
|
129
|
+
pending.push(child);
|
|
130
|
+
}
|
|
131
|
+
const loc = node.loc;
|
|
132
|
+
if (typeof loc !== "object" || loc === null)
|
|
133
|
+
continue;
|
|
134
|
+
const { end, start } = loc;
|
|
135
|
+
const mappedStart = locate(start);
|
|
136
|
+
if (mappedStart === undefined) {
|
|
137
|
+
node.loc = null;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
const mappedEnd = locate(end);
|
|
141
|
+
node.loc = {
|
|
142
|
+
...loc,
|
|
143
|
+
end: mappedEnd === undefined ||
|
|
144
|
+
mappedEnd.line < mappedStart.line ||
|
|
145
|
+
(mappedEnd.line === mappedStart.line &&
|
|
146
|
+
mappedEnd.column < mappedStart.column)
|
|
147
|
+
? mappedStart
|
|
148
|
+
: mappedEnd,
|
|
149
|
+
start: mappedStart,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Decode `mappings` into its segments per generated line, in column order.
|
|
155
|
+
* Segments of every source are kept, and a segment with no source position is
|
|
156
|
+
* kept with source `-1`, so a lookup that lands on either reports no position
|
|
157
|
+
* instead of reaching past it to an earlier mapping.
|
|
158
|
+
*/
|
|
159
|
+
function decodeSegments(mappings) {
|
|
160
|
+
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
161
|
+
const lines = [];
|
|
162
|
+
let source = 0;
|
|
163
|
+
let line = 0;
|
|
164
|
+
let column = 0;
|
|
165
|
+
for (const text of mappings.split(";")) {
|
|
166
|
+
const segments = [];
|
|
167
|
+
let generatedColumn = 0;
|
|
168
|
+
for (const encoded of text.split(",")) {
|
|
169
|
+
if (encoded === "")
|
|
170
|
+
continue;
|
|
171
|
+
const fields = [];
|
|
172
|
+
let value = 0;
|
|
173
|
+
let shift = 0;
|
|
174
|
+
for (const char of encoded) {
|
|
175
|
+
const digit = alphabet.indexOf(char);
|
|
176
|
+
if (digit < 0)
|
|
177
|
+
return lines;
|
|
178
|
+
value += (digit & 31) << shift;
|
|
179
|
+
if ((digit & 32) !== 0) {
|
|
180
|
+
shift += 5;
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
fields.push((value & 1) !== 0 ? -(value >>> 1) : value >>> 1);
|
|
184
|
+
value = 0;
|
|
185
|
+
shift = 0;
|
|
186
|
+
}
|
|
187
|
+
generatedColumn += fields[0] ?? 0;
|
|
188
|
+
if (fields.length < 4) {
|
|
189
|
+
segments.push({ column: 0, generatedColumn, line: 0, source: -1 });
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
source += fields[1];
|
|
193
|
+
line += fields[2];
|
|
194
|
+
column += fields[3];
|
|
195
|
+
segments.push({ column, generatedColumn, line, source });
|
|
196
|
+
}
|
|
197
|
+
lines.push(segments);
|
|
198
|
+
}
|
|
199
|
+
return lines;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export { remapAstLocations };
|
|
203
|
+
//# sourceMappingURL=remapAstLocations.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remapAstLocations.mjs","sources":["../../src/core/remapAstLocations.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
package/lib/core/upstream.d.ts
CHANGED
|
@@ -1,23 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* The subset of the Metro Babel-transformer contract this adapter relies on.
|
|
3
|
-
*
|
|
4
|
-
* Metro loads the module named by `transformer.babelTransformerPath` and calls
|
|
5
|
-
* its `transform` once per file, expecting a Babel AST back. `getCacheKey` is
|
|
6
|
-
* an optional export Metro folds into its transform-cache key; Metro invokes it
|
|
7
|
-
* with arguments (e.g. `{ projectRoot, enableBabelRCLookup }`), so it is typed
|
|
8
|
-
* variadic.
|
|
9
|
-
*/
|
|
10
|
-
export interface UpstreamTransformer {
|
|
11
|
-
transform(params: {
|
|
12
|
-
src: string;
|
|
13
|
-
filename: string;
|
|
14
|
-
options: Record<string, unknown>;
|
|
15
|
-
[key: string]: unknown;
|
|
16
|
-
}): Promise<{
|
|
17
|
-
ast: object;
|
|
18
|
-
}>;
|
|
19
|
-
getCacheKey?: (...args: unknown[]) => string;
|
|
20
|
-
}
|
|
1
|
+
import type { UpstreamTransformer } from "./UpstreamTransformer";
|
|
21
2
|
/**
|
|
22
3
|
* Upstream transformer module specifiers tried (in order) when no explicit
|
|
23
4
|
* `upstreamTransformer` is configured: Expo first, then modern bare React
|
|
@@ -32,13 +13,102 @@ export declare const UPSTREAM_CANDIDATES: readonly ["@expo/metro-config/babel-tr
|
|
|
32
13
|
* 1. An explicit `customPath` (the `upstreamTransformer` option);
|
|
33
14
|
* 2. Each of {@link UPSTREAM_CANDIDATES} in turn.
|
|
34
15
|
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
16
|
+
* `withTtsc` resolves an explicit specifier and the automatic candidates from
|
|
17
|
+
* the consuming project in the Metro config process, and publishes the absolute
|
|
18
|
+
* path it found (see {@link locateProjectUpstreamTransformer}). This worker-side
|
|
19
|
+
* lookup, rooted in the adapter's own location, is what remains for a specifier
|
|
20
|
+
* the project could not resolve, so the adapter carries no Metro/Expo
|
|
21
|
+
* dependency itself. Resolution is not memoised: Node's own module cache
|
|
22
|
+
* already makes the repeated `require` a cheap lookup, and keeping no
|
|
23
|
+
* module-level state lets a changed `upstreamTransformer` always take effect.
|
|
24
|
+
*
|
|
25
|
+
* `load` is the explicit module-loading boundary. Its default uses Node's real
|
|
26
|
+
* `require`; an injected loader must preserve absence versus failure
|
|
27
|
+
* semantics.
|
|
28
|
+
*
|
|
29
|
+
* @evidence contracts/common.md#principled-implementation
|
|
30
|
+
* Node createRequire loads the configured module or documented
|
|
31
|
+
* Expo/React-Native candidates. Resolution is separated from execution: only
|
|
32
|
+
* known entry-absence codes permit another automatic candidate;
|
|
33
|
+
* installed-module initialization failures preserve their cause.
|
|
34
|
+
*
|
|
35
|
+
* @evidence contracts/common.md#clear-and-simple-design
|
|
36
|
+
* Explicit selection and ordered detection share the loading boundary.
|
|
37
|
+
* tryRequire owns absence classification, leaving this function to choose
|
|
38
|
+
* the transformer and attach contextual errors.
|
|
39
|
+
*
|
|
40
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts
|
|
41
|
+
* The loader argument is an explicit dependency-injection boundary, not a
|
|
42
|
+
* test-only runtime branch or a patched require.
|
|
43
|
+
*
|
|
44
|
+
* @evidence contracts/portability.md#os-neutral-implementation
|
|
45
|
+
* Node resolution accepts project-resolved absolute paths on Windows and
|
|
46
|
+
* POSIX. createRequire is rooted at this module via import.meta.url; no
|
|
47
|
+
* path is turned into a shell command. Runtime errors propagate through the
|
|
48
|
+
* same policy on every OS.
|
|
49
|
+
*
|
|
50
|
+
* @evidence contracts/common.md#meaningful-documentation
|
|
51
|
+
* The native JSDoc explains candidate order, project versus worker
|
|
52
|
+
* resolution and absence versus initialization failures. Checked against
|
|
53
|
+
* the documentation skill: separate paragraphs state the contract and why
|
|
54
|
+
* its nonobvious boundary matters; field comments retain their own useful
|
|
55
|
+
* facts.
|
|
56
|
+
*
|
|
57
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources
|
|
58
|
+
* Keeps no state between calls; loaded modules belong to Node's module cache.
|
|
59
|
+
*
|
|
60
|
+
* @evidenceExclude contracts/performance.md#efficient-algorithms
|
|
61
|
+
* At most three candidate probes per call, each a require.resolve followed by one require.
|
|
40
62
|
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
63
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work
|
|
64
|
+
* Deliberately unmemoised: Node's module cache makes a repeated require cheap and a changed option must take effect.
|
|
43
65
|
*/
|
|
44
66
|
export declare function resolveUpstreamTransformer(customPath?: string, load?: (modulePath: string) => UpstreamTransformer | undefined): UpstreamTransformer;
|
|
67
|
+
/**
|
|
68
|
+
* Locate the automatic upstream transformer the consuming project installed,
|
|
69
|
+
* without executing it.
|
|
70
|
+
*
|
|
71
|
+
* The candidates belong to the app, not to this adapter: under pnpm or a linked
|
|
72
|
+
* workspace the adapter sits outside the app's `node_modules` ancestry, so a
|
|
73
|
+
* lookup rooted in the adapter cannot see a transformer only the app installed.
|
|
74
|
+
* `resolve` is rooted in the project; each candidate is tried in
|
|
75
|
+
* {@link UPSTREAM_CANDIDATES} order and an absent one is skipped, exactly as the
|
|
76
|
+
* worker's probe skips it. Only resolution runs here, so a broken installation
|
|
77
|
+
* still fails where the worker loads it, with its own error.
|
|
78
|
+
*
|
|
79
|
+
* @param resolve Resolves a module specifier from the project.
|
|
80
|
+
* @returns The absolute path of the first installed candidate, or `undefined`.
|
|
81
|
+
* @evidence contracts/common.md#principled-implementation
|
|
82
|
+
* The supplied project resolver uses Node module resolution without
|
|
83
|
+
* executing candidate code. Only recognized entry-absence errors continue
|
|
84
|
+
* probing; other resolver failures propagate.
|
|
85
|
+
*
|
|
86
|
+
* @evidence contracts/common.md#clear-and-simple-design
|
|
87
|
+
* Project discovery resolves only the shared candidate list; execution is
|
|
88
|
+
* deferred to the worker loader that owns initialization failures.
|
|
89
|
+
*
|
|
90
|
+
* @evidence contracts/common.md#prohibited-implementation-shortcuts
|
|
91
|
+
* The documented optional-peer order is a product default, not a
|
|
92
|
+
* fixture-specific answer. No module methods are replaced.
|
|
93
|
+
*
|
|
94
|
+
* @evidence contracts/portability.md#os-neutral-implementation
|
|
95
|
+
* The injected resolver owns project filesystem resolution on the host OS.
|
|
96
|
+
* This operation compares module specifiers and returns its absolute result
|
|
97
|
+
* without inventing separators or quoting a command.
|
|
98
|
+
*
|
|
99
|
+
* @evidence contracts/common.md#meaningful-documentation
|
|
100
|
+
* The native JSDoc explains project ownership, ordered probing,
|
|
101
|
+
* nonexecution and the absent result. Checked against the documentation
|
|
102
|
+
* skill: separate paragraphs state the contract and why its nonobvious
|
|
103
|
+
* boundary matters; field comments retain their own useful facts.
|
|
104
|
+
*
|
|
105
|
+
* @evidenceExclude contracts/performance.md#bound-retention-and-release-resources
|
|
106
|
+
* Resolves paths without executing a candidate and retains nothing.
|
|
107
|
+
*
|
|
108
|
+
* @evidenceExclude contracts/performance.md#efficient-algorithms
|
|
109
|
+
* At most three resolutions, stopping at the first one that succeeds.
|
|
110
|
+
*
|
|
111
|
+
* @evidenceExclude contracts/performance.md#reuse-equivalent-work
|
|
112
|
+
* Runs once per withTtsc call, in the config process only.
|
|
113
|
+
*/
|
|
114
|
+
export declare function locateProjectUpstreamTransformer(resolve: (specifier: string) => string): string | undefined;
|