@cplieger/deadset-ts 7.2.0-dev.3 → 8.0.0-dev.1
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/dist/bin/mapper-connection.js +7 -4
- package/dist/src/analysis.js +116 -16
- package/dist/src/augmentations.js +21 -8
- package/dist/src/automatic-types.js +189 -0
- package/dist/src/base-url.js +18 -0
- package/dist/src/component-files.js +96 -31
- package/dist/src/components.js +7 -6
- package/dist/src/config.js +1 -0
- package/dist/src/configuration-files.js +54 -15
- package/dist/src/configured-declarations.js +24 -2
- package/dist/src/conformance-record.js +2 -2
- package/dist/src/contextual-uses.js +26 -26
- package/dist/src/convention-rows.js +75 -0
- package/dist/src/conventions.js +101 -5
- package/dist/src/decorator.js +21 -18
- package/dist/src/dependencies.js +185 -30
- package/dist/src/destructuring.js +77 -2
- package/dist/src/discover.js +188 -85
- package/dist/src/edges.js +1 -1
- package/dist/src/entry-point-gaps.js +7 -10
- package/dist/src/entry-points.js +112 -11
- package/dist/src/findings/completion.js +116 -21
- package/dist/src/findings/dependencies-and-module-machinery.js +71 -25
- package/dist/src/findings/emitters.js +1 -1
- package/dist/src/findings/intra-function.js +44 -13
- package/dist/src/findings/non-code-artifacts.js +66 -33
- package/dist/src/findings/unused-declarations.js +21 -1
- package/dist/src/findings/visibility-narrowing.js +22 -3
- package/dist/src/generated-file.js +11 -0
- package/dist/src/git-ignore.js +86 -0
- package/dist/src/graph.js +4 -1
- package/dist/src/import-bindings.js +97 -0
- package/dist/src/indexed-access.js +130 -28
- package/dist/src/interface-satisfaction.js +30 -12
- package/dist/src/intra-function-parts.js +313 -50
- package/dist/src/inventory.js +63 -8
- package/dist/src/json-document.js +5 -5
- package/dist/src/kinds.js +13 -13
- package/dist/src/matrix.js +18 -9
- package/dist/src/mdx-entries.js +197 -0
- package/dist/src/module-declarations.js +9 -13
- package/dist/src/namespace-objects.js +28 -18
- package/dist/src/overrides.js +45 -9
- package/dist/src/paths.js +9 -0
- package/dist/src/print.js +1 -0
- package/dist/src/ref.js +10 -0
- package/dist/src/references.js +29 -13
- package/dist/src/reflective-lookup.js +8 -6
- package/dist/src/report.js +4 -2
- package/dist/src/reporters/template.js +22 -13
- package/dist/src/resolve.js +2 -0
- package/dist/src/roots.js +110 -38
- package/dist/src/schema.js +1 -0
- package/dist/src/serialization-contract.js +97 -20
- package/dist/src/session.js +26 -1
- package/dist/src/source-files.js +39 -14
- package/dist/src/spelled-names.js +58 -0
- package/dist/src/suppress-file.js +12 -4
- package/dist/src/suppress-inline.js +21 -3
- package/dist/src/suppress.js +7 -5
- package/dist/src/sweep.js +27 -10
- package/dist/src/symbol-keys.js +103 -2
- package/dist/src/template-field.js +9 -6
- package/dist/src/test-support.js +121 -17
- package/dist/src/type-errors.js +116 -35
- package/dist/src/type-parameter-reads.js +172 -0
- package/dist/src/value-reach.js +17 -5
- package/dist/src/verbs/analyze.js +19 -1
- package/dist/src/verbs/explain.js +4 -2
- package/dist/src/version-range.js +10 -5
- package/dist/src/version.js +1 -1
- package/dist/src/workflow-steps.js +119 -22
- package/dist/src/workspace-programs.js +7 -3
- package/dist/src/workspace-resolution.js +72 -13
- package/dist/src/workspace.js +1 -1
- package/jsr.json +1 -1
- package/package.json +2 -2
- package/src/analysis.ts +175 -16
- package/src/augmentations.ts +21 -10
- package/src/automatic-types.ts +246 -0
- package/src/base-url.ts +24 -0
- package/src/component-files.ts +116 -31
- package/src/components.ts +7 -6
- package/src/config.ts +3 -0
- package/src/configuration-files.ts +80 -18
- package/src/configured-declarations.ts +33 -4
- package/src/conformance-record.ts +2 -2
- package/src/contextual-uses.ts +27 -27
- package/src/convention-rows.ts +103 -0
- package/src/conventions.ts +121 -7
- package/src/decorator.ts +28 -18
- package/src/dependencies.ts +249 -43
- package/src/destructuring.ts +93 -2
- package/src/discover.ts +234 -86
- package/src/edges.ts +1 -1
- package/src/entry-point-gaps.ts +8 -11
- package/src/entry-points.ts +133 -11
- package/src/finding.ts +10 -0
- package/src/findings/completion.ts +147 -24
- package/src/findings/dependencies-and-module-machinery.ts +90 -28
- package/src/findings/emitters.ts +1 -1
- package/src/findings/intra-function.ts +56 -14
- package/src/findings/non-code-artifacts.ts +81 -41
- package/src/findings/unused-declarations.ts +25 -1
- package/src/findings/visibility-narrowing.ts +32 -3
- package/src/generated-file.ts +16 -0
- package/src/git-ignore.ts +98 -0
- package/src/graph.ts +6 -0
- package/src/host.ts +5 -9
- package/src/import-bindings.ts +128 -0
- package/src/indexed-access.ts +152 -30
- package/src/interface-satisfaction.ts +33 -13
- package/src/intra-function-parts.ts +415 -64
- package/src/inventory.ts +83 -7
- package/src/json-document.ts +7 -7
- package/src/kinds.ts +13 -13
- package/src/matrix.ts +32 -14
- package/src/mdx-entries.ts +241 -0
- package/src/module-declarations.ts +11 -13
- package/src/namespace-objects.ts +34 -21
- package/src/overrides.ts +56 -9
- package/src/paths.ts +10 -0
- package/src/print.ts +1 -0
- package/src/ref.ts +12 -0
- package/src/references.ts +38 -17
- package/src/reflective-lookup.ts +9 -7
- package/src/report.ts +16 -3
- package/src/reporters/template.ts +21 -13
- package/src/resolve.ts +3 -0
- package/src/roots.ts +163 -45
- package/src/schema.ts +1 -0
- package/src/serialization-contract.ts +108 -17
- package/src/session.ts +29 -1
- package/src/source-files.ts +66 -16
- package/src/spelled-names.ts +102 -0
- package/src/suppress-file.ts +14 -3
- package/src/suppress-inline.ts +21 -2
- package/src/suppress.ts +9 -5
- package/src/sweep.ts +32 -11
- package/src/symbol-keys.ts +129 -2
- package/src/template-field.ts +9 -6
- package/src/test-support.ts +149 -16
- package/src/type-errors.ts +132 -41
- package/src/type-parameter-reads.ts +227 -0
- package/src/value-reach.ts +19 -6
- package/src/verbs/analyze.ts +28 -1
- package/src/verbs/explain.ts +5 -2
- package/src/version-range.ts +10 -5
- package/src/version.ts +1 -1
- package/src/workflow-steps.ts +127 -22
- package/src/workspace-programs.ts +7 -3
- package/src/workspace-resolution.ts +86 -14
- package/src/workspace.ts +1 -1
|
@@ -109,17 +109,17 @@ function scriptTagAt(text, offset) {
|
|
|
109
109
|
/^<script[\s/>]/iu.test(text.slice(offset, offset + 8)));
|
|
110
110
|
}
|
|
111
111
|
/**
|
|
112
|
-
* The
|
|
113
|
-
*
|
|
114
|
-
*
|
|
112
|
+
* The braces opening at `from`, quoted strings skipped. The scan stops unclosed at the
|
|
113
|
+
* end, or before a `<script` start tag that begins a line, so a stray brace in markup
|
|
114
|
+
* never hides a block.
|
|
115
115
|
*/
|
|
116
|
-
function
|
|
116
|
+
function scanBraces(text, from) {
|
|
117
117
|
let depth = 0;
|
|
118
118
|
let at = from;
|
|
119
119
|
while (at < text.length) {
|
|
120
120
|
const code = text.charCodeAt(at);
|
|
121
121
|
if (scriptTagAt(text, at)) {
|
|
122
|
-
return
|
|
122
|
+
return { end: at, closed: false };
|
|
123
123
|
}
|
|
124
124
|
if (code === 0x22 || code === 0x27 || code === 0x60) {
|
|
125
125
|
at = pastQuoted(text, at);
|
|
@@ -131,24 +131,84 @@ function pastBraces(text, from) {
|
|
|
131
131
|
else if (code === 0x7d) {
|
|
132
132
|
depth -= 1;
|
|
133
133
|
if (depth === 0) {
|
|
134
|
-
return at + 1;
|
|
134
|
+
return { end: at + 1, closed: true };
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
at += 1;
|
|
138
138
|
}
|
|
139
|
-
return
|
|
139
|
+
return { end: text.length, closed: false };
|
|
140
|
+
}
|
|
141
|
+
/** The offset past the `}` matching the `{` at `from`, or -1 where {@link scanBraces} finds none. */
|
|
142
|
+
function pastBraces(text, from) {
|
|
143
|
+
const braces = scanBraces(text, from);
|
|
144
|
+
return braces.closed ? braces.end : -1;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* The dialect each framework's parser reads: Vue's tokenizer, Svelte's parser and Astro's
|
|
148
|
+
* tokenizer, the last for any other extension too. `.vue` also closes `--!>`, which Vue
|
|
149
|
+
* does not, so a block Vue drops there is still read.
|
|
150
|
+
* https://github.com/vuejs/core/blob/main/packages/compiler-core/src/tokenizer.ts
|
|
151
|
+
* https://github.com/sveltejs/svelte/blob/main/packages/svelte/src/compiler/phases/1-parse/state/element.js
|
|
152
|
+
* https://github.com/withastro/compiler/blob/main/internal/token.go
|
|
153
|
+
*/
|
|
154
|
+
function dialectOf(extension) {
|
|
155
|
+
switch (extension.toLowerCase()) {
|
|
156
|
+
case ".svelte":
|
|
157
|
+
return { shortComments: false, braceIsText: false };
|
|
158
|
+
case ".vue":
|
|
159
|
+
return { shortComments: true, braceIsText: true };
|
|
160
|
+
default:
|
|
161
|
+
return { shortComments: true, braceIsText: false };
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/** The offset past the comment whose `<!--` is at `lt`, or the text's length where none ends it. */
|
|
165
|
+
function pastComment(text, lt, dialect) {
|
|
166
|
+
if (!dialect.shortComments) {
|
|
167
|
+
const close = text.indexOf("-->", lt + 4);
|
|
168
|
+
return close < 0 ? text.length : close + 3;
|
|
169
|
+
}
|
|
170
|
+
const dashes = text.indexOf("-->", lt + 2);
|
|
171
|
+
const bang = text.indexOf("--!>", lt + 2);
|
|
172
|
+
if (bang >= 0 && (dashes < 0 || bang < dashes)) {
|
|
173
|
+
return bang + 4;
|
|
174
|
+
}
|
|
175
|
+
return dashes < 0 ? text.length : dashes + 3;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Where an unmatched `{` of a start tag ends its braced value: at the first `>`, or where
|
|
179
|
+
* the brace scan stopped where that comes first.
|
|
180
|
+
*/
|
|
181
|
+
function unmatchedBraceEnd(text, from, braces) {
|
|
182
|
+
const close = text.indexOf(">", from);
|
|
183
|
+
return close >= 0 && close < braces.end ? close : braces.end;
|
|
140
184
|
}
|
|
141
|
-
/**
|
|
142
|
-
|
|
185
|
+
/**
|
|
186
|
+
* The start tag whose `<` is at `from`, to its `>`. A quoted or braced value may hold `>`.
|
|
187
|
+
* Past an unmatched `{`, the tag also ends before a `<script` start tag that begins a line.
|
|
188
|
+
*/
|
|
189
|
+
function readStartTag(text, from, nameStop, dialect) {
|
|
143
190
|
const name = text.slice(from + 1, nameStop).toLowerCase();
|
|
144
191
|
const attributes = [];
|
|
145
192
|
let at = nameStop;
|
|
146
193
|
let selfClosing = false;
|
|
194
|
+
let unmatched = false;
|
|
195
|
+
/** The braces opening at `at`, and whether they make a braced value rather than text. */
|
|
196
|
+
const bracesAt = () => {
|
|
197
|
+
if (text.charCodeAt(at) !== 0x7b) {
|
|
198
|
+
return undefined;
|
|
199
|
+
}
|
|
200
|
+
const braces = scanBraces(text, at);
|
|
201
|
+
return { braces, value: braces.closed || !dialect.braceIsText };
|
|
202
|
+
};
|
|
203
|
+
const bracedEnd = (braces) => braces.closed ? braces.end : unmatchedBraceEnd(text, at, braces);
|
|
147
204
|
while (at < text.length) {
|
|
148
205
|
const code = text.charCodeAt(at);
|
|
149
206
|
if (code === 0x3e) {
|
|
150
207
|
return { name, end: at + 1, selfClosing, attributes };
|
|
151
208
|
}
|
|
209
|
+
if (unmatched && scriptTagAt(text, at)) {
|
|
210
|
+
return { name, end: at, selfClosing, attributes };
|
|
211
|
+
}
|
|
152
212
|
if (isSpace(code)) {
|
|
153
213
|
at += 1;
|
|
154
214
|
continue;
|
|
@@ -160,12 +220,13 @@ function readStartTag(text, from, nameStop) {
|
|
|
160
220
|
}
|
|
161
221
|
selfClosing = false;
|
|
162
222
|
const start = at;
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
223
|
+
const bare = bracesAt();
|
|
224
|
+
unmatched ||= bare?.braces.closed === false;
|
|
225
|
+
if (bare?.value === true) {
|
|
226
|
+
at = bracedEnd(bare.braces);
|
|
166
227
|
attributes.push({
|
|
167
228
|
name: "",
|
|
168
|
-
value: text.slice(start + 1, at - 1),
|
|
229
|
+
value: text.slice(start + 1, bare.braces.closed ? at - 1 : at),
|
|
169
230
|
start,
|
|
170
231
|
end: at,
|
|
171
232
|
valueStart: start + 1,
|
|
@@ -191,17 +252,19 @@ function readStartTag(text, from, nameStop) {
|
|
|
191
252
|
at += 1;
|
|
192
253
|
}
|
|
193
254
|
const opening = text.charCodeAt(at);
|
|
255
|
+
const braced = bracesAt();
|
|
256
|
+
unmatched ||= braced?.braces.closed === false;
|
|
194
257
|
if (opening === 0x22 || opening === 0x27) {
|
|
195
|
-
|
|
258
|
+
// An attribute value has no escapes: a backslash before its quote is text.
|
|
259
|
+
const close = text.indexOf(text.charAt(at), at + 1);
|
|
196
260
|
valueStart = at + 1;
|
|
197
|
-
value = text.slice(at + 1,
|
|
198
|
-
at =
|
|
261
|
+
value = text.slice(at + 1, close < 0 ? text.length : close);
|
|
262
|
+
at = close < 0 ? text.length : close + 1;
|
|
199
263
|
}
|
|
200
|
-
else if (
|
|
201
|
-
const
|
|
202
|
-
const stop = past < 0 ? text.length : past;
|
|
264
|
+
else if (braced?.value === true) {
|
|
265
|
+
const stop = bracedEnd(braced.braces);
|
|
203
266
|
valueStart = at + 1;
|
|
204
|
-
value = text.slice(at + 1,
|
|
267
|
+
value = text.slice(at + 1, braced.braces.closed ? stop - 1 : stop);
|
|
205
268
|
at = stop;
|
|
206
269
|
}
|
|
207
270
|
else {
|
|
@@ -305,9 +368,11 @@ function scriptsInside(text, from, to) {
|
|
|
305
368
|
* The scan keeps a stack of open elements. A comment, a doctype or processing instruction and
|
|
306
369
|
* an expression in braces are text, and an end tag closes back to the nearest open element of
|
|
307
370
|
* its name. A raw-text element at the top level runs to its own end tag, and only a `script`
|
|
308
|
-
* element whose start tag begins a line at the top level is a block.
|
|
371
|
+
* element whose start tag begins a line at the top level is a block. The file's extension
|
|
372
|
+
* names the framework whose reading of a comment and of a stray brace applies.
|
|
309
373
|
*/
|
|
310
|
-
export function readComponent(text) {
|
|
374
|
+
export function readComponent(text, extension) {
|
|
375
|
+
const dialect = dialectOf(extension);
|
|
311
376
|
const blocks = [];
|
|
312
377
|
const imports = [];
|
|
313
378
|
const warnings = [];
|
|
@@ -337,8 +402,7 @@ export function readComponent(text) {
|
|
|
337
402
|
}
|
|
338
403
|
const lt = at;
|
|
339
404
|
if (text.startsWith("<!--", lt)) {
|
|
340
|
-
|
|
341
|
-
at = close < 0 ? text.length : close + 3;
|
|
405
|
+
at = pastComment(text, lt, dialect);
|
|
342
406
|
continue;
|
|
343
407
|
}
|
|
344
408
|
const next = text.charCodeAt(lt + 1);
|
|
@@ -362,7 +426,7 @@ export function readComponent(text) {
|
|
|
362
426
|
at = lt + 1;
|
|
363
427
|
continue;
|
|
364
428
|
}
|
|
365
|
-
const tag = readStartTag(text, lt, nameStop);
|
|
429
|
+
const tag = readStartTag(text, lt, nameStop, dialect);
|
|
366
430
|
const rawText = open.length === 0 && RAW_TEXT.has(tag.name);
|
|
367
431
|
for (const attribute of rawText ? [] : tag.attributes) {
|
|
368
432
|
if (attribute.value !== "") {
|
|
@@ -435,9 +499,9 @@ export function readComponent(text) {
|
|
|
435
499
|
}
|
|
436
500
|
}
|
|
437
501
|
/**
|
|
438
|
-
* Whether the module's text exports a default at its top level: an `export default
|
|
439
|
-
* or an export clause naming `default`, outside every comment,
|
|
440
|
-
* regular expression, and outside every bracket.
|
|
502
|
+
* Whether the module's text exports a default at its top level: an `export default`, an
|
|
503
|
+
* `export * as default`, or an export clause naming `default`, outside every comment,
|
|
504
|
+
* string, template and regular expression, and outside every bracket.
|
|
441
505
|
*/
|
|
442
506
|
export function exportsDefault(text) {
|
|
443
507
|
const words = [];
|
|
@@ -525,7 +589,8 @@ export function exportsDefault(text) {
|
|
|
525
589
|
const matched = word.exec(text);
|
|
526
590
|
if (matched !== null) {
|
|
527
591
|
const name = matched[0];
|
|
528
|
-
|
|
592
|
+
const starAs = words.at(-3) === "export" && words.at(-2) === "*" && words.at(-1) === "as";
|
|
593
|
+
if (depth === 0 && (words.at(-1) === "export" || starAs) && name === "default") {
|
|
529
594
|
return true;
|
|
530
595
|
}
|
|
531
596
|
if (clause && name === "default") {
|
|
@@ -554,8 +619,8 @@ export function exportsDefault(text) {
|
|
|
554
619
|
return false;
|
|
555
620
|
}
|
|
556
621
|
/** The module one component file's text is read as, and the span map back to the file. */
|
|
557
|
-
export function componentModule(text) {
|
|
558
|
-
const { blocks, imports } = readComponent(text);
|
|
622
|
+
export function componentModule(text, fileExtension) {
|
|
623
|
+
const { blocks, imports } = readComponent(text, fileExtension);
|
|
559
624
|
const out = [];
|
|
560
625
|
let at = 0;
|
|
561
626
|
let language;
|
package/dist/src/components.js
CHANGED
|
@@ -129,12 +129,12 @@ function linesSpanned(symbols) {
|
|
|
129
129
|
return total;
|
|
130
130
|
}
|
|
131
131
|
/**
|
|
132
|
-
* The dead declarations by subgraph place (`at`, and `position` per graph declaration),
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
* test
|
|
132
|
+
* The dead declarations by subgraph place (`at`, and `position` per graph declaration), a
|
|
133
|
+
* test file's only where it tests dead code or nothing the tests reach. `adjacent` holds each
|
|
134
|
+
* reference one makes to another, but an unreferenced test-file declaration's to production
|
|
135
|
+
* code, which decides nothing; an edge each way between a dead member and its dead container;
|
|
136
|
+
* and one back from each non-ambient production target of an admitted test, which puts the
|
|
137
|
+
* test in its subject's component. `references` holds the first alone.
|
|
138
138
|
*/
|
|
139
139
|
function deadSubgraph(graph, dead, testOfDeadCode, unreferencedTest) {
|
|
140
140
|
const at = [];
|
|
@@ -158,6 +158,7 @@ function deadSubgraph(graph, dead, testOfDeadCode, unreferencedTest) {
|
|
|
158
158
|
references[from]?.push(to);
|
|
159
159
|
if (testOfDeadCode[index] === true &&
|
|
160
160
|
graph.test[edge.to] !== true &&
|
|
161
|
+
graph.ambient[edge.to] !== true &&
|
|
161
162
|
graph.subject[edge.to] === true) {
|
|
162
163
|
adjacent[to]?.push(from);
|
|
163
164
|
}
|
package/dist/src/config.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* every string either holds as it likes, so each one that spells a declared dependency
|
|
7
7
|
* uses it and each relative one that names a source file of the program roots the file.
|
|
8
8
|
*/
|
|
9
|
-
import { isExportDeclaration, isImportDeclaration, isNoSubstitutionTemplateLiteral, isStringLiteral, } from "@typescript/native/ast";
|
|
9
|
+
import { isCallExpression, isExportDeclaration, isIdentifier, isImportDeclaration, isNoSubstitutionTemplateLiteral, isStringLiteral, SyntaxKind, } from "@typescript/native/ast";
|
|
10
10
|
import { dirnamePath, joinPath, relativePath, resolvePath } from "./paths.js";
|
|
11
11
|
const MANIFEST = "package.json";
|
|
12
12
|
/** A configuration module's name, each form with the spelling a root reports it by. */
|
|
@@ -270,8 +270,9 @@ export function configurationFiles(host, targetRoot, files, parse) {
|
|
|
270
270
|
const dirs = new Set([root]);
|
|
271
271
|
for (const file of files.byPath.values()) {
|
|
272
272
|
const form = configurationModuleForm(basename(file.fileName));
|
|
273
|
-
|
|
274
|
-
|
|
273
|
+
const fileDir = dirnamePath(file.fileName);
|
|
274
|
+
if (form !== undefined && holdsManifest(fileDir)) {
|
|
275
|
+
modules.push({ file, form, manifestDir: fileDir });
|
|
275
276
|
}
|
|
276
277
|
for (let dir = dirnamePath(file.fileName); relativePath(root, dir) !== undefined && !dirs.has(dir); dir = dirnamePath(dir)) {
|
|
277
278
|
dirs.add(dir);
|
|
@@ -301,7 +302,7 @@ export function configurationFiles(host, targetRoot, files, parse) {
|
|
|
301
302
|
pending.push({ path, manifestDir: dir });
|
|
302
303
|
}
|
|
303
304
|
else if (held !== undefined) {
|
|
304
|
-
modules.push({ file: held, form: "a script's configuration flag" });
|
|
305
|
+
modules.push({ file: held, form: "a script's configuration flag", manifestDir: dir });
|
|
305
306
|
}
|
|
306
307
|
else {
|
|
307
308
|
named.push({ path, manifestDir: dir });
|
|
@@ -345,18 +346,56 @@ export function configurationFiles(host, targetRoot, files, parse) {
|
|
|
345
346
|
: outsideModules(host, root, [...dirs], files, holdsManifest, parse, named),
|
|
346
347
|
};
|
|
347
348
|
}
|
|
348
|
-
/** The
|
|
349
|
-
function
|
|
350
|
-
|
|
351
|
-
|
|
349
|
+
/** The literal module specifier one `require(...)` or `import(...)` call names, if any. */
|
|
350
|
+
function calledSpecifier(node) {
|
|
351
|
+
if (!isCallExpression(node) || node.arguments.length !== 1) {
|
|
352
|
+
return undefined;
|
|
353
|
+
}
|
|
354
|
+
const callee = node.expression;
|
|
355
|
+
const loads = callee.kind === SyntaxKind.ImportKeyword || (isIdentifier(callee) && callee.text === "require");
|
|
356
|
+
return loads ? node.arguments[0] : undefined;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* The relative specifiers one file's imports and re-exports name, and those its
|
|
360
|
+
* `require(...)` and `import(...)` calls name by a literal.
|
|
361
|
+
*/
|
|
362
|
+
export function relativeSpecifiers(file) {
|
|
363
|
+
const known = specifiersOf.get(file);
|
|
364
|
+
if (known !== undefined) {
|
|
365
|
+
return known;
|
|
366
|
+
}
|
|
367
|
+
const found = [];
|
|
368
|
+
const named = (specifier) => {
|
|
369
|
+
if (specifier !== undefined &&
|
|
370
|
+
(isStringLiteral(specifier) || isNoSubstitutionTemplateLiteral(specifier)) &&
|
|
371
|
+
RELATIVE_PREFIXES.some((prefix) => specifier.text.startsWith(prefix))) {
|
|
372
|
+
found.push(specifier.text);
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
const visit = (node) => {
|
|
376
|
+
named(calledSpecifier(node));
|
|
377
|
+
node.forEachChild(visit);
|
|
378
|
+
};
|
|
379
|
+
for (const statement of file.statements) {
|
|
380
|
+
named(isImportDeclaration(statement) || isExportDeclaration(statement)
|
|
352
381
|
? statement.moduleSpecifier
|
|
353
|
-
: undefined;
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
382
|
+
: undefined);
|
|
383
|
+
visit(statement);
|
|
384
|
+
}
|
|
385
|
+
specifiersOf.set(file, found);
|
|
386
|
+
return found;
|
|
387
|
+
}
|
|
388
|
+
/** What {@link relativeSpecifiers} found in each file, which every project of a run reads alike. */
|
|
389
|
+
const specifiersOf = new WeakMap();
|
|
390
|
+
/**
|
|
391
|
+
* The files no program holds that a chain from `seeds` reads: each seed, and each file
|
|
392
|
+
* below the target root one imports, re-exports or loads by a relative specifier that no
|
|
393
|
+
* program holds either, until no further file joins, each read against the manifest
|
|
394
|
+
* directory its seed names. A file that cannot be read is skipped.
|
|
395
|
+
*/
|
|
396
|
+
export function outsideChain(host, targetRoot, files, parse, seeds) {
|
|
397
|
+
const root = resolvePath(host.workingDirectory(), targetRoot);
|
|
398
|
+
return outsideModules(host, root, [], files, () => false, parse, seeds);
|
|
360
399
|
}
|
|
361
400
|
/**
|
|
362
401
|
* The paths one relative specifier from a configuration file may reach, in the order they
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { isCallExpression, isExportDeclaration, isExternalModuleReference, isImportDeclaration, isImportEqualsDeclaration, isNoSubstitutionTemplateLiteral, isStringLiteral, SyntaxKind, } from "@typescript/native/ast";
|
|
8
8
|
import { SymbolFlags } from "@typescript/native/sync";
|
|
9
9
|
import { resolvedTargets } from "./calls.js";
|
|
10
|
+
import { declaredComponent } from "./inventory.js";
|
|
10
11
|
import { must, Unanswerable } from "./query.js";
|
|
11
12
|
import { nameComponent } from "./ref.js";
|
|
12
13
|
/** The meanings a name of the global scope is looked up under: any of them. */
|
|
@@ -80,9 +81,30 @@ function candidatesOf(project, container, isStatic) {
|
|
|
80
81
|
...exported.filter((member) => !isClass || !isClassMember(member)),
|
|
81
82
|
];
|
|
82
83
|
}
|
|
83
|
-
/**
|
|
84
|
+
/**
|
|
85
|
+
* The properties of the type one container declares, or of its constructor's type for a
|
|
86
|
+
* static member. A member a symbol keys is bound to that type later than the container's
|
|
87
|
+
* symbol table is built, so the table does not hold it.
|
|
88
|
+
*/
|
|
89
|
+
function typeMembersOf(project, container, isStatic) {
|
|
90
|
+
const queries = project.queries;
|
|
91
|
+
const type = must(isStatic ? queries.typeOfSymbol(container) : queries.declaredTypeOf(container));
|
|
92
|
+
return type === undefined ? [] : must(queries.propertiesOf(type));
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* The member of one container a path component spells, followed through any alias. A
|
|
96
|
+
* component in brackets is a computed key, compared with the key each declaration of a
|
|
97
|
+
* candidate writes: the checker names a member a symbol keys by no spelling of its own.
|
|
98
|
+
*/
|
|
84
99
|
function memberOf(project, container, component, isStatic) {
|
|
85
|
-
const
|
|
100
|
+
const computed = component.startsWith("[");
|
|
101
|
+
const candidates = candidatesOf(project, container, isStatic);
|
|
102
|
+
const member = (computed ? [...candidates, ...typeMembersOf(project, container, isStatic)] : candidates).find((candidate) => nameComponent(candidate.name) === component ||
|
|
103
|
+
(computed &&
|
|
104
|
+
candidate.declarations.some((handle) => {
|
|
105
|
+
const node = project.declarationAt(handle)?.node;
|
|
106
|
+
return node !== undefined && declaredComponent(node) === component;
|
|
107
|
+
})));
|
|
86
108
|
return member === undefined ? undefined : unaliased(project, member);
|
|
87
109
|
}
|
|
88
110
|
/** The declaration a path names from one starting symbol, component by component. */
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
// Regenerate it with `UPDATE_GOLDEN=1 npx vitest --run src/conformance.corpus.test.ts`.
|
|
3
3
|
export const RECORD = {
|
|
4
4
|
conformance: {
|
|
5
|
-
corpusVersion: "
|
|
5
|
+
corpusVersion: "6.0.0",
|
|
6
6
|
result: "pass",
|
|
7
|
-
digest: "sha256:
|
|
7
|
+
digest: "sha256:76c07ac14bda6e302444918d832b01844bac6f29b6f22a742d538804480c0769",
|
|
8
8
|
},
|
|
9
9
|
gaps: [],
|
|
10
10
|
};
|
|
@@ -196,12 +196,13 @@ export function contextualUses(project, held, chains, literals, flows, cap, prop
|
|
|
196
196
|
};
|
|
197
197
|
const calls = new Map();
|
|
198
198
|
/**
|
|
199
|
-
* The parameter and return types of a type's
|
|
200
|
-
*
|
|
199
|
+
* The parameter and return types of each of a type's call signatures, or of each of its
|
|
200
|
+
* construct signatures where it has no call signature, read once per type.
|
|
201
201
|
*/
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
202
|
+
const callsOf = (type) => {
|
|
203
|
+
const known = calls.get(type.id);
|
|
204
|
+
if (known !== undefined) {
|
|
205
|
+
return known;
|
|
205
206
|
}
|
|
206
207
|
lookups += 1;
|
|
207
208
|
let kind = SignatureKind.Call;
|
|
@@ -211,28 +212,26 @@ export function contextualUses(project, held, chains, literals, flows, cap, prop
|
|
|
211
212
|
kind = SignatureKind.Construct;
|
|
212
213
|
signatures = queries.signaturesOf(type, kind);
|
|
213
214
|
}
|
|
214
|
-
|
|
215
|
-
const [signature, ...more] = isAnswered(signatures) ? signatures : [];
|
|
216
|
-
if (signature !== undefined && more.length === 0) {
|
|
215
|
+
const called = (isAnswered(signatures) ? signatures : []).map((signature) => {
|
|
217
216
|
const parameters = signature.parameters.map((_parameter, index) => queries.parameterType(signature, index));
|
|
218
217
|
const result = queries.returnTypeOf(signature);
|
|
219
218
|
lookups += parameters.length + 1;
|
|
220
|
-
|
|
219
|
+
return {
|
|
221
220
|
kind,
|
|
222
221
|
parameters: parameters.map((one) => (isAnswered(one) ? one : undefined)),
|
|
223
222
|
result: isAnswered(result) ? result : undefined,
|
|
224
223
|
};
|
|
225
|
-
}
|
|
224
|
+
});
|
|
226
225
|
calls.set(type.id, called);
|
|
227
226
|
return called;
|
|
228
227
|
};
|
|
229
228
|
/**
|
|
230
229
|
* Each object type a value of `source` is checked as against the object types of
|
|
231
|
-
* `target`: the value's own, and through array elements,
|
|
232
|
-
* checked the other way,
|
|
233
|
-
*
|
|
234
|
-
* A source that is one of the target's own object types matches
|
|
235
|
-
* pair is compared once, which ends the walk over a recursive type.
|
|
230
|
+
* `target`: the value's own, and through array elements, every pair of signatures
|
|
231
|
+
* (parameters checked the other way, then results), which reads more than the overload
|
|
232
|
+
* the checker picks, and the type arguments of two instantiations by place, as an array
|
|
233
|
+
* reaches `Iterable<T>`. A source that is one of the target's own object types matches
|
|
234
|
+
* it as it stands. Each pair is compared once, which ends the walk over a recursive type.
|
|
236
235
|
*/
|
|
237
236
|
const compared = (source, target, into, seen) => {
|
|
238
237
|
const key = `${String(source?.id)}>${String(target?.id)}`;
|
|
@@ -253,7 +252,7 @@ export function contextualUses(project, held, chains, literals, flows, cap, prop
|
|
|
253
252
|
into.push({ source: one, targets, outside: true });
|
|
254
253
|
}
|
|
255
254
|
const element = elementOf(one);
|
|
256
|
-
const called = element === undefined ?
|
|
255
|
+
const called = element === undefined ? callsOf(one) : [];
|
|
257
256
|
const sourceArguments = typeArgumentsOf(one);
|
|
258
257
|
for (const part of targets) {
|
|
259
258
|
const partElement = elementOf(part);
|
|
@@ -267,17 +266,17 @@ export function contextualUses(project, held, chains, literals, flows, cap, prop
|
|
|
267
266
|
sourceArguments.slice(0, targetArguments.length).forEach((argument, index) => {
|
|
268
267
|
compared(argument, targetArguments[index], into, seen);
|
|
269
268
|
});
|
|
270
|
-
if (element !== undefined) {
|
|
269
|
+
if (element !== undefined || called.length === 0) {
|
|
271
270
|
continue;
|
|
272
271
|
}
|
|
273
|
-
const other
|
|
274
|
-
|
|
275
|
-
|
|
272
|
+
for (const other of callsOf(part)) {
|
|
273
|
+
for (const signature of called.filter((one) => one.kind === other.kind)) {
|
|
274
|
+
other.parameters.forEach((parameter, index) => {
|
|
275
|
+
compared(parameter, signature.parameters[index], into, seen);
|
|
276
|
+
});
|
|
277
|
+
compared(signature.result, other.result, into, seen);
|
|
278
|
+
}
|
|
276
279
|
}
|
|
277
|
-
other.parameters.forEach((parameter, index) => {
|
|
278
|
-
compared(parameter, called.parameters[index], into, seen);
|
|
279
|
-
});
|
|
280
|
-
compared(called.result, other.result, into, seen);
|
|
281
280
|
}
|
|
282
281
|
}
|
|
283
282
|
};
|
|
@@ -298,7 +297,7 @@ export function contextualUses(project, held, chains, literals, flows, cap, prop
|
|
|
298
297
|
if (element !== undefined) {
|
|
299
298
|
return mayCarry(element, seen);
|
|
300
299
|
}
|
|
301
|
-
return
|
|
300
|
+
return callsOf(part).length > 0 || typeArgumentsOf(part).some((one) => mayCarry(one, seen));
|
|
302
301
|
});
|
|
303
302
|
};
|
|
304
303
|
const sourced = carried.flatMap((flow, index) => {
|
|
@@ -330,10 +329,11 @@ export function contextualUses(project, held, chains, literals, flows, cap, prop
|
|
|
330
329
|
continue;
|
|
331
330
|
}
|
|
332
331
|
for (const [name, member] of sourceMembers) {
|
|
332
|
+
// A member the position shares with the source is no read of it. A member with no
|
|
333
|
+
// declaration, as a mapped type over literal keys has, is never one it shares.
|
|
333
334
|
const read = tables.some((table) => {
|
|
334
335
|
const declared = table.get(name);
|
|
335
336
|
return (declared !== undefined &&
|
|
336
|
-
declared.handles.size > 0 &&
|
|
337
337
|
![...declared.handles].some((handle) => member.handles.has(handle)));
|
|
338
338
|
});
|
|
339
339
|
if (read) {
|
|
@@ -10,6 +10,8 @@ const SCRIPT = "{js,jsx,ts,tsx}";
|
|
|
10
10
|
const MODULE = "{js,mjs,ts,mts}";
|
|
11
11
|
const ANY_MODULE = "{js,mjs,cjs,ts,mts,cts}";
|
|
12
12
|
const VITE_CONFIG = `vite.config.${ANY_MODULE}`;
|
|
13
|
+
/** The legacy configuration files of ESLint that hold JSON or a CommonJS module. */
|
|
14
|
+
const ESLINTRC = [".eslintrc", ".eslintrc.json", ".eslintrc.{js,cjs}"];
|
|
13
15
|
const SVELTEKIT_ROUTE_FILES = "+{page,layout,server,page.server,layout.server,error}";
|
|
14
16
|
/** SvelteKit's entries other than its parameter matchers. */
|
|
15
17
|
const SVELTEKIT_ENTRIES = [
|
|
@@ -111,6 +113,25 @@ export const CONVENTION_ROWS = [
|
|
|
111
113
|
package: "@babel/runtime",
|
|
112
114
|
})),
|
|
113
115
|
},
|
|
116
|
+
{
|
|
117
|
+
name: "docusaurus",
|
|
118
|
+
package: "@docusaurus/core",
|
|
119
|
+
range: ">=2.0.0 <4.0.0",
|
|
120
|
+
entries: [`src/pages/**/*.${SCRIPT}`, `src/theme/**/*.${SCRIPT}`],
|
|
121
|
+
moves: [],
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: "eslint",
|
|
125
|
+
package: "eslint",
|
|
126
|
+
range: ">=4.0.0 <10.0.0",
|
|
127
|
+
entries: [],
|
|
128
|
+
moves: [],
|
|
129
|
+
prefixedNames: [
|
|
130
|
+
{ property: "plugins", files: ESLINTRC, prefix: "eslint-plugin" },
|
|
131
|
+
{ property: "extends", files: ESLINTRC, prefix: "eslint-plugin", marker: "plugin:" },
|
|
132
|
+
{ property: "extends", files: ESLINTRC, prefix: "eslint-config" },
|
|
133
|
+
],
|
|
134
|
+
},
|
|
114
135
|
{
|
|
115
136
|
name: "eslint",
|
|
116
137
|
package: "eslint",
|
|
@@ -118,6 +139,13 @@ export const CONVENTION_ROWS = [
|
|
|
118
139
|
entries: [`**/eslint.config.${ANY_MODULE}`],
|
|
119
140
|
moves: [],
|
|
120
141
|
},
|
|
142
|
+
{
|
|
143
|
+
name: "expo",
|
|
144
|
+
package: "expo",
|
|
145
|
+
range: ">=44.0.0 <60.0.0",
|
|
146
|
+
entries: [`{index,App}.${SCRIPT}`],
|
|
147
|
+
moves: [],
|
|
148
|
+
},
|
|
121
149
|
{
|
|
122
150
|
name: "expo-router",
|
|
123
151
|
package: "expo-router",
|
|
@@ -202,6 +230,28 @@ export const CONVENTION_ROWS = [
|
|
|
202
230
|
},
|
|
203
231
|
],
|
|
204
232
|
},
|
|
233
|
+
{
|
|
234
|
+
name: "oclif",
|
|
235
|
+
package: "@oclif/core",
|
|
236
|
+
range: ">=1.0.0 <5.0.0",
|
|
237
|
+
entries: [`src/{commands,hooks}/**/*.${SCRIPT}`],
|
|
238
|
+
moves: [],
|
|
239
|
+
manifestEntries: [
|
|
240
|
+
{ key: "oclif.commands", directory: true },
|
|
241
|
+
{ key: "oclif.hooks", directory: false },
|
|
242
|
+
],
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
name: "parcel",
|
|
246
|
+
package: "parcel",
|
|
247
|
+
range: ">=2.0.0 <3.0.0",
|
|
248
|
+
entries: [],
|
|
249
|
+
moves: [],
|
|
250
|
+
manifestEntries: [
|
|
251
|
+
{ key: "source", directory: false },
|
|
252
|
+
{ key: "targets.*.source", directory: false },
|
|
253
|
+
],
|
|
254
|
+
},
|
|
205
255
|
{
|
|
206
256
|
name: "prettier",
|
|
207
257
|
package: "prettier",
|
|
@@ -493,6 +543,31 @@ export const CONVENTION_ROWS = [
|
|
|
493
543
|
},
|
|
494
544
|
],
|
|
495
545
|
},
|
|
546
|
+
{
|
|
547
|
+
name: "tanstack-router",
|
|
548
|
+
package: "@tanstack/router-plugin",
|
|
549
|
+
range: ">=1.0.0 <2.0.0",
|
|
550
|
+
entries: [`<routes>/**/*.${SCRIPT}`],
|
|
551
|
+
moves: [
|
|
552
|
+
{
|
|
553
|
+
id: "routes",
|
|
554
|
+
defaults: ["src/routes"],
|
|
555
|
+
readings: [
|
|
556
|
+
{ property: "routesDirectory", files: ["tsr.config.json"] },
|
|
557
|
+
{
|
|
558
|
+
property: "routesDirectory",
|
|
559
|
+
files: [VITE_CONFIG],
|
|
560
|
+
call: { module: "@tanstack/router-plugin/vite", export: "tanstackRouter" },
|
|
561
|
+
},
|
|
562
|
+
{
|
|
563
|
+
property: "routesDirectory",
|
|
564
|
+
files: [VITE_CONFIG],
|
|
565
|
+
call: { module: "@tanstack/router-plugin/vite", export: "TanStackRouterVite" },
|
|
566
|
+
},
|
|
567
|
+
],
|
|
568
|
+
},
|
|
569
|
+
],
|
|
570
|
+
},
|
|
496
571
|
{
|
|
497
572
|
name: "vite",
|
|
498
573
|
package: "vite",
|