@outfitter/tooling 0.3.3 → 0.3.5
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/.markdownlint-cli2.jsonc +55 -55
- package/README.md +33 -24
- package/dist/bun-version-compat.d.ts +2 -0
- package/dist/bun-version-compat.js +10 -0
- package/dist/cli/check-boundary-invocations.d.ts +34 -0
- package/dist/cli/check-boundary-invocations.js +14 -0
- package/dist/cli/check-bunup-registry.d.ts +36 -0
- package/dist/cli/check-bunup-registry.js +12 -0
- package/dist/cli/check-changeset.d.ts +82 -0
- package/dist/cli/check-changeset.js +24 -0
- package/dist/cli/check-clean-tree.d.ts +36 -0
- package/dist/cli/check-clean-tree.js +14 -0
- package/dist/cli/check-exports.d.ts +3 -0
- package/dist/cli/check-exports.js +17 -0
- package/dist/cli/check-home-paths.d.ts +31 -0
- package/dist/cli/check-home-paths.js +12 -0
- package/dist/cli/check-markdown-links.d.ts +42 -0
- package/dist/cli/check-markdown-links.js +13 -0
- package/dist/cli/check-readme-imports.d.ts +61 -0
- package/dist/{shared/chunk-7tdgbqb0.js → cli/check-readme-imports.js} +7 -6
- package/dist/cli/check-tsdoc.d.ts +5 -0
- package/dist/cli/check-tsdoc.js +42 -0
- package/dist/cli/check.d.ts +19 -0
- package/dist/cli/check.js +10 -0
- package/dist/cli/fix.d.ts +19 -0
- package/dist/cli/fix.js +10 -0
- package/dist/cli/index.js +61 -1218
- package/dist/cli/init.d.ts +31 -0
- package/dist/cli/init.js +12 -0
- 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 +37 -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 +7 -0
- package/dist/cli/pre-push.js +29 -0
- package/dist/cli/upgrade-bun.d.ts +8 -0
- package/dist/cli/upgrade-bun.js +9 -0
- package/dist/index.d.ts +9 -186
- package/dist/index.js +4 -42
- package/dist/registry/build.d.ts +4 -0
- package/dist/registry/build.js +279 -0
- package/dist/registry/index.d.ts +3 -0
- package/dist/registry/index.js +1 -0
- package/dist/registry/schema.d.ts +2 -0
- package/dist/registry/schema.js +28 -0
- package/dist/shared/@outfitter/tooling-0zjz8eg9.js +106 -0
- package/dist/shared/@outfitter/tooling-1hez6j9d.js +21 -0
- package/dist/shared/@outfitter/tooling-2vv5y3s4.js +145 -0
- package/dist/shared/{chunk-cmde0fwx.js → @outfitter/tooling-5xxctk9b.js} +12 -138
- package/dist/shared/@outfitter/tooling-5ynz680q.js +59 -0
- package/dist/shared/@outfitter/tooling-7437rmy6.js +39 -0
- package/dist/shared/@outfitter/tooling-8qcwr06t.d.ts +74 -0
- package/dist/shared/@outfitter/tooling-9ram55dd.js +69 -0
- package/dist/shared/@outfitter/tooling-9vs606gq.d.ts +3 -0
- package/dist/shared/@outfitter/tooling-a4bfx4be.js +21 -0
- package/dist/shared/@outfitter/tooling-a59br34g.js +32 -0
- package/dist/shared/@outfitter/tooling-a6q3zh7t.js +86 -0
- package/dist/shared/@outfitter/tooling-amrbp7cm.js +102 -0
- package/dist/shared/@outfitter/tooling-ayps7c4x.js +58 -0
- package/dist/shared/@outfitter/tooling-c8q6mj8z.js +228 -0
- package/dist/shared/@outfitter/tooling-cb0b8wsx.d.ts +57 -0
- package/dist/shared/@outfitter/tooling-ctmgnap5.js +19 -0
- package/dist/shared/@outfitter/tooling-f8q38e9z.d.ts +16 -0
- package/dist/shared/@outfitter/tooling-gcdvsqqp.js +73 -0
- package/dist/shared/@outfitter/tooling-h5dnevjw.js +139 -0
- package/dist/shared/@outfitter/tooling-j8d1h2zd.d.ts +10 -0
- package/dist/shared/@outfitter/tooling-ja1zg5yc.js +214 -0
- package/dist/shared/@outfitter/tooling-jnrs9rqd.js +4 -0
- package/dist/shared/@outfitter/tooling-mkynjra9.js +23 -0
- package/dist/shared/@outfitter/tooling-mq2xvz96.js +285 -0
- package/dist/shared/@outfitter/tooling-pq47jv6t.js +213 -0
- package/dist/shared/@outfitter/tooling-sjm8nebx.d.ts +109 -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-vjmhvpjq.d.ts +29 -0
- package/dist/shared/@outfitter/tooling-wwm97f47.js +81 -0
- package/dist/shared/@outfitter/tooling-y43b117h.d.ts +13 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +8 -0
- package/lefthook.yml +5 -1
- package/package.json +140 -131
- package/registry/registry.json +19 -12
- package/tsconfig.preset.bun.json +5 -5
- package/tsconfig.preset.json +33 -33
- package/biome.json +0 -81
- package/dist/shared/chunk-3s189drz.js +0 -4
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
isTypesBunVersionCompatible,
|
|
4
|
+
parseSemver
|
|
5
|
+
} from "./tooling-mkynjra9.js";
|
|
6
|
+
|
|
7
|
+
// packages/tooling/src/cli/upgrade-bun.ts
|
|
8
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
9
|
+
import { join } from "path";
|
|
10
|
+
var COLORS = {
|
|
11
|
+
reset: "\x1B[0m",
|
|
12
|
+
red: "\x1B[31m",
|
|
13
|
+
green: "\x1B[32m",
|
|
14
|
+
yellow: "\x1B[33m",
|
|
15
|
+
blue: "\x1B[34m"
|
|
16
|
+
};
|
|
17
|
+
function log(msg) {
|
|
18
|
+
process.stdout.write(`${msg}
|
|
19
|
+
`);
|
|
20
|
+
}
|
|
21
|
+
function info(msg) {
|
|
22
|
+
process.stdout.write(`${COLORS.blue}\u25B8${COLORS.reset} ${msg}
|
|
23
|
+
`);
|
|
24
|
+
}
|
|
25
|
+
function success(msg) {
|
|
26
|
+
process.stdout.write(`${COLORS.green}\u2713${COLORS.reset} ${msg}
|
|
27
|
+
`);
|
|
28
|
+
}
|
|
29
|
+
function warn(msg) {
|
|
30
|
+
process.stdout.write(`${COLORS.yellow}!${COLORS.reset} ${msg}
|
|
31
|
+
`);
|
|
32
|
+
}
|
|
33
|
+
async function fetchLatestVersion() {
|
|
34
|
+
const response = await fetch("https://api.github.com/repos/oven-sh/bun/releases/latest");
|
|
35
|
+
const data = await response.json();
|
|
36
|
+
const match = data.tag_name.match(/bun-v(.+)/);
|
|
37
|
+
if (!match?.[1]) {
|
|
38
|
+
throw new Error(`Could not parse version from tag: ${data.tag_name}`);
|
|
39
|
+
}
|
|
40
|
+
return match[1];
|
|
41
|
+
}
|
|
42
|
+
async function resolveTypesBunVersion(targetVersion) {
|
|
43
|
+
const response = await fetch("https://registry.npmjs.org/@types%2fbun");
|
|
44
|
+
if (!response.ok) {
|
|
45
|
+
throw new Error(`Failed to fetch @types/bun metadata: ${response.status}`);
|
|
46
|
+
}
|
|
47
|
+
const data = await response.json();
|
|
48
|
+
if (data.versions && Object.hasOwn(data.versions, targetVersion)) {
|
|
49
|
+
return targetVersion;
|
|
50
|
+
}
|
|
51
|
+
if (data.versions) {
|
|
52
|
+
const compatible = Object.keys(data.versions).filter((candidate) => isTypesBunVersionCompatible(targetVersion, candidate)).map((candidate) => ({
|
|
53
|
+
version: candidate,
|
|
54
|
+
parsed: parseSemver(candidate)
|
|
55
|
+
})).filter((candidate) => !!candidate.parsed).toSorted((left, right) => right.parsed.patch - left.parsed.patch);
|
|
56
|
+
const preferred = compatible[0];
|
|
57
|
+
if (preferred) {
|
|
58
|
+
return preferred.version;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const latest = data["dist-tags"]?.latest;
|
|
62
|
+
if (latest && isTypesBunVersionCompatible(targetVersion, latest)) {
|
|
63
|
+
return latest;
|
|
64
|
+
}
|
|
65
|
+
return targetVersion;
|
|
66
|
+
}
|
|
67
|
+
function findPackageJsonFiles(dir) {
|
|
68
|
+
const files = new Set;
|
|
69
|
+
const glob = new Bun.Glob("**/package.json");
|
|
70
|
+
for (const path of glob.scanSync({ cwd: dir })) {
|
|
71
|
+
if (!path.includes("node_modules")) {
|
|
72
|
+
files.add(join(dir, path));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const gitList = Bun.spawnSync(["git", "ls-files", "--cached", "--others", "--exclude-standard"], { cwd: dir });
|
|
76
|
+
if (gitList.exitCode === 0) {
|
|
77
|
+
const trackedAndUntrackedFiles = new TextDecoder().decode(gitList.stdout).split(`
|
|
78
|
+
`).filter((path) => path.endsWith("package.json")).filter((path) => !path.includes("node_modules")).map((path) => join(dir, path)).filter((path) => existsSync(path));
|
|
79
|
+
for (const filePath of trackedAndUntrackedFiles) {
|
|
80
|
+
files.add(filePath);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return [...files].toSorted();
|
|
84
|
+
}
|
|
85
|
+
function updatePackageManager(filePath, version) {
|
|
86
|
+
const content = readFileSync(filePath, "utf-8");
|
|
87
|
+
const pattern = /"packageManager":\s*"bun@[\d.]+"/;
|
|
88
|
+
if (!pattern.test(content)) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
const updated = content.replace(pattern, `"packageManager": "bun@${version}"`);
|
|
92
|
+
if (updated !== content) {
|
|
93
|
+
writeFileSync(filePath, updated);
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
function updateEnginesBun(filePath, version) {
|
|
99
|
+
const content = readFileSync(filePath, "utf-8");
|
|
100
|
+
const pattern = /"bun":\s*">=[\d.]+"/;
|
|
101
|
+
if (!pattern.test(content)) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
const updated = content.replace(pattern, `"bun": ">=${version}"`);
|
|
105
|
+
if (updated !== content) {
|
|
106
|
+
writeFileSync(filePath, updated);
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
function updateTypesBun(filePath, version) {
|
|
112
|
+
const content = readFileSync(filePath, "utf-8");
|
|
113
|
+
const pattern = /"@types\/bun":\s*"\^[\d.]+"/;
|
|
114
|
+
if (!pattern.test(content)) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
const updated = content.replace(pattern, `"@types/bun": "^${version}"`);
|
|
118
|
+
if (updated !== content) {
|
|
119
|
+
writeFileSync(filePath, updated);
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
async function runUpgradeBun(targetVersion, options = {}) {
|
|
125
|
+
const cwd = process.cwd();
|
|
126
|
+
const bunVersionFile = join(cwd, ".bun-version");
|
|
127
|
+
let version = targetVersion;
|
|
128
|
+
if (!version) {
|
|
129
|
+
info("Fetching latest Bun version...");
|
|
130
|
+
version = await fetchLatestVersion();
|
|
131
|
+
log(`Latest version: ${version}`);
|
|
132
|
+
}
|
|
133
|
+
const currentVersion = existsSync(bunVersionFile) ? readFileSync(bunVersionFile, "utf-8").trim() : "unknown";
|
|
134
|
+
log(`Current version: ${currentVersion}`);
|
|
135
|
+
if (currentVersion === version) {
|
|
136
|
+
success(`Already on version ${version}`);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
log("");
|
|
140
|
+
info(`Upgrading Bun: ${currentVersion} \u2192 ${version}`);
|
|
141
|
+
log("");
|
|
142
|
+
let typesVersion = version;
|
|
143
|
+
try {
|
|
144
|
+
info("Resolving @types/bun version...");
|
|
145
|
+
typesVersion = await resolveTypesBunVersion(version);
|
|
146
|
+
if (typesVersion !== version) {
|
|
147
|
+
warn(`@types/bun ${version} is not published yet; using @types/bun ${typesVersion}`);
|
|
148
|
+
}
|
|
149
|
+
} catch (error) {
|
|
150
|
+
warn(`Could not resolve @types/bun metadata (${error instanceof Error ? error.message : "unknown error"}), defaulting to ${version}`);
|
|
151
|
+
}
|
|
152
|
+
writeFileSync(bunVersionFile, `${version}
|
|
153
|
+
`);
|
|
154
|
+
success("Updated .bun-version");
|
|
155
|
+
const packageFiles = findPackageJsonFiles(cwd);
|
|
156
|
+
info("Updating packageManager...");
|
|
157
|
+
for (const file of packageFiles) {
|
|
158
|
+
if (updatePackageManager(file, version)) {
|
|
159
|
+
log(` ${file.replace(`${cwd}/`, "")}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
info("Updating engines.bun...");
|
|
163
|
+
for (const file of packageFiles) {
|
|
164
|
+
if (updateEnginesBun(file, version)) {
|
|
165
|
+
log(` ${file.replace(`${cwd}/`, "")}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
info("Updating @types/bun...");
|
|
169
|
+
for (const file of packageFiles) {
|
|
170
|
+
if (updateTypesBun(file, typesVersion)) {
|
|
171
|
+
log(` ${file.replace(`${cwd}/`, "")}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (options.install !== false) {
|
|
175
|
+
log("");
|
|
176
|
+
info(`Installing Bun ${version}...`);
|
|
177
|
+
const installResult = Bun.spawnSync([
|
|
178
|
+
"bash",
|
|
179
|
+
"-c",
|
|
180
|
+
`curl -fsSL https://bun.sh/install | bash -s "bun-v${version}"`
|
|
181
|
+
]);
|
|
182
|
+
if (installResult.exitCode !== 0) {
|
|
183
|
+
warn("Could not install Bun automatically");
|
|
184
|
+
log("Install manually: curl -fsSL https://bun.sh/install | bash");
|
|
185
|
+
} else {
|
|
186
|
+
success(`Bun ${version} installed`);
|
|
187
|
+
log("");
|
|
188
|
+
info("Updating lockfile...");
|
|
189
|
+
const bunInstall = Bun.spawnSync(["bun", "install"], {
|
|
190
|
+
cwd,
|
|
191
|
+
env: {
|
|
192
|
+
...process.env,
|
|
193
|
+
BUN_INSTALL: `${process.env["HOME"]}/.bun`,
|
|
194
|
+
PATH: `${process.env["HOME"]}/.bun/bin:${process.env["PATH"]}`
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
if (bunInstall.exitCode === 0) {
|
|
198
|
+
success("Lockfile updated");
|
|
199
|
+
} else {
|
|
200
|
+
warn("Could not update lockfile - run 'bun install' manually");
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
log("");
|
|
205
|
+
success("Done! Changes ready to commit:");
|
|
206
|
+
log(" - .bun-version");
|
|
207
|
+
log(" - package.json files (packageManager, engines.bun, @types/bun)");
|
|
208
|
+
log(" - bun.lock");
|
|
209
|
+
log("");
|
|
210
|
+
log(`Commit with: git add -A && git commit -m 'chore: upgrade Bun to ${version}'`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export { runUpgradeBun };
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { ZodType } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* File entry in a block.
|
|
4
|
+
*/
|
|
5
|
+
interface FileEntry {
|
|
6
|
+
/** Destination path relative to project root */
|
|
7
|
+
path: string;
|
|
8
|
+
/** File contents (embedded in registry) */
|
|
9
|
+
content: string;
|
|
10
|
+
/** Whether to chmod +x after copying */
|
|
11
|
+
executable?: boolean | undefined;
|
|
12
|
+
/** Whether to process as a template (future) */
|
|
13
|
+
template?: boolean | undefined;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Schema for a file entry in a block.
|
|
17
|
+
* Represents a file that will be copied to the user's project.
|
|
18
|
+
*/
|
|
19
|
+
declare const FileEntrySchema: ZodType<FileEntry>;
|
|
20
|
+
/**
|
|
21
|
+
* Block in the registry.
|
|
22
|
+
*/
|
|
23
|
+
interface Block {
|
|
24
|
+
/** Block name (matches the key in blocks record) */
|
|
25
|
+
name: string;
|
|
26
|
+
/** Human-readable description */
|
|
27
|
+
description: string;
|
|
28
|
+
/** Files included in this block */
|
|
29
|
+
files?: FileEntry[] | undefined;
|
|
30
|
+
/** npm dependencies to add to package.json */
|
|
31
|
+
dependencies?: Record<string, string> | undefined;
|
|
32
|
+
/** npm devDependencies to add to package.json */
|
|
33
|
+
devDependencies?: Record<string, string> | undefined;
|
|
34
|
+
/** Other blocks this block extends (for composite blocks) */
|
|
35
|
+
extends?: string[] | undefined;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Schema for a block in the registry.
|
|
39
|
+
* A block is a collection of related files that can be added together.
|
|
40
|
+
*/
|
|
41
|
+
declare const BlockSchema: ZodType<Block>;
|
|
42
|
+
/**
|
|
43
|
+
* Complete registry structure.
|
|
44
|
+
*/
|
|
45
|
+
interface Registry {
|
|
46
|
+
/** Registry schema version */
|
|
47
|
+
version: string;
|
|
48
|
+
/** Map of block name to block definition */
|
|
49
|
+
blocks: Record<string, Block>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Schema for the complete registry.
|
|
53
|
+
* Contains all available blocks with their files and metadata.
|
|
54
|
+
*/
|
|
55
|
+
declare const RegistrySchema: ZodType<Registry>;
|
|
56
|
+
/**
|
|
57
|
+
* Block definition used in the build script.
|
|
58
|
+
* Specifies how to collect source files into a block.
|
|
59
|
+
*/
|
|
60
|
+
interface BlockDefinition {
|
|
61
|
+
/** Human-readable description */
|
|
62
|
+
description: string;
|
|
63
|
+
/** Source file paths (relative to repo root) */
|
|
64
|
+
files?: string[];
|
|
65
|
+
/** Remap source paths to destination paths */
|
|
66
|
+
remap?: Record<string, string>;
|
|
67
|
+
/** npm dependencies */
|
|
68
|
+
dependencies?: Record<string, string>;
|
|
69
|
+
/** npm devDependencies */
|
|
70
|
+
devDependencies?: Record<string, string>;
|
|
71
|
+
/** Other blocks this block extends */
|
|
72
|
+
extends?: string[];
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Configuration for the registry build.
|
|
76
|
+
*/
|
|
77
|
+
interface RegistryBuildConfig {
|
|
78
|
+
/** Registry schema version */
|
|
79
|
+
version: string;
|
|
80
|
+
/** Block definitions */
|
|
81
|
+
blocks: Record<string, BlockDefinition>;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Result of adding a block to a project.
|
|
85
|
+
*/
|
|
86
|
+
interface AddBlockResult {
|
|
87
|
+
/** Files that were created */
|
|
88
|
+
created: string[];
|
|
89
|
+
/** Files that were skipped (already exist) */
|
|
90
|
+
skipped: string[];
|
|
91
|
+
/** Files that were overwritten (with --force) */
|
|
92
|
+
overwritten: string[];
|
|
93
|
+
/** Dependencies added to package.json */
|
|
94
|
+
dependencies: Record<string, string>;
|
|
95
|
+
/** devDependencies added to package.json */
|
|
96
|
+
devDependencies: Record<string, string>;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Options for the add command.
|
|
100
|
+
*/
|
|
101
|
+
interface AddBlockOptions {
|
|
102
|
+
/** Overwrite existing files */
|
|
103
|
+
force?: boolean;
|
|
104
|
+
/** Show what would be added without making changes */
|
|
105
|
+
dryRun?: boolean;
|
|
106
|
+
/** Working directory (defaults to cwd) */
|
|
107
|
+
cwd?: string;
|
|
108
|
+
}
|
|
109
|
+
export { FileEntry, FileEntrySchema, Block, BlockSchema, Registry, RegistrySchema, BlockDefinition, RegistryBuildConfig, AddBlockResult, AddBlockOptions };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { CheckTsDocOptions, CoverageLevel, DeclarationCoverage, PackageCoverage, TsDocCheckResult } from "./tooling-tj9p41vj.js";
|
|
2
|
+
import ts from "typescript";
|
|
3
|
+
/**
|
|
4
|
+
* Check whether a node is an exported declaration worth checking.
|
|
5
|
+
*
|
|
6
|
+
* Returns true for function, interface, type alias, class, enum, and variable
|
|
7
|
+
* declarations that carry the `export` keyword. Re-exports (`{ ... } from`)
|
|
8
|
+
* and `*` are excluded since TSDoc belongs at the definition site.
|
|
9
|
+
*/
|
|
10
|
+
declare function isExportedDeclaration(node: ts.Node): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Extract the name of a declaration node.
|
|
13
|
+
*
|
|
14
|
+
* For variable statements, returns the name of the first variable declarator.
|
|
15
|
+
* Returns `undefined` for anonymous declarations (e.g., `function() {}`).
|
|
16
|
+
*/
|
|
17
|
+
declare function getDeclarationName(node: ts.Node): string | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Determine the kind label for a declaration node.
|
|
20
|
+
*
|
|
21
|
+
* Maps AST node types to human-readable kind strings used in coverage reports.
|
|
22
|
+
*/
|
|
23
|
+
declare function getDeclarationKind(node: ts.Node): string;
|
|
24
|
+
/**
|
|
25
|
+
* Classify a declaration's TSDoc coverage level.
|
|
26
|
+
*
|
|
27
|
+
* - `"documented"` -- has a JSDoc comment with a description. For interfaces
|
|
28
|
+
* and classes, all members must also have JSDoc comments.
|
|
29
|
+
* - `"partial"` -- the declaration has a JSDoc comment but some members
|
|
30
|
+
* (in interfaces/classes) lack documentation.
|
|
31
|
+
* - `"undocumented"` -- no JSDoc comment at all.
|
|
32
|
+
*/
|
|
33
|
+
declare function classifyDeclaration(node: ts.Node, sourceFile: ts.SourceFile): CoverageLevel;
|
|
34
|
+
/**
|
|
35
|
+
* Analyze all exported declarations in a source file for TSDoc coverage.
|
|
36
|
+
*
|
|
37
|
+
* Walks top-level statements, filters to exported declarations, and
|
|
38
|
+
* classifies each for documentation coverage.
|
|
39
|
+
*/
|
|
40
|
+
declare function analyzeSourceFile(sourceFile: ts.SourceFile): DeclarationCoverage[];
|
|
41
|
+
/**
|
|
42
|
+
* Calculate aggregate coverage statistics from declaration results.
|
|
43
|
+
*
|
|
44
|
+
* Partial documentation counts as half coverage in the percentage calculation.
|
|
45
|
+
* An empty array returns 100% (no declarations to check).
|
|
46
|
+
*/
|
|
47
|
+
declare function calculateCoverage(declarations: readonly DeclarationCoverage[]): {
|
|
48
|
+
documented: number;
|
|
49
|
+
partial: number;
|
|
50
|
+
undocumented: number;
|
|
51
|
+
total: number;
|
|
52
|
+
percentage: number;
|
|
53
|
+
};
|
|
54
|
+
/** Discover packages with src/index.ts entry points. */
|
|
55
|
+
declare function discoverPackages(cwd: string): Array<{
|
|
56
|
+
name: string;
|
|
57
|
+
path: string;
|
|
58
|
+
entryPoint: string;
|
|
59
|
+
}>;
|
|
60
|
+
/**
|
|
61
|
+
* Collect source files referenced by re-exports in a barrel file.
|
|
62
|
+
*
|
|
63
|
+
* For `{ ... } from "./foo"` and `* from "./bar"`, resolves
|
|
64
|
+
* the module specifier to a source file in the program. Returns only files
|
|
65
|
+
* within the package (skips external modules).
|
|
66
|
+
*/
|
|
67
|
+
declare function collectReExportedSourceFiles(sourceFile: ts.SourceFile, program: ts.Program, pkgPath: string): ts.SourceFile[];
|
|
68
|
+
/** Analyze a single package entry point, returning coverage data. */
|
|
69
|
+
declare function analyzePackage(pkg: {
|
|
70
|
+
name: string;
|
|
71
|
+
path: string;
|
|
72
|
+
entryPoint: string;
|
|
73
|
+
}, workspaceCwd: string): PackageCoverage;
|
|
74
|
+
/**
|
|
75
|
+
* Analyze TSDoc coverage across workspace packages.
|
|
76
|
+
*
|
|
77
|
+
* Pure function that discovers packages, analyzes TSDoc coverage on exported
|
|
78
|
+
* declarations, and returns the aggregated result. Does not print output or
|
|
79
|
+
* call `process.exit()`.
|
|
80
|
+
*
|
|
81
|
+
* @param options - Analysis options (paths, strict mode, coverage threshold)
|
|
82
|
+
* @returns Aggregated coverage result across all packages, or `null` if no packages found
|
|
83
|
+
*/
|
|
84
|
+
declare function analyzeCheckTsdoc(options?: CheckTsDocOptions): TsDocCheckResult | null;
|
|
85
|
+
export { isExportedDeclaration, getDeclarationName, getDeclarationKind, classifyDeclaration, analyzeSourceFile, calculateCoverage, discoverPackages, collectReExportedSourceFiles, analyzePackage, analyzeCheckTsdoc };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { ZodType } from "zod";
|
|
2
|
+
/** Coverage classification for a single declaration. */
|
|
3
|
+
type CoverageLevel = "documented" | "partial" | "undocumented";
|
|
4
|
+
/** Result for a single exported declaration. */
|
|
5
|
+
interface DeclarationCoverage {
|
|
6
|
+
readonly name: string;
|
|
7
|
+
readonly kind: string;
|
|
8
|
+
readonly level: CoverageLevel;
|
|
9
|
+
readonly file: string;
|
|
10
|
+
readonly line: number;
|
|
11
|
+
}
|
|
12
|
+
/** Coverage summary statistics. */
|
|
13
|
+
interface CoverageSummary {
|
|
14
|
+
readonly documented: number;
|
|
15
|
+
readonly partial: number;
|
|
16
|
+
readonly undocumented: number;
|
|
17
|
+
readonly total: number;
|
|
18
|
+
readonly percentage: number;
|
|
19
|
+
}
|
|
20
|
+
/** Per-package TSDoc coverage stats. */
|
|
21
|
+
interface PackageCoverage {
|
|
22
|
+
readonly name: string;
|
|
23
|
+
readonly path: string;
|
|
24
|
+
readonly declarations: readonly DeclarationCoverage[];
|
|
25
|
+
readonly documented: number;
|
|
26
|
+
readonly partial: number;
|
|
27
|
+
readonly undocumented: number;
|
|
28
|
+
readonly total: number;
|
|
29
|
+
readonly percentage: number;
|
|
30
|
+
}
|
|
31
|
+
/** Aggregated result across all packages. */
|
|
32
|
+
interface TsDocCheckResult {
|
|
33
|
+
readonly ok: boolean;
|
|
34
|
+
readonly packages: readonly PackageCoverage[];
|
|
35
|
+
readonly summary: CoverageSummary;
|
|
36
|
+
}
|
|
37
|
+
/** Zod schema for {@link CoverageLevel}. */
|
|
38
|
+
declare const coverageLevelSchema: ZodType<CoverageLevel>;
|
|
39
|
+
/** Zod schema for {@link DeclarationCoverage}. */
|
|
40
|
+
declare const declarationCoverageSchema: ZodType<DeclarationCoverage>;
|
|
41
|
+
/** Zod schema for {@link CoverageSummary}. */
|
|
42
|
+
declare const coverageSummarySchema: ZodType<CoverageSummary>;
|
|
43
|
+
/** Zod schema for {@link PackageCoverage}. */
|
|
44
|
+
declare const packageCoverageSchema: ZodType<PackageCoverage>;
|
|
45
|
+
/** Zod schema for {@link TsDocCheckResult}. */
|
|
46
|
+
declare const tsDocCheckResultSchema: ZodType<TsDocCheckResult>;
|
|
47
|
+
/** Options for the check-tsdoc command. */
|
|
48
|
+
interface CheckTsDocOptions {
|
|
49
|
+
readonly strict?: boolean | undefined;
|
|
50
|
+
readonly json?: boolean | undefined;
|
|
51
|
+
readonly minCoverage?: number | undefined;
|
|
52
|
+
readonly cwd?: string | undefined;
|
|
53
|
+
readonly paths?: readonly string[] | undefined;
|
|
54
|
+
}
|
|
55
|
+
export { CoverageLevel, DeclarationCoverage, CoverageSummary, PackageCoverage, TsDocCheckResult, coverageLevelSchema, declarationCoverageSchema, coverageSummarySchema, packageCoverageSchema, tsDocCheckResultSchema, CheckTsDocOptions };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Bun version compatibility helpers.
|
|
3
|
+
*
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Parsed semantic version components.
|
|
8
|
+
*/
|
|
9
|
+
interface ParsedSemver {
|
|
10
|
+
readonly major: number;
|
|
11
|
+
readonly minor: number;
|
|
12
|
+
readonly patch: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Parse a semver-like value into numeric components.
|
|
16
|
+
*
|
|
17
|
+
* Accepts standard versions like `1.3.10` and prerelease variants like
|
|
18
|
+
* `1.3.10-canary.1` by reading only the numeric major/minor/patch prefix.
|
|
19
|
+
*/
|
|
20
|
+
declare function parseSemver(version: string): ParsedSemver | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Whether a candidate `@types/bun` version is compatible with a target Bun version.
|
|
23
|
+
*
|
|
24
|
+
* Compatibility rule:
|
|
25
|
+
* - same major/minor
|
|
26
|
+
* - candidate patch <= Bun patch
|
|
27
|
+
*/
|
|
28
|
+
declare function isTypesBunVersionCompatible(bunVersion: string, bunTypesVersion: string): boolean;
|
|
29
|
+
export { ParsedSemver, parseSemver, isTypesBunVersionCompatible };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/tooling/src/cli/check-bunup-registry.ts
|
|
3
|
+
import { resolve } from "path";
|
|
4
|
+
function extractBunupFilterName(script) {
|
|
5
|
+
const match = script.match(/bunup\s+--filter\s+(\S+)/);
|
|
6
|
+
return match?.[1] ?? null;
|
|
7
|
+
}
|
|
8
|
+
function findUnregisteredPackages(packagesWithFilter, registeredNames) {
|
|
9
|
+
const registered = new Set(registeredNames);
|
|
10
|
+
const missing = packagesWithFilter.filter((name) => !registered.has(name)).toSorted();
|
|
11
|
+
return {
|
|
12
|
+
ok: missing.length === 0,
|
|
13
|
+
missing
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
var COLORS = {
|
|
17
|
+
reset: "\x1B[0m",
|
|
18
|
+
red: "\x1B[31m",
|
|
19
|
+
green: "\x1B[32m",
|
|
20
|
+
yellow: "\x1B[33m",
|
|
21
|
+
blue: "\x1B[34m",
|
|
22
|
+
dim: "\x1B[2m"
|
|
23
|
+
};
|
|
24
|
+
async function runCheckBunupRegistry() {
|
|
25
|
+
const cwd = process.cwd();
|
|
26
|
+
const configPath = resolve(cwd, "bunup.config.ts");
|
|
27
|
+
let registeredNames;
|
|
28
|
+
try {
|
|
29
|
+
const configModule = await import(configPath);
|
|
30
|
+
const rawConfig = configModule.default;
|
|
31
|
+
if (!Array.isArray(rawConfig)) {
|
|
32
|
+
process.stderr.write(`bunup.config.ts must export a workspace array
|
|
33
|
+
`);
|
|
34
|
+
process.exitCode = 1;
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
registeredNames = rawConfig.map((entry) => entry.name);
|
|
38
|
+
} catch {
|
|
39
|
+
process.stderr.write(`Could not load bunup.config.ts from ${cwd}
|
|
40
|
+
`);
|
|
41
|
+
process.exitCode = 1;
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const packagesWithFilter = [];
|
|
45
|
+
const glob = new Bun.Glob("{packages,apps}/*/package.json");
|
|
46
|
+
for (const match of glob.scanSync({ cwd })) {
|
|
47
|
+
const pkgPath = resolve(cwd, match);
|
|
48
|
+
try {
|
|
49
|
+
const pkg = await Bun.file(pkgPath).json();
|
|
50
|
+
const buildScript = pkg.scripts?.["build"];
|
|
51
|
+
if (!buildScript)
|
|
52
|
+
continue;
|
|
53
|
+
const filterName = extractBunupFilterName(buildScript);
|
|
54
|
+
if (filterName) {
|
|
55
|
+
packagesWithFilter.push(filterName);
|
|
56
|
+
}
|
|
57
|
+
} catch {}
|
|
58
|
+
}
|
|
59
|
+
const result = findUnregisteredPackages(packagesWithFilter, registeredNames);
|
|
60
|
+
if (result.ok) {
|
|
61
|
+
process.stdout.write(`${COLORS.green}All ${packagesWithFilter.length} packages with bunup --filter are registered in bunup.config.ts.${COLORS.reset}
|
|
62
|
+
`);
|
|
63
|
+
process.exitCode = 0;
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
process.stderr.write(`${COLORS.red}${result.missing.length} package(s) have bunup --filter build scripts but are not registered in bunup.config.ts:${COLORS.reset}
|
|
67
|
+
|
|
68
|
+
`);
|
|
69
|
+
for (const name of result.missing) {
|
|
70
|
+
process.stderr.write(` ${COLORS.yellow}${name}${COLORS.reset} ${COLORS.dim}(missing from workspace array)${COLORS.reset}
|
|
71
|
+
`);
|
|
72
|
+
}
|
|
73
|
+
process.stderr.write(`
|
|
74
|
+
Add the missing entries to ${COLORS.blue}bunup.config.ts${COLORS.reset} defineWorkspace array.
|
|
75
|
+
`);
|
|
76
|
+
process.stderr.write(`Without registration, ${COLORS.dim}bunup --filter <name>${COLORS.reset} silently exits with no output.
|
|
77
|
+
`);
|
|
78
|
+
process.exitCode = 1;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export { extractBunupFilterName, findUnregisteredPackages, runCheckBunupRegistry };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Options for the check-exports command */
|
|
2
|
+
interface CheckExportsOptions {
|
|
3
|
+
readonly json?: boolean;
|
|
4
|
+
}
|
|
5
|
+
/** Resolve whether to output JSON based on options and env */
|
|
6
|
+
declare function resolveJsonMode(options?: CheckExportsOptions): boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Run check-exports across all workspace packages.
|
|
9
|
+
*
|
|
10
|
+
* Reads the bunup workspace config to discover packages and their * settings, then compares expected vs actual exports in each package.json.
|
|
11
|
+
*/
|
|
12
|
+
declare function runCheckExports(options?: CheckExportsOptions): Promise<void>;
|
|
13
|
+
export { CheckExportsOptions, resolveJsonMode, runCheckExports };
|
package/dist/version.js
ADDED
package/lefthook.yml
CHANGED
|
@@ -13,6 +13,10 @@ pre-commit:
|
|
|
13
13
|
run: bun x ultracite fix {staged_files}
|
|
14
14
|
stage_fixed: true
|
|
15
15
|
|
|
16
|
+
home-paths:
|
|
17
|
+
glob: "*.{js,jsx,json,jsonc,css,md,ts,tsx,yaml,yml}"
|
|
18
|
+
run: bun x @outfitter/tooling check-home-paths {staged_files}
|
|
19
|
+
|
|
16
20
|
typecheck:
|
|
17
21
|
glob: "*.{ts,tsx}"
|
|
18
22
|
run: bun run typecheck
|
|
@@ -25,4 +29,4 @@ pre-push:
|
|
|
25
29
|
# (*-tests, */tests, *_tests). Otherwise runs verify:ci (or a strict
|
|
26
30
|
# fallback: typecheck/check-or-lint/build/test).
|
|
27
31
|
# Requires: @outfitter/tooling as a dev dependency.
|
|
28
|
-
run:
|
|
32
|
+
run: bun x @outfitter/tooling pre-push
|