@cplieger/deadset-ts 8.0.0-dev.13 → 8.0.0-dev.15
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/src/alias-chain.js +2 -2
- package/dist/src/analysis.js +2 -1
- package/dist/src/conformance-record.js +1 -1
- package/dist/src/consumers.js +1 -1
- package/dist/src/contract/contract.generated.js +1 -0
- package/dist/src/dependencies.js +2 -1
- package/dist/src/entry-points.js +26 -95
- package/dist/src/file-facts.js +1 -1
- package/dist/src/graph.js +0 -1
- package/dist/src/inventory/commonjs.js +462 -0
- package/dist/src/inventory/enumerate.js +166 -0
- package/dist/src/inventory/syntax.js +236 -0
- package/dist/src/inventory.js +305 -705
- package/dist/src/load/discover.js +1 -1
- package/dist/src/load/package-scope.js +56 -0
- package/dist/src/own-body.js +28 -0
- package/dist/src/position.js +41 -0
- package/dist/src/ref.js +11 -20
- package/dist/src/references.js +91 -30
- package/dist/src/session.js +3 -0
- package/dist/src/suppress-inline.js +33 -81
- package/dist/src/unseen-loads.js +106 -17
- package/jsr.json +1 -1
- package/package.json +2 -2
- package/src/alias-chain.ts +2 -2
- package/src/analysis.ts +2 -1
- package/src/conformance-record.ts +1 -1
- package/src/consumers.ts +1 -1
- package/src/contract/contract.generated.ts +1 -0
- package/src/dependencies.ts +2 -1
- package/src/entry-points.ts +30 -107
- package/src/file-facts.ts +1 -1
- package/src/graph.ts +0 -1
- package/src/inventory/commonjs.ts +611 -0
- package/src/inventory/enumerate.ts +202 -0
- package/src/inventory/syntax.ts +311 -0
- package/src/inventory.ts +424 -878
- package/src/load/discover.ts +1 -1
- package/src/load/package-scope.ts +74 -0
- package/src/own-body.ts +42 -0
- package/src/position.ts +42 -0
- package/src/ref.ts +11 -20
- package/src/references.ts +116 -53
- package/src/session.ts +17 -1
- package/src/suppress-inline.ts +53 -89
- package/src/unseen-loads.ts +166 -18
package/src/suppress-inline.ts
CHANGED
|
@@ -5,18 +5,21 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { CODE } from "./contract/contract.generated.ts";
|
|
8
|
-
import {
|
|
9
|
-
isConstructorDeclaration,
|
|
10
|
-
SyntaxKind,
|
|
11
|
-
type Node,
|
|
12
|
-
type SourceFile,
|
|
13
|
-
} from "@typescript/native/ast";
|
|
8
|
+
import { SyntaxKind, type Node, type SourceFile } from "@typescript/native/ast";
|
|
14
9
|
import { importBindings } from "./import-bindings.ts";
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
10
|
+
import { nodeKey, type Inventory } from "./inventory.ts";
|
|
11
|
+
import { relativePath } from "./paths.ts";
|
|
12
|
+
import {
|
|
13
|
+
byPosition,
|
|
14
|
+
lineEnd,
|
|
15
|
+
positionKey,
|
|
16
|
+
renderPosition,
|
|
17
|
+
startLine,
|
|
18
|
+
tokenStart,
|
|
19
|
+
type Position,
|
|
20
|
+
} from "./position.ts";
|
|
17
21
|
import { importRef } from "./ref.ts";
|
|
18
22
|
import type { ProjectView } from "./session.ts";
|
|
19
|
-
import { signatureOf } from "./signatures.ts";
|
|
20
23
|
import {
|
|
21
24
|
isPartKind,
|
|
22
25
|
SuppressionError,
|
|
@@ -88,43 +91,6 @@ const LITERALS: ReadonlySet<SyntaxKind> = new Set([
|
|
|
88
91
|
SyntaxKind.JsxText,
|
|
89
92
|
]);
|
|
90
93
|
|
|
91
|
-
/** Whether one character ends a line, under the language's line terminators. */
|
|
92
|
-
function endsLine(char: string | undefined): boolean {
|
|
93
|
-
return char === "\n" || char === "\r" || char === "\u2028" || char === "\u2029";
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/** The offset where the line holding `from` ends. */
|
|
97
|
-
function lineEnd(text: string, from: number): number {
|
|
98
|
-
let at = from;
|
|
99
|
-
while (at < text.length && !endsLine(text[at])) {
|
|
100
|
-
at += 1;
|
|
101
|
-
}
|
|
102
|
-
return at;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* The offset of the first token at or after `from`: whitespace, comments and a leading
|
|
107
|
-
* hashbang are trivia.
|
|
108
|
-
*/
|
|
109
|
-
export function tokenStart(text: string, from: number): number {
|
|
110
|
-
let at = from;
|
|
111
|
-
if (at === 0 && text.startsWith("#!")) {
|
|
112
|
-
at = lineEnd(text, 0);
|
|
113
|
-
}
|
|
114
|
-
for (;;) {
|
|
115
|
-
if (/\s/u.test(text[at] ?? "")) {
|
|
116
|
-
at += 1;
|
|
117
|
-
} else if (text.startsWith("//", at)) {
|
|
118
|
-
at = lineEnd(text, at);
|
|
119
|
-
} else if (text.startsWith("/*", at)) {
|
|
120
|
-
const close = text.indexOf("*/", at + 2);
|
|
121
|
-
at = close < 0 ? text.length : close + 2;
|
|
122
|
-
} else {
|
|
123
|
-
return at;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
94
|
/**
|
|
129
95
|
* Every `//` line comment of one file, in file order. The text between tokens is read as
|
|
130
96
|
* the language reads it: a block comment holds no line comment, and the literal tokens the
|
|
@@ -191,63 +157,59 @@ function bindingKey(one: Binding): string {
|
|
|
191
157
|
return `${one.declared?.id ?? ""}\u0000${one.part.id}`;
|
|
192
158
|
}
|
|
193
159
|
|
|
160
|
+
/** One declaration the inventory places, the declaration that holds its parts, and its lines. */
|
|
161
|
+
interface Placed {
|
|
162
|
+
readonly one: Declared;
|
|
163
|
+
readonly holder: Declared;
|
|
164
|
+
readonly startLines: readonly number[];
|
|
165
|
+
readonly partLines: readonly number[];
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** The placed declarations of a project by the path of the file each is in. */
|
|
169
|
+
function placedByPath(
|
|
170
|
+
held: Inventory,
|
|
171
|
+
symbols: ReadonlyMap<string, Declared>,
|
|
172
|
+
): ReadonlyMap<string, readonly Placed[]> {
|
|
173
|
+
const byPath = new Map<string, Placed[]>();
|
|
174
|
+
for (const [id, placed] of held.placement) {
|
|
175
|
+
const one = symbols.get(id);
|
|
176
|
+
const holder = symbols.get(placed.holder);
|
|
177
|
+
if (one === undefined || holder === undefined) {
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
const at = byPath.get(one.position.path) ?? [];
|
|
181
|
+
at.push({ one, holder, startLines: placed.startLines, partLines: placed.partLines });
|
|
182
|
+
byPath.set(one.position.path, at);
|
|
183
|
+
}
|
|
184
|
+
return byPath;
|
|
185
|
+
}
|
|
186
|
+
|
|
194
187
|
/**
|
|
195
|
-
* The declarations of one file by the line each
|
|
196
|
-
*
|
|
197
|
-
* keyword, and a variable written on a later line of the statement also begins there. A
|
|
198
|
-
* function also begins, for a directive, on the line of each of its parameters, and a
|
|
199
|
-
* class, for a part kind, on the lines of its constructor and its constructor's parameters.
|
|
188
|
+
* The declarations of one file by the line a directive binds each on, as the inventory
|
|
189
|
+
* places them, and each import binding of the file on its statement's lines.
|
|
200
190
|
*/
|
|
201
191
|
function declarationLines(
|
|
202
192
|
file: SourceFile,
|
|
193
|
+
placed: readonly Placed[],
|
|
203
194
|
held: Inventory,
|
|
204
|
-
symbols: ReadonlyMap<string, Declared>,
|
|
205
195
|
targetRoot: string,
|
|
206
196
|
): ReadonlyMap<number, readonly Binding[]> {
|
|
207
197
|
const below = new Map<number, Binding[]>();
|
|
208
|
-
const add = (
|
|
209
|
-
const line = file.getLineAndCharacterOfPosition(tokenStart(file.text, offset)).line + 1;
|
|
198
|
+
const add = (line: number, one: Binding): void => {
|
|
210
199
|
const at = below.get(line) ?? [];
|
|
211
200
|
if (!at.some((known) => bindingKey(known) === bindingKey(one))) {
|
|
212
201
|
at.push(one);
|
|
213
202
|
}
|
|
214
203
|
below.set(line, at);
|
|
215
204
|
};
|
|
216
|
-
const
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
};
|
|
220
|
-
const visit = (node: Node): void => {
|
|
221
|
-
const one = declaredAt(node);
|
|
222
|
-
const owner = isConstructorDeclaration(node) ? declaredAt(node.parent) : undefined;
|
|
223
|
-
if (owner !== undefined && isConstructorDeclaration(node)) {
|
|
224
|
-
for (const at of [node, ...node.parameters.filter((one) => !isParameterProperty(one))]) {
|
|
225
|
-
add(at.pos, { declared: undefined, part: owner });
|
|
226
|
-
}
|
|
205
|
+
for (const { one, holder, startLines, partLines } of placed) {
|
|
206
|
+
for (const line of startLines) {
|
|
207
|
+
add(line, { declared: one, part: holder });
|
|
227
208
|
}
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
if (one !== undefined && holder !== undefined) {
|
|
231
|
-
add(node.pos, { declared: one, part: holder });
|
|
232
|
-
}
|
|
233
|
-
if (one !== undefined) {
|
|
234
|
-
// A parameter on a later line of a wrapped signature takes its directive on the
|
|
235
|
-
// line above it, inside the parameter list, and the directive binds the function.
|
|
236
|
-
for (const parameter of signatureOf(node)?.parameters ?? []) {
|
|
237
|
-
add(parameter.pos, { declared: one, part: one });
|
|
238
|
-
}
|
|
239
|
-
const list = node.parent;
|
|
240
|
-
if (
|
|
241
|
-
node.kind === SyntaxKind.VariableDeclaration &&
|
|
242
|
-
list.kind === SyntaxKind.VariableDeclarationList &&
|
|
243
|
-
list.parent.kind === SyntaxKind.VariableStatement
|
|
244
|
-
) {
|
|
245
|
-
add(list.parent.pos, { declared: one, part: one });
|
|
246
|
-
}
|
|
209
|
+
for (const line of partLines) {
|
|
210
|
+
add(line, { declared: undefined, part: one });
|
|
247
211
|
}
|
|
248
|
-
|
|
249
|
-
};
|
|
250
|
-
file.forEachChild(visit);
|
|
212
|
+
}
|
|
251
213
|
// An import binding is a subject of its own, bound on its line as a declaration is.
|
|
252
214
|
const fileId = held.declarations.get(nodeKey(file, file));
|
|
253
215
|
const moduleRef = held.symbols.find((symbol) => symbol.id === fileId)?.ref ?? "";
|
|
@@ -259,7 +221,7 @@ function declarationLines(
|
|
|
259
221
|
statement = statement.parent;
|
|
260
222
|
}
|
|
261
223
|
for (const at of [statement.pos, name.getStart()]) {
|
|
262
|
-
add(at, { declared: one, part: one });
|
|
224
|
+
add(startLine(file, at), { declared: one, part: one });
|
|
263
225
|
}
|
|
264
226
|
}
|
|
265
227
|
return below;
|
|
@@ -296,13 +258,15 @@ export function inlineDirectives<Brand>(
|
|
|
296
258
|
.filter((symbol) => symbol.kind !== "file")
|
|
297
259
|
.map((symbol) => [symbol.id, { id: symbol.id, ref: symbol.ref, position: symbol.position }]),
|
|
298
260
|
);
|
|
261
|
+
const placed = placedByPath(held, symbols);
|
|
299
262
|
const found: InlineDirective[] = [];
|
|
300
263
|
for (const file of project.ownSourceFiles()) {
|
|
301
264
|
const comments = lineComments(file).filter((comment) => CANDIDATE.test(comment.text));
|
|
302
265
|
if (comments.length === 0) {
|
|
303
266
|
continue;
|
|
304
267
|
}
|
|
305
|
-
const
|
|
268
|
+
const path = relativePath(targetRoot, file.fileName) ?? "";
|
|
269
|
+
const below = declarationLines(file, placed.get(path) ?? [], held, targetRoot);
|
|
306
270
|
for (const comment of comments) {
|
|
307
271
|
const site = renderPosition(file, targetRoot, comment.offset);
|
|
308
272
|
const target = trails(file, comment.offset) ? site.line : site.line + 1;
|
package/src/unseen-loads.ts
CHANGED
|
@@ -1,26 +1,36 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The loads
|
|
3
|
-
* `import()` or `require()` call whose
|
|
4
|
-
* naming a directory below the calling file's. The
|
|
5
|
-
* analysis does not see, so the configuration's
|
|
2
|
+
* The loads by a computed path: those whose files the analysis resolves, and those of a
|
|
3
|
+
* project's production files it cannot resolve, an `import()` or `require()` call whose
|
|
4
|
+
* path starts with no relative literal naming a directory below the calling file's. The
|
|
5
|
+
* files such a load runs are roots the analysis does not see, so the configuration's
|
|
6
|
+
* roots are never complete.
|
|
6
7
|
*/
|
|
7
8
|
|
|
8
9
|
import {
|
|
9
10
|
isBinaryExpression,
|
|
10
11
|
isCallExpression,
|
|
11
12
|
isIdentifier,
|
|
13
|
+
isMetaProperty,
|
|
12
14
|
isNoSubstitutionTemplateLiteral,
|
|
15
|
+
isObjectLiteralExpression,
|
|
16
|
+
isPropertyAccessExpression,
|
|
17
|
+
isPropertyAssignment,
|
|
13
18
|
isStringLiteral,
|
|
14
19
|
isTemplateExpression,
|
|
15
20
|
SyntaxKind,
|
|
21
|
+
type CallExpression,
|
|
22
|
+
type Identifier,
|
|
16
23
|
type Node,
|
|
24
|
+
type SourceFile,
|
|
17
25
|
} from "@typescript/native/ast";
|
|
26
|
+
import { literalsOf } from "./entry-points.ts";
|
|
27
|
+
import { hasOwnBody } from "./own-body.ts";
|
|
18
28
|
import { dirnamePath, relativePath, resolvePath } from "./paths.ts";
|
|
19
29
|
import { renderPosition, type Position } from "./position.ts";
|
|
20
|
-
import { UNANSWERED } from "./query.ts";
|
|
21
30
|
import type { ProjectView } from "./session.ts";
|
|
22
31
|
import { sourceFilesOf } from "./source-files.ts";
|
|
23
32
|
import { unwrapped } from "./unwrap.ts";
|
|
33
|
+
import { globExpression } from "./text/glob.ts";
|
|
24
34
|
|
|
25
35
|
/** One load the analysis cannot resolve, in the configuration whose production file writes it. */
|
|
26
36
|
export interface UnseenLoad {
|
|
@@ -30,7 +40,7 @@ export interface UnseenLoad {
|
|
|
30
40
|
}
|
|
31
41
|
|
|
32
42
|
/** The literal text a computed path starts with: a template's head, or a concatenation's first literal. */
|
|
33
|
-
|
|
43
|
+
function leadingLiteral(node: Node): string | undefined {
|
|
34
44
|
const at = unwrapped(node);
|
|
35
45
|
if (isTemplateExpression(at)) {
|
|
36
46
|
return at.head.text;
|
|
@@ -45,11 +55,11 @@ export function leadingLiteral(node: Node): string | undefined {
|
|
|
45
55
|
}
|
|
46
56
|
|
|
47
57
|
/**
|
|
48
|
-
* The directory below `dir` a computed load's leading literal names,
|
|
49
|
-
*
|
|
50
|
-
*
|
|
58
|
+
* The directory below `dir` a computed load's leading literal names, every file of which
|
|
59
|
+
* the load reaches, or none where it names a package, `dir` itself or a directory holding
|
|
60
|
+
* it, each of which would name every sibling.
|
|
51
61
|
*/
|
|
52
|
-
|
|
62
|
+
function computedLoadDirectory(dir: string, prefix: string): string | undefined {
|
|
53
63
|
if (!(prefix.startsWith("./") || prefix.startsWith("../")) || !prefix.endsWith("/")) {
|
|
54
64
|
return undefined;
|
|
55
65
|
}
|
|
@@ -57,6 +67,150 @@ export function computedLoadDirectory(dir: string, prefix: string): string | und
|
|
|
57
67
|
return target === dir || relativePath(target, dir) !== undefined ? undefined : target;
|
|
58
68
|
}
|
|
59
69
|
|
|
70
|
+
/** One `import()`, `require()` or `import.meta.glob()` call, and the declaration whose code writes it. */
|
|
71
|
+
export interface LoadCall {
|
|
72
|
+
readonly call: CallExpression;
|
|
73
|
+
readonly from: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** One file a computed load may load, by its id. */
|
|
77
|
+
interface Loadable {
|
|
78
|
+
readonly file: SourceFile;
|
|
79
|
+
readonly fileId: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** One load by a computed path whose files the analysis resolves. */
|
|
83
|
+
interface ComputedLoad {
|
|
84
|
+
readonly call: CallExpression;
|
|
85
|
+
readonly from: string;
|
|
86
|
+
/** The `require` the call names; {@link hasOwnBody} owns which one loads nothing. */
|
|
87
|
+
readonly require: Identifier | undefined;
|
|
88
|
+
readonly files: readonly Loadable[];
|
|
89
|
+
/** Whether the load reads each file as text or as a URL, which references no declaration of it. */
|
|
90
|
+
readonly asset: boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Whether the load is an eager glob, which Vite turns into static imports at the top of
|
|
93
|
+
* the calling module (https://vite.dev/guide/features.html#glob-import), so the file
|
|
94
|
+
* evaluates what it loads wherever the call is written.
|
|
95
|
+
*/
|
|
96
|
+
readonly eager: boolean;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Whether one node is a call that may load a module by a computed path. */
|
|
100
|
+
export function isLoadCall(node: Node): node is CallExpression {
|
|
101
|
+
if (!isCallExpression(node)) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
const callee = node.expression;
|
|
105
|
+
return (
|
|
106
|
+
callee.kind === SyntaxKind.ImportKeyword ||
|
|
107
|
+
(isIdentifier(callee) && callee.text === "require") ||
|
|
108
|
+
isMetaGlob(node)
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* The resolved computed loads among one file's load calls, each loading the `candidates`
|
|
114
|
+
* but the file itself: an `import()` or `require()` whose path starts with a relative
|
|
115
|
+
* literal naming a directory below the file's, every candidate below it, and an
|
|
116
|
+
* `import.meta.glob` of relative literal globs, every candidate one matches, each read
|
|
117
|
+
* against the file's directory, as a bundler's context module reads them.
|
|
118
|
+
*/
|
|
119
|
+
export function computedLoadsOf(
|
|
120
|
+
file: SourceFile,
|
|
121
|
+
calls: readonly LoadCall[],
|
|
122
|
+
candidates: readonly Loadable[],
|
|
123
|
+
): readonly ComputedLoad[] {
|
|
124
|
+
const dir = dirnamePath(file.fileName);
|
|
125
|
+
const others = candidates.filter((candidate) => candidate.file !== file);
|
|
126
|
+
return calls.flatMap(({ call, from }): ComputedLoad[] => {
|
|
127
|
+
const [first, options] = call.arguments;
|
|
128
|
+
if (first === undefined) {
|
|
129
|
+
return [];
|
|
130
|
+
}
|
|
131
|
+
if (isMetaGlob(call)) {
|
|
132
|
+
const globs = (literalsOf(first) ?? [])
|
|
133
|
+
.filter((pattern) => pattern.startsWith("./") || pattern.startsWith("../"))
|
|
134
|
+
.map((pattern) => globExpression(resolvePath(dir, pattern)));
|
|
135
|
+
const files = others.filter((candidate) =>
|
|
136
|
+
globs.some((glob) => glob.test(candidate.file.fileName)),
|
|
137
|
+
);
|
|
138
|
+
const asset = readsAsAsset(options);
|
|
139
|
+
return [{ call, from, require: undefined, files, asset, eager: isEager(options) }];
|
|
140
|
+
}
|
|
141
|
+
const prefix = leadingLiteral(first);
|
|
142
|
+
const target = prefix === undefined ? undefined : computedLoadDirectory(dir, prefix);
|
|
143
|
+
if (target === undefined) {
|
|
144
|
+
return [];
|
|
145
|
+
}
|
|
146
|
+
const files = others.filter(
|
|
147
|
+
(candidate) => relativePath(target, candidate.file.fileName) !== undefined,
|
|
148
|
+
);
|
|
149
|
+
const callee = call.expression;
|
|
150
|
+
const require = isIdentifier(callee) ? callee : undefined;
|
|
151
|
+
return [{ call, from, require, files, asset: false, eager: false }];
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Whether one call is `import.meta.glob(...)`. */
|
|
156
|
+
function isMetaGlob(node: CallExpression): boolean {
|
|
157
|
+
if (!isPropertyAccessExpression(node.expression)) {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
const { expression, name } = node.expression;
|
|
161
|
+
return (
|
|
162
|
+
isIdentifier(name) &&
|
|
163
|
+
name.text === "glob" &&
|
|
164
|
+
isMetaProperty(expression) &&
|
|
165
|
+
expression.keywordToken === SyntaxKind.ImportKeyword
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** The values an `import.meta.glob` options object assigns, each by its literal key. */
|
|
170
|
+
function globOptions(options: Node | undefined): readonly [string, Node][] {
|
|
171
|
+
const literal = options === undefined ? undefined : unwrapped(options);
|
|
172
|
+
if (literal === undefined || !isObjectLiteralExpression(literal)) {
|
|
173
|
+
return [];
|
|
174
|
+
}
|
|
175
|
+
return literal.properties.flatMap((property): [string, Node][] =>
|
|
176
|
+
isPropertyAssignment(property) &&
|
|
177
|
+
(isIdentifier(property.name) || isStringLiteral(property.name))
|
|
178
|
+
? [[property.name.text, unwrapped(property.initializer)]]
|
|
179
|
+
: [],
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** Whether the options of an `import.meta.glob` set `eager` to `true`. */
|
|
184
|
+
function isEager(options: Node | undefined): boolean {
|
|
185
|
+
return globOptions(options).some(
|
|
186
|
+
([key, value]) => key === "eager" && value.kind === SyntaxKind.TrueKeyword,
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** The query parameters that make Vite load a file as its text or its URL, each written bare. */
|
|
191
|
+
const ASSET_QUERY = /(?:^\?|&)(?:raw|url|inline)(?:&|$)/u;
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Whether the options of an `import.meta.glob` read each file as text or as a URL: a
|
|
195
|
+
* `query` string holding a bare `raw`, `url` or `inline`, or an `as` of `raw` or `url`,
|
|
196
|
+
* which Vite reads as that query (https://vite.dev/guide/features.html#glob-import). Any
|
|
197
|
+
* other query is read as loading the module, which keeps more than it may.
|
|
198
|
+
*/
|
|
199
|
+
function readsAsAsset(options: Node | undefined): boolean {
|
|
200
|
+
return globOptions(options).some(([key, value]) => {
|
|
201
|
+
if (!isStringLiteral(value) && !isNoSubstitutionTemplateLiteral(value)) {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
if (key === "as") {
|
|
205
|
+
return value.text === "raw" || value.text === "url";
|
|
206
|
+
}
|
|
207
|
+
return (
|
|
208
|
+
key === "query" &&
|
|
209
|
+
ASSET_QUERY.test(value.text.startsWith("?") ? value.text : `?${value.text}`)
|
|
210
|
+
);
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
60
214
|
/** Whether a computed path's leading literal names a package, whose files are none of the target's. */
|
|
61
215
|
function namesPackage(prefix: string | undefined): boolean {
|
|
62
216
|
return (
|
|
@@ -71,8 +225,7 @@ function namesPackage(prefix: string | undefined): boolean {
|
|
|
71
225
|
/**
|
|
72
226
|
* Every unresolved load the project's production files write, in file then source order.
|
|
73
227
|
* `testFiles` holds the test files by their path below the target root. A `require` call
|
|
74
|
-
* loads a module
|
|
75
|
-
* files: one the project declares is its own function.
|
|
228
|
+
* loads a module unless {@link hasOwnBody} reads it as the project's own function.
|
|
76
229
|
*/
|
|
77
230
|
export function unseenLoadsOf<Brand>(
|
|
78
231
|
project: ProjectView<Brand>,
|
|
@@ -116,15 +269,10 @@ export function unseenLoadsOf<Brand>(
|
|
|
116
269
|
file.forEachChild(visit);
|
|
117
270
|
}
|
|
118
271
|
const requires = sites.flatMap((site) => (site.require === undefined ? [] : [site.require]));
|
|
119
|
-
const own = project.ownPaths();
|
|
120
272
|
const local = new Set<Node>();
|
|
121
273
|
project.symbolsAt(requires.map((node) => project.handle(node))).forEach((symbol, at) => {
|
|
122
274
|
const node = requires[at];
|
|
123
|
-
if (
|
|
124
|
-
node !== undefined &&
|
|
125
|
-
symbol !== UNANSWERED &&
|
|
126
|
-
symbol?.declarations.some((handle) => own.has(handle.path)) === true
|
|
127
|
-
) {
|
|
275
|
+
if (node !== undefined && typeof symbol === "object" && hasOwnBody(project, symbol)) {
|
|
128
276
|
local.add(node);
|
|
129
277
|
}
|
|
130
278
|
});
|