jsdoczoom 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/barrel.js +47 -28
- package/dist/cli.js +108 -47
- package/dist/drilldown.js +12 -7
- package/dist/file-discovery.js +15 -10
- package/dist/index.js +6 -1
- package/dist/jsdoc-parser.js +3 -3
- package/dist/lint.js +3 -3
- package/dist/search.js +226 -0
- package/dist/text-format.js +3 -3
- package/dist/type-declarations.js +164 -33
- package/dist/validate.js +3 -3
- package/package.json +1 -1
- package/types/barrel.d.ts +2 -2
- package/types/file-discovery.d.ts +2 -2
- package/types/index.d.ts +6 -1
- package/types/jsdoc-parser.d.ts +3 -1
- package/types/search.d.ts +44 -0
- package/types/type-declarations.d.ts +53 -1
package/dist/barrel.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { access, readdir } from "node:fs/promises";
|
|
2
2
|
import { basename, dirname, join, relative, resolve } from "node:path";
|
|
3
3
|
/**
|
|
4
4
|
* Barrel detection and child discovery for index.ts/index.tsx files.
|
|
@@ -34,29 +34,34 @@ export function isBarrel(filePath) {
|
|
|
34
34
|
* @param _cwd - Working directory (unused, kept for API consistency)
|
|
35
35
|
* @returns Sorted array of absolute paths to child files
|
|
36
36
|
*/
|
|
37
|
-
export function getBarrelChildren(barrelPath, _cwd) {
|
|
37
|
+
export async function getBarrelChildren(barrelPath, _cwd) {
|
|
38
38
|
const dir = dirname(barrelPath);
|
|
39
39
|
const barrelName = basename(barrelPath);
|
|
40
|
-
let
|
|
40
|
+
let rawEntries;
|
|
41
41
|
try {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
.
|
|
42
|
+
const dirents = await readdir(dir, { withFileTypes: true });
|
|
43
|
+
rawEntries = dirents.map((e) => ({
|
|
44
|
+
name: e.name,
|
|
45
|
+
isDirectory: e.isDirectory(),
|
|
46
|
+
}));
|
|
47
|
+
} catch {
|
|
48
|
+
// Directory unreadable (permissions, deleted, etc.) — no children discoverable
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
const entries = (
|
|
52
|
+
await Promise.all(
|
|
53
|
+
rawEntries.map(async (entry) => {
|
|
45
54
|
if (entry.isDirectory) {
|
|
46
|
-
|
|
47
|
-
const childBarrel = findChildBarrel(resolve(dir, entry.name));
|
|
55
|
+
const childBarrel = await findChildBarrel(resolve(dir, entry.name));
|
|
48
56
|
return childBarrel ? [childBarrel] : [];
|
|
49
57
|
}
|
|
50
|
-
// Sibling file: must be .ts/.tsx, not .d.ts, not the barrel itself
|
|
51
58
|
if (isTsFile(entry.name) && entry.name !== barrelName) {
|
|
52
59
|
return [resolve(dir, entry.name)];
|
|
53
60
|
}
|
|
54
61
|
return [];
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return [];
|
|
59
|
-
}
|
|
62
|
+
}),
|
|
63
|
+
)
|
|
64
|
+
).flat();
|
|
60
65
|
return entries.sort();
|
|
61
66
|
}
|
|
62
67
|
/**
|
|
@@ -72,16 +77,21 @@ function isTsFile(name) {
|
|
|
72
77
|
* index.ts takes priority over index.tsx.
|
|
73
78
|
* Returns the absolute path to the barrel, or null if none found.
|
|
74
79
|
*/
|
|
75
|
-
function findChildBarrel(subdirPath) {
|
|
80
|
+
async function findChildBarrel(subdirPath) {
|
|
76
81
|
const tsPath = resolve(subdirPath, "index.ts");
|
|
77
|
-
|
|
82
|
+
try {
|
|
83
|
+
await access(tsPath);
|
|
78
84
|
return tsPath;
|
|
85
|
+
} catch {
|
|
86
|
+
// no index.ts
|
|
79
87
|
}
|
|
80
88
|
const tsxPath = resolve(subdirPath, "index.tsx");
|
|
81
|
-
|
|
89
|
+
try {
|
|
90
|
+
await access(tsxPath);
|
|
82
91
|
return tsxPath;
|
|
92
|
+
} catch {
|
|
93
|
+
return null;
|
|
83
94
|
}
|
|
84
|
-
return null;
|
|
85
95
|
}
|
|
86
96
|
/** Minimum number of .ts/.tsx files in a directory to require a barrel. */
|
|
87
97
|
export const BARREL_THRESHOLD = 3;
|
|
@@ -89,7 +99,7 @@ export const BARREL_THRESHOLD = 3;
|
|
|
89
99
|
* Find directories with more than BARREL_THRESHOLD .ts/.tsx files
|
|
90
100
|
* that lack a barrel file (index.ts or index.tsx).
|
|
91
101
|
*/
|
|
92
|
-
export function findMissingBarrels(filePaths, cwd) {
|
|
102
|
+
export async function findMissingBarrels(filePaths, cwd) {
|
|
93
103
|
const dirCounts = new Map();
|
|
94
104
|
for (const filePath of filePaths) {
|
|
95
105
|
if (isBarrel(filePath)) continue;
|
|
@@ -97,14 +107,23 @@ export function findMissingBarrels(filePaths, cwd) {
|
|
|
97
107
|
dirCounts.set(dir, (dirCounts.get(dir) ?? 0) + 1);
|
|
98
108
|
}
|
|
99
109
|
const missing = [];
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
110
|
+
await Promise.all(
|
|
111
|
+
[...dirCounts.entries()].map(async ([dir, count]) => {
|
|
112
|
+
if (count <= BARREL_THRESHOLD) return;
|
|
113
|
+
const [tsExists, tsxExists] = await Promise.all([
|
|
114
|
+
access(join(dir, "index.ts")).then(
|
|
115
|
+
() => true,
|
|
116
|
+
() => false,
|
|
117
|
+
),
|
|
118
|
+
access(join(dir, "index.tsx")).then(
|
|
119
|
+
() => true,
|
|
120
|
+
() => false,
|
|
121
|
+
),
|
|
122
|
+
]);
|
|
123
|
+
if (!tsExists && !tsxExists) {
|
|
124
|
+
missing.push(relative(cwd, dir) || ".");
|
|
125
|
+
}
|
|
126
|
+
}),
|
|
127
|
+
);
|
|
109
128
|
return missing.sort();
|
|
110
129
|
}
|
package/dist/cli.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
3
|
import { dirname, resolve } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { drilldown, drilldownFiles } from "./drilldown.js";
|
|
6
6
|
import { JsdocError } from "./errors.js";
|
|
7
7
|
import { lint, lintFiles } from "./lint.js";
|
|
8
|
+
import { search, searchFiles } from "./search.js";
|
|
8
9
|
import { parseSelector } from "./selector.js";
|
|
9
10
|
import { RULE_EXPLANATIONS, SKILL_TEXT } from "./skill-text.js";
|
|
10
11
|
import { formatTextOutput } from "./text-format.js";
|
|
@@ -28,48 +29,54 @@ Each file has four detail levels (1-indexed): @1 summary, @2 description,
|
|
|
28
29
|
Options:
|
|
29
30
|
-h, --help Show this help text
|
|
30
31
|
-v, --version Show version number
|
|
31
|
-
-c, --check
|
|
32
|
-
-l, --lint
|
|
32
|
+
-c, --check Validate file-level structure (has JSDoc block, @summary, description)
|
|
33
|
+
-l, --lint Lint comprehensive JSDoc quality (file-level + function-level tags)
|
|
33
34
|
-s, --skill Print JSDoc writing guidelines
|
|
34
35
|
--json Output as JSON (default is plain text)
|
|
35
36
|
--pretty Format JSON output with 2-space indent (use with --json)
|
|
36
37
|
--limit N Max results shown (default 500)
|
|
37
38
|
--no-gitignore Include files ignored by .gitignore
|
|
39
|
+
--search <query> Search files by regex pattern
|
|
38
40
|
--disable-cache Skip all cache operations
|
|
39
41
|
--cache-directory Override cache directory (default: system temp)
|
|
40
42
|
--explain-rule R Explain a lint rule with examples (e.g. jsdoc/informative-docs)
|
|
41
43
|
|
|
42
44
|
Selector:
|
|
43
45
|
A glob pattern or file path, optionally with @depth suffix (1-4).
|
|
46
|
+
|
|
44
47
|
Examples:
|
|
45
48
|
jsdoczoom src/**/*.ts # All .ts files at depth 1 (summary)
|
|
46
|
-
jsdoczoom src/
|
|
49
|
+
jsdoczoom src/foo.ts@2 # Single file at depth 2 (description)
|
|
47
50
|
jsdoczoom **/*.ts@3 # All .ts files at depth 3 (type decls)
|
|
48
51
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
Search (--search):
|
|
53
|
+
Searches all **/*.{ts,tsx} files (or a selector's file set) by regex.
|
|
54
|
+
|
|
55
|
+
Examples:
|
|
56
|
+
jsdoczoom --search "CacheConfig" # find where CacheConfig is used
|
|
57
|
+
jsdoczoom --search "auth-loader" # searches file name
|
|
58
|
+
jsdoczoom src/*.ts --search "TODO|FIXME" # restrict to a file subset
|
|
54
59
|
|
|
55
60
|
Output:
|
|
56
61
|
Plain text by default. Each item has a "# path@depth" header followed by
|
|
57
62
|
content. Use the header value as the next selector to drill deeper.
|
|
58
|
-
|
|
63
|
+
|
|
64
|
+
Use --json for machine-parseable JSON output, use "next_id" to drill deeper.
|
|
59
65
|
|
|
60
66
|
Type declarations (@3) include source line annotations (// LN or // LN-LM)
|
|
61
67
|
so you can locate the implementation in the source file.
|
|
62
68
|
|
|
69
|
+
Stdin:
|
|
70
|
+
Pipe file paths one per line (useful with -c/-l for targeted validation):
|
|
71
|
+
git diff --name-only | jsdoczoom -c # validate changed files
|
|
72
|
+
cat filelist.txt | jsdoczoom -l # lint a specific set of files
|
|
73
|
+
|
|
63
74
|
Barrel gating (glob mode):
|
|
64
75
|
A barrel's @summary and description reflect the cumulative functionality
|
|
65
76
|
of its directory's children, not the barrel file itself. Barrels with a
|
|
66
77
|
@summary gate sibling files at depths 1-2. At depth 3 the barrel
|
|
67
78
|
disappears and its children appear at depth 1.
|
|
68
79
|
|
|
69
|
-
Modes:
|
|
70
|
-
-c Validate file-level structure (has JSDoc block, @summary, description)
|
|
71
|
-
-l Lint comprehensive JSDoc quality (file-level + function-level tags)
|
|
72
|
-
|
|
73
80
|
Exit codes:
|
|
74
81
|
0 Success (all files pass)
|
|
75
82
|
1 Runtime error (invalid arguments, missing files)
|
|
@@ -79,12 +86,7 @@ Workflow:
|
|
|
79
86
|
$ jsdoczoom src/**/*.ts # list summaries
|
|
80
87
|
$ jsdoczoom src/utils@2 # drill into description
|
|
81
88
|
$ jsdoczoom src/utils@3 # see type declarations
|
|
82
|
-
|
|
83
|
-
Pipe examples:
|
|
84
|
-
$ jsdoczoom src/utils.ts@3 | grep "functionName" # find symbol + source line
|
|
85
|
-
$ jsdoczoom src/utils.ts@3 | grep "// L" # list all declarations with lines
|
|
86
|
-
$ jsdoczoom src/**/*.ts | grep "^#" # list all file headers
|
|
87
|
-
$ grep -rl "term" src/ --include="*.ts" | jsdoczoom # describe matching files
|
|
89
|
+
$ jsdoczoom src/**/*.ts | grep "^#" # list all file headers
|
|
88
90
|
`;
|
|
89
91
|
/**
|
|
90
92
|
* Parse a flag that requires a value argument.
|
|
@@ -111,6 +113,7 @@ function parseArgs(args) {
|
|
|
111
113
|
cacheDirectory: undefined,
|
|
112
114
|
explainRule: undefined,
|
|
113
115
|
selectorArg: undefined,
|
|
116
|
+
searchQuery: undefined,
|
|
114
117
|
};
|
|
115
118
|
for (let i = 0; i < args.length; i++) {
|
|
116
119
|
const arg = args[i];
|
|
@@ -170,6 +173,15 @@ function parseArgs(args) {
|
|
|
170
173
|
i = nextIndex;
|
|
171
174
|
continue;
|
|
172
175
|
}
|
|
176
|
+
if (arg === "--search") {
|
|
177
|
+
const { value, nextIndex } = parseValueFlag(args, i);
|
|
178
|
+
if (value === undefined) {
|
|
179
|
+
throw new JsdocError("INVALID_SELECTOR", "--search requires a value");
|
|
180
|
+
}
|
|
181
|
+
parsed.searchQuery = value;
|
|
182
|
+
i = nextIndex;
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
173
185
|
// Unknown flag or positional arg
|
|
174
186
|
if (arg.startsWith("-")) {
|
|
175
187
|
throw new JsdocError("INVALID_SELECTOR", `Unrecognized option: ${arg}`);
|
|
@@ -212,10 +224,22 @@ async function processStdin(
|
|
|
212
224
|
limit,
|
|
213
225
|
cwd,
|
|
214
226
|
cacheConfig,
|
|
227
|
+
searchQuery,
|
|
215
228
|
) {
|
|
216
229
|
const stdinPaths = parseStdinPaths(stdin, cwd);
|
|
217
230
|
const depth =
|
|
218
231
|
selectorArg !== undefined ? extractDepthFromArg(selectorArg) : undefined;
|
|
232
|
+
if (searchQuery !== undefined) {
|
|
233
|
+
const result = await searchFiles(
|
|
234
|
+
stdinPaths,
|
|
235
|
+
searchQuery,
|
|
236
|
+
cwd,
|
|
237
|
+
limit,
|
|
238
|
+
cacheConfig,
|
|
239
|
+
);
|
|
240
|
+
writeDrilldownResult(result, json, pretty);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
219
243
|
if (lintMode) {
|
|
220
244
|
const result = await lintFiles(stdinPaths, cwd, limit, cacheConfig);
|
|
221
245
|
writeLintResult(result, pretty);
|
|
@@ -246,10 +270,23 @@ async function processSelector(
|
|
|
246
270
|
gitignore,
|
|
247
271
|
cwd,
|
|
248
272
|
cacheConfig,
|
|
273
|
+
searchQuery,
|
|
249
274
|
) {
|
|
250
275
|
const selector = selectorArg
|
|
251
276
|
? parseSelector(selectorArg)
|
|
252
277
|
: { type: "glob", pattern: "**/*.{ts,tsx}", depth: undefined };
|
|
278
|
+
if (searchQuery !== undefined) {
|
|
279
|
+
const result = await search(
|
|
280
|
+
{ type: selector.type, pattern: selector.pattern, depth: undefined },
|
|
281
|
+
searchQuery,
|
|
282
|
+
cwd,
|
|
283
|
+
gitignore,
|
|
284
|
+
limit,
|
|
285
|
+
cacheConfig,
|
|
286
|
+
);
|
|
287
|
+
writeDrilldownResult(result, json, pretty);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
253
290
|
if (lintMode) {
|
|
254
291
|
const result = await lint(selector, cwd, limit, gitignore, cacheConfig);
|
|
255
292
|
writeLintResult(result, pretty);
|
|
@@ -291,13 +328,13 @@ function handleHelp() {
|
|
|
291
328
|
/**
|
|
292
329
|
* Handle --version flag by reading and printing version from package.json.
|
|
293
330
|
*/
|
|
294
|
-
function handleVersion() {
|
|
331
|
+
async function handleVersion() {
|
|
295
332
|
const pkgPath = resolve(
|
|
296
333
|
dirname(fileURLToPath(import.meta.url)),
|
|
297
334
|
"..",
|
|
298
335
|
"package.json",
|
|
299
336
|
);
|
|
300
|
-
const pkg = JSON.parse(
|
|
337
|
+
const pkg = JSON.parse(await readFile(pkgPath, "utf-8"));
|
|
301
338
|
void process.stdout.write(`${pkg.version}\n`);
|
|
302
339
|
}
|
|
303
340
|
/**
|
|
@@ -323,37 +360,59 @@ function handleExplainRule(ruleName) {
|
|
|
323
360
|
),
|
|
324
361
|
);
|
|
325
362
|
}
|
|
363
|
+
/**
|
|
364
|
+
* Handle early-exit flags that print output and return without processing files.
|
|
365
|
+
* Returns true if an early-exit flag was handled.
|
|
366
|
+
*/
|
|
367
|
+
async function handleEarlyExitFlags(parsed) {
|
|
368
|
+
if (parsed.help) {
|
|
369
|
+
handleHelp();
|
|
370
|
+
return true;
|
|
371
|
+
}
|
|
372
|
+
if (parsed.version) {
|
|
373
|
+
await handleVersion();
|
|
374
|
+
return true;
|
|
375
|
+
}
|
|
376
|
+
if (parsed.skillMode) {
|
|
377
|
+
handleSkill();
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
if (parsed.explainRule !== undefined) {
|
|
381
|
+
handleExplainRule(parsed.explainRule);
|
|
382
|
+
return true;
|
|
383
|
+
}
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Validate that mode flags are not used in incompatible combinations.
|
|
388
|
+
* Returns true if validation passed (no conflicts), false if an error was written.
|
|
389
|
+
*/
|
|
390
|
+
function validateModeCombinations(parsed) {
|
|
391
|
+
if (parsed.checkMode && parsed.lintMode) {
|
|
392
|
+
writeError(
|
|
393
|
+
new JsdocError("INVALID_SELECTOR", "Cannot use -c and -l together"),
|
|
394
|
+
);
|
|
395
|
+
return false;
|
|
396
|
+
}
|
|
397
|
+
if (
|
|
398
|
+
parsed.searchQuery !== undefined &&
|
|
399
|
+
(parsed.checkMode || parsed.lintMode)
|
|
400
|
+
) {
|
|
401
|
+
writeError(
|
|
402
|
+
new JsdocError("INVALID_SELECTOR", "Cannot use --search with -c or -l"),
|
|
403
|
+
);
|
|
404
|
+
return false;
|
|
405
|
+
}
|
|
406
|
+
return true;
|
|
407
|
+
}
|
|
326
408
|
/**
|
|
327
409
|
* Main CLI entry point. Exported for testability.
|
|
328
410
|
*/
|
|
329
411
|
export async function main(args, stdin) {
|
|
330
412
|
try {
|
|
331
413
|
const parsed = parseArgs(args);
|
|
332
|
-
|
|
333
|
-
if (parsed
|
|
334
|
-
handleHelp();
|
|
335
|
-
return;
|
|
336
|
-
}
|
|
337
|
-
if (parsed.version) {
|
|
338
|
-
handleVersion();
|
|
339
|
-
return;
|
|
340
|
-
}
|
|
341
|
-
if (parsed.skillMode) {
|
|
342
|
-
handleSkill();
|
|
343
|
-
return;
|
|
344
|
-
}
|
|
345
|
-
if (parsed.explainRule !== undefined) {
|
|
346
|
-
handleExplainRule(parsed.explainRule);
|
|
347
|
-
return;
|
|
348
|
-
}
|
|
349
|
-
// Validate mode combinations
|
|
350
|
-
if (parsed.checkMode && parsed.lintMode) {
|
|
351
|
-
writeError(
|
|
352
|
-
new JsdocError("INVALID_SELECTOR", "Cannot use -c and -l together"),
|
|
353
|
-
);
|
|
354
|
-
return;
|
|
355
|
-
}
|
|
356
|
-
// Build cache config and process files
|
|
414
|
+
if (await handleEarlyExitFlags(parsed)) return;
|
|
415
|
+
if (!validateModeCombinations(parsed)) return;
|
|
357
416
|
const cacheConfig = {
|
|
358
417
|
enabled: !parsed.disableCache,
|
|
359
418
|
directory: parsed.cacheDirectory ?? DEFAULT_CACHE_DIR,
|
|
@@ -370,6 +429,7 @@ export async function main(args, stdin) {
|
|
|
370
429
|
parsed.limit,
|
|
371
430
|
cwd,
|
|
372
431
|
cacheConfig,
|
|
432
|
+
parsed.searchQuery,
|
|
373
433
|
);
|
|
374
434
|
} else {
|
|
375
435
|
await processSelector(
|
|
@@ -382,6 +442,7 @@ export async function main(args, stdin) {
|
|
|
382
442
|
parsed.gitignore,
|
|
383
443
|
cwd,
|
|
384
444
|
cacheConfig,
|
|
445
|
+
parsed.searchQuery,
|
|
385
446
|
);
|
|
386
447
|
}
|
|
387
448
|
} catch (error) {
|
package/dist/drilldown.js
CHANGED
|
@@ -32,8 +32,13 @@ function buildLevels(info, typeDeclarations, fileContent) {
|
|
|
32
32
|
return [
|
|
33
33
|
summary !== null ? { text: summary } : null,
|
|
34
34
|
description !== null ? { text: description } : null,
|
|
35
|
-
{
|
|
36
|
-
|
|
35
|
+
{
|
|
36
|
+
text:
|
|
37
|
+
typeDeclarations.length > 0
|
|
38
|
+
? `\`\`\`typescript\n${typeDeclarations}\n\`\`\``
|
|
39
|
+
: typeDeclarations,
|
|
40
|
+
},
|
|
41
|
+
{ text: `\`\`\`typescript\n${fileContent}\n\`\`\`` },
|
|
37
42
|
];
|
|
38
43
|
}
|
|
39
44
|
/**
|
|
@@ -177,7 +182,7 @@ async function gatherBarrelInfos(barrelPaths, cwd, config) {
|
|
|
177
182
|
const info = await processWithCache(config, "drilldown", content, () =>
|
|
178
183
|
parseFileSummaries(barrelPath),
|
|
179
184
|
);
|
|
180
|
-
const children = getBarrelChildren(barrelPath, cwd);
|
|
185
|
+
const children = await getBarrelChildren(barrelPath, cwd);
|
|
181
186
|
return {
|
|
182
187
|
type: "info",
|
|
183
188
|
data: {
|
|
@@ -328,7 +333,7 @@ export async function drilldown(
|
|
|
328
333
|
) {
|
|
329
334
|
const depth = selector.depth ?? 1;
|
|
330
335
|
if (selector.type === "path") {
|
|
331
|
-
const files = discoverFiles(selector.pattern, cwd, gitignore);
|
|
336
|
+
const files = await discoverFiles(selector.pattern, cwd, gitignore);
|
|
332
337
|
if (files.length > 1) {
|
|
333
338
|
// Directory expanded to multiple files — route through glob pipeline
|
|
334
339
|
const results = await processGlobWithBarrels(files, depth, cwd, config);
|
|
@@ -358,7 +363,7 @@ export async function drilldown(
|
|
|
358
363
|
barrelEffectiveDepth = 3;
|
|
359
364
|
}
|
|
360
365
|
if (barrelEffectiveDepth >= 3) {
|
|
361
|
-
const children = getBarrelChildren(filePath, cwd);
|
|
366
|
+
const children = await getBarrelChildren(filePath, cwd);
|
|
362
367
|
const childDepth = barrelEffectiveDepth - 2;
|
|
363
368
|
const results = await collectSafeResults(
|
|
364
369
|
children,
|
|
@@ -385,7 +390,7 @@ export async function drilldown(
|
|
|
385
390
|
return { items, truncated: false };
|
|
386
391
|
}
|
|
387
392
|
// Glob selector — apply barrel gating
|
|
388
|
-
const files = discoverFiles(selector.pattern, cwd, gitignore);
|
|
393
|
+
const files = await discoverFiles(selector.pattern, cwd, gitignore);
|
|
389
394
|
if (files.length === 0) {
|
|
390
395
|
throw new JsdocError(
|
|
391
396
|
"NO_FILES_MATCHED",
|
|
@@ -422,7 +427,7 @@ export async function drilldownFiles(
|
|
|
422
427
|
config = DEFAULT_CACHE_CONFIG,
|
|
423
428
|
) {
|
|
424
429
|
const d = depth ?? 1;
|
|
425
|
-
const ig = loadGitignore(cwd);
|
|
430
|
+
const ig = await loadGitignore(cwd);
|
|
426
431
|
const tsFiles = filePaths.filter((f) => {
|
|
427
432
|
if (!(f.endsWith(".ts") || f.endsWith(".tsx")) || f.endsWith(".d.ts")) {
|
|
428
433
|
return false;
|
package/dist/file-discovery.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFile, stat } from "node:fs/promises";
|
|
2
2
|
import { dirname, join, relative, resolve } from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { glob } from "glob";
|
|
4
4
|
import ignore from "ignore";
|
|
5
5
|
import { JsdocError } from "./errors.js";
|
|
6
6
|
/**
|
|
@@ -15,13 +15,13 @@ import { JsdocError } from "./errors.js";
|
|
|
15
15
|
* Walk from `cwd` up to the filesystem root, collecting .gitignore entries.
|
|
16
16
|
* Returns an Ignore instance loaded with all discovered rules.
|
|
17
17
|
*/
|
|
18
|
-
export function loadGitignore(cwd) {
|
|
18
|
+
export async function loadGitignore(cwd) {
|
|
19
19
|
const ig = ignore();
|
|
20
20
|
let dir = resolve(cwd);
|
|
21
21
|
while (true) {
|
|
22
22
|
const gitignorePath = join(dir, ".gitignore");
|
|
23
|
-
|
|
24
|
-
const content =
|
|
23
|
+
try {
|
|
24
|
+
const content = await readFile(gitignorePath, "utf-8");
|
|
25
25
|
const prefix = relative(cwd, dir);
|
|
26
26
|
const lines = content
|
|
27
27
|
.split("\n")
|
|
@@ -32,6 +32,8 @@ export function loadGitignore(cwd) {
|
|
|
32
32
|
// relative to `cwd`, which is where glob results are anchored.
|
|
33
33
|
ig.add(prefix ? `${prefix}/${line}` : line);
|
|
34
34
|
}
|
|
35
|
+
} catch {
|
|
36
|
+
// No .gitignore at this level, continue walking up
|
|
35
37
|
}
|
|
36
38
|
const parent = dirname(dir);
|
|
37
39
|
if (parent === dir) break;
|
|
@@ -52,25 +54,28 @@ export function loadGitignore(cwd) {
|
|
|
52
54
|
* @returns Array of absolute file paths
|
|
53
55
|
* @throws {JsdocError} FILE_NOT_FOUND when a direct path does not exist
|
|
54
56
|
*/
|
|
55
|
-
export function discoverFiles(pattern, cwd, gitignore = true) {
|
|
57
|
+
export async function discoverFiles(pattern, cwd, gitignore = true) {
|
|
56
58
|
const hasGlobChars = /[*?[\]{]/.test(pattern);
|
|
57
59
|
if (hasGlobChars) {
|
|
58
|
-
const matches =
|
|
60
|
+
const matches = await glob(pattern, { cwd, absolute: true });
|
|
59
61
|
let filtered = matches.filter(
|
|
60
62
|
(f) => (f.endsWith(".ts") || f.endsWith(".tsx")) && !f.endsWith(".d.ts"),
|
|
61
63
|
);
|
|
62
64
|
if (gitignore) {
|
|
63
|
-
const ig = loadGitignore(cwd);
|
|
65
|
+
const ig = await loadGitignore(cwd);
|
|
64
66
|
filtered = filtered.filter((abs) => !ig.ignores(relative(cwd, abs)));
|
|
65
67
|
}
|
|
66
68
|
return filtered.sort();
|
|
67
69
|
}
|
|
68
70
|
// Direct path
|
|
69
71
|
const resolved = resolve(cwd, pattern);
|
|
70
|
-
|
|
72
|
+
let statResult;
|
|
73
|
+
try {
|
|
74
|
+
statResult = await stat(resolved);
|
|
75
|
+
} catch {
|
|
71
76
|
throw new JsdocError("FILE_NOT_FOUND", `File not found: ${pattern}`);
|
|
72
77
|
}
|
|
73
|
-
if (
|
|
78
|
+
if (statResult.isDirectory()) {
|
|
74
79
|
return discoverFiles(`${resolved}/**`, cwd, gitignore);
|
|
75
80
|
}
|
|
76
81
|
return [resolved];
|
package/dist/index.js
CHANGED
|
@@ -13,8 +13,13 @@ export { JsdocError } from "./errors.js";
|
|
|
13
13
|
export { discoverFiles } from "./file-discovery.js";
|
|
14
14
|
export { extractFileJsdoc, parseFileSummaries } from "./jsdoc-parser.js";
|
|
15
15
|
export { lint, lintFiles } from "./lint.js";
|
|
16
|
+
export { search, searchFiles } from "./search.js";
|
|
16
17
|
export { parseSelector } from "./selector.js";
|
|
17
18
|
export { formatTextOutput } from "./text-format.js";
|
|
18
|
-
export {
|
|
19
|
+
export {
|
|
20
|
+
extractSourceBlocks,
|
|
21
|
+
generateTypeDeclarations,
|
|
22
|
+
splitDeclarations,
|
|
23
|
+
} from "./type-declarations.js";
|
|
19
24
|
export { DEFAULT_CACHE_DIR, VALIDATION_STATUS_PRIORITY } from "./types.js";
|
|
20
25
|
export { validate, validateFiles } from "./validate.js";
|
package/dist/jsdoc-parser.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
2
|
import ts from "typescript";
|
|
3
3
|
import { JsdocError } from "./errors.js";
|
|
4
4
|
import { appendText, DESCRIPTION_TAGS } from "./types.js";
|
|
@@ -220,10 +220,10 @@ function parseJsdocContent(jsdocText) {
|
|
|
220
220
|
* Reads the file, extracts the first file-level JSDoc block, and parses the first
|
|
221
221
|
* @summary tag and free-text description.
|
|
222
222
|
*/
|
|
223
|
-
export function parseFileSummaries(filePath) {
|
|
223
|
+
export async function parseFileSummaries(filePath) {
|
|
224
224
|
let sourceText;
|
|
225
225
|
try {
|
|
226
|
-
sourceText =
|
|
226
|
+
sourceText = await readFile(filePath, "utf-8");
|
|
227
227
|
} catch {
|
|
228
228
|
throw new JsdocError("FILE_NOT_FOUND", `File not found: ${filePath}`);
|
|
229
229
|
}
|
package/dist/lint.js
CHANGED
|
@@ -89,7 +89,7 @@ export async function lint(
|
|
|
89
89
|
gitignore = true,
|
|
90
90
|
config = { enabled: true, directory: DEFAULT_CACHE_DIR },
|
|
91
91
|
) {
|
|
92
|
-
const files = discoverFiles(selector.pattern, cwd, gitignore);
|
|
92
|
+
const files = await discoverFiles(selector.pattern, cwd, gitignore);
|
|
93
93
|
if (files.length === 0 && selector.type === "glob") {
|
|
94
94
|
throw new JsdocError(
|
|
95
95
|
"NO_FILES_MATCHED",
|
|
@@ -101,7 +101,7 @@ export async function lint(
|
|
|
101
101
|
const fileResults = await Promise.all(
|
|
102
102
|
tsFiles.map((f) => lintSingleFile(eslint, f, cwd, config)),
|
|
103
103
|
);
|
|
104
|
-
const missingBarrels = findMissingBarrels(tsFiles, cwd);
|
|
104
|
+
const missingBarrels = await findMissingBarrels(tsFiles, cwd);
|
|
105
105
|
return buildLintResult(fileResults, tsFiles.length, limit, missingBarrels);
|
|
106
106
|
}
|
|
107
107
|
/**
|
|
@@ -129,6 +129,6 @@ export async function lintFiles(
|
|
|
129
129
|
const fileResults = await Promise.all(
|
|
130
130
|
tsFiles.map((f) => lintSingleFile(eslint, f, cwd, config)),
|
|
131
131
|
);
|
|
132
|
-
const missingBarrels = findMissingBarrels(tsFiles, cwd);
|
|
132
|
+
const missingBarrels = await findMissingBarrels(tsFiles, cwd);
|
|
133
133
|
return buildLintResult(fileResults, tsFiles.length, limit, missingBarrels);
|
|
134
134
|
}
|
package/dist/search.js
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { relative } from "node:path";
|
|
3
|
+
import { processWithCache } from "./cache.js";
|
|
4
|
+
import { JsdocError } from "./errors.js";
|
|
5
|
+
import { discoverFiles, loadGitignore } from "./file-discovery.js";
|
|
6
|
+
import { parseFileSummaries } from "./jsdoc-parser.js";
|
|
7
|
+
import {
|
|
8
|
+
extractAllSourceBlocks,
|
|
9
|
+
generateTypeDeclarations,
|
|
10
|
+
splitDeclarations,
|
|
11
|
+
} from "./type-declarations.js";
|
|
12
|
+
import { DEFAULT_CACHE_DIR } from "./types.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Searches TypeScript files for a regex query, returning results at the
|
|
16
|
+
* shallowest matching depth level. Processes each file through a 4-level
|
|
17
|
+
* matching hierarchy: filename/path, summary, description/declarations,
|
|
18
|
+
* and full source content.
|
|
19
|
+
*
|
|
20
|
+
* @summary Search TypeScript documentation and source by regex query
|
|
21
|
+
*/
|
|
22
|
+
/** Default cache configuration used when no config is provided. */
|
|
23
|
+
const DEFAULT_CACHE_CONFIG = {
|
|
24
|
+
enabled: true,
|
|
25
|
+
directory: DEFAULT_CACHE_DIR,
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Compute the display id path for a file.
|
|
29
|
+
* Uses simple relative path — no barrel-to-directory conversion.
|
|
30
|
+
*/
|
|
31
|
+
function displayPath(filePath, cwd) {
|
|
32
|
+
return relative(cwd, filePath);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Extract the sort key from an output entry (either next_id or id).
|
|
36
|
+
*/
|
|
37
|
+
function sortKey(entry) {
|
|
38
|
+
if ("next_id" in entry) return entry.next_id;
|
|
39
|
+
return entry.id;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Process a single file through the 4-level search hierarchy.
|
|
43
|
+
* Returns an OutputEntry if the file matches the query at any level,
|
|
44
|
+
* or null if there is no match. PARSE_ERROR exceptions are rethrown
|
|
45
|
+
* for the caller to handle silently.
|
|
46
|
+
*/
|
|
47
|
+
async function processFileSafe(filePath, regex, cwd, config) {
|
|
48
|
+
const content = await readFile(filePath, "utf-8");
|
|
49
|
+
const idPath = displayPath(filePath, cwd);
|
|
50
|
+
// Parse summaries once for levels 1-3a
|
|
51
|
+
const info = await processWithCache(config, "drilldown", content, () =>
|
|
52
|
+
parseFileSummaries(filePath),
|
|
53
|
+
);
|
|
54
|
+
// Level 1: filename/path match — fall back through levels like drilldown
|
|
55
|
+
if (regex.test(idPath)) {
|
|
56
|
+
if (info.summary !== null) {
|
|
57
|
+
return { next_id: `${idPath}@2`, text: info.summary };
|
|
58
|
+
}
|
|
59
|
+
if (info.description !== null) {
|
|
60
|
+
return { next_id: `${idPath}@3`, text: info.description };
|
|
61
|
+
}
|
|
62
|
+
const dts = await processWithCache(
|
|
63
|
+
config,
|
|
64
|
+
"drilldown",
|
|
65
|
+
`${content}\0typedecl`,
|
|
66
|
+
() => generateTypeDeclarations(filePath),
|
|
67
|
+
);
|
|
68
|
+
if (dts.length > 0) {
|
|
69
|
+
const chunks = splitDeclarations(dts);
|
|
70
|
+
return {
|
|
71
|
+
next_id: `${idPath}@4`,
|
|
72
|
+
text: chunks.map((c) => `\`\`\`typescript\n${c}\n\`\`\``).join("\n\n"),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return { id: `${idPath}@4`, text: `\`\`\`typescript\n${content}\n\`\`\`` };
|
|
76
|
+
}
|
|
77
|
+
// Level 2: summary match
|
|
78
|
+
if (info.summary !== null && regex.test(info.summary)) {
|
|
79
|
+
return { next_id: `${idPath}@2`, text: info.summary };
|
|
80
|
+
}
|
|
81
|
+
// Level 3a: description match
|
|
82
|
+
if (info.description !== null && regex.test(info.description)) {
|
|
83
|
+
return { next_id: `${idPath}@3`, text: info.description };
|
|
84
|
+
}
|
|
85
|
+
// Level 3b: type declaration match
|
|
86
|
+
const dts = await processWithCache(
|
|
87
|
+
config,
|
|
88
|
+
"drilldown",
|
|
89
|
+
`${content}\0typedecl`,
|
|
90
|
+
() => generateTypeDeclarations(filePath),
|
|
91
|
+
);
|
|
92
|
+
if (dts.length > 0) {
|
|
93
|
+
const chunks = splitDeclarations(dts);
|
|
94
|
+
const matching = chunks.filter((c) => regex.test(c));
|
|
95
|
+
if (matching.length > 0) {
|
|
96
|
+
return {
|
|
97
|
+
next_id: `${idPath}@3`,
|
|
98
|
+
text: matching
|
|
99
|
+
.map((c) => `\`\`\`typescript\n${c}\n\`\`\``)
|
|
100
|
+
.join("\n\n"),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Level 4: source match — cache block extraction, filter by regex
|
|
105
|
+
const allBlocks = await processWithCache(
|
|
106
|
+
config,
|
|
107
|
+
"drilldown",
|
|
108
|
+
`${content}\0sourceblocks`,
|
|
109
|
+
() => extractAllSourceBlocks(filePath, content),
|
|
110
|
+
);
|
|
111
|
+
const matchingBlocks = allBlocks.filter((b) => regex.test(b.blockText));
|
|
112
|
+
if (matchingBlocks.length > 0) {
|
|
113
|
+
const fenced = matchingBlocks
|
|
114
|
+
.map((b) => `\`\`\`typescript\n${b.annotation}\n${b.blockText}\n\`\`\``)
|
|
115
|
+
.join("\n\n");
|
|
116
|
+
return { id: `${idPath}@4`, text: fenced };
|
|
117
|
+
}
|
|
118
|
+
if (regex.test(content)) {
|
|
119
|
+
return { id: `${idPath}@4`, text: `\`\`\`typescript\n${content}\n\`\`\`` };
|
|
120
|
+
}
|
|
121
|
+
return null; // no match
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Compile a query string into a case-insensitive regex.
|
|
125
|
+
* Throws INVALID_SELECTOR if the query is not a valid regex pattern.
|
|
126
|
+
*/
|
|
127
|
+
function compileRegex(query) {
|
|
128
|
+
try {
|
|
129
|
+
return new RegExp(query, "i");
|
|
130
|
+
} catch (_error) {
|
|
131
|
+
throw new JsdocError("INVALID_SELECTOR", `Invalid regex: ${query}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Apply limit/truncation to sorted results.
|
|
136
|
+
*/
|
|
137
|
+
function applyLimit(sorted, limit) {
|
|
138
|
+
const count = sorted.length;
|
|
139
|
+
const isTruncated = count > limit;
|
|
140
|
+
return {
|
|
141
|
+
items: sorted.slice(0, limit),
|
|
142
|
+
truncated: isTruncated,
|
|
143
|
+
...(isTruncated ? { total: count } : {}),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Process a list of file paths through the search hierarchy and return sorted results.
|
|
148
|
+
* Files with parse errors are silently skipped.
|
|
149
|
+
*/
|
|
150
|
+
async function searchFileList(files, regex, cwd, limit, config) {
|
|
151
|
+
const results = await Promise.all(
|
|
152
|
+
files.map(async (filePath) => {
|
|
153
|
+
try {
|
|
154
|
+
return await processFileSafe(filePath, regex, cwd, config);
|
|
155
|
+
} catch (error) {
|
|
156
|
+
if (error instanceof JsdocError && error.code === "PARSE_ERROR") {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
161
|
+
}),
|
|
162
|
+
);
|
|
163
|
+
const matched = results.filter((r) => r !== null);
|
|
164
|
+
const sorted = matched.sort((a, b) => sortKey(a).localeCompare(sortKey(b)));
|
|
165
|
+
return applyLimit(sorted, limit);
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Search TypeScript files matching a selector for a regex query.
|
|
169
|
+
*
|
|
170
|
+
* Discovers files from the selector pattern, processes each through
|
|
171
|
+
* the 4-level matching hierarchy, and returns results at the shallowest
|
|
172
|
+
* matching depth. Files with parse errors are silently skipped.
|
|
173
|
+
*
|
|
174
|
+
* @param selector - Parsed selector with type and pattern
|
|
175
|
+
* @param query - Regex query string (case-insensitive)
|
|
176
|
+
* @param cwd - Working directory for file resolution
|
|
177
|
+
* @param gitignore - Whether to respect .gitignore rules (default true)
|
|
178
|
+
* @param limit - Maximum number of results to return (default 100)
|
|
179
|
+
* @param config - Cache configuration
|
|
180
|
+
* @throws {JsdocError} INVALID_SELECTOR for invalid regex
|
|
181
|
+
*/
|
|
182
|
+
export async function search(
|
|
183
|
+
selector,
|
|
184
|
+
query,
|
|
185
|
+
cwd,
|
|
186
|
+
gitignore = true,
|
|
187
|
+
limit = 100,
|
|
188
|
+
config = DEFAULT_CACHE_CONFIG,
|
|
189
|
+
) {
|
|
190
|
+
const regex = compileRegex(query);
|
|
191
|
+
const files = await discoverFiles(selector.pattern, cwd, gitignore);
|
|
192
|
+
return searchFileList(files, regex, cwd, limit, config);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Search an explicit list of file paths for a regex query.
|
|
196
|
+
*
|
|
197
|
+
* Used for stdin input. Filters to .ts/.tsx files, excludes .d.ts.
|
|
198
|
+
* Processes each file through the 4-level matching hierarchy.
|
|
199
|
+
*
|
|
200
|
+
* @param filePaths - Array of absolute file paths
|
|
201
|
+
* @param query - Regex query string (case-insensitive)
|
|
202
|
+
* @param cwd - Working directory for relative path output
|
|
203
|
+
* @param limit - Maximum number of results to return (default 100)
|
|
204
|
+
* @param config - Cache configuration
|
|
205
|
+
* @throws {JsdocError} INVALID_SELECTOR for invalid regex
|
|
206
|
+
*/
|
|
207
|
+
export async function searchFiles(
|
|
208
|
+
filePaths,
|
|
209
|
+
query,
|
|
210
|
+
cwd,
|
|
211
|
+
limit = 100,
|
|
212
|
+
config = DEFAULT_CACHE_CONFIG,
|
|
213
|
+
) {
|
|
214
|
+
const regex = compileRegex(query);
|
|
215
|
+
const ig = await loadGitignore(cwd);
|
|
216
|
+
const tsFiles = filePaths.filter((f) => {
|
|
217
|
+
if (!(f.endsWith(".ts") || f.endsWith(".tsx")) || f.endsWith(".d.ts")) {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
const rel = relative(cwd, f);
|
|
221
|
+
// Files outside cwd (traversal paths) are beyond the gitignore scope
|
|
222
|
+
if (rel.startsWith("..")) return true;
|
|
223
|
+
return !ig.ignores(rel);
|
|
224
|
+
});
|
|
225
|
+
return searchFileList(tsFiles, regex, cwd, limit, config);
|
|
226
|
+
}
|
package/dist/text-format.js
CHANGED
|
@@ -19,12 +19,12 @@ function formatEntry(entry) {
|
|
|
19
19
|
if (children) {
|
|
20
20
|
lines.push(`## children: ${children.join(", ")}`);
|
|
21
21
|
}
|
|
22
|
-
if (text) lines.push(text);
|
|
22
|
+
if (text) lines.push("", text);
|
|
23
23
|
return lines.join("\n");
|
|
24
24
|
}
|
|
25
25
|
// Terminal item
|
|
26
26
|
if (!text) return `# ${entry.id}`;
|
|
27
|
-
return `# ${entry.id}\n${text}`;
|
|
27
|
+
return `# ${entry.id}\n\n${text}`;
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
30
|
* Format a DrilldownResult as plain text for CLI output.
|
|
@@ -32,7 +32,7 @@ function formatEntry(entry) {
|
|
|
32
32
|
*/
|
|
33
33
|
export function formatTextOutput(result) {
|
|
34
34
|
const blocks = result.items.map(formatEntry);
|
|
35
|
-
let output = blocks.join("\n\n");
|
|
35
|
+
let output = blocks.join("\n\n\n");
|
|
36
36
|
if (result.truncated && result.total !== undefined) {
|
|
37
37
|
output += `\n\n# truncated (showing ${result.items.length} of ${result.total})`;
|
|
38
38
|
} else if (result.truncated) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { dirname } from "node:path";
|
|
3
3
|
import ts from "typescript";
|
|
4
4
|
import { JsdocError } from "./errors.js";
|
|
@@ -153,12 +153,44 @@ export function resetCache() {
|
|
|
153
153
|
compilerOptionsCache.clear();
|
|
154
154
|
serviceCache.clear();
|
|
155
155
|
}
|
|
156
|
+
/**
|
|
157
|
+
* Extract exported symbol names from a top-level statement.
|
|
158
|
+
* Returns an array of names (variable statements may have multiple).
|
|
159
|
+
*/
|
|
160
|
+
function exportedNamesOf(statement) {
|
|
161
|
+
if (ts.isTypeAliasDeclaration(statement)) return [statement.name.text];
|
|
162
|
+
if (ts.isInterfaceDeclaration(statement)) return [statement.name.text];
|
|
163
|
+
if (ts.isFunctionDeclaration(statement) && statement.name) {
|
|
164
|
+
return [statement.name.text];
|
|
165
|
+
}
|
|
166
|
+
if (ts.isClassDeclaration(statement) && statement.name) {
|
|
167
|
+
return [statement.name.text];
|
|
168
|
+
}
|
|
169
|
+
if (ts.isEnumDeclaration(statement)) return [statement.name.text];
|
|
170
|
+
if (ts.isVariableStatement(statement)) {
|
|
171
|
+
return statement.declarationList.declarations
|
|
172
|
+
.filter((d) => ts.isIdentifier(d.name))
|
|
173
|
+
.map((d) => d.name.text);
|
|
174
|
+
}
|
|
175
|
+
return [];
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Return true if the statement has an `export` modifier.
|
|
179
|
+
*/
|
|
180
|
+
function isExportedStatement(statement) {
|
|
181
|
+
if (!ts.canHaveModifiers(statement)) return false;
|
|
182
|
+
return (
|
|
183
|
+
ts
|
|
184
|
+
.getModifiers(statement)
|
|
185
|
+
?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false
|
|
186
|
+
);
|
|
187
|
+
}
|
|
156
188
|
/**
|
|
157
189
|
* Build a map of exported symbol names to their source line ranges.
|
|
158
190
|
* Walks top-level statements looking for export modifiers.
|
|
159
191
|
*/
|
|
160
|
-
function buildSourceLineMap(filePath) {
|
|
161
|
-
const content =
|
|
192
|
+
async function buildSourceLineMap(filePath) {
|
|
193
|
+
const content = await readFile(filePath, "utf-8");
|
|
162
194
|
const sourceFile = ts.createSourceFile(
|
|
163
195
|
filePath,
|
|
164
196
|
content,
|
|
@@ -167,36 +199,14 @@ function buildSourceLineMap(filePath) {
|
|
|
167
199
|
);
|
|
168
200
|
const map = new Map();
|
|
169
201
|
for (const statement of sourceFile.statements) {
|
|
170
|
-
|
|
171
|
-
const modifiers = ts.canHaveModifiers(statement)
|
|
172
|
-
? ts.getModifiers(statement)
|
|
173
|
-
: undefined;
|
|
174
|
-
const isExported = modifiers?.some(
|
|
175
|
-
(m) => m.kind === ts.SyntaxKind.ExportKeyword,
|
|
176
|
-
);
|
|
177
|
-
if (!isExported) continue;
|
|
202
|
+
if (!isExportedStatement(statement)) continue;
|
|
178
203
|
const start =
|
|
179
204
|
sourceFile.getLineAndCharacterOfPosition(statement.getStart(sourceFile))
|
|
180
205
|
.line + 1;
|
|
181
206
|
const end =
|
|
182
207
|
sourceFile.getLineAndCharacterOfPosition(statement.getEnd()).line + 1;
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
addName(statement.name.text);
|
|
186
|
-
} else if (ts.isInterfaceDeclaration(statement)) {
|
|
187
|
-
addName(statement.name.text);
|
|
188
|
-
} else if (ts.isFunctionDeclaration(statement) && statement.name) {
|
|
189
|
-
addName(statement.name.text);
|
|
190
|
-
} else if (ts.isClassDeclaration(statement) && statement.name) {
|
|
191
|
-
addName(statement.name.text);
|
|
192
|
-
} else if (ts.isEnumDeclaration(statement)) {
|
|
193
|
-
addName(statement.name.text);
|
|
194
|
-
} else if (ts.isVariableStatement(statement)) {
|
|
195
|
-
for (const decl of statement.declarationList.declarations) {
|
|
196
|
-
if (ts.isIdentifier(decl.name)) {
|
|
197
|
-
addName(decl.name.text);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
208
|
+
for (const name of exportedNamesOf(statement)) {
|
|
209
|
+
map.set(name, { start, end });
|
|
200
210
|
}
|
|
201
211
|
}
|
|
202
212
|
return map;
|
|
@@ -234,8 +244,8 @@ function findChunkStart(lines, declLine) {
|
|
|
234
244
|
* Inserts GitHub-style `// LN` or `// LN-LM` on a separate line before each
|
|
235
245
|
* declaration chunk, with a blank line above for visual separation.
|
|
236
246
|
*/
|
|
237
|
-
function annotateWithSourceLines(dtsText, filePath) {
|
|
238
|
-
const lineMap = buildSourceLineMap(filePath);
|
|
247
|
+
async function annotateWithSourceLines(dtsText, filePath) {
|
|
248
|
+
const lineMap = await buildSourceLineMap(filePath);
|
|
239
249
|
if (lineMap.size === 0) return dtsText;
|
|
240
250
|
const lines = dtsText.split("\n");
|
|
241
251
|
// First pass: map chunk start lines to their annotations
|
|
@@ -263,6 +273,127 @@ function annotateWithSourceLines(dtsText, filePath) {
|
|
|
263
273
|
}
|
|
264
274
|
return result.join("\n");
|
|
265
275
|
}
|
|
276
|
+
/**
|
|
277
|
+
* Splits annotated .d.ts text into individual declaration chunks.
|
|
278
|
+
*
|
|
279
|
+
* Splits on blank-line boundaries that precede a line annotation (`// L`)
|
|
280
|
+
* or a declaration keyword (`export`, `declare`). Each chunk is a complete
|
|
281
|
+
* declaration including its preceding JSDoc comment and line annotation.
|
|
282
|
+
* Trailing whitespace is trimmed from each chunk. Empty chunks are filtered out.
|
|
283
|
+
*
|
|
284
|
+
* @param dtsText - The annotated .d.ts text to split
|
|
285
|
+
* @returns Array of declaration chunks
|
|
286
|
+
*/
|
|
287
|
+
export function splitDeclarations(dtsText) {
|
|
288
|
+
if (dtsText === "") return [];
|
|
289
|
+
const lines = dtsText.split("\n");
|
|
290
|
+
const chunks = [];
|
|
291
|
+
let start = 0;
|
|
292
|
+
for (let i = 1; i < lines.length; i++) {
|
|
293
|
+
// A split point is a blank line followed by a line annotation or declaration keyword
|
|
294
|
+
if (lines[i - 1].trim() === "") {
|
|
295
|
+
const nextLine = lines[i].trimStart();
|
|
296
|
+
const isAnnotation = nextLine.startsWith("// L");
|
|
297
|
+
const isDeclaration =
|
|
298
|
+
nextLine.startsWith("export") || nextLine.startsWith("declare");
|
|
299
|
+
if (isAnnotation || isDeclaration) {
|
|
300
|
+
const chunk = lines
|
|
301
|
+
.slice(start, i - 1)
|
|
302
|
+
.join("\n")
|
|
303
|
+
.trimEnd();
|
|
304
|
+
if (chunk !== "") chunks.push(chunk);
|
|
305
|
+
start = i;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
// Push final chunk
|
|
310
|
+
const last = lines.slice(start).join("\n").trimEnd();
|
|
311
|
+
if (last !== "") chunks.push(last);
|
|
312
|
+
return chunks;
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Find the 0-based line index where a leading JSDoc block starts above a statement.
|
|
316
|
+
* Skips blank lines above the statement, then looks for a `/** ... * /` block.
|
|
317
|
+
* Returns `statementLine` itself when no leading JSDoc is found.
|
|
318
|
+
*/
|
|
319
|
+
function findJsdocStart(lines, statementLine) {
|
|
320
|
+
let line = statementLine - 1;
|
|
321
|
+
// Skip blank lines immediately before the statement
|
|
322
|
+
while (line >= 0 && lines[line].trim() === "") line--;
|
|
323
|
+
if (line < 0) return statementLine;
|
|
324
|
+
const trimmed = lines[line].trimEnd();
|
|
325
|
+
if (trimmed === "*/" || trimmed.endsWith("*/")) {
|
|
326
|
+
// Multi-line JSDoc: walk back to find opening /**
|
|
327
|
+
for (let j = line; j >= 0; j--) {
|
|
328
|
+
if (lines[j].trimStart().startsWith("/**")) return j;
|
|
329
|
+
}
|
|
330
|
+
} else if (trimmed.trimStart().startsWith("/**")) {
|
|
331
|
+
// Single-line JSDoc: /** comment */
|
|
332
|
+
return line;
|
|
333
|
+
}
|
|
334
|
+
return statementLine;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Extracts all top-level source blocks from a TypeScript file.
|
|
338
|
+
* Walks all top-level statements (not just exported), includes leading
|
|
339
|
+
* JSDoc comments, and annotates each block with source line references.
|
|
340
|
+
* Import declarations are excluded.
|
|
341
|
+
*
|
|
342
|
+
* Returns an empty array for empty files.
|
|
343
|
+
*
|
|
344
|
+
* @param filePath - Absolute path to the TypeScript source file
|
|
345
|
+
* @param content - Pre-read file content (avoids redundant disk read)
|
|
346
|
+
*/
|
|
347
|
+
export function extractAllSourceBlocks(filePath, content) {
|
|
348
|
+
if (content.trim() === "") return [];
|
|
349
|
+
const lines = content.split("\n");
|
|
350
|
+
const sourceFile = ts.createSourceFile(
|
|
351
|
+
filePath,
|
|
352
|
+
content,
|
|
353
|
+
ts.ScriptTarget.Latest,
|
|
354
|
+
true,
|
|
355
|
+
);
|
|
356
|
+
const blocks = [];
|
|
357
|
+
const seen = new Set();
|
|
358
|
+
for (const statement of sourceFile.statements) {
|
|
359
|
+
// Skip import declarations — callers fall back to full file for import matches
|
|
360
|
+
if (ts.isImportDeclaration(statement)) continue;
|
|
361
|
+
const endLine =
|
|
362
|
+
sourceFile.getLineAndCharacterOfPosition(statement.getEnd()).line + 1;
|
|
363
|
+
const stmtStartLine = sourceFile.getLineAndCharacterOfPosition(
|
|
364
|
+
statement.getStart(sourceFile),
|
|
365
|
+
).line; // 0-based
|
|
366
|
+
const jsdocStartLine = findJsdocStart(lines, stmtStartLine); // 0-based
|
|
367
|
+
// Skip if we've already included this block (deduplication by start position)
|
|
368
|
+
if (seen.has(jsdocStartLine)) continue;
|
|
369
|
+
seen.add(jsdocStartLine);
|
|
370
|
+
// Extract the source text for this block (lines are 0-based, endLine is 1-based)
|
|
371
|
+
const blockText = lines.slice(jsdocStartLine, endLine).join("\n");
|
|
372
|
+
const startLine1based = jsdocStartLine + 1; // 1-based
|
|
373
|
+
const annotation = formatLineRef({ start: startLine1based, end: endLine });
|
|
374
|
+
blocks.push({ annotation, blockText });
|
|
375
|
+
}
|
|
376
|
+
return blocks;
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Extracts top-level source blocks from a file that match a regex.
|
|
380
|
+
*
|
|
381
|
+
* Walks all top-level statements (not just exported ones), includes leading
|
|
382
|
+
* JSDoc comments, and tests the regex against each block's source text.
|
|
383
|
+
* Matching blocks are annotated with `// LN` or `// LN-LM` and joined with
|
|
384
|
+
* blank line separators.
|
|
385
|
+
*
|
|
386
|
+
* @param filePath - Absolute path to the TypeScript source file
|
|
387
|
+
* @param content - Pre-read file content (avoids redundant disk read)
|
|
388
|
+
* @param regex - Regular expression to test against each block's source text
|
|
389
|
+
* @returns Annotated matching blocks joined with blank lines, or null if no match
|
|
390
|
+
*/
|
|
391
|
+
export function extractSourceBlocks(filePath, content, regex) {
|
|
392
|
+
const allBlocks = extractAllSourceBlocks(filePath, content);
|
|
393
|
+
const matching = allBlocks.filter((b) => regex.test(b.blockText));
|
|
394
|
+
if (matching.length === 0) return null;
|
|
395
|
+
return matching.map((b) => `${b.annotation}\n${b.blockText}`).join("\n\n");
|
|
396
|
+
}
|
|
266
397
|
/**
|
|
267
398
|
* Generates TypeScript declaration output from a source file.
|
|
268
399
|
*
|
|
@@ -282,10 +413,10 @@ function annotateWithSourceLines(dtsText, filePath) {
|
|
|
282
413
|
* @returns The declaration output as a string
|
|
283
414
|
* @throws {JsdocError} If the file cannot be read or parsed
|
|
284
415
|
*/
|
|
285
|
-
export function generateTypeDeclarations(filePath) {
|
|
416
|
+
export async function generateTypeDeclarations(filePath) {
|
|
286
417
|
// Verify the file exists and throw FILE_NOT_FOUND for any read errors
|
|
287
418
|
try {
|
|
288
|
-
|
|
419
|
+
await readFile(filePath, "utf-8");
|
|
289
420
|
} catch (_error) {
|
|
290
421
|
throw new JsdocError("FILE_NOT_FOUND", `Failed to read file: ${filePath}`);
|
|
291
422
|
}
|
|
@@ -319,7 +450,7 @@ export function generateTypeDeclarations(filePath) {
|
|
|
319
450
|
cleaned = "";
|
|
320
451
|
}
|
|
321
452
|
if (cleaned.length > 0) {
|
|
322
|
-
cleaned = annotateWithSourceLines(cleaned, filePath);
|
|
453
|
+
cleaned = await annotateWithSourceLines(cleaned, filePath);
|
|
323
454
|
}
|
|
324
455
|
return cleaned;
|
|
325
456
|
}
|
package/dist/validate.js
CHANGED
|
@@ -97,7 +97,7 @@ export async function validate(
|
|
|
97
97
|
gitignore = true,
|
|
98
98
|
config = { enabled: true, directory: DEFAULT_CACHE_DIR },
|
|
99
99
|
) {
|
|
100
|
-
const files = discoverFiles(selector.pattern, cwd, gitignore);
|
|
100
|
+
const files = await discoverFiles(selector.pattern, cwd, gitignore);
|
|
101
101
|
if (files.length === 0) {
|
|
102
102
|
throw new JsdocError(
|
|
103
103
|
"NO_FILES_MATCHED",
|
|
@@ -108,7 +108,7 @@ export async function validate(
|
|
|
108
108
|
const statuses = await Promise.all(
|
|
109
109
|
files.map((f) => classifyFile(eslint, f, cwd, config)),
|
|
110
110
|
);
|
|
111
|
-
const missingBarrels = findMissingBarrels(files, cwd);
|
|
111
|
+
const missingBarrels = await findMissingBarrels(files, cwd);
|
|
112
112
|
return buildGroupedResult(statuses, missingBarrels, limit);
|
|
113
113
|
}
|
|
114
114
|
/**
|
|
@@ -135,6 +135,6 @@ export async function validateFiles(
|
|
|
135
135
|
const statuses = await Promise.all(
|
|
136
136
|
tsFiles.map((f) => classifyFile(eslint, f, cwd, config)),
|
|
137
137
|
);
|
|
138
|
-
const missingBarrels = findMissingBarrels(tsFiles, cwd);
|
|
138
|
+
const missingBarrels = await findMissingBarrels(tsFiles, cwd);
|
|
139
139
|
return buildGroupedResult(statuses, missingBarrels, limit);
|
|
140
140
|
}
|
package/package.json
CHANGED
package/types/barrel.d.ts
CHANGED
|
@@ -32,7 +32,7 @@ export declare function isBarrel(filePath: string): boolean;
|
|
|
32
32
|
export declare function getBarrelChildren(
|
|
33
33
|
barrelPath: string,
|
|
34
34
|
_cwd: string,
|
|
35
|
-
): string[]
|
|
35
|
+
): Promise<string[]>;
|
|
36
36
|
/** Minimum number of .ts/.tsx files in a directory to require a barrel. */
|
|
37
37
|
export declare const BARREL_THRESHOLD = 3;
|
|
38
38
|
/**
|
|
@@ -42,4 +42,4 @@ export declare const BARREL_THRESHOLD = 3;
|
|
|
42
42
|
export declare function findMissingBarrels(
|
|
43
43
|
filePaths: string[],
|
|
44
44
|
cwd: string,
|
|
45
|
-
): string[]
|
|
45
|
+
): Promise<string[]>;
|
|
@@ -11,7 +11,7 @@ import { type Ignore } from "ignore";
|
|
|
11
11
|
* Walk from `cwd` up to the filesystem root, collecting .gitignore entries.
|
|
12
12
|
* Returns an Ignore instance loaded with all discovered rules.
|
|
13
13
|
*/
|
|
14
|
-
export declare function loadGitignore(cwd: string): Ignore
|
|
14
|
+
export declare function loadGitignore(cwd: string): Promise<Ignore>;
|
|
15
15
|
/**
|
|
16
16
|
* Resolve a selector pattern to a list of .ts/.tsx file paths.
|
|
17
17
|
*
|
|
@@ -29,4 +29,4 @@ export declare function discoverFiles(
|
|
|
29
29
|
pattern: string,
|
|
30
30
|
cwd: string,
|
|
31
31
|
gitignore?: boolean,
|
|
32
|
-
): string[]
|
|
32
|
+
): Promise<string[]>;
|
package/types/index.d.ts
CHANGED
|
@@ -13,9 +13,14 @@ export { JsdocError } from "./errors.js";
|
|
|
13
13
|
export { discoverFiles } from "./file-discovery.js";
|
|
14
14
|
export { extractFileJsdoc, parseFileSummaries } from "./jsdoc-parser.js";
|
|
15
15
|
export { lint, lintFiles } from "./lint.js";
|
|
16
|
+
export { search, searchFiles } from "./search.js";
|
|
16
17
|
export { parseSelector } from "./selector.js";
|
|
17
18
|
export { formatTextOutput } from "./text-format.js";
|
|
18
|
-
export {
|
|
19
|
+
export {
|
|
20
|
+
extractSourceBlocks,
|
|
21
|
+
generateTypeDeclarations,
|
|
22
|
+
splitDeclarations,
|
|
23
|
+
} from "./type-declarations.js";
|
|
19
24
|
export {
|
|
20
25
|
type CacheConfig,
|
|
21
26
|
type CacheOperationMode,
|
package/types/jsdoc-parser.d.ts
CHANGED
|
@@ -24,4 +24,6 @@ export declare function extractFileJsdoc(
|
|
|
24
24
|
* Reads the file, extracts the first file-level JSDoc block, and parses the first
|
|
25
25
|
* @summary tag and free-text description.
|
|
26
26
|
*/
|
|
27
|
-
export declare function parseFileSummaries(
|
|
27
|
+
export declare function parseFileSummaries(
|
|
28
|
+
filePath: string,
|
|
29
|
+
): Promise<ParsedFileInfo>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { CacheConfig, DrilldownResult, SelectorInfo } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Search TypeScript files matching a selector for a regex query.
|
|
4
|
+
*
|
|
5
|
+
* Discovers files from the selector pattern, processes each through
|
|
6
|
+
* the 4-level matching hierarchy, and returns results at the shallowest
|
|
7
|
+
* matching depth. Files with parse errors are silently skipped.
|
|
8
|
+
*
|
|
9
|
+
* @param selector - Parsed selector with type and pattern
|
|
10
|
+
* @param query - Regex query string (case-insensitive)
|
|
11
|
+
* @param cwd - Working directory for file resolution
|
|
12
|
+
* @param gitignore - Whether to respect .gitignore rules (default true)
|
|
13
|
+
* @param limit - Maximum number of results to return (default 100)
|
|
14
|
+
* @param config - Cache configuration
|
|
15
|
+
* @throws {JsdocError} INVALID_SELECTOR for invalid regex
|
|
16
|
+
*/
|
|
17
|
+
export declare function search(
|
|
18
|
+
selector: SelectorInfo,
|
|
19
|
+
query: string,
|
|
20
|
+
cwd: string,
|
|
21
|
+
gitignore?: boolean,
|
|
22
|
+
limit?: number,
|
|
23
|
+
config?: CacheConfig,
|
|
24
|
+
): Promise<DrilldownResult>;
|
|
25
|
+
/**
|
|
26
|
+
* Search an explicit list of file paths for a regex query.
|
|
27
|
+
*
|
|
28
|
+
* Used for stdin input. Filters to .ts/.tsx files, excludes .d.ts.
|
|
29
|
+
* Processes each file through the 4-level matching hierarchy.
|
|
30
|
+
*
|
|
31
|
+
* @param filePaths - Array of absolute file paths
|
|
32
|
+
* @param query - Regex query string (case-insensitive)
|
|
33
|
+
* @param cwd - Working directory for relative path output
|
|
34
|
+
* @param limit - Maximum number of results to return (default 100)
|
|
35
|
+
* @param config - Cache configuration
|
|
36
|
+
* @throws {JsdocError} INVALID_SELECTOR for invalid regex
|
|
37
|
+
*/
|
|
38
|
+
export declare function searchFiles(
|
|
39
|
+
filePaths: string[],
|
|
40
|
+
query: string,
|
|
41
|
+
cwd: string,
|
|
42
|
+
limit?: number,
|
|
43
|
+
config?: CacheConfig,
|
|
44
|
+
): Promise<DrilldownResult>;
|
|
@@ -35,6 +35,56 @@ export declare function getLanguageService(
|
|
|
35
35
|
* @internal
|
|
36
36
|
*/
|
|
37
37
|
export declare function resetCache(): void;
|
|
38
|
+
/**
|
|
39
|
+
* Splits annotated .d.ts text into individual declaration chunks.
|
|
40
|
+
*
|
|
41
|
+
* Splits on blank-line boundaries that precede a line annotation (`// L`)
|
|
42
|
+
* or a declaration keyword (`export`, `declare`). Each chunk is a complete
|
|
43
|
+
* declaration including its preceding JSDoc comment and line annotation.
|
|
44
|
+
* Trailing whitespace is trimmed from each chunk. Empty chunks are filtered out.
|
|
45
|
+
*
|
|
46
|
+
* @param dtsText - The annotated .d.ts text to split
|
|
47
|
+
* @returns Array of declaration chunks
|
|
48
|
+
*/
|
|
49
|
+
export declare function splitDeclarations(dtsText: string): string[];
|
|
50
|
+
/** A single top-level source block with its line annotation. */
|
|
51
|
+
export interface SourceBlock {
|
|
52
|
+
annotation: string;
|
|
53
|
+
blockText: string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Extracts all top-level source blocks from a TypeScript file.
|
|
57
|
+
* Walks all top-level statements (not just exported), includes leading
|
|
58
|
+
* JSDoc comments, and annotates each block with source line references.
|
|
59
|
+
* Import declarations are excluded.
|
|
60
|
+
*
|
|
61
|
+
* Returns an empty array for empty files.
|
|
62
|
+
*
|
|
63
|
+
* @param filePath - Absolute path to the TypeScript source file
|
|
64
|
+
* @param content - Pre-read file content (avoids redundant disk read)
|
|
65
|
+
*/
|
|
66
|
+
export declare function extractAllSourceBlocks(
|
|
67
|
+
filePath: string,
|
|
68
|
+
content: string,
|
|
69
|
+
): SourceBlock[];
|
|
70
|
+
/**
|
|
71
|
+
* Extracts top-level source blocks from a file that match a regex.
|
|
72
|
+
*
|
|
73
|
+
* Walks all top-level statements (not just exported ones), includes leading
|
|
74
|
+
* JSDoc comments, and tests the regex against each block's source text.
|
|
75
|
+
* Matching blocks are annotated with `// LN` or `// LN-LM` and joined with
|
|
76
|
+
* blank line separators.
|
|
77
|
+
*
|
|
78
|
+
* @param filePath - Absolute path to the TypeScript source file
|
|
79
|
+
* @param content - Pre-read file content (avoids redundant disk read)
|
|
80
|
+
* @param regex - Regular expression to test against each block's source text
|
|
81
|
+
* @returns Annotated matching blocks joined with blank lines, or null if no match
|
|
82
|
+
*/
|
|
83
|
+
export declare function extractSourceBlocks(
|
|
84
|
+
filePath: string,
|
|
85
|
+
content: string,
|
|
86
|
+
regex: RegExp,
|
|
87
|
+
): string | null;
|
|
38
88
|
/**
|
|
39
89
|
* Generates TypeScript declaration output from a source file.
|
|
40
90
|
*
|
|
@@ -54,4 +104,6 @@ export declare function resetCache(): void;
|
|
|
54
104
|
* @returns The declaration output as a string
|
|
55
105
|
* @throws {JsdocError} If the file cannot be read or parsed
|
|
56
106
|
*/
|
|
57
|
-
export declare function generateTypeDeclarations(
|
|
107
|
+
export declare function generateTypeDeclarations(
|
|
108
|
+
filePath: string,
|
|
109
|
+
): Promise<string>;
|