carrick 0.3.124 → 0.3.126
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/package.json +6 -6
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/sidecar/dist/src/capture/api.d.ts +5 -1
- package/sidecar/dist/src/capture/check-classify.js +11 -5
- package/sidecar/dist/src/capture/check.js +8 -1
- package/sidecar/dist/src/capture/deep-walk.d.ts +31 -0
- package/sidecar/dist/src/capture/deep-walk.js +43 -1
- package/sidecar/dist/src/capture/deno-project.d.ts +7 -0
- package/sidecar/dist/src/capture/deno-project.js +7 -0
- package/sidecar/dist/src/capture/index.d.ts +1 -1
- package/sidecar/dist/src/capture/index.js +1 -1
- package/sidecar/dist/src/capture/service-config.d.ts +11 -0
- package/sidecar/dist/src/capture/service-config.js +40 -0
- package/sidecar/dist/src/declaration-attribution.d.ts +11 -0
- package/sidecar/dist/src/declaration-attribution.js +16 -0
- package/sidecar/dist/src/implementations.d.ts +43 -0
- package/sidecar/dist/src/implementations.js +224 -0
- package/sidecar/dist/src/index.js +35 -1
- package/sidecar/dist/src/library-claims.d.ts +17 -2
- package/sidecar/dist/src/library-claims.js +242 -47
- package/sidecar/dist/src/position-rows.d.ts +119 -1
- package/sidecar/dist/src/position-rows.js +163 -9
- package/sidecar/dist/src/project-loader.d.ts +31 -0
- package/sidecar/dist/src/project-loader.js +67 -4
- package/sidecar/dist/src/type-inferrer.d.ts +150 -16
- package/sidecar/dist/src/type-inferrer.js +651 -94
- package/sidecar/dist/src/types.d.ts +46 -2
- package/sidecar/dist/src/validators.d.ts +73 -0
- package/sidecar/dist/src/validators.js +10 -0
|
@@ -71,6 +71,17 @@ export declare const FLAG_TOO_MANY = 16;
|
|
|
71
71
|
export declare const FLAG_PUBLISHED = 32;
|
|
72
72
|
/** The declaration is what its module exports as `export =` or `default` (carrick#2508). */
|
|
73
73
|
export declare const FLAG_MODULE_DEFAULT = 64;
|
|
74
|
+
/**
|
|
75
|
+
* A `call` row only: the call's declared return is the result of the
|
|
76
|
+
* function it is handed (`ReturnType<F>`, a bare type parameter the handed
|
|
77
|
+
* function returns, or an awaitable of one), so the library runs it and
|
|
78
|
+
* hands its result back (carrick#2634). Set when the call passes at least
|
|
79
|
+
* one argument at a function-typed parameter and every such parameter
|
|
80
|
+
* meets that return. The last free bit of the `u8`: the next flag widens
|
|
81
|
+
* the field and bumps the format. Pinned to `FLAG_RUNS_HANDED` in
|
|
82
|
+
* `src/position_rows.rs` by `position-rows-runs-handed.test.ts`.
|
|
83
|
+
*/
|
|
84
|
+
export declare const FLAG_RUNS_HANDED = 128;
|
|
74
85
|
/** The most `decl` rows one position lists. */
|
|
75
86
|
export declare const DECL_CAP = 16;
|
|
76
87
|
/** The kinds {@link kindOf} gives a node; `decl` and `jsx` rows are written beside them. */
|
|
@@ -109,14 +120,26 @@ export interface PositionRowsOptions {
|
|
|
109
120
|
export declare function writePositionRows(options: PositionRowsOptions): PositionRowsCounts;
|
|
110
121
|
/** What a declaration's file is written as in a row's `decl_file`: `lib:<name>` for the default library, else `PathNames`'. */
|
|
111
122
|
export declare function declaringFileName(program: ts.Program, paths: PathNames, file: ts.SourceFile): string | null;
|
|
123
|
+
/** The installed package directory a resolved file lies in, and its name, as the module graph places it. */
|
|
124
|
+
export type PackageDirectoryOf = (fileName: string) => {
|
|
125
|
+
directory: string;
|
|
126
|
+
name: string;
|
|
127
|
+
} | undefined;
|
|
112
128
|
/**
|
|
113
129
|
* Names for the files rows point at, never an absolute path: relative to the
|
|
114
130
|
* scanned repo's root when inside it (its real path too, so a root reached
|
|
115
131
|
* through a link still contains its files), `lib:<name>` for the compiler's
|
|
116
132
|
* default library, `pkg:<name>/<path>` for an installed package outside the
|
|
117
133
|
* root, and nothing otherwise.
|
|
134
|
+
*
|
|
135
|
+
* An installed package outside the root is found by its path's last
|
|
136
|
+
* `node_modules` segment, else by `packageDirectoryOf`: the module graph's
|
|
137
|
+
* answer where the resolver serves a package from a cache whose path has no
|
|
138
|
+
* `node_modules` segment (carrick#2599). Either way the directory must hold a
|
|
139
|
+
* package.json.
|
|
118
140
|
*/
|
|
119
141
|
export declare class PathNames {
|
|
142
|
+
private readonly packageDirectoryOf;
|
|
120
143
|
private readonly roots;
|
|
121
144
|
private readonly packageNames;
|
|
122
145
|
/**
|
|
@@ -126,7 +149,7 @@ export declare class PathNames {
|
|
|
126
149
|
*/
|
|
127
150
|
private readonly declaringFiles;
|
|
128
151
|
private readonly declaringPackages;
|
|
129
|
-
constructor(scanRoot: string);
|
|
152
|
+
constructor(scanRoot: string, packageDirectoryOf?: PackageDirectoryOf);
|
|
130
153
|
/** The root `file` lies under, and its path from there; `undefined` outside every root. */
|
|
131
154
|
private inside;
|
|
132
155
|
/** A request file's name: relative to the root, which every file the scanner sends is inside. */
|
|
@@ -151,5 +174,100 @@ export declare class PathNames {
|
|
|
151
174
|
*/
|
|
152
175
|
scrubType(text: string): string;
|
|
153
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* The rows of one program's files. The `implementations` action
|
|
179
|
+
* (`implementations.ts`, carrick#2432) reads a call's symbol and names its
|
|
180
|
+
* answers through the same walker, so they join as the rows do.
|
|
181
|
+
*/
|
|
182
|
+
export declare class PositionWalker {
|
|
183
|
+
private readonly program;
|
|
184
|
+
private readonly paths;
|
|
185
|
+
private readonly loader;
|
|
186
|
+
private readonly checker;
|
|
187
|
+
/** Each module's exports by name, and its `export =` and `default` targets, aliases followed. */
|
|
188
|
+
private readonly moduleExports;
|
|
189
|
+
constructor(program: ts.Program, paths: PathNames, loader: ProjectLoader);
|
|
190
|
+
rowsOf(sourceFile: ts.SourceFile): PositionRow[];
|
|
191
|
+
private row;
|
|
192
|
+
/**
|
|
193
|
+
* Whether a call's declared return is the result of the function it is
|
|
194
|
+
* handed (carrick#2634), read from the declared signature through public
|
|
195
|
+
* checker calls. See {@link FLAG_RUNS_HANDED}.
|
|
196
|
+
*/
|
|
197
|
+
private runsHanded;
|
|
198
|
+
/** The call signatures of a parameter's type, a type parameter read through its constraint, `null` and `undefined` skipped. */
|
|
199
|
+
private callSignaturesOf;
|
|
200
|
+
/** The compiler's own awaited type differs from the type: `Promise<R>`, `PromiseLike<R>`. */
|
|
201
|
+
private isAwaitable;
|
|
202
|
+
/**
|
|
203
|
+
* An intrinsic element's type: the attributes `JSX.IntrinsicElements`
|
|
204
|
+
* gives its tag. The compiler types an intrinsic tag name itself as the
|
|
205
|
+
* error type, though the name resolves. A tag is intrinsic when it is an
|
|
206
|
+
* identifier that starts with a lowercase letter or holds a `-`, as the
|
|
207
|
+
* language reads it.
|
|
208
|
+
*/
|
|
209
|
+
private intrinsicProps;
|
|
210
|
+
/** How rows name a position in a program file: its file, 1-based line and package. */
|
|
211
|
+
located(declaringFile: ts.SourceFile, position: number): {
|
|
212
|
+
declFile: string | null;
|
|
213
|
+
declLine: number;
|
|
214
|
+
declPackage: string | null;
|
|
215
|
+
};
|
|
216
|
+
/**
|
|
217
|
+
* The declaration a member with none is attributed to (carrick#2448): the
|
|
218
|
+
* mapped type node, or the alias naming it, of the receiver's type. A
|
|
219
|
+
* mapped type's members are made by the checker and have no declaration of
|
|
220
|
+
* their own. This is the owner `memberTypeWithoutDeclaration` reads such a
|
|
221
|
+
* member at (type-structural-expander.ts): the type's symbol, else its
|
|
222
|
+
* alias symbol. A union or intersection receiver contributes the one
|
|
223
|
+
* constituent that has the property; with several the row is left as it is.
|
|
224
|
+
*/
|
|
225
|
+
private ownerDeclaration;
|
|
226
|
+
/** The declaration of a type's symbol, else of its alias symbol: a mapped type node, or the alias naming it. */
|
|
227
|
+
private typeDeclaration;
|
|
228
|
+
/**
|
|
229
|
+
* How `declaration`'s module publishes `symbol`: {@link FLAG_PUBLISHED}
|
|
230
|
+
* when under the symbol's own name (or as a global), and
|
|
231
|
+
* {@link FLAG_MODULE_DEFAULT} when as the module's `export =` or `default`.
|
|
232
|
+
* The module is the nearest ambient `declare module "…"` or the file.
|
|
233
|
+
*/
|
|
234
|
+
private publication;
|
|
235
|
+
/** A namespace member, found among the exports of the module it sits in. */
|
|
236
|
+
private publishedByExports;
|
|
237
|
+
private publishedIn;
|
|
238
|
+
/** The type of the object a `member` or `call` row's name is read on, if the name is read on one. */
|
|
239
|
+
private receiverOf;
|
|
240
|
+
/**
|
|
241
|
+
* The `decl` rows of a `call` or `member` row (spans unset): one per
|
|
242
|
+
* declaration the receiver's union constituents give the member, when they
|
|
243
|
+
* give more than one (carrick#2448). A constituent's declaration is its
|
|
244
|
+
* member's ({@link attributedDeclaration}), or, for a member with none, the
|
|
245
|
+
* constituent type's own ({@link typeDeclaration}). Two instantiations of
|
|
246
|
+
* one generic class, or one class and `undefined`, give one, and no row.
|
|
247
|
+
* A throw writes one row flagged {@link FLAG_CHECKER_THREW}.
|
|
248
|
+
*/
|
|
249
|
+
private declRows;
|
|
250
|
+
/** The row's symbol, following aliases to a target that has a declaration. */
|
|
251
|
+
symbolOf(kind: PositionKind, node: ts.Node): ts.Symbol | undefined;
|
|
252
|
+
/**
|
|
253
|
+
* In a TypeScript file `const { a } = require("m")` binds `a` as `any` with
|
|
254
|
+
* the binding as its only declaration (TypeScript resolves `require` itself
|
|
255
|
+
* only in JavaScript files), so a trace stops there. Name the export the
|
|
256
|
+
* call resolves to instead (carrick#2454): for the binding itself, and for
|
|
257
|
+
* `m.x` where `m` is a whole-module binding. `undefined` when the name is
|
|
258
|
+
* not such a binding, the specifier is not a literal or does not resolve,
|
|
259
|
+
* or the module has no such export.
|
|
260
|
+
*/
|
|
261
|
+
private requiredExport;
|
|
262
|
+
/** The `require` binding a symbol is, when its only declaration is one. */
|
|
263
|
+
private requireBinding;
|
|
264
|
+
/** `"m"` of a call `require("m")` to the global `require`, else `undefined`. */
|
|
265
|
+
private requireSpecifier;
|
|
266
|
+
private exportOfRequired;
|
|
267
|
+
private host;
|
|
268
|
+
private resolutionHost;
|
|
269
|
+
}
|
|
270
|
+
/** Order two `decl_file` names, `null` last. */
|
|
271
|
+
export declare function compareNames(a: string | null, b: string | null): number;
|
|
154
272
|
/** The kind of row a node gives, if any (exported for the editor-parity test, carrick#2418). */
|
|
155
273
|
export declare function kindOf(node: ts.Node): NodeKind | undefined;
|
|
@@ -53,7 +53,7 @@ import * as path from 'node:path';
|
|
|
53
53
|
import * as zlib from 'node:zlib';
|
|
54
54
|
import { ts } from 'ts-morph';
|
|
55
55
|
import { WriteGuard, installedPackageDirectory, isDeclarationFileName, moduleExport } from './capture/index.js';
|
|
56
|
-
import { attributedDeclaration, installedPackageOf } from './declaration-attribution.js';
|
|
56
|
+
import { attributedDeclaration, installedPackageOf, typeDeclaration } from './declaration-attribution.js';
|
|
57
57
|
import { typeToStringAsDeclared } from './print-type.js';
|
|
58
58
|
/** The format's version, written in the header. */
|
|
59
59
|
export const POSITION_ROWS_VERSION = 4;
|
|
@@ -76,6 +76,17 @@ export const FLAG_TOO_MANY = 16;
|
|
|
76
76
|
export const FLAG_PUBLISHED = 32;
|
|
77
77
|
/** The declaration is what its module exports as `export =` or `default` (carrick#2508). */
|
|
78
78
|
export const FLAG_MODULE_DEFAULT = 64;
|
|
79
|
+
/**
|
|
80
|
+
* A `call` row only: the call's declared return is the result of the
|
|
81
|
+
* function it is handed (`ReturnType<F>`, a bare type parameter the handed
|
|
82
|
+
* function returns, or an awaitable of one), so the library runs it and
|
|
83
|
+
* hands its result back (carrick#2634). Set when the call passes at least
|
|
84
|
+
* one argument at a function-typed parameter and every such parameter
|
|
85
|
+
* meets that return. The last free bit of the `u8`: the next flag widens
|
|
86
|
+
* the field and bumps the format. Pinned to `FLAG_RUNS_HANDED` in
|
|
87
|
+
* `src/position_rows.rs` by `position-rows-runs-handed.test.ts`.
|
|
88
|
+
*/
|
|
89
|
+
export const FLAG_RUNS_HANDED = 128;
|
|
79
90
|
/** The most `decl` rows one position lists. */
|
|
80
91
|
export const DECL_CAP = 16;
|
|
81
92
|
const COMPARE_OPERATORS = new Set([
|
|
@@ -102,7 +113,7 @@ export function writePositionRows(options) {
|
|
|
102
113
|
const { loader, files, scanRoot, outPath, service } = options;
|
|
103
114
|
const outDir = path.dirname(outPath);
|
|
104
115
|
const guard = WriteGuard.of({ dirs: [outDir], protect: [loader.getRepoRoot(), scanRoot] });
|
|
105
|
-
const paths = new PathNames(scanRoot);
|
|
116
|
+
const paths = new PathNames(scanRoot, (file) => loader.packageDirectoryOf(file));
|
|
106
117
|
const lines = new Array(files.length);
|
|
107
118
|
const counts = {
|
|
108
119
|
files: files.length,
|
|
@@ -256,8 +267,15 @@ function fileLine(line) {
|
|
|
256
267
|
* through a link still contains its files), `lib:<name>` for the compiler's
|
|
257
268
|
* default library, `pkg:<name>/<path>` for an installed package outside the
|
|
258
269
|
* root, and nothing otherwise.
|
|
270
|
+
*
|
|
271
|
+
* An installed package outside the root is found by its path's last
|
|
272
|
+
* `node_modules` segment, else by `packageDirectoryOf`: the module graph's
|
|
273
|
+
* answer where the resolver serves a package from a cache whose path has no
|
|
274
|
+
* `node_modules` segment (carrick#2599). Either way the directory must hold a
|
|
275
|
+
* package.json.
|
|
259
276
|
*/
|
|
260
277
|
export class PathNames {
|
|
278
|
+
packageDirectoryOf;
|
|
261
279
|
roots;
|
|
262
280
|
packageNames = new Map();
|
|
263
281
|
/**
|
|
@@ -267,7 +285,8 @@ export class PathNames {
|
|
|
267
285
|
*/
|
|
268
286
|
declaringFiles = new Map();
|
|
269
287
|
declaringPackages = new Map();
|
|
270
|
-
constructor(scanRoot) {
|
|
288
|
+
constructor(scanRoot, packageDirectoryOf = () => undefined) {
|
|
289
|
+
this.packageDirectoryOf = packageDirectoryOf;
|
|
271
290
|
const resolved = path.resolve(scanRoot);
|
|
272
291
|
let real = resolved;
|
|
273
292
|
try {
|
|
@@ -306,7 +325,7 @@ export class PathNames {
|
|
|
306
325
|
const inside = this.inside(fileName);
|
|
307
326
|
if (inside)
|
|
308
327
|
return toSlashes(inside.rel);
|
|
309
|
-
const installed = installedPackageDirectory(this.roots[0], fileName);
|
|
328
|
+
const installed = installedPackageDirectory(this.roots[0], fileName) ?? this.packageDirectoryOf(fileName);
|
|
310
329
|
if (installed && fs.existsSync(path.join(installed.directory, 'package.json'))) {
|
|
311
330
|
return `pkg:${installed.name}/${toSlashes(path.relative(installed.directory, fileName))}`;
|
|
312
331
|
}
|
|
@@ -381,8 +400,12 @@ export class PathNames {
|
|
|
381
400
|
function toSlashes(p) {
|
|
382
401
|
return p.split(path.sep).join('/');
|
|
383
402
|
}
|
|
384
|
-
/**
|
|
385
|
-
|
|
403
|
+
/**
|
|
404
|
+
* The rows of one program's files. The `implementations` action
|
|
405
|
+
* (`implementations.ts`, carrick#2432) reads a call's symbol and names its
|
|
406
|
+
* answers through the same walker, so they join as the rows do.
|
|
407
|
+
*/
|
|
408
|
+
export class PositionWalker {
|
|
386
409
|
program;
|
|
387
410
|
paths;
|
|
388
411
|
loader;
|
|
@@ -426,6 +449,8 @@ class PositionWalker {
|
|
|
426
449
|
if (type.flags & ts.TypeFlags.Any) {
|
|
427
450
|
flags |= type.intrinsicName === 'error' ? FLAG_ERROR_TYPE : FLAG_ANY;
|
|
428
451
|
}
|
|
452
|
+
if (kind === 'call' && this.runsHanded(node))
|
|
453
|
+
flags |= FLAG_RUNS_HANDED;
|
|
429
454
|
const typeText = this.paths.scrubType(typeToStringAsDeclared(this.checker, type, undefined));
|
|
430
455
|
if (!symbol)
|
|
431
456
|
return [kind, start, end, null, null, null, null, typeText, flags];
|
|
@@ -453,6 +478,114 @@ class PositionWalker {
|
|
|
453
478
|
return [kind, start, end, null, null, null, null, null, FLAG_CHECKER_THREW];
|
|
454
479
|
}
|
|
455
480
|
}
|
|
481
|
+
/**
|
|
482
|
+
* Whether a call's declared return is the result of the function it is
|
|
483
|
+
* handed (carrick#2634), read from the declared signature through public
|
|
484
|
+
* checker calls. See {@link FLAG_RUNS_HANDED}.
|
|
485
|
+
*/
|
|
486
|
+
runsHanded(node) {
|
|
487
|
+
if (!ts.isCallExpression(node) && !ts.isNewExpression(node))
|
|
488
|
+
return false;
|
|
489
|
+
const args = node.arguments ?? [];
|
|
490
|
+
if (args.length === 0)
|
|
491
|
+
return false;
|
|
492
|
+
try {
|
|
493
|
+
const declaration = this.checker.getResolvedSignature(node)?.declaration;
|
|
494
|
+
if (!declaration || ts.isJSDocSignature(declaration))
|
|
495
|
+
return false;
|
|
496
|
+
const declared = this.checker.getSignatureFromDeclaration(declaration);
|
|
497
|
+
if (!declared)
|
|
498
|
+
return false;
|
|
499
|
+
const own = new Set(declared.getTypeParameters() ?? []);
|
|
500
|
+
const results = new Map();
|
|
501
|
+
const resultOf = (f) => {
|
|
502
|
+
let atom = results.get(f);
|
|
503
|
+
if (!atom)
|
|
504
|
+
results.set(f, (atom = {}));
|
|
505
|
+
return atom;
|
|
506
|
+
};
|
|
507
|
+
const atoms = (t, out, depth = 0) => {
|
|
508
|
+
if (depth > 8)
|
|
509
|
+
return out;
|
|
510
|
+
if (t.flags & ts.TypeFlags.TypeParameter) {
|
|
511
|
+
if (own.has(t))
|
|
512
|
+
out.add(t);
|
|
513
|
+
}
|
|
514
|
+
else if (t.flags & ts.TypeFlags.UnionOrIntersection) {
|
|
515
|
+
for (const member of t.types)
|
|
516
|
+
atoms(member, out, depth + 1);
|
|
517
|
+
}
|
|
518
|
+
else if (t.flags & ts.TypeFlags.Conditional) {
|
|
519
|
+
const f = resultOfConditional(t, own);
|
|
520
|
+
if (f)
|
|
521
|
+
out.add(resultOf(f));
|
|
522
|
+
}
|
|
523
|
+
else if (isReference(t) && this.isAwaitable(t)) {
|
|
524
|
+
for (const arg of this.checker.getTypeArguments(t))
|
|
525
|
+
atoms(arg, out, depth + 1);
|
|
526
|
+
}
|
|
527
|
+
return out;
|
|
528
|
+
};
|
|
529
|
+
const returned = atoms(this.checker.getReturnTypeOfSignature(declared), new Set());
|
|
530
|
+
if (returned.size === 0)
|
|
531
|
+
return false;
|
|
532
|
+
const parameters = declared.getParameters();
|
|
533
|
+
let handed = 0;
|
|
534
|
+
for (let i = 0; i < args.length; i++) {
|
|
535
|
+
const parameter = parameters[Math.min(i, parameters.length - 1)];
|
|
536
|
+
if (!parameter)
|
|
537
|
+
continue;
|
|
538
|
+
const isRest = parameter.valueDeclaration !== undefined &&
|
|
539
|
+
ts.isParameter(parameter.valueDeclaration) &&
|
|
540
|
+
parameter.valueDeclaration.dotDotDotToken !== undefined;
|
|
541
|
+
if (!isRest && i >= parameters.length)
|
|
542
|
+
continue;
|
|
543
|
+
let type = this.checker.getTypeOfSymbolAtLocation(parameter, declaration);
|
|
544
|
+
if (isRest) {
|
|
545
|
+
if (!isReference(type))
|
|
546
|
+
continue;
|
|
547
|
+
const [element] = this.checker.getTypeArguments(type);
|
|
548
|
+
if (!element)
|
|
549
|
+
continue;
|
|
550
|
+
type = element;
|
|
551
|
+
}
|
|
552
|
+
const signatures = this.callSignaturesOf(type);
|
|
553
|
+
if (signatures.length === 0)
|
|
554
|
+
continue;
|
|
555
|
+
handed++;
|
|
556
|
+
const mine = new Set();
|
|
557
|
+
if (type.flags & ts.TypeFlags.TypeParameter)
|
|
558
|
+
mine.add(resultOf(type));
|
|
559
|
+
else
|
|
560
|
+
for (const signature of signatures)
|
|
561
|
+
atoms(this.checker.getReturnTypeOfSignature(signature), mine);
|
|
562
|
+
if (![...mine].some((atom) => returned.has(atom)))
|
|
563
|
+
return false;
|
|
564
|
+
}
|
|
565
|
+
return handed > 0;
|
|
566
|
+
}
|
|
567
|
+
catch {
|
|
568
|
+
return false;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
/** The call signatures of a parameter's type, a type parameter read through its constraint, `null` and `undefined` skipped. */
|
|
572
|
+
callSignaturesOf(type) {
|
|
573
|
+
let t = type;
|
|
574
|
+
if (t.flags & ts.TypeFlags.TypeParameter)
|
|
575
|
+
t = this.checker.getBaseConstraintOfType(t) ?? t;
|
|
576
|
+
if (t.flags & ts.TypeFlags.Union) {
|
|
577
|
+
const kept = t.types.filter((m) => !(m.flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined)));
|
|
578
|
+
if (kept.length !== 1)
|
|
579
|
+
return [];
|
|
580
|
+
t = kept[0];
|
|
581
|
+
}
|
|
582
|
+
return this.checker.getSignaturesOfType(t, ts.SignatureKind.Call);
|
|
583
|
+
}
|
|
584
|
+
/** The compiler's own awaited type differs from the type: `Promise<R>`, `PromiseLike<R>`. */
|
|
585
|
+
isAwaitable(t) {
|
|
586
|
+
const awaited = this.checker.getAwaitedType(t);
|
|
587
|
+
return awaited !== undefined && awaited !== t;
|
|
588
|
+
}
|
|
456
589
|
/**
|
|
457
590
|
* An intrinsic element's type: the attributes `JSX.IntrinsicElements`
|
|
458
591
|
* gives its tag. The compiler types an intrinsic tag name itself as the
|
|
@@ -495,8 +628,7 @@ class PositionWalker {
|
|
|
495
628
|
}
|
|
496
629
|
/** The declaration of a type's symbol, else of its alias symbol: a mapped type node, or the alias naming it. */
|
|
497
630
|
typeDeclaration(type) {
|
|
498
|
-
|
|
499
|
-
return attributedDeclaration(() => this.program, owner?.declarations ?? []);
|
|
631
|
+
return typeDeclaration(() => this.program, type);
|
|
500
632
|
}
|
|
501
633
|
/**
|
|
502
634
|
* How `declaration`'s module publishes `symbol`: {@link FLAG_PUBLISHED}
|
|
@@ -742,7 +874,7 @@ function tail(row) {
|
|
|
742
874
|
return [row[3], row[4], row[5], row[6], row[7], row[8]];
|
|
743
875
|
}
|
|
744
876
|
/** Order two `decl_file` names, `null` last. */
|
|
745
|
-
function compareNames(a, b) {
|
|
877
|
+
export function compareNames(a, b) {
|
|
746
878
|
if (a === b)
|
|
747
879
|
return 0;
|
|
748
880
|
if (a === null)
|
|
@@ -751,6 +883,28 @@ function compareNames(a, b) {
|
|
|
751
883
|
return -1;
|
|
752
884
|
return a < b ? -1 : 1;
|
|
753
885
|
}
|
|
886
|
+
function isReference(t) {
|
|
887
|
+
return !!(t.flags & ts.TypeFlags.Object) && !!(t.objectFlags & ts.ObjectFlags.Reference);
|
|
888
|
+
}
|
|
889
|
+
/**
|
|
890
|
+
* The type parameter `F` of a conditional written `F extends (...) => infer R ? R : _`
|
|
891
|
+
* (`ReturnType<F>`): the result of `F`. `Parameters<F>` and the like infer
|
|
892
|
+
* elsewhere and give nothing.
|
|
893
|
+
*/
|
|
894
|
+
function resultOfConditional(t, own) {
|
|
895
|
+
if (!(t.checkType.flags & ts.TypeFlags.TypeParameter) || !own.has(t.checkType))
|
|
896
|
+
return undefined;
|
|
897
|
+
const node = t.root.node;
|
|
898
|
+
if (!ts.isFunctionTypeNode(node.extendsType))
|
|
899
|
+
return undefined;
|
|
900
|
+
const returns = node.extendsType.type;
|
|
901
|
+
if (!ts.isInferTypeNode(returns))
|
|
902
|
+
return undefined;
|
|
903
|
+
const yielded = node.trueType;
|
|
904
|
+
if (!ts.isTypeReferenceNode(yielded) || !ts.isIdentifier(yielded.typeName))
|
|
905
|
+
return undefined;
|
|
906
|
+
return yielded.typeName.text === returns.typeParameter.name.text ? t.checkType : undefined;
|
|
907
|
+
}
|
|
754
908
|
/** The kind of row a node gives, if any (exported for the editor-parity test, carrick#2418). */
|
|
755
909
|
export function kindOf(node) {
|
|
756
910
|
if (ts.isCallExpression(node) || ts.isNewExpression(node) || ts.isTaggedTemplateExpression(node)) {
|
|
@@ -80,6 +80,16 @@ export declare class ProjectLoader {
|
|
|
80
80
|
private denoProject;
|
|
81
81
|
/** The tsconfig the service names, when the project is built from one. */
|
|
82
82
|
private namedTsconfigPath;
|
|
83
|
+
/**
|
|
84
|
+
* Set when the service has no config at or above its root: each file is then
|
|
85
|
+
* typed under the nearest `tsconfig.json`/`jsconfig.json` on its way up to
|
|
86
|
+
* the service root (carrick#1604), and the default project holds the rest.
|
|
87
|
+
*/
|
|
88
|
+
private inferredService;
|
|
89
|
+
/** The per-directory answers of `fileConfigPath` for this service. */
|
|
90
|
+
private readonly fileConfigs;
|
|
91
|
+
/** The reference owner rule of each config a file was found under. */
|
|
92
|
+
private readonly ownerRules;
|
|
83
93
|
/** Each file's owning config (carrick#1604), resolved on first use. */
|
|
84
94
|
private configPathFor;
|
|
85
95
|
/** Programs built for owning projects other than the named tsconfig. */
|
|
@@ -128,6 +138,12 @@ export declare class ProjectLoader {
|
|
|
128
138
|
* as before.
|
|
129
139
|
*/
|
|
130
140
|
projectKeyFor(file?: string): string;
|
|
141
|
+
/**
|
|
142
|
+
* `projectKeyFor` for a service with no config at its root: the owner of the
|
|
143
|
+
* nearest config on the file's way up to the service root, or `''` (the
|
|
144
|
+
* default project, under the inferred options) for a file with none.
|
|
145
|
+
*/
|
|
146
|
+
private inferredKeyFor;
|
|
131
147
|
/**
|
|
132
148
|
* The project behind a key from `projectKeyFor`, built on first use and
|
|
133
149
|
* kept: a service whose files all belong to one project builds one.
|
|
@@ -166,6 +182,21 @@ export declare class ProjectLoader {
|
|
|
166
182
|
* through Deno, and for a file the workspace itself owns.
|
|
167
183
|
*/
|
|
168
184
|
packageOf(filePath: string): string | undefined;
|
|
185
|
+
/**
|
|
186
|
+
* The installed package directory a resolved file lies in, and its name,
|
|
187
|
+
* when the module graph places it (carrick#2599). `undefined` for a project
|
|
188
|
+
* that did not resolve through Deno: there the path says it.
|
|
189
|
+
*/
|
|
190
|
+
packageDirectoryOf(filePath: string): {
|
|
191
|
+
directory: string;
|
|
192
|
+
name: string;
|
|
193
|
+
} | undefined;
|
|
194
|
+
/**
|
|
195
|
+
* How the module graph resolves `name` from `from`, as the program did
|
|
196
|
+
* (carrick#2599). `undefined` for a project that did not resolve through
|
|
197
|
+
* Deno: there Node resolution is the program's.
|
|
198
|
+
*/
|
|
199
|
+
resolveThroughGraph(name: string, from: string, options: import('typescript').CompilerOptions): import('typescript').ResolvedModuleFull | undefined;
|
|
169
200
|
/**
|
|
170
201
|
* Check if the project has been successfully initialized
|
|
171
202
|
*/
|
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { Project, getCompilerOptionsFromTsConfig } from 'ts-morph';
|
|
13
13
|
import * as path from 'node:path';
|
|
14
|
+
import ts from 'typescript';
|
|
14
15
|
import * as fs from 'node:fs';
|
|
15
|
-
import { DenoProject, findDenoConfig, findServiceTsconfig, referenceRedirectOf, serviceConfigPath, sidecarCompilerOptions, untrustedReadBound, } from './capture/index.js';
|
|
16
|
+
import { DenoProject, findDenoConfig, fileConfigPath, findServiceTsconfig, referenceRedirectOf, serviceConfigPath, sidecarCompilerOptions, untrustedReadBound, } from './capture/index.js';
|
|
16
17
|
import { moduleFormatResolutionHost } from './module-format.js';
|
|
17
18
|
import { ExternalImports, registerExternalImports } from './origin.js';
|
|
18
19
|
/**
|
|
@@ -92,6 +93,11 @@ const TARGET_MAP = {
|
|
|
92
93
|
'ES2023': 10,
|
|
93
94
|
'ESNext': 99,
|
|
94
95
|
};
|
|
96
|
+
/** Whether the config's own `compilerOptions` state `maxNodeModuleJsDepth`. */
|
|
97
|
+
function statesJsDepth(configPath) {
|
|
98
|
+
const raw = ts.readConfigFile(configPath, ts.sys.readFile).config;
|
|
99
|
+
return raw?.compilerOptions?.maxNodeModuleJsDepth !== undefined;
|
|
100
|
+
}
|
|
95
101
|
/**
|
|
96
102
|
* ProjectLoader - Manages ts-morph Project initialization and access
|
|
97
103
|
*
|
|
@@ -126,6 +132,16 @@ export class ProjectLoader {
|
|
|
126
132
|
denoProject;
|
|
127
133
|
/** The tsconfig the service names, when the project is built from one. */
|
|
128
134
|
namedTsconfigPath;
|
|
135
|
+
/**
|
|
136
|
+
* Set when the service has no config at or above its root: each file is then
|
|
137
|
+
* typed under the nearest `tsconfig.json`/`jsconfig.json` on its way up to
|
|
138
|
+
* the service root (carrick#1604), and the default project holds the rest.
|
|
139
|
+
*/
|
|
140
|
+
inferredService = false;
|
|
141
|
+
/** The per-directory answers of `fileConfigPath` for this service. */
|
|
142
|
+
fileConfigs = new Map();
|
|
143
|
+
/** The reference owner rule of each config a file was found under. */
|
|
144
|
+
ownerRules = new Map();
|
|
129
145
|
/** Each file's owning config (carrick#1604), resolved on first use. */
|
|
130
146
|
configPathFor;
|
|
131
147
|
/** Programs built for owning projects other than the named tsconfig. */
|
|
@@ -231,6 +247,7 @@ export class ProjectLoader {
|
|
|
231
247
|
}
|
|
232
248
|
else {
|
|
233
249
|
this.log('No tsconfig.json found, using default compiler options');
|
|
250
|
+
this.inferredService = true;
|
|
234
251
|
this.buildProject = (kind) => {
|
|
235
252
|
const project = new Project({
|
|
236
253
|
...this.fileSystemOption(),
|
|
@@ -239,7 +256,7 @@ export class ProjectLoader {
|
|
|
239
256
|
});
|
|
240
257
|
// Add source files from common locations
|
|
241
258
|
if (kind === 'listed')
|
|
242
|
-
this.addDefaultSourceFiles(project);
|
|
259
|
+
this.addDefaultSourceFiles(project, (file) => this.projectKeyFor(file) === '');
|
|
243
260
|
return project;
|
|
244
261
|
};
|
|
245
262
|
}
|
|
@@ -347,11 +364,18 @@ export class ProjectLoader {
|
|
|
347
364
|
*/
|
|
348
365
|
projectFromConfig(configPath, kind = 'listed') {
|
|
349
366
|
const imports = new ExternalImports();
|
|
367
|
+
const options = getCompilerOptionsFromTsConfig(configPath).options;
|
|
368
|
+
// TypeScript reads a `jsconfig.json` with an implicit depth of 2, which
|
|
369
|
+
// loads the JavaScript sources of installed packages; every other program
|
|
370
|
+
// here reads 0. A depth the config states is kept (carrick#1604).
|
|
371
|
+
if (this.inferredService && path.basename(configPath) === 'jsconfig.json' && !statesJsDepth(configPath)) {
|
|
372
|
+
options.maxNodeModuleJsDepth = 0;
|
|
373
|
+
}
|
|
350
374
|
const project = new Project({
|
|
351
375
|
...this.fileSystemOption(),
|
|
352
376
|
tsConfigFilePath: configPath,
|
|
353
377
|
// Over the tsconfig's own options: ts-morph lays these on top.
|
|
354
|
-
compilerOptions: sidecarCompilerOptions(optionsFor(kind,
|
|
378
|
+
compilerOptions: sidecarCompilerOptions(optionsFor(kind, options)),
|
|
355
379
|
skipAddingFilesFromTsConfig: kind === 'scripts',
|
|
356
380
|
// Each import resolves in its file's own module format (carrick#1619),
|
|
357
381
|
// a referenced project's unbuilt output reads as its source, under its
|
|
@@ -373,12 +397,33 @@ export class ProjectLoader {
|
|
|
373
397
|
* as before.
|
|
374
398
|
*/
|
|
375
399
|
projectKeyFor(file) {
|
|
400
|
+
if (this.inferredService)
|
|
401
|
+
return this.inferredKeyFor(file);
|
|
376
402
|
if (!this.namedTsconfigPath || file === undefined)
|
|
377
403
|
return '';
|
|
378
404
|
this.configPathFor ??= serviceConfigPath(this.namedTsconfigPath, (d) => this.logError(d));
|
|
379
405
|
const config = this.configPathFor(path.resolve(this.repoRoot, file));
|
|
380
406
|
return config === this.namedTsconfigPath ? '' : config;
|
|
381
407
|
}
|
|
408
|
+
/**
|
|
409
|
+
* `projectKeyFor` for a service with no config at its root: the owner of the
|
|
410
|
+
* nearest config on the file's way up to the service root, or `''` (the
|
|
411
|
+
* default project, under the inferred options) for a file with none.
|
|
412
|
+
*/
|
|
413
|
+
inferredKeyFor(file) {
|
|
414
|
+
if (file === undefined)
|
|
415
|
+
return '';
|
|
416
|
+
const absolute = path.resolve(this.repoRoot, file);
|
|
417
|
+
const config = fileConfigPath(absolute, this.repoRoot, this.fileConfigs);
|
|
418
|
+
if (config === undefined)
|
|
419
|
+
return '';
|
|
420
|
+
let owner = this.ownerRules.get(config);
|
|
421
|
+
if (!owner) {
|
|
422
|
+
owner = serviceConfigPath(config, (d) => this.logError(d));
|
|
423
|
+
this.ownerRules.set(config, owner);
|
|
424
|
+
}
|
|
425
|
+
return owner(absolute);
|
|
426
|
+
}
|
|
382
427
|
/**
|
|
383
428
|
* The project behind a key from `projectKeyFor`, built on first use and
|
|
384
429
|
* kept: a service whose files all belong to one project builds one.
|
|
@@ -457,6 +502,22 @@ export class ProjectLoader {
|
|
|
457
502
|
packageOf(filePath) {
|
|
458
503
|
return this.denoProject?.packageOf(filePath);
|
|
459
504
|
}
|
|
505
|
+
/**
|
|
506
|
+
* The installed package directory a resolved file lies in, and its name,
|
|
507
|
+
* when the module graph places it (carrick#2599). `undefined` for a project
|
|
508
|
+
* that did not resolve through Deno: there the path says it.
|
|
509
|
+
*/
|
|
510
|
+
packageDirectoryOf(filePath) {
|
|
511
|
+
return this.denoProject?.packageDirectoryOf(filePath);
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* How the module graph resolves `name` from `from`, as the program did
|
|
515
|
+
* (carrick#2599). `undefined` for a project that did not resolve through
|
|
516
|
+
* Deno: there Node resolution is the program's.
|
|
517
|
+
*/
|
|
518
|
+
resolveThroughGraph(name, from, options) {
|
|
519
|
+
return this.denoProject?.resolve(name, from, options);
|
|
520
|
+
}
|
|
460
521
|
/**
|
|
461
522
|
* Check if the project has been successfully initialized
|
|
462
523
|
*/
|
|
@@ -512,7 +573,7 @@ export class ProjectLoader {
|
|
|
512
573
|
* dependencies installed it cost 40 s and added nothing the compiler needs
|
|
513
574
|
* (carrick#749).
|
|
514
575
|
*/
|
|
515
|
-
addDefaultSourceFiles(project) {
|
|
576
|
+
addDefaultSourceFiles(project, keep = () => true) {
|
|
516
577
|
const globs = DEFAULT_SOURCE_PATTERNS.map((pattern) => path.join(this.repoRoot, pattern));
|
|
517
578
|
// Negated last so it applies to every pattern above it.
|
|
518
579
|
globs.push(`!${path.join(this.repoRoot, '**/node_modules/**')}`);
|
|
@@ -525,6 +586,8 @@ export class ProjectLoader {
|
|
|
525
586
|
return;
|
|
526
587
|
}
|
|
527
588
|
for (const filePath of filePaths) {
|
|
589
|
+
if (!keep(filePath))
|
|
590
|
+
continue;
|
|
528
591
|
try {
|
|
529
592
|
project.addSourceFileAtPath(filePath);
|
|
530
593
|
}
|