@outfitter/tooling 0.3.4 → 0.3.6-canary-20260317141105
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/README.md +12 -3
- package/dist/cli/check-changeset.d.ts +28 -12
- package/dist/cli/check-changeset.js +5 -1
- package/dist/cli/check-exports.d.ts +2 -1
- package/dist/cli/check-exports.js +6 -3
- package/dist/cli/check-home-paths.d.ts +31 -0
- package/dist/cli/check-home-paths.js +12 -0
- package/dist/cli/check-readme-imports.d.ts +2 -1
- package/dist/cli/check-tsdoc.d.ts +4 -1
- package/dist/cli/check-tsdoc.js +16 -10
- package/dist/cli/index.js +16 -4
- package/dist/cli/internal/exports-analysis.d.ts +2 -0
- package/dist/cli/internal/exports-analysis.js +10 -0
- package/dist/cli/internal/exports-fs.d.ts +17 -0
- package/dist/cli/internal/exports-fs.js +9 -0
- package/dist/cli/internal/pre-push-checks.d.ts +2 -0
- package/dist/cli/internal/pre-push-checks.js +39 -0
- package/dist/cli/internal/tsdoc-analysis.d.ts +3 -0
- package/dist/cli/internal/tsdoc-analysis.js +26 -0
- package/dist/cli/internal/tsdoc-formatting.d.ts +3 -0
- package/dist/cli/internal/tsdoc-formatting.js +10 -0
- package/dist/cli/internal/tsdoc-types.d.ts +2 -0
- package/dist/cli/internal/tsdoc-types.js +16 -0
- package/dist/cli/pre-push.d.ts +3 -56
- package/dist/cli/pre-push.js +8 -4
- package/dist/index.d.ts +4 -1
- package/dist/shared/@outfitter/tooling-0zjz8eg9.js +106 -0
- package/dist/shared/@outfitter/{tooling-875svjnz.js → tooling-5xxctk9b.js} +2 -113
- package/dist/shared/@outfitter/tooling-5ynz680q.js +59 -0
- package/dist/shared/@outfitter/tooling-7437rmy6.js +39 -0
- package/dist/shared/@outfitter/tooling-a59br34g.js +32 -0
- package/dist/shared/@outfitter/tooling-a6q3zh7t.js +86 -0
- package/dist/shared/@outfitter/tooling-ayps7c4x.js +58 -0
- package/dist/shared/@outfitter/{tooling-wesswf21.d.ts → tooling-cb0b8wsx.d.ts} +9 -11
- package/dist/shared/@outfitter/tooling-f8q38e9z.d.ts +16 -0
- package/dist/shared/@outfitter/tooling-h5dnevjw.js +139 -0
- package/dist/shared/@outfitter/tooling-h6br5wds.d.ts +113 -0
- package/dist/shared/@outfitter/tooling-j7bczn7t.js +189 -0
- package/dist/shared/@outfitter/tooling-j8d1h2zd.d.ts +10 -0
- package/dist/shared/@outfitter/{tooling-d363b88r.js → tooling-jyanp8q2.js} +107 -143
- package/dist/shared/@outfitter/tooling-mq2xvz96.js +285 -0
- package/dist/shared/@outfitter/tooling-stgnc2zx.d.ts +85 -0
- package/dist/shared/@outfitter/tooling-tj9p41vj.d.ts +55 -0
- package/dist/shared/@outfitter/tooling-y43b117h.d.ts +13 -0
- package/lefthook.yml +5 -1
- package/package.json +10 -4
- package/registry/registry.json +5 -5
- package/dist/shared/@outfitter/tooling-6cxfdx0q.js +0 -187
- package/dist/shared/@outfitter/tooling-h04te11c.js +0 -231
- package/dist/shared/@outfitter/tooling-njw4z34x.d.ts +0 -140
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
computeExpectedExports
|
|
4
|
+
} from "./tooling-a6q3zh7t.js";
|
|
5
|
+
import {
|
|
6
|
+
compareExports
|
|
7
|
+
} from "./tooling-ayps7c4x.js";
|
|
8
|
+
|
|
9
|
+
// packages/tooling/src/cli/check-exports.ts
|
|
10
|
+
import { resolve } from "path";
|
|
11
|
+
var COLORS = {
|
|
12
|
+
reset: "\x1B[0m",
|
|
13
|
+
red: "\x1B[31m",
|
|
14
|
+
green: "\x1B[32m",
|
|
15
|
+
yellow: "\x1B[33m",
|
|
16
|
+
blue: "\x1B[34m",
|
|
17
|
+
dim: "\x1B[2m"
|
|
18
|
+
};
|
|
19
|
+
function resolveJsonMode(options = {}) {
|
|
20
|
+
return options.json ?? process.env["OUTFITTER_JSON"] === "1";
|
|
21
|
+
}
|
|
22
|
+
async function runCheckExports(options = {}) {
|
|
23
|
+
const cwd = process.cwd();
|
|
24
|
+
const configPath = resolve(cwd, "bunup.config.ts");
|
|
25
|
+
let workspaces;
|
|
26
|
+
try {
|
|
27
|
+
const configModule = await import(configPath);
|
|
28
|
+
const rawConfig = configModule.default;
|
|
29
|
+
if (!Array.isArray(rawConfig)) {
|
|
30
|
+
process.stderr.write(`bunup.config.ts must export a workspace array
|
|
31
|
+
`);
|
|
32
|
+
process.exitCode = 1;
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
workspaces = rawConfig;
|
|
36
|
+
} catch {
|
|
37
|
+
process.stderr.write(`Could not load bunup.config.ts from ${cwd}
|
|
38
|
+
`);
|
|
39
|
+
process.exitCode = 1;
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const results = [];
|
|
43
|
+
for (const workspace of workspaces) {
|
|
44
|
+
const packageRoot = resolve(cwd, workspace.root);
|
|
45
|
+
const pkgPath = resolve(packageRoot, "package.json");
|
|
46
|
+
let pkg;
|
|
47
|
+
try {
|
|
48
|
+
pkg = await Bun.file(pkgPath).json();
|
|
49
|
+
} catch {
|
|
50
|
+
results.push({ name: workspace.name, status: "ok" });
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const actual = typeof pkg.exports === "object" && pkg.exports !== null ? pkg.exports : {};
|
|
54
|
+
const expected = computeExpectedExports(packageRoot, workspace, pkg);
|
|
55
|
+
results.push(compareExports({
|
|
56
|
+
name: workspace.name,
|
|
57
|
+
actual,
|
|
58
|
+
expected,
|
|
59
|
+
path: workspace.root
|
|
60
|
+
}));
|
|
61
|
+
}
|
|
62
|
+
const checkResult = {
|
|
63
|
+
ok: results.every((r) => r.status === "ok"),
|
|
64
|
+
packages: results
|
|
65
|
+
};
|
|
66
|
+
if (resolveJsonMode(options)) {
|
|
67
|
+
process.stdout.write(`${JSON.stringify(checkResult, null, 2)}
|
|
68
|
+
`);
|
|
69
|
+
} else {
|
|
70
|
+
const drifted = results.filter((r) => r.status === "drift");
|
|
71
|
+
if (drifted.length === 0) {
|
|
72
|
+
process.stdout.write(`${COLORS.green}All ${results.length} packages have exports in sync.${COLORS.reset}
|
|
73
|
+
`);
|
|
74
|
+
} else {
|
|
75
|
+
process.stderr.write(`${COLORS.red}Export drift detected in ${drifted.length} package(s):${COLORS.reset}
|
|
76
|
+
|
|
77
|
+
`);
|
|
78
|
+
for (const result of drifted) {
|
|
79
|
+
const drift = result.drift;
|
|
80
|
+
if (!drift)
|
|
81
|
+
continue;
|
|
82
|
+
process.stderr.write(` ${COLORS.yellow}${result.name}${COLORS.reset} ${COLORS.dim}(${drift.path})${COLORS.reset}
|
|
83
|
+
`);
|
|
84
|
+
for (const key of drift.added) {
|
|
85
|
+
process.stderr.write(` ${COLORS.green}+ ${key}${COLORS.reset} ${COLORS.dim}(missing from package.json)${COLORS.reset}
|
|
86
|
+
`);
|
|
87
|
+
}
|
|
88
|
+
for (const key of drift.removed) {
|
|
89
|
+
process.stderr.write(` ${COLORS.red}- ${key}${COLORS.reset} ${COLORS.dim}(not in source)${COLORS.reset}
|
|
90
|
+
`);
|
|
91
|
+
}
|
|
92
|
+
for (const entry of drift.changed) {
|
|
93
|
+
process.stderr.write(` ${COLORS.yellow}~ ${entry.key}${COLORS.reset} ${COLORS.dim}(value mismatch)${COLORS.reset}
|
|
94
|
+
`);
|
|
95
|
+
}
|
|
96
|
+
process.stderr.write(`
|
|
97
|
+
`);
|
|
98
|
+
}
|
|
99
|
+
process.stderr.write(`Run ${COLORS.blue}bun run build${COLORS.reset} to regenerate exports.
|
|
100
|
+
`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
process.exitCode = checkResult.ok ? 0 : 1;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export { resolveJsonMode, runCheckExports };
|
|
@@ -3,44 +3,9 @@ import {
|
|
|
3
3
|
__require
|
|
4
4
|
} from "./tooling-jnrs9rqd.js";
|
|
5
5
|
|
|
6
|
-
// packages/tooling/src/cli/
|
|
6
|
+
// packages/tooling/src/cli/internal/tsdoc-analysis.ts
|
|
7
7
|
import { resolve } from "path";
|
|
8
8
|
import ts from "typescript";
|
|
9
|
-
import { z } from "zod";
|
|
10
|
-
var coverageLevelSchema = z.enum([
|
|
11
|
-
"documented",
|
|
12
|
-
"partial",
|
|
13
|
-
"undocumented"
|
|
14
|
-
]);
|
|
15
|
-
var declarationCoverageSchema = z.object({
|
|
16
|
-
name: z.string(),
|
|
17
|
-
kind: z.string(),
|
|
18
|
-
level: coverageLevelSchema,
|
|
19
|
-
file: z.string(),
|
|
20
|
-
line: z.number()
|
|
21
|
-
});
|
|
22
|
-
var coverageSummarySchema = z.object({
|
|
23
|
-
documented: z.number(),
|
|
24
|
-
partial: z.number(),
|
|
25
|
-
undocumented: z.number(),
|
|
26
|
-
total: z.number(),
|
|
27
|
-
percentage: z.number()
|
|
28
|
-
});
|
|
29
|
-
var packageCoverageSchema = z.object({
|
|
30
|
-
name: z.string(),
|
|
31
|
-
path: z.string(),
|
|
32
|
-
declarations: z.array(declarationCoverageSchema),
|
|
33
|
-
documented: z.number(),
|
|
34
|
-
partial: z.number(),
|
|
35
|
-
undocumented: z.number(),
|
|
36
|
-
total: z.number(),
|
|
37
|
-
percentage: z.number()
|
|
38
|
-
});
|
|
39
|
-
var tsDocCheckResultSchema = z.object({
|
|
40
|
-
ok: z.boolean(),
|
|
41
|
-
packages: z.array(packageCoverageSchema),
|
|
42
|
-
summary: coverageSummarySchema
|
|
43
|
-
});
|
|
44
9
|
function isExportedDeclaration(node) {
|
|
45
10
|
if (ts.isExportDeclaration(node))
|
|
46
11
|
return false;
|
|
@@ -159,24 +124,6 @@ function calculateCoverage(declarations) {
|
|
|
159
124
|
const percentage = Math.round(score / total * 100);
|
|
160
125
|
return { documented, partial, undocumented, total, percentage };
|
|
161
126
|
}
|
|
162
|
-
var COLORS = {
|
|
163
|
-
reset: "\x1B[0m",
|
|
164
|
-
red: "\x1B[31m",
|
|
165
|
-
green: "\x1B[32m",
|
|
166
|
-
yellow: "\x1B[33m",
|
|
167
|
-
blue: "\x1B[34m",
|
|
168
|
-
dim: "\x1B[2m",
|
|
169
|
-
bold: "\x1B[1m"
|
|
170
|
-
};
|
|
171
|
-
function resolveJsonMode(options = {}) {
|
|
172
|
-
return options.json ?? process.env["OUTFITTER_JSON"] === "1";
|
|
173
|
-
}
|
|
174
|
-
function bar(percentage, width = 20) {
|
|
175
|
-
const filled = Math.round(percentage / 100 * width);
|
|
176
|
-
const empty = width - filled;
|
|
177
|
-
const color = percentage >= 80 ? COLORS.green : percentage >= 50 ? COLORS.yellow : COLORS.red;
|
|
178
|
-
return `${color}${"\u2588".repeat(filled)}${COLORS.dim}${"\u2591".repeat(empty)}${COLORS.reset}`;
|
|
179
|
-
}
|
|
180
127
|
function discoverPackages(cwd) {
|
|
181
128
|
const packages = [];
|
|
182
129
|
const seenEntryPoints = new Set;
|
|
@@ -344,63 +291,5 @@ function analyzeCheckTsdoc(options = {}) {
|
|
|
344
291
|
summary
|
|
345
292
|
};
|
|
346
293
|
}
|
|
347
|
-
function printCheckTsdocHuman(result, options) {
|
|
348
|
-
process.stdout.write(`
|
|
349
|
-
${COLORS.bold}TSDoc Coverage Report${COLORS.reset}
|
|
350
|
-
|
|
351
|
-
`);
|
|
352
|
-
for (const pkg of result.packages) {
|
|
353
|
-
const color = pkg.percentage >= 80 ? COLORS.green : pkg.percentage >= 50 ? COLORS.yellow : COLORS.red;
|
|
354
|
-
process.stdout.write(` ${color}${pkg.percentage.toString().padStart(3)}%${COLORS.reset} ${bar(pkg.percentage)} ${pkg.name}
|
|
355
|
-
`);
|
|
356
|
-
if (pkg.total > 0) {
|
|
357
|
-
const parts = [];
|
|
358
|
-
if (pkg.documented > 0)
|
|
359
|
-
parts.push(`${COLORS.green}${pkg.documented} documented${COLORS.reset}`);
|
|
360
|
-
if (pkg.partial > 0)
|
|
361
|
-
parts.push(`${COLORS.yellow}${pkg.partial} partial${COLORS.reset}`);
|
|
362
|
-
if (pkg.undocumented > 0)
|
|
363
|
-
parts.push(`${COLORS.red}${pkg.undocumented} undocumented${COLORS.reset}`);
|
|
364
|
-
process.stdout.write(` ${COLORS.dim}${pkg.total} declarations:${COLORS.reset} ${parts.join(", ")}
|
|
365
|
-
`);
|
|
366
|
-
} else {
|
|
367
|
-
process.stdout.write(` ${COLORS.dim}no exported declarations${COLORS.reset}
|
|
368
|
-
`);
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
const { summary } = result;
|
|
372
|
-
process.stdout.write(`
|
|
373
|
-
${COLORS.bold}Summary:${COLORS.reset} ${summary.percentage}% coverage (${summary.documented} documented, ${summary.partial} partial, ${summary.undocumented} undocumented of ${summary.total} total)
|
|
374
|
-
`);
|
|
375
|
-
const minCoverage = options?.minCoverage ?? 0;
|
|
376
|
-
if (options?.strict && summary.percentage < minCoverage) {
|
|
377
|
-
process.stderr.write(`
|
|
378
|
-
${COLORS.red}Coverage ${summary.percentage}% is below minimum threshold of ${minCoverage}%${COLORS.reset}
|
|
379
|
-
`);
|
|
380
|
-
}
|
|
381
|
-
process.stdout.write(`
|
|
382
|
-
`);
|
|
383
|
-
}
|
|
384
|
-
async function runCheckTsdoc(options = {}) {
|
|
385
|
-
const result = analyzeCheckTsdoc(options);
|
|
386
|
-
if (!result) {
|
|
387
|
-
process.stderr.write(`No packages found with src/index.ts entry points.
|
|
388
|
-
` + `Searched: packages/*/src/index.ts, apps/*/src/index.ts, src/index.ts
|
|
389
|
-
` + `Use --package <path> to specify a package path explicitly.
|
|
390
|
-
`);
|
|
391
|
-
process.exitCode = 1;
|
|
392
|
-
return;
|
|
393
|
-
}
|
|
394
|
-
if (resolveJsonMode(options)) {
|
|
395
|
-
process.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
396
|
-
`);
|
|
397
|
-
} else {
|
|
398
|
-
printCheckTsdocHuman(result, {
|
|
399
|
-
strict: options.strict,
|
|
400
|
-
minCoverage: options.minCoverage
|
|
401
|
-
});
|
|
402
|
-
}
|
|
403
|
-
process.exitCode = result.ok ? 0 : 1;
|
|
404
|
-
}
|
|
405
294
|
|
|
406
|
-
export {
|
|
295
|
+
export { isExportedDeclaration, getDeclarationName, getDeclarationKind, classifyDeclaration, analyzeSourceFile, calculateCoverage, discoverPackages, collectReExportedSourceFiles, analyzePackage, analyzeCheckTsdoc };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/tooling/src/cli/internal/tsdoc-formatting.ts
|
|
3
|
+
var COLORS = {
|
|
4
|
+
reset: "\x1B[0m",
|
|
5
|
+
red: "\x1B[31m",
|
|
6
|
+
green: "\x1B[32m",
|
|
7
|
+
yellow: "\x1B[33m",
|
|
8
|
+
blue: "\x1B[34m",
|
|
9
|
+
dim: "\x1B[2m",
|
|
10
|
+
bold: "\x1B[1m"
|
|
11
|
+
};
|
|
12
|
+
function resolveJsonMode(options = {}) {
|
|
13
|
+
return options.json ?? process.env["OUTFITTER_JSON"] === "1";
|
|
14
|
+
}
|
|
15
|
+
function bar(percentage, width = 20) {
|
|
16
|
+
const filled = Math.round(percentage / 100 * width);
|
|
17
|
+
const empty = width - filled;
|
|
18
|
+
const color = percentage >= 80 ? COLORS.green : percentage >= 50 ? COLORS.yellow : COLORS.red;
|
|
19
|
+
return `${color}${"\u2588".repeat(filled)}${COLORS.dim}${"\u2591".repeat(empty)}${COLORS.reset}`;
|
|
20
|
+
}
|
|
21
|
+
function printCheckTsdocHuman(result, options) {
|
|
22
|
+
process.stdout.write(`
|
|
23
|
+
${COLORS.bold}TSDoc Coverage Report${COLORS.reset}
|
|
24
|
+
|
|
25
|
+
`);
|
|
26
|
+
for (const pkg of result.packages) {
|
|
27
|
+
const color = pkg.percentage >= 80 ? COLORS.green : pkg.percentage >= 50 ? COLORS.yellow : COLORS.red;
|
|
28
|
+
process.stdout.write(` ${color}${pkg.percentage.toString().padStart(3)}%${COLORS.reset} ${bar(pkg.percentage)} ${pkg.name}
|
|
29
|
+
`);
|
|
30
|
+
if (pkg.total > 0) {
|
|
31
|
+
const parts = [];
|
|
32
|
+
if (pkg.documented > 0)
|
|
33
|
+
parts.push(`${COLORS.green}${pkg.documented} documented${COLORS.reset}`);
|
|
34
|
+
if (pkg.partial > 0)
|
|
35
|
+
parts.push(`${COLORS.yellow}${pkg.partial} partial${COLORS.reset}`);
|
|
36
|
+
if (pkg.undocumented > 0)
|
|
37
|
+
parts.push(`${COLORS.red}${pkg.undocumented} undocumented${COLORS.reset}`);
|
|
38
|
+
process.stdout.write(` ${COLORS.dim}${pkg.total} declarations:${COLORS.reset} ${parts.join(", ")}
|
|
39
|
+
`);
|
|
40
|
+
} else {
|
|
41
|
+
process.stdout.write(` ${COLORS.dim}no exported declarations${COLORS.reset}
|
|
42
|
+
`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const { summary } = result;
|
|
46
|
+
process.stdout.write(`
|
|
47
|
+
${COLORS.bold}Summary:${COLORS.reset} ${summary.percentage}% coverage (${summary.documented} documented, ${summary.partial} partial, ${summary.undocumented} undocumented of ${summary.total} total)
|
|
48
|
+
`);
|
|
49
|
+
const minCoverage = options?.minCoverage ?? 0;
|
|
50
|
+
if (options?.strict && summary.percentage < minCoverage) {
|
|
51
|
+
process.stderr.write(`
|
|
52
|
+
${COLORS.red}Coverage ${summary.percentage}% is below minimum threshold of ${minCoverage}%${COLORS.reset}
|
|
53
|
+
`);
|
|
54
|
+
}
|
|
55
|
+
process.stdout.write(`
|
|
56
|
+
`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export { resolveJsonMode, printCheckTsdocHuman };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/tooling/src/cli/internal/tsdoc-types.ts
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
var coverageLevelSchema = z.enum([
|
|
5
|
+
"documented",
|
|
6
|
+
"partial",
|
|
7
|
+
"undocumented"
|
|
8
|
+
]);
|
|
9
|
+
var declarationCoverageSchema = z.object({
|
|
10
|
+
name: z.string(),
|
|
11
|
+
kind: z.string(),
|
|
12
|
+
level: coverageLevelSchema,
|
|
13
|
+
file: z.string(),
|
|
14
|
+
line: z.number()
|
|
15
|
+
});
|
|
16
|
+
var coverageSummarySchema = z.object({
|
|
17
|
+
documented: z.number(),
|
|
18
|
+
partial: z.number(),
|
|
19
|
+
undocumented: z.number(),
|
|
20
|
+
total: z.number(),
|
|
21
|
+
percentage: z.number()
|
|
22
|
+
});
|
|
23
|
+
var packageCoverageSchema = z.object({
|
|
24
|
+
name: z.string(),
|
|
25
|
+
path: z.string(),
|
|
26
|
+
declarations: z.array(declarationCoverageSchema),
|
|
27
|
+
documented: z.number(),
|
|
28
|
+
partial: z.number(),
|
|
29
|
+
undocumented: z.number(),
|
|
30
|
+
total: z.number(),
|
|
31
|
+
percentage: z.number()
|
|
32
|
+
});
|
|
33
|
+
var tsDocCheckResultSchema = z.object({
|
|
34
|
+
ok: z.boolean(),
|
|
35
|
+
packages: z.array(packageCoverageSchema),
|
|
36
|
+
summary: coverageSummarySchema
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
export { coverageLevelSchema, declarationCoverageSchema, coverageSummarySchema, packageCoverageSchema, tsDocCheckResultSchema };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
printCheckTsdocHuman,
|
|
4
|
+
resolveJsonMode
|
|
5
|
+
} from "./tooling-5ynz680q.js";
|
|
6
|
+
import {
|
|
7
|
+
analyzeCheckTsdoc
|
|
8
|
+
} from "./tooling-5xxctk9b.js";
|
|
9
|
+
// packages/tooling/src/cli/check-tsdoc.ts
|
|
10
|
+
async function runCheckTsdoc(options = {}) {
|
|
11
|
+
const result = analyzeCheckTsdoc(options);
|
|
12
|
+
if (!result) {
|
|
13
|
+
process.stderr.write(`No packages found with src/index.ts entry points.
|
|
14
|
+
` + `Searched: packages/*/src/index.ts, apps/*/src/index.ts, src/index.ts
|
|
15
|
+
` + `Use --package <path> to specify a package path explicitly.
|
|
16
|
+
`);
|
|
17
|
+
process.exitCode = 1;
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (resolveJsonMode(options)) {
|
|
21
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
22
|
+
`);
|
|
23
|
+
} else {
|
|
24
|
+
printCheckTsdocHuman(result, {
|
|
25
|
+
strict: options.strict,
|
|
26
|
+
minCoverage: options.minCoverage
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
process.exitCode = result.ok ? 0 : 1;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export { runCheckTsdoc };
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
entryToSubpath
|
|
4
|
+
} from "./tooling-ayps7c4x.js";
|
|
5
|
+
|
|
6
|
+
// packages/tooling/src/cli/internal/exports-fs.ts
|
|
7
|
+
function matchesExclude(subpath, excludes) {
|
|
8
|
+
return excludes.some((pattern) => new Bun.Glob(pattern).match(subpath));
|
|
9
|
+
}
|
|
10
|
+
var CLI_EXCLUSION_PATTERNS = [
|
|
11
|
+
"**/cli.ts",
|
|
12
|
+
"**/cli/index.ts",
|
|
13
|
+
"**/bin.ts",
|
|
14
|
+
"**/bin/index.ts"
|
|
15
|
+
];
|
|
16
|
+
function isCliEntrypoint(entry) {
|
|
17
|
+
return CLI_EXCLUSION_PATTERNS.some((pattern) => new Bun.Glob(pattern).match(entry));
|
|
18
|
+
}
|
|
19
|
+
function buildExportValue(entry) {
|
|
20
|
+
const distPath = entry.replace(/^src\//, "").replace(/\.[cm]?[jt]sx?$/, "");
|
|
21
|
+
return {
|
|
22
|
+
import: {
|
|
23
|
+
types: `./dist/${distPath}.d.ts`,
|
|
24
|
+
default: `./dist/${distPath}.js`
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function discoverEntries(packageRoot) {
|
|
29
|
+
const glob = new Bun.Glob("src/**/*.ts");
|
|
30
|
+
const entries = [];
|
|
31
|
+
for (const match of glob.scanSync({ cwd: packageRoot, dot: false })) {
|
|
32
|
+
if (match.includes("__tests__") || match.endsWith(".test.ts")) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
entries.push(match);
|
|
36
|
+
}
|
|
37
|
+
return entries.toSorted();
|
|
38
|
+
}
|
|
39
|
+
function addConfigFileExports(expected, pkg) {
|
|
40
|
+
const CONFIG_RE = /\.(json|jsonc|yml|yaml|toml)$/;
|
|
41
|
+
const configFiles = (pkg.files ?? []).filter((file) => CONFIG_RE.test(file) && file !== "package.json");
|
|
42
|
+
for (const file of configFiles) {
|
|
43
|
+
expected[`./${file}`] = `./${file}`;
|
|
44
|
+
let base = file.replace(CONFIG_RE, "");
|
|
45
|
+
const match = base.match(/^(.+)\.preset(?:\.(.+))?$/);
|
|
46
|
+
if (match?.[1]) {
|
|
47
|
+
base = match[2] ? `${match[1]}-${match[2]}` : match[1];
|
|
48
|
+
}
|
|
49
|
+
if (base !== file) {
|
|
50
|
+
expected[`./${base}`] = `./${file}`;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function computeExpectedExports(packageRoot, workspace, pkg) {
|
|
55
|
+
const entries = discoverEntries(packageRoot);
|
|
56
|
+
const exportsConfig = typeof workspace.config?.exports === "object" ? workspace.config.exports : undefined;
|
|
57
|
+
const excludes = exportsConfig?.exclude ?? [];
|
|
58
|
+
const customExports = exportsConfig?.customExports ?? {};
|
|
59
|
+
const expected = {};
|
|
60
|
+
const subpathEntries = new Map;
|
|
61
|
+
for (const entry of entries) {
|
|
62
|
+
if (isCliEntrypoint(entry))
|
|
63
|
+
continue;
|
|
64
|
+
const subpath = entryToSubpath(entry);
|
|
65
|
+
if (matchesExclude(subpath, excludes))
|
|
66
|
+
continue;
|
|
67
|
+
const existing = subpathEntries.get(subpath);
|
|
68
|
+
if (existing) {
|
|
69
|
+
if (!existing.endsWith("/index.ts") && entry.endsWith("/index.ts")) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
subpathEntries.set(subpath, entry);
|
|
74
|
+
}
|
|
75
|
+
for (const [subpath, entry] of subpathEntries) {
|
|
76
|
+
expected[subpath] = buildExportValue(entry);
|
|
77
|
+
}
|
|
78
|
+
for (const [key, value] of Object.entries(customExports)) {
|
|
79
|
+
expected[`./${key.replace(/^\.\//, "")}`] = value;
|
|
80
|
+
}
|
|
81
|
+
addConfigFileExports(expected, pkg);
|
|
82
|
+
expected["./package.json"] = "./package.json";
|
|
83
|
+
return expected;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export { computeExpectedExports };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/tooling/src/cli/internal/exports-analysis.ts
|
|
3
|
+
function entryToSubpath(entry) {
|
|
4
|
+
const stripped = entry.replace(/^src\//, "").replace(/\.[cm]?[jt]sx?$/, "");
|
|
5
|
+
if (stripped === "index") {
|
|
6
|
+
return ".";
|
|
7
|
+
}
|
|
8
|
+
if (stripped.endsWith("/index")) {
|
|
9
|
+
return `./${stripped.slice(0, -"/index".length)}`;
|
|
10
|
+
}
|
|
11
|
+
return `./${stripped}`;
|
|
12
|
+
}
|
|
13
|
+
function compareExports(input) {
|
|
14
|
+
const { name, actual, expected, path } = input;
|
|
15
|
+
const actualKeys = new Set(Object.keys(actual));
|
|
16
|
+
const expectedKeys = new Set(Object.keys(expected));
|
|
17
|
+
const added = [];
|
|
18
|
+
const removed = [];
|
|
19
|
+
const changed = [];
|
|
20
|
+
for (const key of expectedKeys) {
|
|
21
|
+
if (!actualKeys.has(key)) {
|
|
22
|
+
added.push(key);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
for (const key of actualKeys) {
|
|
26
|
+
if (!expectedKeys.has(key)) {
|
|
27
|
+
removed.push(key);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
for (const key of actualKeys) {
|
|
31
|
+
if (expectedKeys.has(key)) {
|
|
32
|
+
const actualValue = actual[key];
|
|
33
|
+
const expectedValue = expected[key];
|
|
34
|
+
if (JSON.stringify(actualValue) !== JSON.stringify(expectedValue)) {
|
|
35
|
+
changed.push({ key, expected: expectedValue, actual: actualValue });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
added.sort();
|
|
40
|
+
removed.sort();
|
|
41
|
+
changed.sort((a, b) => a.key.localeCompare(b.key));
|
|
42
|
+
if (added.length === 0 && removed.length === 0 && changed.length === 0) {
|
|
43
|
+
return { name, status: "ok" };
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
name,
|
|
47
|
+
status: "drift",
|
|
48
|
+
drift: {
|
|
49
|
+
package: name,
|
|
50
|
+
path: path ?? "",
|
|
51
|
+
added,
|
|
52
|
+
removed,
|
|
53
|
+
changed
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export { entryToSubpath, compareExports };
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure analysis functions for comparing maps.
|
|
3
|
+
*
|
|
4
|
+
* Types and stateless comparison logic used by the check-exports command.
|
|
5
|
+
* No filesystem access — all inputs are passed as arguments.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
1
9
|
/** A package.json map: keys are subpaths, values are conditions or strings */
|
|
2
10
|
type ExportMap = Record<string, unknown>;
|
|
3
11
|
/** Describes drift between expected and actual exports for a single package */
|
|
@@ -46,14 +54,4 @@ declare function entryToSubpath(entry: string): string;
|
|
|
46
54
|
* Returns a PackageResult with status "ok" or "drift" and detailed diff.
|
|
47
55
|
*/
|
|
48
56
|
declare function compareExports(input: CompareInput): PackageResult;
|
|
49
|
-
|
|
50
|
-
readonly json?: boolean;
|
|
51
|
-
}
|
|
52
|
-
declare function resolveJsonMode(options?: CheckExportsOptions): boolean;
|
|
53
|
-
/**
|
|
54
|
-
* Run check-exports across all workspace packages.
|
|
55
|
-
*
|
|
56
|
-
* Reads the bunup workspace config to discover packages and their * settings, then compares expected vs actual exports in each package.json.
|
|
57
|
-
*/
|
|
58
|
-
declare function runCheckExports(options?: CheckExportsOptions): Promise<void>;
|
|
59
|
-
export { ExportMap, ExportDrift, PackageResult, CheckResult, CompareInput, entryToSubpath, compareExports, CheckExportsOptions, resolveJsonMode, runCheckExports };
|
|
57
|
+
export { ExportMap, ExportDrift, PackageResult, CheckResult, CompareInput, entryToSubpath, compareExports };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { CheckTsDocOptions, TsDocCheckResult } from "./tooling-tj9p41vj.js";
|
|
2
|
+
/** Resolve whether JSON output mode is active. */
|
|
3
|
+
declare function resolveJsonMode(options?: CheckTsDocOptions): boolean;
|
|
4
|
+
/**
|
|
5
|
+
* Print a TSDoc coverage result in human-readable format.
|
|
6
|
+
*
|
|
7
|
+
* Renders a bar chart per package with summary statistics. Writes to stdout/stderr.
|
|
8
|
+
*
|
|
9
|
+
* @param result - The coverage result to print
|
|
10
|
+
* @param options - Display options (strict mode, coverage threshold for warning)
|
|
11
|
+
*/
|
|
12
|
+
declare function printCheckTsdocHuman(result: TsDocCheckResult, options?: {
|
|
13
|
+
strict?: boolean | undefined;
|
|
14
|
+
minCoverage?: number | undefined;
|
|
15
|
+
}): void;
|
|
16
|
+
export { resolveJsonMode, printCheckTsdocHuman };
|