@metamask-previews/platform-api-docs 0.0.0-preview-6235c3779 → 0.0.0-preview-e15374a5d
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/cli.cjs +7 -4
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +7 -4
- package/dist/cli.mjs.map +1 -1
- package/dist/extraction.cjs +119 -150
- package/dist/extraction.cjs.map +1 -1
- package/dist/extraction.d.cts +0 -14
- package/dist/extraction.d.cts.map +1 -1
- package/dist/extraction.d.mts +0 -14
- package/dist/extraction.d.mts.map +1 -1
- package/dist/extraction.mjs +118 -148
- package/dist/extraction.mjs.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +0 -16
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +0 -16
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs.map +1 -1
- package/package.json +1 -1
package/dist/extraction.mjs
CHANGED
|
@@ -120,57 +120,63 @@ function hasDeprecatedJsDocTag(node) {
|
|
|
120
120
|
// Type-resolution helpers (powered by ts-morph's type checker)
|
|
121
121
|
// ---------------------------------------------------------------------------
|
|
122
122
|
/**
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
* name. Returns the method declaration when found.
|
|
123
|
+
* Locates the type that represents a method on a class (e.g.
|
|
124
|
+
* `Class['method']`), which itself comes from a messenger action handler.
|
|
126
125
|
*
|
|
127
|
-
* @param typeNode - The
|
|
128
|
-
* @returns The method declaration, or null.
|
|
126
|
+
* @param typeNode - The node that represents the indexed access.
|
|
127
|
+
* @returns The found method declaration, or null.
|
|
129
128
|
*/
|
|
130
|
-
function
|
|
131
|
-
if
|
|
129
|
+
function findClassMethodDeclaration(typeNode) {
|
|
130
|
+
// Fundamental check: if we don't have `Class['method']`, we can't do anything.
|
|
131
|
+
if (!NodeGuards.isIndexedAccessTypeNode(typeNode)) {
|
|
132
132
|
return null;
|
|
133
133
|
}
|
|
134
|
+
// The type that represents the class being accessed.
|
|
135
|
+
// EXAMPLE:
|
|
136
|
+
// FooController['someMethod']
|
|
137
|
+
// ^^^^^^^^^^^^^
|
|
134
138
|
const objectType = typeNode.getObjectTypeNode();
|
|
139
|
+
// The type that represents the property being accessed.
|
|
140
|
+
// EXAMPLE:
|
|
141
|
+
// FooController['someMethod']
|
|
142
|
+
// ^^^^^^^^^^^^
|
|
135
143
|
const indexType = typeNode.getIndexTypeNode();
|
|
136
|
-
//
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
return null;
|
|
140
|
-
}
|
|
141
|
-
// istanbul ignore next: the index in `Class['method']` is always a literal
|
|
142
|
-
// type node in valid handler syntax.
|
|
143
|
-
if (!NodeGuards.isLiteralTypeNode(indexType)) {
|
|
144
|
+
// To access a property on a type, it must be a type we can access properties of.
|
|
145
|
+
if (!NodeGuards.isTypeReference(objectType) ||
|
|
146
|
+
!NodeGuards.isLiteralTypeNode(indexType)) {
|
|
144
147
|
return null;
|
|
145
148
|
}
|
|
146
149
|
const indexLiteral = indexType.getLiteral();
|
|
147
|
-
//
|
|
148
|
-
// The first two land as `StringLiteral`; the bare template literal is a
|
|
149
|
-
// `NoSubstitutionTemplateLiteral`.
|
|
150
|
-
// istanbul ignore next: numeric/boolean indices aren't valid method names.
|
|
150
|
+
// Names of methods must be static strings; they cannot be template strings.
|
|
151
151
|
if (!NodeGuards.isStringLiteral(indexLiteral) &&
|
|
152
152
|
!NodeGuards.isNoSubstitutionTemplateLiteral(indexLiteral)) {
|
|
153
153
|
return null;
|
|
154
154
|
}
|
|
155
155
|
const methodName = indexLiteral.getLiteralValue();
|
|
156
|
+
// Reject qualified-name type names, as we need a plain identifier to
|
|
157
|
+
// resolve the symbol.
|
|
158
|
+
// EXAMPLE:
|
|
159
|
+
// import * as somePackage from '...';
|
|
160
|
+
// somePackage.FooController['someMethod']
|
|
161
|
+
// ^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
156
162
|
const classNameNode = objectType.getTypeName();
|
|
157
|
-
// istanbul ignore next: qualified-name class references aren't used in
|
|
158
|
-
// messenger handler types.
|
|
159
163
|
if (!NodeGuards.isIdentifier(classNameNode)) {
|
|
160
164
|
return null;
|
|
161
165
|
}
|
|
166
|
+
// Since we know we have a type reference, we can assume that we have a symbol.
|
|
167
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
162
168
|
const localSymbol = classNameNode.getSymbol();
|
|
163
|
-
//
|
|
164
|
-
// in
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}
|
|
168
|
-
//
|
|
169
|
-
//
|
|
169
|
+
// If we have a type imported from another file, ensure that when we access
|
|
170
|
+
// the declaration, it's the type declaration in the other file, not the
|
|
171
|
+
// import declaration in this file.
|
|
172
|
+
// EXAMPLE:
|
|
173
|
+
// import { FooController } from '@metamask/foo-controller';
|
|
174
|
+
// FooController['someMethod']
|
|
175
|
+
// ^^^^^^^^^^^^^
|
|
170
176
|
const symbol = localSymbol.getAliasedSymbol() ?? localSymbol;
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
177
|
+
for (const declaration of symbol.getDeclarations()) {
|
|
178
|
+
// We must have a class to treat the property on the object type as a
|
|
179
|
+
// method.
|
|
174
180
|
if (NodeGuards.isClassDeclaration(declaration)) {
|
|
175
181
|
const method = declaration.getMethod(methodName);
|
|
176
182
|
if (method) {
|
|
@@ -178,40 +184,37 @@ function resolveIndexedAccessMethod(typeNode) {
|
|
|
178
184
|
}
|
|
179
185
|
}
|
|
180
186
|
}
|
|
181
|
-
// istanbul ignore next: only reached if the indexed method isn't declared
|
|
182
|
-
// on any of the resolved class declarations, which doesn't happen in valid
|
|
183
|
-
// handler types.
|
|
184
187
|
return null;
|
|
185
188
|
}
|
|
186
189
|
// ---------------------------------------------------------------------------
|
|
187
190
|
// Method info
|
|
188
191
|
// ---------------------------------------------------------------------------
|
|
189
192
|
/**
|
|
190
|
-
* Build
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
+
* Build the textual signature of a class method — its parameter list and
|
|
194
|
+
* return type expressed as a TypeScript function type — so a handler that
|
|
195
|
+
* references `Class['method']` can be rendered as `(arg: T) => R` instead of
|
|
196
|
+
* the bare indexed-access syntax.
|
|
193
197
|
*
|
|
194
198
|
* @param method - The method declaration.
|
|
195
|
-
* @returns The
|
|
199
|
+
* @returns The signature, e.g. `(id: number) => Promise<string>`.
|
|
196
200
|
*/
|
|
197
|
-
function
|
|
201
|
+
function buildMethodSignature(method) {
|
|
198
202
|
const signatureParams = method
|
|
199
203
|
.getParameters()
|
|
200
204
|
.map((param) => {
|
|
205
|
+
const rest = param.isRestParameter() ? '...' : '';
|
|
201
206
|
const paramName = param.getNameNode().getText();
|
|
202
207
|
const optional = param.hasQuestionToken() ? '?' : '';
|
|
203
208
|
const typeNode = param.getTypeNode();
|
|
204
209
|
const paramType = typeNode ? typeNode.getText() : 'unknown';
|
|
205
|
-
return `${paramName}${optional}: ${paramType}`;
|
|
210
|
+
return `${rest}${paramName}${optional}: ${paramType}`;
|
|
206
211
|
})
|
|
207
212
|
.join(', ');
|
|
208
213
|
const returnTypeNode = method.getReturnTypeNode();
|
|
209
214
|
const returnType = returnTypeNode ? returnTypeNode.getText() : 'void';
|
|
210
215
|
// For async methods, the declared return type already includes `Promise<>`,
|
|
211
216
|
// so we don't need to wrap again.
|
|
212
|
-
|
|
213
|
-
const { description, params, returns } = extractJsDoc(method);
|
|
214
|
-
return { jsDoc: description, params, returns, signature };
|
|
217
|
+
return `(${signatureParams}) => ${returnType}`;
|
|
215
218
|
}
|
|
216
219
|
/**
|
|
217
220
|
* Looks for Messenger types in the source file (that is, those that are type
|
|
@@ -312,13 +315,16 @@ function recursivelyFindMessengerCapabilityTypeDeclarations(node, kind, visitedT
|
|
|
312
315
|
// ^^^^^^^
|
|
313
316
|
// // Good
|
|
314
317
|
// type Actions = FooControllerSomeAction;
|
|
318
|
+
// ^^^^^^^^^^^^^^^^^^^^^^^
|
|
315
319
|
// // Good
|
|
316
320
|
// type Actions = ControllerGetStateAction<...>;
|
|
321
|
+
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
317
322
|
if (!NodeGuards.isTypeReference(node)) {
|
|
318
323
|
return result;
|
|
319
324
|
}
|
|
320
325
|
const nameNode = node.getTypeName();
|
|
321
|
-
// Reject qualified-name type
|
|
326
|
+
// Reject qualified-name type names, as we need a plain identifier to
|
|
327
|
+
// resolve the symbol.
|
|
322
328
|
// EXAMPLE:
|
|
323
329
|
// import * as somePackage from '...';
|
|
324
330
|
// type Actions = somePackage.FooControllerSomeAction;
|
|
@@ -330,8 +336,8 @@ function recursivelyFindMessengerCapabilityTypeDeclarations(node, kind, visitedT
|
|
|
330
336
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
331
337
|
const localSymbol = nameNode.getSymbol();
|
|
332
338
|
// If we have a type imported from another file, ensure that when we access
|
|
333
|
-
// the declaration, it's the
|
|
334
|
-
//
|
|
339
|
+
// the declaration, it's the type declaration in the other file, not the
|
|
340
|
+
// import declaration in this file.
|
|
335
341
|
// EXAMPLE:
|
|
336
342
|
// import { FooControllerSomeAction } from '@metamask/foo-controller';
|
|
337
343
|
// type Actions = FooControllerSomeAction;
|
|
@@ -350,14 +356,22 @@ function recursivelyFindMessengerCapabilityTypeDeclarations(node, kind, visitedT
|
|
|
350
356
|
// If we have a type alias, then we have to handle a few scenarios.
|
|
351
357
|
// EXAMPLES:
|
|
352
358
|
// type FooControllerMethodActions = ...
|
|
359
|
+
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
353
360
|
// type FooControllerSomeAction = ...
|
|
361
|
+
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
354
362
|
if (NodeGuards.isTypeAliasDeclaration(declaration)) {
|
|
355
363
|
const body = declaration.getTypeNode();
|
|
356
|
-
// If
|
|
357
|
-
//
|
|
364
|
+
// If the body is a union type or a plain type reference (not a utility
|
|
365
|
+
// type), walk it.
|
|
366
|
+
// EXAMPLES:
|
|
358
367
|
// type FooControllerMethodActions = FooControllerSomeAction | FooControllerSomeOtherAction
|
|
359
|
-
//
|
|
360
|
-
|
|
368
|
+
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
369
|
+
// type DelegationControllerMethodActions = DelegationControllerSignDelegationAction
|
|
370
|
+
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
371
|
+
if (body &&
|
|
372
|
+
(NodeGuards.isUnionTypeNode(body) ||
|
|
373
|
+
(NodeGuards.isTypeReference(body) &&
|
|
374
|
+
body.getTypeArguments().length === 0))) {
|
|
361
375
|
const innerResult = recursivelyFindMessengerCapabilityTypeDeclarations(body, kind, result.visitedTypeDeclarations);
|
|
362
376
|
result.capabilityTypeDeclarations.push(...innerResult.capabilityTypeDeclarations);
|
|
363
377
|
for (const typeDeclaration of innerResult.visitedTypeDeclarations) {
|
|
@@ -365,32 +379,56 @@ function recursivelyFindMessengerCapabilityTypeDeclarations(node, kind, visitedT
|
|
|
365
379
|
}
|
|
366
380
|
continue;
|
|
367
381
|
}
|
|
368
|
-
//
|
|
369
|
-
//
|
|
370
|
-
//
|
|
371
|
-
//
|
|
372
|
-
//
|
|
373
|
-
//
|
|
374
|
-
//
|
|
375
|
-
if (body &&
|
|
376
|
-
|
|
377
|
-
|
|
382
|
+
// A TypeReference body with type arguments is a capability-type-
|
|
383
|
+
// constructor invocation (e.g. `ControllerGetStateAction<typeof name,
|
|
384
|
+
// State>`). Tag it so the constructor extractor can read `body`
|
|
385
|
+
// directly without re-checking its shape.
|
|
386
|
+
// EXAMPLE:
|
|
387
|
+
// type FooControllerSomeAction = ControllerGetStateAction<...>
|
|
388
|
+
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
389
|
+
if (body && NodeGuards.isTypeReference(body)) {
|
|
390
|
+
// Reject qualified-name constructor type names, as we need a plain
|
|
391
|
+
// identifier to match the constructor by name.
|
|
392
|
+
// EXAMPLE:
|
|
393
|
+
// // Bad
|
|
394
|
+
// import * as somePackage from '...';
|
|
395
|
+
// type FooControllerSomeAction = somePackage.ControllerGetStateAction<...>
|
|
396
|
+
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
397
|
+
const constructorTypeName = body.getTypeName();
|
|
398
|
+
if (NodeGuards.isIdentifier(constructorTypeName)) {
|
|
399
|
+
result.capabilityTypeDeclarations.push({
|
|
400
|
+
bodyShape: 'constructor',
|
|
401
|
+
kind,
|
|
402
|
+
declaration,
|
|
403
|
+
body,
|
|
404
|
+
typeName: constructorTypeName,
|
|
405
|
+
});
|
|
406
|
+
}
|
|
378
407
|
continue;
|
|
379
408
|
}
|
|
380
|
-
//
|
|
381
|
-
//
|
|
409
|
+
// Anything else (a type literal, intersection, conditional, …) gets
|
|
410
|
+
// tagged for the literal extractor, which knows how to read members
|
|
411
|
+
// off a type literal and rejects exotic shapes.
|
|
382
412
|
// EXAMPLE:
|
|
383
413
|
// type FooControllerSomeAction = { ... }
|
|
384
414
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
385
|
-
result.capabilityTypeDeclarations.push({
|
|
415
|
+
result.capabilityTypeDeclarations.push({
|
|
416
|
+
bodyShape: 'object',
|
|
417
|
+
kind,
|
|
418
|
+
declaration,
|
|
419
|
+
});
|
|
386
420
|
}
|
|
387
|
-
//
|
|
388
|
-
//
|
|
421
|
+
// Interfaces always carry their members directly — tag for the literal
|
|
422
|
+
// extractor.
|
|
389
423
|
// EXAMPLE:
|
|
390
424
|
// interface FooControllerSomeAction { ... }
|
|
391
425
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
392
426
|
else if (NodeGuards.isInterfaceDeclaration(declaration)) {
|
|
393
|
-
result.capabilityTypeDeclarations.push({
|
|
427
|
+
result.capabilityTypeDeclarations.push({
|
|
428
|
+
bodyShape: 'object',
|
|
429
|
+
kind,
|
|
430
|
+
declaration,
|
|
431
|
+
});
|
|
394
432
|
}
|
|
395
433
|
}
|
|
396
434
|
return result;
|
|
@@ -410,8 +448,10 @@ function recursivelyFindMessengerCapabilityTypeDeclarations(node, kind, visitedT
|
|
|
410
448
|
* (may be `null` if the type is ineligible for extraction).
|
|
411
449
|
*/
|
|
412
450
|
function extractFromMessengerCapabilityTypeDeclaration(capabilityTypeDeclaration, projectPath) {
|
|
413
|
-
|
|
414
|
-
tryToExtractFromCapabilityTypeConstructor(capabilityTypeDeclaration, projectPath)
|
|
451
|
+
if (capabilityTypeDeclaration.bodyShape === 'constructor') {
|
|
452
|
+
return tryToExtractFromCapabilityTypeConstructor(capabilityTypeDeclaration, projectPath);
|
|
453
|
+
}
|
|
454
|
+
return tryToExtractFromMessengerCapabilityTypeLiteral(capabilityTypeDeclaration, projectPath);
|
|
415
455
|
}
|
|
416
456
|
/**
|
|
417
457
|
* If a messenger capability type is a type alias or interface and its body is
|
|
@@ -431,7 +471,7 @@ function extractFromMessengerCapabilityTypeDeclaration(capabilityTypeDeclaration
|
|
|
431
471
|
*/
|
|
432
472
|
function tryToExtractFromMessengerCapabilityTypeLiteral(capabilityTypeDeclaration, projectPath) {
|
|
433
473
|
const { declaration, kind } = capabilityTypeDeclaration;
|
|
434
|
-
//
|
|
474
|
+
// We must have a object type alias or an interface, and the body must not be empty.
|
|
435
475
|
// EXAMPLES:
|
|
436
476
|
// // Good
|
|
437
477
|
// type FooControllerSomeAction = {
|
|
@@ -478,25 +518,14 @@ function tryToExtractFromMessengerCapabilityTypeLiteral(capabilityTypeDeclaratio
|
|
|
478
518
|
let handlerOrPayloadSignature = handlerOrPayloadPropertyTypeNode
|
|
479
519
|
.getText()
|
|
480
520
|
.trim();
|
|
481
|
-
|
|
482
|
-
// For actions
|
|
483
|
-
//
|
|
484
|
-
//
|
|
485
|
-
// TODO: We don't want to do this.
|
|
521
|
+
const { description: jsDoc, params, returns } = extractJsDoc(declaration);
|
|
522
|
+
// For actions that represent methods (e.g. `Class['method']`), walk the
|
|
523
|
+
// handler type to find the underlying handler signature
|
|
524
|
+
// (e.g. `(id: number) => Promise<string>`).
|
|
486
525
|
if (kind === 'action') {
|
|
487
|
-
const
|
|
488
|
-
if (
|
|
489
|
-
|
|
490
|
-
handlerOrPayloadSignature = info.signature;
|
|
491
|
-
if (!jsDoc && info.jsDoc) {
|
|
492
|
-
jsDoc = info.jsDoc;
|
|
493
|
-
}
|
|
494
|
-
if (params.length === 0 && info.params.length > 0) {
|
|
495
|
-
params = info.params;
|
|
496
|
-
}
|
|
497
|
-
if (!returns && info.returns) {
|
|
498
|
-
returns = info.returns;
|
|
499
|
-
}
|
|
526
|
+
const methodDeclaration = findClassMethodDeclaration(handlerOrPayloadPropertyTypeNode);
|
|
527
|
+
if (methodDeclaration) {
|
|
528
|
+
handlerOrPayloadSignature = buildMethodSignature(methodDeclaration);
|
|
500
529
|
}
|
|
501
530
|
}
|
|
502
531
|
const sourceFile = declaration.getSourceFile();
|
|
@@ -545,7 +574,7 @@ function getMessengerCapabilityTypeString(capabilityTypeProperties) {
|
|
|
545
574
|
// }
|
|
546
575
|
// type FooControllerSomeAction = {
|
|
547
576
|
// type: `${typeof CONTROLLER_NAME}:someAction`;
|
|
548
|
-
//
|
|
577
|
+
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
549
578
|
// }
|
|
550
579
|
const resolvedType = typeNode.getType();
|
|
551
580
|
if (resolvedType.isStringLiteral()) {
|
|
@@ -592,37 +621,7 @@ function findProperty(propertySignatures, name) {
|
|
|
592
621
|
* doesn't match.
|
|
593
622
|
*/
|
|
594
623
|
function tryToExtractFromCapabilityTypeConstructor(capabilityTypeDeclaration, projectPath) {
|
|
595
|
-
const { declaration, kind } = capabilityTypeDeclaration;
|
|
596
|
-
// Basic check: we need a type alias, otherwise the rest doesn't make sense.
|
|
597
|
-
// EXAMPLE:
|
|
598
|
-
// type FooControllerSomeAction = ...
|
|
599
|
-
if (!NodeGuards.isTypeAliasDeclaration(declaration)) {
|
|
600
|
-
return null;
|
|
601
|
-
}
|
|
602
|
-
// We want our type alias to be a reference to a utility type.
|
|
603
|
-
// Non-null assertion: We can assume the type has a body of some kind,
|
|
604
|
-
// otherwise it wouldn't compile.
|
|
605
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
606
|
-
const body = declaration.getTypeNode();
|
|
607
|
-
// We already determined in
|
|
608
|
-
// `recursivelyFindMessengerCapabilityTypeDeclarations` that the declaration
|
|
609
|
-
// is a type reference.
|
|
610
|
-
// TODO: Capture the following information ahead of time so we don't need to
|
|
611
|
-
// check this again.
|
|
612
|
-
// istanbul ignore next
|
|
613
|
-
if (!NodeGuards.isTypeReference(body)) {
|
|
614
|
-
return null;
|
|
615
|
-
}
|
|
616
|
-
const typeName = body.getTypeName();
|
|
617
|
-
// We already determined in
|
|
618
|
-
// `recursivelyFindMessengerCapabilityTypeDeclarations` that the type is an
|
|
619
|
-
// identifier.
|
|
620
|
-
// TODO: Capture the following information ahead of time so we don't need to
|
|
621
|
-
// check this again.
|
|
622
|
-
// istanbul ignore next
|
|
623
|
-
if (!NodeGuards.isIdentifier(typeName)) {
|
|
624
|
-
return null;
|
|
625
|
-
}
|
|
624
|
+
const { declaration, kind, body, typeName } = capabilityTypeDeclaration;
|
|
626
625
|
// The name of the utility type should be either `ControllerGetStateAction`
|
|
627
626
|
// (for actions) or `ControllerStateChangeEvent` (for events).
|
|
628
627
|
// EXAMPLES:
|
|
@@ -724,33 +723,4 @@ export function extractFromSourceFile(sourceFile, projectPath) {
|
|
|
724
723
|
}
|
|
725
724
|
return messengerCapabilityPackets;
|
|
726
725
|
}
|
|
727
|
-
/**
|
|
728
|
-
* Convenience wrapper: extract from a single file by path. Loads the file
|
|
729
|
-
* and any sibling files in its parent directory into a fresh Project so
|
|
730
|
-
* relative imports resolve.
|
|
731
|
-
*
|
|
732
|
-
* For batch operations across many files, prefer
|
|
733
|
-
* {@link createExtractionProject} + {@link extractFromSourceFile} so one
|
|
734
|
-
* Project amortizes the type-checker setup across the whole run.
|
|
735
|
-
*
|
|
736
|
-
* @param filePath - The absolute path to the TypeScript file.
|
|
737
|
-
* @param projectPath - Base path for computing relative source paths.
|
|
738
|
-
* @returns A promise that resolves to the extracted capability list.
|
|
739
|
-
*/
|
|
740
|
-
export async function extractFromFile(filePath, projectPath) {
|
|
741
|
-
const project = createExtractionProject();
|
|
742
|
-
const parentDir = path.dirname(filePath);
|
|
743
|
-
// Load the file's directory so single-hop relative imports resolve.
|
|
744
|
-
project.addSourceFilesAtPaths([
|
|
745
|
-
path.join(parentDir, '**/*.ts'),
|
|
746
|
-
path.join(parentDir, '**/*.d.cts'),
|
|
747
|
-
]);
|
|
748
|
-
const sourceFile = project.getSourceFile(filePath);
|
|
749
|
-
// istanbul ignore next: the path always exists since the caller just
|
|
750
|
-
// wrote the file or it was scanned from disk.
|
|
751
|
-
if (!sourceFile) {
|
|
752
|
-
return [];
|
|
753
|
-
}
|
|
754
|
-
return extractFromSourceFile(sourceFile, projectPath);
|
|
755
|
-
}
|
|
756
726
|
//# sourceMappingURL=extraction.mjs.map
|