@variance-authority/core 0.1.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/CHANGELOG.md +79 -0
- package/LICENSE +21 -0
- package/README.md +169 -0
- package/dist/artifact.d.ts +47 -0
- package/dist/artifact.js +2 -0
- package/dist/artifact.js.map +1 -0
- package/dist/attribute/because.d.ts +43 -0
- package/dist/attribute/because.js +64 -0
- package/dist/attribute/because.js.map +1 -0
- package/dist/attribute/boundary.d.ts +191 -0
- package/dist/attribute/boundary.js +328 -0
- package/dist/attribute/boundary.js.map +1 -0
- package/dist/attribute/call-site.d.ts +96 -0
- package/dist/attribute/call-site.js +183 -0
- package/dist/attribute/call-site.js.map +1 -0
- package/dist/attribute/component-hash.d.ts +188 -0
- package/dist/attribute/component-hash.js +312 -0
- package/dist/attribute/component-hash.js.map +1 -0
- package/dist/attribute/composition-fixture.d.ts +18 -0
- package/dist/attribute/composition-fixture.js +53 -0
- package/dist/attribute/composition-fixture.js.map +1 -0
- package/dist/attribute/composition.d.ts +182 -0
- package/dist/attribute/composition.js +169 -0
- package/dist/attribute/composition.js.map +1 -0
- package/dist/attribute/control.d.ts +43 -0
- package/dist/attribute/control.js +54 -0
- package/dist/attribute/control.js.map +1 -0
- package/dist/attribute/coverage.d.ts +99 -0
- package/dist/attribute/coverage.js +86 -0
- package/dist/attribute/coverage.js.map +1 -0
- package/dist/attribute/divergence.d.ts +102 -0
- package/dist/attribute/divergence.js +152 -0
- package/dist/attribute/divergence.js.map +1 -0
- package/dist/attribute/index.d.ts +43 -0
- package/dist/attribute/index.js +29 -0
- package/dist/attribute/index.js.map +1 -0
- package/dist/attribute/instability.d.ts +89 -0
- package/dist/attribute/instability.js +183 -0
- package/dist/attribute/instability.js.map +1 -0
- package/dist/attribute/instances.d.ts +145 -0
- package/dist/attribute/instances.js +140 -0
- package/dist/attribute/instances.js.map +1 -0
- package/dist/attribute/locate.d.ts +22 -0
- package/dist/attribute/locate.js +100 -0
- package/dist/attribute/locate.js.map +1 -0
- package/dist/attribute/mask.d.ts +156 -0
- package/dist/attribute/mask.js +201 -0
- package/dist/attribute/mask.js.map +1 -0
- package/dist/attribute/movement.d.ts +211 -0
- package/dist/attribute/movement.js +181 -0
- package/dist/attribute/movement.js.map +1 -0
- package/dist/attribute/region.d.ts +188 -0
- package/dist/attribute/region.js +144 -0
- package/dist/attribute/region.js.map +1 -0
- package/dist/attribute/source-map.d.ts +98 -0
- package/dist/attribute/source-map.js +264 -0
- package/dist/attribute/source-map.js.map +1 -0
- package/dist/attribute/source.d.ts +77 -0
- package/dist/attribute/source.js +95 -0
- package/dist/attribute/source.js.map +1 -0
- package/dist/attribute/stack.d.ts +80 -0
- package/dist/attribute/stack.js +108 -0
- package/dist/attribute/stack.js.map +1 -0
- package/dist/compare/band.d.ts +85 -0
- package/dist/compare/band.js +70 -0
- package/dist/compare/band.js.map +1 -0
- package/dist/compare/cascade.d.ts +45 -0
- package/dist/compare/cascade.js +71 -0
- package/dist/compare/cascade.js.map +1 -0
- package/dist/compare/derive.d.ts +78 -0
- package/dist/compare/derive.js +39 -0
- package/dist/compare/derive.js.map +1 -0
- package/dist/compare/diff/attribution.d.ts +23 -0
- package/dist/compare/diff/attribution.js +244 -0
- package/dist/compare/diff/attribution.js.map +1 -0
- package/dist/compare/diff/compare-nodes.d.ts +15 -0
- package/dist/compare/diff/compare-nodes.js +227 -0
- package/dist/compare/diff/compare-nodes.js.map +1 -0
- package/dist/compare/diff/components.d.ts +20 -0
- package/dist/compare/diff/components.js +76 -0
- package/dist/compare/diff/components.js.map +1 -0
- package/dist/compare/diff/delta.d.ts +165 -0
- package/dist/compare/diff/delta.js +12 -0
- package/dist/compare/diff/delta.js.map +1 -0
- package/dist/compare/diff/index.d.ts +69 -0
- package/dist/compare/diff/index.js +101 -0
- package/dist/compare/diff/index.js.map +1 -0
- package/dist/compare/diff/match.d.ts +37 -0
- package/dist/compare/diff/match.js +167 -0
- package/dist/compare/diff/match.js.map +1 -0
- package/dist/compare/explain.d.ts +23 -0
- package/dist/compare/explain.js +190 -0
- package/dist/compare/explain.js.map +1 -0
- package/dist/compare/holding-diff.d.ts +25 -0
- package/dist/compare/holding-diff.js +74 -0
- package/dist/compare/holding-diff.js.map +1 -0
- package/dist/compare/impact.d.ts +56 -0
- package/dist/compare/impact.js +124 -0
- package/dist/compare/impact.js.map +1 -0
- package/dist/compare/index.d.ts +30 -0
- package/dist/compare/index.js +22 -0
- package/dist/compare/index.js.map +1 -0
- package/dist/compare/instance.d.ts +30 -0
- package/dist/compare/instance.js +145 -0
- package/dist/compare/instance.js.map +1 -0
- package/dist/compare/observability.d.ts +49 -0
- package/dist/compare/observability.js +57 -0
- package/dist/compare/observability.js.map +1 -0
- package/dist/compare/parting.d.ts +179 -0
- package/dist/compare/parting.js +244 -0
- package/dist/compare/parting.js.map +1 -0
- package/dist/compare/slice.d.ts +74 -0
- package/dist/compare/slice.js +79 -0
- package/dist/compare/slice.js.map +1 -0
- package/dist/compare/value.d.ts +60 -0
- package/dist/compare/value.js +111 -0
- package/dist/compare/value.js.map +1 -0
- package/dist/format/accessibility.d.ts +27 -0
- package/dist/format/accessibility.js +17 -0
- package/dist/format/accessibility.js.map +1 -0
- package/dist/format/canonical.d.ts +38 -0
- package/dist/format/canonical.js +65 -0
- package/dist/format/canonical.js.map +1 -0
- package/dist/format/capture.d.ts +212 -0
- package/dist/format/capture.js +2 -0
- package/dist/format/capture.js.map +1 -0
- package/dist/format/document.d.ts +279 -0
- package/dist/format/document.js +65 -0
- package/dist/format/document.js.map +1 -0
- package/dist/format/environment.d.ts +120 -0
- package/dist/format/environment.js +53 -0
- package/dist/format/environment.js.map +1 -0
- package/dist/format/hash.d.ts +45 -0
- package/dist/format/hash.js +41 -0
- package/dist/format/hash.js.map +1 -0
- package/dist/format/holding.d.ts +134 -0
- package/dist/format/holding.js +2 -0
- package/dist/format/holding.js.map +1 -0
- package/dist/format/index.d.ts +39 -0
- package/dist/format/index.js +25 -0
- package/dist/format/index.js.map +1 -0
- package/dist/format/intervention.d.ts +87 -0
- package/dist/format/intervention.js +11 -0
- package/dist/format/intervention.js.map +1 -0
- package/dist/format/profile.d.ts +59 -0
- package/dist/format/profile.js +66 -0
- package/dist/format/profile.js.map +1 -0
- package/dist/format/provenance.d.ts +179 -0
- package/dist/format/provenance.js +222 -0
- package/dist/format/provenance.js.map +1 -0
- package/dist/format/sha256.d.ts +34 -0
- package/dist/format/sha256.js +152 -0
- package/dist/format/sha256.js.map +1 -0
- package/dist/format/snapshot.d.ts +263 -0
- package/dist/format/snapshot.js +2 -0
- package/dist/format/snapshot.js.map +1 -0
- package/dist/format/stabilize.d.ts +190 -0
- package/dist/format/stabilize.js +333 -0
- package/dist/format/stabilize.js.map +1 -0
- package/dist/format/tier.d.ts +19 -0
- package/dist/format/tier.js +11 -0
- package/dist/format/tier.js.map +1 -0
- package/dist/format/value.d.ts +115 -0
- package/dist/format/value.js +182 -0
- package/dist/format/value.js.map +1 -0
- package/dist/format/wiring.d.ts +115 -0
- package/dist/format/wiring.js +60 -0
- package/dist/format/wiring.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/judge/docket.d.ts +85 -0
- package/dist/judge/docket.js +162 -0
- package/dist/judge/docket.js.map +1 -0
- package/dist/judge/fingerprint.d.ts +53 -0
- package/dist/judge/fingerprint.js +144 -0
- package/dist/judge/fingerprint.js.map +1 -0
- package/dist/judge/ignore.d.ts +206 -0
- package/dist/judge/ignore.js +237 -0
- package/dist/judge/ignore.js.map +1 -0
- package/dist/judge/index.d.ts +44 -0
- package/dist/judge/index.js +34 -0
- package/dist/judge/index.js.map +1 -0
- package/dist/judge/inspect.d.ts +148 -0
- package/dist/judge/inspect.js +208 -0
- package/dist/judge/inspect.js.map +1 -0
- package/dist/judge/intent.d.ts +146 -0
- package/dist/judge/intent.js +150 -0
- package/dist/judge/intent.js.map +1 -0
- package/dist/judge/locale.d.ts +111 -0
- package/dist/judge/locale.js +247 -0
- package/dist/judge/locale.js.map +1 -0
- package/dist/judge/scope.d.ts +41 -0
- package/dist/judge/scope.js +47 -0
- package/dist/judge/scope.js.map +1 -0
- package/dist/judge/sensitivity.d.ts +202 -0
- package/dist/judge/sensitivity.js +152 -0
- package/dist/judge/sensitivity.js.map +1 -0
- package/dist/judge/trail.d.ts +122 -0
- package/dist/judge/trail.js +96 -0
- package/dist/judge/trail.js.map +1 -0
- package/dist/judge/verdict.d.ts +39 -0
- package/dist/judge/verdict.js +32 -0
- package/dist/judge/verdict.js.map +1 -0
- package/dist/plan/index.d.ts +12 -0
- package/dist/plan/index.js +11 -0
- package/dist/plan/index.js.map +1 -0
- package/dist/plan/tool.d.ts +85 -0
- package/dist/plan/tool.js +87 -0
- package/dist/plan/tool.js.map +1 -0
- package/dist/relate/graph.d.ts +190 -0
- package/dist/relate/graph.js +234 -0
- package/dist/relate/graph.js.map +1 -0
- package/dist/relate/index.d.ts +16 -0
- package/dist/relate/index.js +16 -0
- package/dist/relate/index.js.map +1 -0
- package/dist/relate/merkle.d.ts +110 -0
- package/dist/relate/merkle.js +246 -0
- package/dist/relate/merkle.js.map +1 -0
- package/dist/relate/reach.d.ts +65 -0
- package/dist/relate/reach.js +111 -0
- package/dist/relate/reach.js.map +1 -0
- package/dist/relate/records.d.ts +129 -0
- package/dist/relate/records.js +112 -0
- package/dist/relate/records.js.map +1 -0
- package/dist/rules/index.d.ts +19 -0
- package/dist/rules/index.js +17 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/normalize/alias.d.ts +50 -0
- package/dist/rules/normalize/alias.js +111 -0
- package/dist/rules/normalize/alias.js.map +1 -0
- package/dist/rules/normalize/cascade.d.ts +85 -0
- package/dist/rules/normalize/cascade.js +324 -0
- package/dist/rules/normalize/cascade.js.map +1 -0
- package/dist/rules/normalize/color.d.ts +30 -0
- package/dist/rules/normalize/color.js +185 -0
- package/dist/rules/normalize/color.js.map +1 -0
- package/dist/rules/normalize/fixture.d.ts +83 -0
- package/dist/rules/normalize/fixture.js +74 -0
- package/dist/rules/normalize/fixture.js.map +1 -0
- package/dist/rules/normalize/index.d.ts +39 -0
- package/dist/rules/normalize/index.js +268 -0
- package/dist/rules/normalize/index.js.map +1 -0
- package/dist/rules/normalize/project.d.ts +42 -0
- package/dist/rules/normalize/project.js +83 -0
- package/dist/rules/normalize/project.js.map +1 -0
- package/dist/rules/normalize/shorthand.d.ts +38 -0
- package/dist/rules/normalize/shorthand.js +388 -0
- package/dist/rules/normalize/shorthand.js.map +1 -0
- package/dist/rules/normalize/value.d.ts +23 -0
- package/dist/rules/normalize/value.js +162 -0
- package/dist/rules/normalize/value.js.map +1 -0
- package/dist/rules/normalize/wrapper.d.ts +16 -0
- package/dist/rules/normalize/wrapper.js +137 -0
- package/dist/rules/normalize/wrapper.js.map +1 -0
- package/dist/rules/ruleset.d.ts +93 -0
- package/dist/rules/ruleset.js +175 -0
- package/dist/rules/ruleset.js.map +1 -0
- package/mark.svg +30 -0
- package/package.json +69 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A generated position, mapped back to the source somebody wrote.
|
|
3
|
+
*
|
|
4
|
+
* Every build in ordinary use already ships this. A dev server emits it inline
|
|
5
|
+
* on each module it transforms; a production bundler writes it beside the
|
|
6
|
+
* bundle. It is the same argument the rest of provenance keeps making — the
|
|
7
|
+
* information is not missing from the build, only from the hop that would carry
|
|
8
|
+
* it — and it is what makes a browser stack frame usable: a frame says
|
|
9
|
+
* `/src/probe.jsx:23:26` about a file the browser was served, and the file the
|
|
10
|
+
* reviewer has to open is line 21 of the file they wrote.
|
|
11
|
+
*
|
|
12
|
+
* **Written out rather than installed.** `core` has no third-party dependencies
|
|
13
|
+
* (ADR-0013) and a source map is a documented format with one interesting part,
|
|
14
|
+
* so decoding it here costs less than the rule it would break. What is
|
|
15
|
+
* deliberately *not* implemented is left as a refusal rather than a wrong
|
|
16
|
+
* answer: an index map's sections are resolved, and a map whose version this
|
|
17
|
+
* does not recognise returns nothing at all.
|
|
18
|
+
*/
|
|
19
|
+
/** Base64 VLQ, the one encoding a source map has that JSON does not. */
|
|
20
|
+
const DIGITS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
21
|
+
const VALUE_OF = new Int8Array(128).fill(-1);
|
|
22
|
+
for (let i = 0; i < DIGITS.length; i += 1)
|
|
23
|
+
VALUE_OF[DIGITS.charCodeAt(i)] = i;
|
|
24
|
+
function decodeMappings(mappings, sourceCount) {
|
|
25
|
+
const lines = [];
|
|
26
|
+
let sourceIndex = 0;
|
|
27
|
+
let originalLine = 0;
|
|
28
|
+
let originalColumn = 0;
|
|
29
|
+
for (const encoded of mappings.split(';')) {
|
|
30
|
+
const segments = [];
|
|
31
|
+
let generatedColumn = 0;
|
|
32
|
+
if (encoded !== '') {
|
|
33
|
+
for (const field of encoded.split(',')) {
|
|
34
|
+
const values = decodeVlq(field);
|
|
35
|
+
// A one-field segment says "generated code with no origin" — a bundler's
|
|
36
|
+
// own prelude, a helper it injected. It advances the column and maps
|
|
37
|
+
// nowhere, so recording it would let a lookup land on it and answer with
|
|
38
|
+
// whatever the previous segment's source was.
|
|
39
|
+
if (values.length < 4) {
|
|
40
|
+
if (values.length > 0)
|
|
41
|
+
generatedColumn += values[0];
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
generatedColumn += values[0];
|
|
45
|
+
sourceIndex += values[1];
|
|
46
|
+
originalLine += values[2];
|
|
47
|
+
originalColumn += values[3];
|
|
48
|
+
if (sourceIndex >= 0 && sourceIndex < sourceCount) {
|
|
49
|
+
segments.push({ generatedColumn, sourceIndex, originalLine, originalColumn });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// The format does not promise ordered columns, and the lookup is a binary
|
|
54
|
+
// search that assumes them. Sorting once here is cheaper than a scan per
|
|
55
|
+
// frame, and every map met so far was already in order.
|
|
56
|
+
segments.sort((a, b) => a.generatedColumn - b.generatedColumn);
|
|
57
|
+
lines.push(segments);
|
|
58
|
+
}
|
|
59
|
+
return lines;
|
|
60
|
+
}
|
|
61
|
+
function decodeVlq(field) {
|
|
62
|
+
const values = [];
|
|
63
|
+
let value = 0;
|
|
64
|
+
let shift = 0;
|
|
65
|
+
for (let i = 0; i < field.length; i += 1) {
|
|
66
|
+
const code = field.charCodeAt(i);
|
|
67
|
+
const digit = code < 128 ? VALUE_OF[code] : -1;
|
|
68
|
+
if (digit < 0)
|
|
69
|
+
return values; // Not a source map. Stop rather than invent.
|
|
70
|
+
const more = (digit & 32) !== 0;
|
|
71
|
+
value += (digit & 31) << shift;
|
|
72
|
+
if (more) {
|
|
73
|
+
shift += 5;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const negative = (value & 1) === 1;
|
|
77
|
+
const magnitude = value >>> 1;
|
|
78
|
+
values.push(negative ? (magnitude === 0 ? -0x80000000 : -magnitude) : magnitude);
|
|
79
|
+
value = 0;
|
|
80
|
+
shift = 0;
|
|
81
|
+
}
|
|
82
|
+
return values;
|
|
83
|
+
}
|
|
84
|
+
function resolveSources(raw) {
|
|
85
|
+
if (!Array.isArray(raw.sources))
|
|
86
|
+
return null;
|
|
87
|
+
const root = typeof raw.sourceRoot === 'string' ? raw.sourceRoot.replace(/\/+$/, '') : '';
|
|
88
|
+
return raw.sources.map((source) => {
|
|
89
|
+
if (typeof source !== 'string')
|
|
90
|
+
return '';
|
|
91
|
+
if (root === '' || source === '' || /^[a-z][a-z0-9+.-]*:/i.test(source))
|
|
92
|
+
return source;
|
|
93
|
+
return source.startsWith('/') ? `${root}${source}` : `${root}/${source}`;
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Parse a source map, or decide this is not one.
|
|
98
|
+
*
|
|
99
|
+
* Returns a list because an index map is several maps at several offsets, and
|
|
100
|
+
* flattening them here means the lookup does not have to know which kind it was
|
|
101
|
+
* handed. An ordinary map is a list of one.
|
|
102
|
+
*
|
|
103
|
+
* Every refusal is silent and total. The caller is resolving a stack frame for a
|
|
104
|
+
* report, and a map that does not parse means one node reports no location —
|
|
105
|
+
* which is a state the whole provenance path already treats as normal.
|
|
106
|
+
*/
|
|
107
|
+
export function parseSourceMap(text) {
|
|
108
|
+
let raw;
|
|
109
|
+
try {
|
|
110
|
+
raw = JSON.parse(text);
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
if (raw === null || typeof raw !== 'object')
|
|
116
|
+
return [];
|
|
117
|
+
if (raw.version !== undefined && raw.version !== 3)
|
|
118
|
+
return [];
|
|
119
|
+
if (Array.isArray(raw.sections)) {
|
|
120
|
+
return raw.sections.flatMap((section) => {
|
|
121
|
+
if (section === null || typeof section !== 'object')
|
|
122
|
+
return [];
|
|
123
|
+
const { offset, map } = section;
|
|
124
|
+
if (offset === null || typeof offset !== 'object')
|
|
125
|
+
return [];
|
|
126
|
+
if (map === null || typeof map !== 'object')
|
|
127
|
+
return [];
|
|
128
|
+
const { line, column } = offset;
|
|
129
|
+
if (typeof line !== 'number' || typeof column !== 'number')
|
|
130
|
+
return [];
|
|
131
|
+
return parseSourceMap(JSON.stringify(map)).map((parsed) => ({
|
|
132
|
+
...parsed,
|
|
133
|
+
offset: { line, column },
|
|
134
|
+
}));
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
const sources = resolveSources(raw);
|
|
138
|
+
if (sources === null || typeof raw.mappings !== 'string')
|
|
139
|
+
return [];
|
|
140
|
+
return [
|
|
141
|
+
{
|
|
142
|
+
sources,
|
|
143
|
+
lines: decodeMappings(raw.mappings, sources.length),
|
|
144
|
+
offset: { line: 0, column: 0 },
|
|
145
|
+
},
|
|
146
|
+
];
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* The source position a generated position came from, or nothing.
|
|
150
|
+
*
|
|
151
|
+
* `line` and `column` are 1-based on the way in and on the way out, because both
|
|
152
|
+
* ends of this are 1-based: a browser's stack frame counts from one, and so does
|
|
153
|
+
* every editor a reviewer opens the answer in. The wire format counts from zero
|
|
154
|
+
* and that stays inside this file.
|
|
155
|
+
*
|
|
156
|
+
* A position between two mappings resolves to the earlier one — which is the
|
|
157
|
+
* standard reading, and the right one: generated code that maps nowhere belongs
|
|
158
|
+
* to the last construct that did.
|
|
159
|
+
*/
|
|
160
|
+
export function originalPositionFor(maps, line, column) {
|
|
161
|
+
for (const map of sectionsFor(maps, line, column)) {
|
|
162
|
+
// A section's own coordinates are relative to where it was placed. Only the
|
|
163
|
+
// first generated line of a section is column-shifted; every line after it
|
|
164
|
+
// starts at zero, the same way a paragraph only indents its first line.
|
|
165
|
+
const localLine = line - map.offset.line;
|
|
166
|
+
const localColumn = localLine === 1 ? column - map.offset.column : column;
|
|
167
|
+
const segments = map.lines[localLine - 1];
|
|
168
|
+
if (segments === undefined || segments.length === 0)
|
|
169
|
+
continue;
|
|
170
|
+
const found = segmentAt(segments, localColumn - 1);
|
|
171
|
+
if (found === undefined)
|
|
172
|
+
continue;
|
|
173
|
+
const source = map.sources[found.sourceIndex];
|
|
174
|
+
if (source === undefined || source === '')
|
|
175
|
+
continue;
|
|
176
|
+
return { source, line: found.originalLine + 1, column: found.originalColumn + 1 };
|
|
177
|
+
}
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* The sections a position could be in, innermost placement first.
|
|
182
|
+
*
|
|
183
|
+
* An ordinary map is one section at the origin and this is the identity. For an
|
|
184
|
+
* index map the sections partition the generated file, so the answer is the last
|
|
185
|
+
* one that starts at or before the position — searched in reverse for exactly
|
|
186
|
+
* that reason.
|
|
187
|
+
*/
|
|
188
|
+
function sectionsFor(maps, line, column) {
|
|
189
|
+
if (maps.length <= 1)
|
|
190
|
+
return maps;
|
|
191
|
+
const candidates = maps.filter((map) => map.offset.line < line || (map.offset.line === line - 1 && map.offset.column < column));
|
|
192
|
+
return candidates.length === 0 ? [] : [candidates[candidates.length - 1]];
|
|
193
|
+
}
|
|
194
|
+
function segmentAt(segments, column) {
|
|
195
|
+
let low = 0;
|
|
196
|
+
let high = segments.length - 1;
|
|
197
|
+
let found;
|
|
198
|
+
while (low <= high) {
|
|
199
|
+
const middle = (low + high) >> 1;
|
|
200
|
+
const segment = segments[middle];
|
|
201
|
+
if (segment.generatedColumn <= column) {
|
|
202
|
+
found = segment;
|
|
203
|
+
low = middle + 1;
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
high = middle - 1;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// Before the first mapping on the line. The line is still attributable — the
|
|
210
|
+
// first construct on it is what generated code at column 0 belongs to.
|
|
211
|
+
return found ?? segments[0];
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* The map a served module points at, as a URL or as the map itself.
|
|
215
|
+
*
|
|
216
|
+
* Two forms, and a build uses whichever suits it: a dev server inlines the map
|
|
217
|
+
* as a `data:` URI because it is transforming one module in memory, and a
|
|
218
|
+
* production bundler writes a sibling `.map` because inlining megabytes into
|
|
219
|
+
* every response is not free.
|
|
220
|
+
*
|
|
221
|
+
* Read from the end of the file. The comment is a trailing annotation by
|
|
222
|
+
* construction, and a bundle that merely *mentions* the token — this file does,
|
|
223
|
+
* a few lines up — would otherwise hand back its own prose.
|
|
224
|
+
*/
|
|
225
|
+
export function sourceMappingUrlOf(code) {
|
|
226
|
+
const found = [...code.matchAll(/^[/*]{2,}[#@]\s*sourceMappingURL=(\S+)\s*(?:\*\/)?$/gm)];
|
|
227
|
+
const last = found[found.length - 1];
|
|
228
|
+
return last === undefined ? null : last[1];
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* The JSON of a `data:` source-map URL, when that is what it is.
|
|
232
|
+
*
|
|
233
|
+
* Base64 is what every bundler writes; the spec permits percent-encoding, and
|
|
234
|
+
* `decodeURIComponent` is what reads it. Both are handled because handling the
|
|
235
|
+
* second costs one branch.
|
|
236
|
+
*/
|
|
237
|
+
export function inlineSourceMapOf(url) {
|
|
238
|
+
const match = /^data:application\/json[^,]*,(.*)$/s.exec(url);
|
|
239
|
+
if (match === null)
|
|
240
|
+
return null;
|
|
241
|
+
const payload = match[1];
|
|
242
|
+
if (!/;base64/i.test(url)) {
|
|
243
|
+
try {
|
|
244
|
+
return decodeURIComponent(payload);
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
try {
|
|
251
|
+
// `atob` in a browser, `Buffer` in Node — this package may not assume either,
|
|
252
|
+
// so it uses whichever the host actually has.
|
|
253
|
+
const decode = globalThis.atob;
|
|
254
|
+
if (decode !== undefined)
|
|
255
|
+
return decode(payload);
|
|
256
|
+
const buffer = globalThis
|
|
257
|
+
.Buffer;
|
|
258
|
+
return buffer === undefined ? null : buffer.from(payload, 'base64').toString('utf8');
|
|
259
|
+
}
|
|
260
|
+
catch {
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
//# sourceMappingURL=source-map.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-map.js","sourceRoot":"","sources":["../../src/attribute/source-map.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * A generated position, mapped back to the source somebody wrote.\n *\n * Every build in ordinary use already ships this. A dev server emits it inline\n * on each module it transforms; a production bundler writes it beside the\n * bundle. It is the same argument the rest of provenance keeps making — the\n * information is not missing from the build, only from the hop that would carry\n * it — and it is what makes a browser stack frame usable: a frame says\n * `/src/probe.jsx:23:26` about a file the browser was served, and the file the\n * reviewer has to open is line 21 of the file they wrote.\n *\n * **Written out rather than installed.** `core` has no third-party dependencies\n * (ADR-0013) and a source map is a documented format with one interesting part,\n * so decoding it here costs less than the rule it would break. What is\n * deliberately *not* implemented is left as a refusal rather than a wrong\n * answer: an index map's sections are resolved, and a map whose version this\n * does not recognise returns nothing at all.\n */\n\n/** Base64 VLQ, the one encoding a source map has that JSON does not. */\nconst DIGITS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\n\nconst VALUE_OF = new Int8Array(128).fill(-1);\nfor (let i = 0; i < DIGITS.length; i += 1) VALUE_OF[DIGITS.charCodeAt(i)] = i;\n\n/**\n * One decoded mapping: where a generated column came from.\n *\n * Column-only because the generated *line* is the index of the array this sits\n * in — that is how the `;`-separated wire format is shaped, and keeping it that\n * way is what makes the lookup a binary search rather than a scan.\n */\ninterface Segment {\n readonly generatedColumn: number;\n readonly sourceIndex: number;\n readonly originalLine: number;\n readonly originalColumn: number;\n}\n\nexport interface SourceMap {\n /** Source paths as the map states them, already resolved against `sourceRoot`. */\n readonly sources: readonly string[];\n /** Segments per generated line, `[0]` being generated line 1. */\n readonly lines: readonly (readonly Segment[])[];\n /** Non-zero only for a section of an index map. */\n readonly offset: { readonly line: number; readonly column: number };\n}\n\nexport interface OriginalPosition {\n /** The source path, exactly as the map spells it. */\n readonly source: string;\n /** 1-based, matching how a stack frame and an editor both count. */\n readonly line: number;\n /** 1-based, for the same reason. The wire format is 0-based; this is not. */\n readonly column: number;\n}\n\n/**\n * The shape a map arrives in, before anything has been checked about it.\n *\n * Deliberately loose. This is parsed from whatever a dev server served, which\n * on a bad day is an HTML error page, and every field is therefore treated as\n * absent until it proves otherwise.\n */\ninterface RawMap {\n readonly version?: unknown;\n readonly sources?: unknown;\n readonly sourceRoot?: unknown;\n readonly mappings?: unknown;\n readonly sections?: unknown;\n}\n\nfunction decodeMappings(mappings: string, sourceCount: number): Segment[][] {\n const lines: Segment[][] = [];\n\n let sourceIndex = 0;\n let originalLine = 0;\n let originalColumn = 0;\n\n for (const encoded of mappings.split(';')) {\n const segments: Segment[] = [];\n let generatedColumn = 0;\n\n if (encoded !== '') {\n for (const field of encoded.split(',')) {\n const values = decodeVlq(field);\n\n // A one-field segment says \"generated code with no origin\" — a bundler's\n // own prelude, a helper it injected. It advances the column and maps\n // nowhere, so recording it would let a lookup land on it and answer with\n // whatever the previous segment's source was.\n if (values.length < 4) {\n if (values.length > 0) generatedColumn += values[0]!;\n continue;\n }\n\n generatedColumn += values[0]!;\n sourceIndex += values[1]!;\n originalLine += values[2]!;\n originalColumn += values[3]!;\n\n if (sourceIndex >= 0 && sourceIndex < sourceCount) {\n segments.push({ generatedColumn, sourceIndex, originalLine, originalColumn });\n }\n }\n }\n\n // The format does not promise ordered columns, and the lookup is a binary\n // search that assumes them. Sorting once here is cheaper than a scan per\n // frame, and every map met so far was already in order.\n segments.sort((a, b) => a.generatedColumn - b.generatedColumn);\n lines.push(segments);\n }\n\n return lines;\n}\n\nfunction decodeVlq(field: string): number[] {\n const values: number[] = [];\n\n let value = 0;\n let shift = 0;\n\n for (let i = 0; i < field.length; i += 1) {\n const code = field.charCodeAt(i);\n const digit = code < 128 ? VALUE_OF[code]! : -1;\n if (digit < 0) return values; // Not a source map. Stop rather than invent.\n\n const more = (digit & 32) !== 0;\n value += (digit & 31) << shift;\n\n if (more) {\n shift += 5;\n continue;\n }\n\n const negative = (value & 1) === 1;\n const magnitude = value >>> 1;\n values.push(negative ? (magnitude === 0 ? -0x80000000 : -magnitude) : magnitude);\n\n value = 0;\n shift = 0;\n }\n\n return values;\n}\n\nfunction resolveSources(raw: RawMap): string[] | null {\n if (!Array.isArray(raw.sources)) return null;\n\n const root = typeof raw.sourceRoot === 'string' ? raw.sourceRoot.replace(/\\/+$/, '') : '';\n\n return raw.sources.map((source) => {\n if (typeof source !== 'string') return '';\n if (root === '' || source === '' || /^[a-z][a-z0-9+.-]*:/i.test(source)) return source;\n return source.startsWith('/') ? `${root}${source}` : `${root}/${source}`;\n });\n}\n\n/**\n * Parse a source map, or decide this is not one.\n *\n * Returns a list because an index map is several maps at several offsets, and\n * flattening them here means the lookup does not have to know which kind it was\n * handed. An ordinary map is a list of one.\n *\n * Every refusal is silent and total. The caller is resolving a stack frame for a\n * report, and a map that does not parse means one node reports no location —\n * which is a state the whole provenance path already treats as normal.\n */\nexport function parseSourceMap(text: string): SourceMap[] {\n let raw: RawMap;\n try {\n raw = JSON.parse(text) as RawMap;\n } catch {\n return [];\n }\n\n if (raw === null || typeof raw !== 'object') return [];\n if (raw.version !== undefined && raw.version !== 3) return [];\n\n if (Array.isArray(raw.sections)) {\n return raw.sections.flatMap((section) => {\n if (section === null || typeof section !== 'object') return [];\n\n const { offset, map } = section as { offset?: unknown; map?: unknown };\n if (offset === null || typeof offset !== 'object') return [];\n if (map === null || typeof map !== 'object') return [];\n\n const { line, column } = offset as { line?: unknown; column?: unknown };\n if (typeof line !== 'number' || typeof column !== 'number') return [];\n\n return parseSourceMap(JSON.stringify(map)).map((parsed) => ({\n ...parsed,\n offset: { line, column },\n }));\n });\n }\n\n const sources = resolveSources(raw);\n if (sources === null || typeof raw.mappings !== 'string') return [];\n\n return [\n {\n sources,\n lines: decodeMappings(raw.mappings, sources.length),\n offset: { line: 0, column: 0 },\n },\n ];\n}\n\n/**\n * The source position a generated position came from, or nothing.\n *\n * `line` and `column` are 1-based on the way in and on the way out, because both\n * ends of this are 1-based: a browser's stack frame counts from one, and so does\n * every editor a reviewer opens the answer in. The wire format counts from zero\n * and that stays inside this file.\n *\n * A position between two mappings resolves to the earlier one — which is the\n * standard reading, and the right one: generated code that maps nowhere belongs\n * to the last construct that did.\n */\nexport function originalPositionFor(\n maps: readonly SourceMap[],\n line: number,\n column: number,\n): OriginalPosition | null {\n for (const map of sectionsFor(maps, line, column)) {\n // A section's own coordinates are relative to where it was placed. Only the\n // first generated line of a section is column-shifted; every line after it\n // starts at zero, the same way a paragraph only indents its first line.\n const localLine = line - map.offset.line;\n const localColumn = localLine === 1 ? column - map.offset.column : column;\n\n const segments = map.lines[localLine - 1];\n if (segments === undefined || segments.length === 0) continue;\n\n const found = segmentAt(segments, localColumn - 1);\n if (found === undefined) continue;\n\n const source = map.sources[found.sourceIndex];\n if (source === undefined || source === '') continue;\n\n return { source, line: found.originalLine + 1, column: found.originalColumn + 1 };\n }\n\n return null;\n}\n\n/**\n * The sections a position could be in, innermost placement first.\n *\n * An ordinary map is one section at the origin and this is the identity. For an\n * index map the sections partition the generated file, so the answer is the last\n * one that starts at or before the position — searched in reverse for exactly\n * that reason.\n */\nfunction sectionsFor(\n maps: readonly SourceMap[],\n line: number,\n column: number,\n): readonly SourceMap[] {\n if (maps.length <= 1) return maps;\n\n const candidates = maps.filter(\n (map) =>\n map.offset.line < line || (map.offset.line === line - 1 && map.offset.column < column),\n );\n\n return candidates.length === 0 ? [] : [candidates[candidates.length - 1]!];\n}\n\nfunction segmentAt(segments: readonly Segment[], column: number): Segment | undefined {\n let low = 0;\n let high = segments.length - 1;\n let found: Segment | undefined;\n\n while (low <= high) {\n const middle = (low + high) >> 1;\n const segment = segments[middle]!;\n\n if (segment.generatedColumn <= column) {\n found = segment;\n low = middle + 1;\n } else {\n high = middle - 1;\n }\n }\n\n // Before the first mapping on the line. The line is still attributable — the\n // first construct on it is what generated code at column 0 belongs to.\n return found ?? segments[0];\n}\n\n/**\n * The map a served module points at, as a URL or as the map itself.\n *\n * Two forms, and a build uses whichever suits it: a dev server inlines the map\n * as a `data:` URI because it is transforming one module in memory, and a\n * production bundler writes a sibling `.map` because inlining megabytes into\n * every response is not free.\n *\n * Read from the end of the file. The comment is a trailing annotation by\n * construction, and a bundle that merely *mentions* the token — this file does,\n * a few lines up — would otherwise hand back its own prose.\n */\nexport function sourceMappingUrlOf(code: string): string | null {\n const found = [...code.matchAll(/^[/*]{2,}[#@]\\s*sourceMappingURL=(\\S+)\\s*(?:\\*\\/)?$/gm)];\n const last = found[found.length - 1];\n return last === undefined ? null : last[1]!;\n}\n\n/**\n * The JSON of a `data:` source-map URL, when that is what it is.\n *\n * Base64 is what every bundler writes; the spec permits percent-encoding, and\n * `decodeURIComponent` is what reads it. Both are handled because handling the\n * second costs one branch.\n */\nexport function inlineSourceMapOf(url: string): string | null {\n const match = /^data:application\\/json[^,]*,(.*)$/s.exec(url);\n if (match === null) return null;\n\n const payload = match[1]!;\n if (!/;base64/i.test(url)) {\n try {\n return decodeURIComponent(payload);\n } catch {\n return null;\n }\n }\n\n try {\n // `atob` in a browser, `Buffer` in Node — this package may not assume either,\n // so it uses whichever the host actually has.\n const decode = (globalThis as { atob?: (input: string) => string }).atob;\n if (decode !== undefined) return decode(payload);\n\n const buffer = (globalThis as { Buffer?: { from(s: string, e: string): { toString(e: string): string } } })\n .Buffer;\n return buffer === undefined ? null : buffer.from(payload, 'base64').toString('utf8');\n } catch {\n return null;\n }\n}\n"]}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connecting a change to a file.
|
|
3
|
+
*
|
|
4
|
+
* A report that says `Button` is one step short of useful. An agent asked to fix
|
|
5
|
+
* it still has to find `Button`, and a reviewer still has to guess whether the
|
|
6
|
+
* `Button` in question is the design-system one or the local one in checkout.
|
|
7
|
+
* The last hop — **which file** — is what turns a finding into an edit.
|
|
8
|
+
*
|
|
9
|
+
* There are two mechanisms, and this file is the second one.
|
|
10
|
+
*
|
|
11
|
+
* The first is exact. Spec §6.1 assumes the build tells us: every JSX transform
|
|
12
|
+
* already computes each element's file, line and column, and
|
|
13
|
+
* `@variance-authority/jsx-source` is the `jsxImportSource` setting that keeps
|
|
14
|
+
* that location as far as the fiber — React 19 drops it otherwise, on every path.
|
|
15
|
+
* When it is on, a finding names the element's own line and this index is not
|
|
16
|
+
* consulted.
|
|
17
|
+
*
|
|
18
|
+
* The second is this one, and it is what a repository that has changed nothing
|
|
19
|
+
* gets. Attribution names *components*, so a **component → file** index answers
|
|
20
|
+
* the question attribution asks. It is built by reading source rather than by
|
|
21
|
+
* configuring a build, and it answers with where a component is *declared* —
|
|
22
|
+
* coarser than a call site, and enough to open the right file.
|
|
23
|
+
*
|
|
24
|
+
* The index is plain data. `core` performs no I/O (ADR-0006), so building one is
|
|
25
|
+
* a caller's job; resolving against one is here.
|
|
26
|
+
*/
|
|
27
|
+
export interface SourceRef {
|
|
28
|
+
/** Repository-relative, so a report is portable between machines and CI. */
|
|
29
|
+
readonly file: string;
|
|
30
|
+
readonly line: number;
|
|
31
|
+
/** How the declaration was recognised. Carried so a bad match is debuggable. */
|
|
32
|
+
readonly via: 'function' | 'const' | 'class' | 'declared';
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Component name → where it is declared.
|
|
36
|
+
*
|
|
37
|
+
* A name may map to several files. That is not an error to be resolved by
|
|
38
|
+
* picking one: two components genuinely can share a name, and silently choosing
|
|
39
|
+
* the first would send an agent to edit the wrong file with full confidence.
|
|
40
|
+
* Ambiguity is reported (ADR-0003's rule for overlapping projections, applied to
|
|
41
|
+
* source).
|
|
42
|
+
*/
|
|
43
|
+
export type SourceIndex = Readonly<Record<string, readonly SourceRef[]>>;
|
|
44
|
+
export interface Resolution {
|
|
45
|
+
readonly name: string;
|
|
46
|
+
readonly refs: readonly SourceRef[];
|
|
47
|
+
/** `true` when more than one file declares this name. */
|
|
48
|
+
readonly ambiguous: boolean;
|
|
49
|
+
}
|
|
50
|
+
export declare function resolveSource(name: string, index: SourceIndex): Resolution | null;
|
|
51
|
+
/**
|
|
52
|
+
* `file:line`, or a disambiguating list when a name is declared more than once.
|
|
53
|
+
*
|
|
54
|
+
* Formatted as `path:line` because that is the form an editor, a terminal, and
|
|
55
|
+
* every agent harness already know how to open.
|
|
56
|
+
*/
|
|
57
|
+
export declare function formatSource(resolution: Resolution): string;
|
|
58
|
+
/**
|
|
59
|
+
* Extract component declarations from one file's source.
|
|
60
|
+
*
|
|
61
|
+
* A regex scan, and the limits are worth stating rather than discovering. It
|
|
62
|
+
* finds exported and local declarations in the three shapes React components are
|
|
63
|
+
* written in, and it will miss a component produced by a factory, assigned
|
|
64
|
+
* dynamically, or re-exported under another name. It can also match a function
|
|
65
|
+
* that merely looks like a component — capitalised, declared at top level — and
|
|
66
|
+
* name a non-component in a report.
|
|
67
|
+
*
|
|
68
|
+
* Both failures are survivable in a way a wrong file path would not be: a miss
|
|
69
|
+
* degrades the report to the component name, which is what it said before, and a
|
|
70
|
+
* false positive can only appear if attribution already named that identifier.
|
|
71
|
+
* Parsing properly is the right answer eventually; it is not worth a parser
|
|
72
|
+
* dependency to find out whether the link is useful.
|
|
73
|
+
*/
|
|
74
|
+
export declare function indexSource(file: string, contents: string): SourceIndex;
|
|
75
|
+
/** Merge per-file indexes. A name declared in several files keeps every ref. */
|
|
76
|
+
export declare function mergeSourceIndexes(indexes: readonly SourceIndex[]): SourceIndex;
|
|
77
|
+
//# sourceMappingURL=source.d.ts.map
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connecting a change to a file.
|
|
3
|
+
*
|
|
4
|
+
* A report that says `Button` is one step short of useful. An agent asked to fix
|
|
5
|
+
* it still has to find `Button`, and a reviewer still has to guess whether the
|
|
6
|
+
* `Button` in question is the design-system one or the local one in checkout.
|
|
7
|
+
* The last hop — **which file** — is what turns a finding into an edit.
|
|
8
|
+
*
|
|
9
|
+
* There are two mechanisms, and this file is the second one.
|
|
10
|
+
*
|
|
11
|
+
* The first is exact. Spec §6.1 assumes the build tells us: every JSX transform
|
|
12
|
+
* already computes each element's file, line and column, and
|
|
13
|
+
* `@variance-authority/jsx-source` is the `jsxImportSource` setting that keeps
|
|
14
|
+
* that location as far as the fiber — React 19 drops it otherwise, on every path.
|
|
15
|
+
* When it is on, a finding names the element's own line and this index is not
|
|
16
|
+
* consulted.
|
|
17
|
+
*
|
|
18
|
+
* The second is this one, and it is what a repository that has changed nothing
|
|
19
|
+
* gets. Attribution names *components*, so a **component → file** index answers
|
|
20
|
+
* the question attribution asks. It is built by reading source rather than by
|
|
21
|
+
* configuring a build, and it answers with where a component is *declared* —
|
|
22
|
+
* coarser than a call site, and enough to open the right file.
|
|
23
|
+
*
|
|
24
|
+
* The index is plain data. `core` performs no I/O (ADR-0006), so building one is
|
|
25
|
+
* a caller's job; resolving against one is here.
|
|
26
|
+
*/
|
|
27
|
+
export function resolveSource(name, index) {
|
|
28
|
+
const refs = index[name];
|
|
29
|
+
if (refs === undefined || refs.length === 0)
|
|
30
|
+
return null;
|
|
31
|
+
return { name, refs, ambiguous: refs.length > 1 };
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* `file:line`, or a disambiguating list when a name is declared more than once.
|
|
35
|
+
*
|
|
36
|
+
* Formatted as `path:line` because that is the form an editor, a terminal, and
|
|
37
|
+
* every agent harness already know how to open.
|
|
38
|
+
*/
|
|
39
|
+
export function formatSource(resolution) {
|
|
40
|
+
const [first] = resolution.refs;
|
|
41
|
+
if (first === undefined)
|
|
42
|
+
return resolution.name;
|
|
43
|
+
const primary = `${first.file}:${first.line}`;
|
|
44
|
+
if (!resolution.ambiguous)
|
|
45
|
+
return primary;
|
|
46
|
+
const others = resolution.refs.slice(1).map((ref) => `${ref.file}:${ref.line}`);
|
|
47
|
+
return `${primary} (ambiguous — also ${others.join(', ')})`;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Extract component declarations from one file's source.
|
|
51
|
+
*
|
|
52
|
+
* A regex scan, and the limits are worth stating rather than discovering. It
|
|
53
|
+
* finds exported and local declarations in the three shapes React components are
|
|
54
|
+
* written in, and it will miss a component produced by a factory, assigned
|
|
55
|
+
* dynamically, or re-exported under another name. It can also match a function
|
|
56
|
+
* that merely looks like a component — capitalised, declared at top level — and
|
|
57
|
+
* name a non-component in a report.
|
|
58
|
+
*
|
|
59
|
+
* Both failures are survivable in a way a wrong file path would not be: a miss
|
|
60
|
+
* degrades the report to the component name, which is what it said before, and a
|
|
61
|
+
* false positive can only appear if attribution already named that identifier.
|
|
62
|
+
* Parsing properly is the right answer eventually; it is not worth a parser
|
|
63
|
+
* dependency to find out whether the link is useful.
|
|
64
|
+
*/
|
|
65
|
+
export function indexSource(file, contents) {
|
|
66
|
+
const found = {};
|
|
67
|
+
const lines = contents.split('\n');
|
|
68
|
+
const patterns = [
|
|
69
|
+
{ re: /^\s*(?:export\s+)?(?:default\s+)?function\s+([A-Z][A-Za-z0-9_]*)/, via: 'function' },
|
|
70
|
+
{ re: /^\s*(?:export\s+)?(?:const|let)\s+([A-Z][A-Za-z0-9_]*)\s*[:=]/, via: 'const' },
|
|
71
|
+
{ re: /^\s*(?:export\s+)?(?:default\s+)?class\s+([A-Z][A-Za-z0-9_]*)/, via: 'class' },
|
|
72
|
+
];
|
|
73
|
+
for (const [index, line] of lines.entries()) {
|
|
74
|
+
for (const { re, via } of patterns) {
|
|
75
|
+
const match = re.exec(line);
|
|
76
|
+
if (!match)
|
|
77
|
+
continue;
|
|
78
|
+
const name = match[1];
|
|
79
|
+
(found[name] ??= []).push({ file, line: index + 1, via });
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return found;
|
|
84
|
+
}
|
|
85
|
+
/** Merge per-file indexes. A name declared in several files keeps every ref. */
|
|
86
|
+
export function mergeSourceIndexes(indexes) {
|
|
87
|
+
const merged = {};
|
|
88
|
+
for (const index of indexes) {
|
|
89
|
+
for (const [name, refs] of Object.entries(index)) {
|
|
90
|
+
(merged[name] ??= []).push(...refs);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return merged;
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source.js","sourceRoot":"","sources":["../../src/attribute/source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AA4BH,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,KAAkB;IAC5D,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;IACzB,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEzD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;AACpD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,UAAsB;IACjD,MAAM,CAAC,KAAK,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC;IAChC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,UAAU,CAAC,IAAI,CAAC;IAEhD,MAAM,OAAO,GAAG,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;IAC9C,IAAI,CAAC,UAAU,CAAC,SAAS;QAAE,OAAO,OAAO,CAAC;IAE1C,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IAChF,OAAO,GAAG,OAAO,sBAAsB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;AAC9D,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,QAAgB;IACxD,MAAM,KAAK,GAAgC,EAAE,CAAC;IAC9C,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAEnC,MAAM,QAAQ,GAAuE;QACnF,EAAE,EAAE,EAAE,kEAAkE,EAAE,GAAG,EAAE,UAAU,EAAE;QAC3F,EAAE,EAAE,EAAE,+DAA+D,EAAE,GAAG,EAAE,OAAO,EAAE;QACrF,EAAE,EAAE,EAAE,+DAA+D,EAAE,GAAG,EAAE,OAAO,EAAE;KACtF,CAAC;IAEF,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;QAC5C,KAAK,MAAM,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,QAAQ,EAAE,CAAC;YACnC,MAAM,KAAK,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC5B,IAAI,CAAC,KAAK;gBAAE,SAAS;YAErB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;YACvB,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;YAC1D,MAAM;QACR,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,kBAAkB,CAAC,OAA+B;IAChE,MAAM,MAAM,GAAgC,EAAE,CAAC;IAE/C,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACjD,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["/**\n * Connecting a change to a file.\n *\n * A report that says `Button` is one step short of useful. An agent asked to fix\n * it still has to find `Button`, and a reviewer still has to guess whether the\n * `Button` in question is the design-system one or the local one in checkout.\n * The last hop — **which file** — is what turns a finding into an edit.\n *\n * There are two mechanisms, and this file is the second one.\n *\n * The first is exact. Spec §6.1 assumes the build tells us: every JSX transform\n * already computes each element's file, line and column, and\n * `@variance-authority/jsx-source` is the `jsxImportSource` setting that keeps\n * that location as far as the fiber — React 19 drops it otherwise, on every path.\n * When it is on, a finding names the element's own line and this index is not\n * consulted.\n *\n * The second is this one, and it is what a repository that has changed nothing\n * gets. Attribution names *components*, so a **component → file** index answers\n * the question attribution asks. It is built by reading source rather than by\n * configuring a build, and it answers with where a component is *declared* —\n * coarser than a call site, and enough to open the right file.\n *\n * The index is plain data. `core` performs no I/O (ADR-0006), so building one is\n * a caller's job; resolving against one is here.\n */\n\nexport interface SourceRef {\n /** Repository-relative, so a report is portable between machines and CI. */\n readonly file: string;\n readonly line: number;\n /** How the declaration was recognised. Carried so a bad match is debuggable. */\n readonly via: 'function' | 'const' | 'class' | 'declared';\n}\n\n/**\n * Component name → where it is declared.\n *\n * A name may map to several files. That is not an error to be resolved by\n * picking one: two components genuinely can share a name, and silently choosing\n * the first would send an agent to edit the wrong file with full confidence.\n * Ambiguity is reported (ADR-0003's rule for overlapping projections, applied to\n * source).\n */\nexport type SourceIndex = Readonly<Record<string, readonly SourceRef[]>>;\n\nexport interface Resolution {\n readonly name: string;\n readonly refs: readonly SourceRef[];\n /** `true` when more than one file declares this name. */\n readonly ambiguous: boolean;\n}\n\nexport function resolveSource(name: string, index: SourceIndex): Resolution | null {\n const refs = index[name];\n if (refs === undefined || refs.length === 0) return null;\n\n return { name, refs, ambiguous: refs.length > 1 };\n}\n\n/**\n * `file:line`, or a disambiguating list when a name is declared more than once.\n *\n * Formatted as `path:line` because that is the form an editor, a terminal, and\n * every agent harness already know how to open.\n */\nexport function formatSource(resolution: Resolution): string {\n const [first] = resolution.refs;\n if (first === undefined) return resolution.name;\n\n const primary = `${first.file}:${first.line}`;\n if (!resolution.ambiguous) return primary;\n\n const others = resolution.refs.slice(1).map((ref) => `${ref.file}:${ref.line}`);\n return `${primary} (ambiguous — also ${others.join(', ')})`;\n}\n\n/**\n * Extract component declarations from one file's source.\n *\n * A regex scan, and the limits are worth stating rather than discovering. It\n * finds exported and local declarations in the three shapes React components are\n * written in, and it will miss a component produced by a factory, assigned\n * dynamically, or re-exported under another name. It can also match a function\n * that merely looks like a component — capitalised, declared at top level — and\n * name a non-component in a report.\n *\n * Both failures are survivable in a way a wrong file path would not be: a miss\n * degrades the report to the component name, which is what it said before, and a\n * false positive can only appear if attribution already named that identifier.\n * Parsing properly is the right answer eventually; it is not worth a parser\n * dependency to find out whether the link is useful.\n */\nexport function indexSource(file: string, contents: string): SourceIndex {\n const found: Record<string, SourceRef[]> = {};\n const lines = contents.split('\\n');\n\n const patterns: readonly { readonly re: RegExp; readonly via: SourceRef['via'] }[] = [\n { re: /^\\s*(?:export\\s+)?(?:default\\s+)?function\\s+([A-Z][A-Za-z0-9_]*)/, via: 'function' },\n { re: /^\\s*(?:export\\s+)?(?:const|let)\\s+([A-Z][A-Za-z0-9_]*)\\s*[:=]/, via: 'const' },\n { re: /^\\s*(?:export\\s+)?(?:default\\s+)?class\\s+([A-Z][A-Za-z0-9_]*)/, via: 'class' },\n ];\n\n for (const [index, line] of lines.entries()) {\n for (const { re, via } of patterns) {\n const match = re.exec(line);\n if (!match) continue;\n\n const name = match[1]!;\n (found[name] ??= []).push({ file, line: index + 1, via });\n break;\n }\n }\n\n return found;\n}\n\n/** Merge per-file indexes. A name declared in several files keeps every ref. */\nexport function mergeSourceIndexes(indexes: readonly SourceIndex[]): SourceIndex {\n const merged: Record<string, SourceRef[]> = {};\n\n for (const index of indexes) {\n for (const [name, refs] of Object.entries(index)) {\n (merged[name] ??= []).push(...refs);\n }\n }\n\n return merged;\n}\n"]}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { SourceLocation, StackFrame } from '../format/provenance.js';
|
|
2
|
+
import type { OriginalPosition } from './source-map.js';
|
|
3
|
+
/**
|
|
4
|
+
* Reading a call site out of a stack, and deciding which frame is the author.
|
|
5
|
+
*
|
|
6
|
+
* React 19 constructs an `Error` inside its own `jsx`/`jsxDEV` and keeps it on
|
|
7
|
+
* every fiber. That error is a complete answer to "which line wrote this
|
|
8
|
+
* element" that no build has to opt into: the runtime that captured it is frame
|
|
9
|
+
* zero, and whoever called it is frame one. Nothing is configured, nothing is
|
|
10
|
+
* installed, and the location is present in any build where React is the
|
|
11
|
+
* development build — which is every dev server, every Vitest run and every Jest
|
|
12
|
+
* run, because all three are what "not production" means.
|
|
13
|
+
*
|
|
14
|
+
* Two jobs live here and they are separable on purpose. Parsing a stack is a
|
|
15
|
+
* fact about a JavaScript engine. Choosing *which* frame is the author is a
|
|
16
|
+
* policy, and it is the part with a defensible rule rather than a heuristic:
|
|
17
|
+
* the author is the first frame that maps back to code the project wrote. A
|
|
18
|
+
* custom JSX runtime — Emotion, theme-ui — sits between React and the author and
|
|
19
|
+
* is skipped not because it is on a list but because it resolves into
|
|
20
|
+
* `node_modules`.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* A frame is a wire value, not a local one: the page reads it, the collector
|
|
24
|
+
* resolves it, and it travels between them on `Provenance`. It is declared where
|
|
25
|
+
* the rest of provenance is declared, and re-exported here because this is the
|
|
26
|
+
* group that does something with it.
|
|
27
|
+
*/
|
|
28
|
+
export type { StackFrame };
|
|
29
|
+
/**
|
|
30
|
+
* The frames of a stack, in order, outermost call last.
|
|
31
|
+
*
|
|
32
|
+
* The message line is dropped, and so is any line neither engine format
|
|
33
|
+
* recognises — a stack is diagnostic output rather than a data format, and a
|
|
34
|
+
* frame that cannot be read is one fewer candidate rather than a failure.
|
|
35
|
+
*
|
|
36
|
+
* `eval` frames are deliberately not unwrapped. V8 writes them as
|
|
37
|
+
* `at eval (eval at fn (http://host/a.js:1:1), <anonymous>:2:3)`, where two
|
|
38
|
+
* positions are present and the inner one is meaningless outside the eval.
|
|
39
|
+
* Taking the outer one would name the line that *called* `eval` as the line that
|
|
40
|
+
* wrote the element, which is a confident wrong answer.
|
|
41
|
+
*/
|
|
42
|
+
export declare function parseStackFrames(stack: string): StackFrame[];
|
|
43
|
+
/**
|
|
44
|
+
* Whether a path belongs to a dependency rather than to the project.
|
|
45
|
+
*
|
|
46
|
+
* Three spellings of the same fact, because three build tools spell it
|
|
47
|
+
* differently: the directory itself, Vite's prebundled dependency cache, and the
|
|
48
|
+
* `webpack-internal:` scheme wrapping the same directory. The test runs against
|
|
49
|
+
* both the served URL — which saves fetching a map for a megabyte of vendor
|
|
50
|
+
* code — and against the path the map resolves to, which is what actually
|
|
51
|
+
* decides it.
|
|
52
|
+
*
|
|
53
|
+
* A dependency that *is* the subject cannot be attributed by this, and that is
|
|
54
|
+
* the intended reading: a project reviewing its own components wants the line in
|
|
55
|
+
* its own repository, and the line inside `react-dom` that rendered it is never
|
|
56
|
+
* the answer to "what changed".
|
|
57
|
+
*/
|
|
58
|
+
export declare function isVendorPath(path: string): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* The location that wrote this element, chosen from its stack.
|
|
61
|
+
*
|
|
62
|
+
* The rule is one sentence: **the first frame that resolves to a file the
|
|
63
|
+
* project wrote.** Frame zero is always React's own runtime, because React is
|
|
64
|
+
* what constructed the error. Frame one is the author in an ordinary build and
|
|
65
|
+
* is a custom JSX runtime in a build that has one — and this does not need to
|
|
66
|
+
* know which, because Emotion resolves into `node_modules` and the component
|
|
67
|
+
* does not.
|
|
68
|
+
*
|
|
69
|
+
* That is the same answer the recorded-symbol path gives, arrived at from the
|
|
70
|
+
* other side. Where `jsx-source` is installed it wins, because it is exact
|
|
71
|
+
* without a map; this is what a project that installed nothing still gets.
|
|
72
|
+
*
|
|
73
|
+
* `originalFor` is supplied rather than performed because resolving a frame
|
|
74
|
+
* means fetching the module the browser was served, and this package may not
|
|
75
|
+
* assume a network (ADR-0013). The caller owns the fetch, the cache, and the
|
|
76
|
+
* decision about how long to wait; this owns which frame to ask about and when
|
|
77
|
+
* to stop asking.
|
|
78
|
+
*/
|
|
79
|
+
export declare function writerLocationOf(frames: readonly StackFrame[], originalFor: (frame: StackFrame) => OriginalPosition | null): SourceLocation | null;
|
|
80
|
+
//# sourceMappingURL=stack.d.ts.map
|