@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,279 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// @bun
|
|
3
|
+
import"../shared/@outfitter/tooling-jnrs9rqd.js";
|
|
4
|
+
|
|
5
|
+
// packages/tooling/src/registry/build.ts
|
|
6
|
+
import {
|
|
7
|
+
existsSync as existsSync2,
|
|
8
|
+
mkdirSync,
|
|
9
|
+
readFileSync as readFileSync2,
|
|
10
|
+
statSync as statSync2,
|
|
11
|
+
writeFileSync
|
|
12
|
+
} from "fs";
|
|
13
|
+
import { dirname as dirname2, join as join2 } from "path";
|
|
14
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
15
|
+
|
|
16
|
+
// packages/presets/dist/index.js
|
|
17
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "fs";
|
|
18
|
+
import { dirname, isAbsolute, join, relative, resolve } from "path";
|
|
19
|
+
import { fileURLToPath } from "url";
|
|
20
|
+
var DEPENDENCY_SECTIONS = [
|
|
21
|
+
"dependencies",
|
|
22
|
+
"devDependencies",
|
|
23
|
+
"peerDependencies",
|
|
24
|
+
"optionalDependencies"
|
|
25
|
+
];
|
|
26
|
+
function isRecord(value) {
|
|
27
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
28
|
+
}
|
|
29
|
+
function loadWorkspaceCatalog(packageDir) {
|
|
30
|
+
let dir = packageDir;
|
|
31
|
+
for (let i = 0;i < 10; i++) {
|
|
32
|
+
const rootPkgPath = join(dir, "package.json");
|
|
33
|
+
if (existsSync(rootPkgPath)) {
|
|
34
|
+
try {
|
|
35
|
+
const raw = JSON.parse(readFileSync(rootPkgPath, "utf-8"));
|
|
36
|
+
if (isRecord(raw) && isRecord(raw["catalog"])) {
|
|
37
|
+
const catalog = {};
|
|
38
|
+
for (const [name, version] of Object.entries(raw["catalog"])) {
|
|
39
|
+
if (typeof version === "string") {
|
|
40
|
+
catalog[name] = version;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return catalog;
|
|
44
|
+
}
|
|
45
|
+
} catch {}
|
|
46
|
+
}
|
|
47
|
+
const parent = dirname(dir);
|
|
48
|
+
if (parent === dir)
|
|
49
|
+
break;
|
|
50
|
+
dir = parent;
|
|
51
|
+
}
|
|
52
|
+
return {};
|
|
53
|
+
}
|
|
54
|
+
function getResolvedVersions() {
|
|
55
|
+
const packageDir = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
56
|
+
const packageJsonPath = join(packageDir, "package.json");
|
|
57
|
+
const raw = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
58
|
+
if (!isRecord(raw)) {
|
|
59
|
+
throw new Error("@outfitter/presets package.json must be a JSON object");
|
|
60
|
+
}
|
|
61
|
+
const all = {};
|
|
62
|
+
let hasCatalogRefs = false;
|
|
63
|
+
for (const section of DEPENDENCY_SECTIONS) {
|
|
64
|
+
const deps = raw[section];
|
|
65
|
+
if (!isRecord(deps))
|
|
66
|
+
continue;
|
|
67
|
+
for (const [name, version] of Object.entries(deps)) {
|
|
68
|
+
if (typeof version === "string") {
|
|
69
|
+
if (version === "catalog:") {
|
|
70
|
+
hasCatalogRefs = true;
|
|
71
|
+
} else {
|
|
72
|
+
all[name] = version;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (hasCatalogRefs) {
|
|
78
|
+
const catalog = loadWorkspaceCatalog(packageDir);
|
|
79
|
+
const unresolved = [];
|
|
80
|
+
for (const section of DEPENDENCY_SECTIONS) {
|
|
81
|
+
const deps = raw[section];
|
|
82
|
+
if (!isRecord(deps))
|
|
83
|
+
continue;
|
|
84
|
+
for (const [name, version] of Object.entries(deps)) {
|
|
85
|
+
if (version === "catalog:") {
|
|
86
|
+
if (catalog[name]) {
|
|
87
|
+
all[name] = catalog[name];
|
|
88
|
+
} else {
|
|
89
|
+
unresolved.push(name);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (unresolved.length > 0) {
|
|
95
|
+
throw new Error(`Unresolvable catalog: references (no catalog entry found): ${unresolved.join(", ")}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return { all };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// packages/tooling/src/registry/build.ts
|
|
102
|
+
function log(message) {
|
|
103
|
+
process.stdout.write(`${message}
|
|
104
|
+
`);
|
|
105
|
+
}
|
|
106
|
+
function findRepoRoot(startDir) {
|
|
107
|
+
let dir = startDir;
|
|
108
|
+
while (dir !== "/") {
|
|
109
|
+
const pkgPath = join2(dir, "package.json");
|
|
110
|
+
if (existsSync2(pkgPath)) {
|
|
111
|
+
const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
|
|
112
|
+
if (pkg.workspaces) {
|
|
113
|
+
return dir;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
dir = dirname2(dir);
|
|
117
|
+
}
|
|
118
|
+
throw new Error("Could not find repository root");
|
|
119
|
+
}
|
|
120
|
+
function isExecutable(filePath) {
|
|
121
|
+
try {
|
|
122
|
+
const stats = statSync2(filePath);
|
|
123
|
+
return (stats.mode & 64) !== 0;
|
|
124
|
+
} catch {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function readFileEntry(repoRoot, sourcePath, destPath) {
|
|
129
|
+
const fullPath = join2(repoRoot, sourcePath);
|
|
130
|
+
if (!existsSync2(fullPath)) {
|
|
131
|
+
throw new Error(`Source file not found: ${fullPath}`);
|
|
132
|
+
}
|
|
133
|
+
const content = readFileSync2(fullPath, "utf-8");
|
|
134
|
+
const executable = isExecutable(fullPath);
|
|
135
|
+
return {
|
|
136
|
+
path: destPath,
|
|
137
|
+
content,
|
|
138
|
+
...executable && { executable: true }
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function buildBlock(repoRoot, name, def) {
|
|
142
|
+
const block = {
|
|
143
|
+
name,
|
|
144
|
+
description: def.description
|
|
145
|
+
};
|
|
146
|
+
if (def.files && def.files.length > 0) {
|
|
147
|
+
block.files = def.files.map((sourcePath) => {
|
|
148
|
+
const destPath = def.remap?.[sourcePath] ?? sourcePath;
|
|
149
|
+
return readFileEntry(repoRoot, sourcePath, destPath);
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
if (def.dependencies && Object.keys(def.dependencies).length > 0) {
|
|
153
|
+
block.dependencies = def.dependencies;
|
|
154
|
+
}
|
|
155
|
+
if (def.devDependencies && Object.keys(def.devDependencies).length > 0) {
|
|
156
|
+
block.devDependencies = def.devDependencies;
|
|
157
|
+
}
|
|
158
|
+
if (def.extends && def.extends.length > 0) {
|
|
159
|
+
block.extends = def.extends;
|
|
160
|
+
}
|
|
161
|
+
return block;
|
|
162
|
+
}
|
|
163
|
+
function buildRegistry(config, repoRoot) {
|
|
164
|
+
const blocks = {};
|
|
165
|
+
for (const [name, def] of Object.entries(config.blocks)) {
|
|
166
|
+
blocks[name] = buildBlock(repoRoot, name, def);
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
version: config.version,
|
|
170
|
+
blocks
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
function resolveVersion(versions, name) {
|
|
174
|
+
const version = versions[name];
|
|
175
|
+
if (!version) {
|
|
176
|
+
throw new Error(`Missing resolved version for "${name}" in @outfitter/presets`);
|
|
177
|
+
}
|
|
178
|
+
return version;
|
|
179
|
+
}
|
|
180
|
+
function getWorkspacePackageVersion(relativePackageJsonPath) {
|
|
181
|
+
const pkgPath = join2(dirname2(fileURLToPath2(import.meta.url)), relativePackageJsonPath);
|
|
182
|
+
const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
|
|
183
|
+
if (typeof pkg.version !== "string") {
|
|
184
|
+
throw new Error(`Expected version in package.json at ${pkgPath}`);
|
|
185
|
+
}
|
|
186
|
+
return `^${pkg.version}`;
|
|
187
|
+
}
|
|
188
|
+
function getToolingVersion() {
|
|
189
|
+
return getWorkspacePackageVersion("../../package.json");
|
|
190
|
+
}
|
|
191
|
+
function getOxlintPluginVersion() {
|
|
192
|
+
return getWorkspacePackageVersion("../../../oxlint-plugin/package.json");
|
|
193
|
+
}
|
|
194
|
+
function createRegistryConfig() {
|
|
195
|
+
const { all: versions } = getResolvedVersions();
|
|
196
|
+
const toolingVersion = getToolingVersion();
|
|
197
|
+
const oxlintPluginVersion = getOxlintPluginVersion();
|
|
198
|
+
return {
|
|
199
|
+
version: "1.0.0",
|
|
200
|
+
blocks: {
|
|
201
|
+
claude: {
|
|
202
|
+
description: "Claude Code settings and hooks for automated formatting",
|
|
203
|
+
files: [
|
|
204
|
+
".claude/settings.json",
|
|
205
|
+
".claude/hooks/format-code-on-stop.sh"
|
|
206
|
+
]
|
|
207
|
+
},
|
|
208
|
+
linter: {
|
|
209
|
+
description: "Linter and formatter configuration (oxlint/oxfmt) via Ultracite",
|
|
210
|
+
files: [
|
|
211
|
+
"packages/tooling/configs/.oxlintrc.json",
|
|
212
|
+
"packages/tooling/configs/.oxfmtrc.jsonc"
|
|
213
|
+
],
|
|
214
|
+
remap: {
|
|
215
|
+
"packages/tooling/configs/.oxlintrc.json": ".oxlintrc.json",
|
|
216
|
+
"packages/tooling/configs/.oxfmtrc.jsonc": ".oxfmtrc.jsonc"
|
|
217
|
+
},
|
|
218
|
+
devDependencies: {
|
|
219
|
+
"@outfitter/oxlint-plugin": oxlintPluginVersion,
|
|
220
|
+
ultracite: resolveVersion(versions, "ultracite"),
|
|
221
|
+
oxlint: resolveVersion(versions, "oxlint"),
|
|
222
|
+
oxfmt: resolveVersion(versions, "oxfmt")
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
lefthook: {
|
|
226
|
+
description: "Git hooks via Lefthook for pre-commit and pre-push",
|
|
227
|
+
files: ["packages/tooling/lefthook.yml"],
|
|
228
|
+
remap: { "packages/tooling/lefthook.yml": ".lefthook.yml" },
|
|
229
|
+
devDependencies: {
|
|
230
|
+
"@outfitter/tooling": toolingVersion,
|
|
231
|
+
lefthook: resolveVersion(versions, "lefthook"),
|
|
232
|
+
ultracite: resolveVersion(versions, "ultracite")
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
markdownlint: {
|
|
236
|
+
description: "Markdown linting configuration via markdownlint-cli2",
|
|
237
|
+
files: ["packages/tooling/.markdownlint-cli2.jsonc"],
|
|
238
|
+
remap: {
|
|
239
|
+
"packages/tooling/.markdownlint-cli2.jsonc": ".markdownlint-cli2.jsonc"
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
bootstrap: {
|
|
243
|
+
description: "Project bootstrap script for installing tools and dependencies",
|
|
244
|
+
files: ["packages/tooling/templates/bootstrap.sh"],
|
|
245
|
+
remap: {
|
|
246
|
+
"packages/tooling/templates/bootstrap.sh": "scripts/bootstrap.sh"
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
scaffolding: {
|
|
250
|
+
description: "Full starter kit: Claude settings, oxlint/oxfmt, Lefthook, markdownlint, and bootstrap script",
|
|
251
|
+
extends: ["claude", "linter", "lefthook", "markdownlint", "bootstrap"]
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
var REGISTRY_CONFIG = createRegistryConfig();
|
|
257
|
+
function main() {
|
|
258
|
+
const scriptDir = dirname2(new URL(import.meta.url).pathname);
|
|
259
|
+
const repoRoot = findRepoRoot(scriptDir);
|
|
260
|
+
const outputDir = join2(repoRoot, "packages/tooling/registry");
|
|
261
|
+
const outputPath = join2(outputDir, "registry.json");
|
|
262
|
+
log(`Building registry from: ${repoRoot}`);
|
|
263
|
+
if (!existsSync2(outputDir)) {
|
|
264
|
+
mkdirSync(outputDir, { recursive: true });
|
|
265
|
+
}
|
|
266
|
+
const registry = buildRegistry(REGISTRY_CONFIG, repoRoot);
|
|
267
|
+
writeFileSync(outputPath, `${JSON.stringify(registry, null, "\t")}
|
|
268
|
+
`);
|
|
269
|
+
const blockCount = Object.keys(registry.blocks).length;
|
|
270
|
+
const fileCount = Object.values(registry.blocks).flatMap((b) => b.files ?? []).length;
|
|
271
|
+
log(`\u2713 Generated ${outputPath}`);
|
|
272
|
+
log(` ${blockCount} blocks, ${fileCount} files embedded`);
|
|
273
|
+
}
|
|
274
|
+
if (import.meta.main) {
|
|
275
|
+
main();
|
|
276
|
+
}
|
|
277
|
+
export {
|
|
278
|
+
REGISTRY_CONFIG
|
|
279
|
+
};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import "../shared/@outfitter/tooling-xqwn46sx.js";
|
|
2
|
+
import { AddBlockOptions, AddBlockResult, Block, BlockDefinition, BlockSchema, FileEntry, FileEntrySchema, Registry, RegistryBuildConfig, RegistrySchema } from "../shared/@outfitter/tooling-sjm8nebx.js";
|
|
3
|
+
export { RegistrySchema, RegistryBuildConfig, Registry, FileEntrySchema, FileEntry, BlockSchema, BlockDefinition, Block, AddBlockResult, AddBlockOptions };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { BlockSchema, FileEntrySchema, RegistrySchema } from "./schema.js";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { AddBlockOptions, AddBlockResult, Block, BlockDefinition, BlockSchema, FileEntry, FileEntrySchema, Registry, RegistryBuildConfig, RegistrySchema } from "../shared/@outfitter/tooling-sjm8nebx.js";
|
|
2
|
+
export { RegistrySchema, RegistryBuildConfig, Registry, FileEntrySchema, FileEntry, BlockSchema, BlockDefinition, Block, AddBlockResult, AddBlockOptions };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import"../shared/@outfitter/tooling-jnrs9rqd.js";
|
|
3
|
+
|
|
4
|
+
// packages/tooling/src/registry/schema.ts
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
var FileEntrySchema = z.object({
|
|
7
|
+
path: z.string().min(1),
|
|
8
|
+
content: z.string(),
|
|
9
|
+
executable: z.boolean().optional(),
|
|
10
|
+
template: z.boolean().optional()
|
|
11
|
+
});
|
|
12
|
+
var BlockSchema = z.object({
|
|
13
|
+
name: z.string().min(1),
|
|
14
|
+
description: z.string().min(1),
|
|
15
|
+
files: z.array(FileEntrySchema).optional(),
|
|
16
|
+
dependencies: z.record(z.string(), z.string()).optional(),
|
|
17
|
+
devDependencies: z.record(z.string(), z.string()).optional(),
|
|
18
|
+
extends: z.array(z.string()).optional()
|
|
19
|
+
});
|
|
20
|
+
var RegistrySchema = z.object({
|
|
21
|
+
version: z.string(),
|
|
22
|
+
blocks: z.record(z.string(), BlockSchema)
|
|
23
|
+
});
|
|
24
|
+
export {
|
|
25
|
+
RegistrySchema,
|
|
26
|
+
FileEntrySchema,
|
|
27
|
+
BlockSchema
|
|
28
|
+
};
|
|
@@ -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 };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/tooling/src/cli/fix.ts
|
|
3
|
+
function buildFixCommand(options) {
|
|
4
|
+
const cmd = ["ultracite", "fix"];
|
|
5
|
+
if (options.paths && options.paths.length > 0) {
|
|
6
|
+
cmd.push(...options.paths);
|
|
7
|
+
}
|
|
8
|
+
return cmd;
|
|
9
|
+
}
|
|
10
|
+
async function runFix(paths = []) {
|
|
11
|
+
const cmd = buildFixCommand({ paths });
|
|
12
|
+
process.stdout.write(`Running: bun x ${cmd.join(" ")}
|
|
13
|
+
`);
|
|
14
|
+
const proc = Bun.spawn(["bun", "x", ...cmd], {
|
|
15
|
+
stdio: ["inherit", "inherit", "inherit"]
|
|
16
|
+
});
|
|
17
|
+
const exitCode = await proc.exited;
|
|
18
|
+
process.exitCode = exitCode;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { buildFixCommand, runFix };
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
canBypassRedPhaseByChangedFiles,
|
|
4
|
+
checkBunVersion,
|
|
5
|
+
createVerificationPlan,
|
|
6
|
+
getChangedFilesForPush,
|
|
7
|
+
getCurrentBranch,
|
|
8
|
+
hasPackageSourceChanges,
|
|
9
|
+
hasRedPhaseBranchInContext,
|
|
10
|
+
isRedPhaseBranch,
|
|
11
|
+
isReleaseBranch,
|
|
12
|
+
isScaffoldBranch,
|
|
13
|
+
printTsdocSummary,
|
|
14
|
+
readPackageScripts
|
|
15
|
+
} from "./tooling-c8q6mj8z.js";
|
|
16
|
+
// packages/tooling/src/cli/pre-push.ts
|
|
17
|
+
var COLORS = {
|
|
18
|
+
reset: "\x1B[0m",
|
|
19
|
+
red: "\x1B[31m",
|
|
20
|
+
green: "\x1B[32m",
|
|
21
|
+
yellow: "\x1B[33m",
|
|
22
|
+
blue: "\x1B[34m"
|
|
23
|
+
};
|
|
24
|
+
function log(msg) {
|
|
25
|
+
process.stdout.write(`${msg}
|
|
26
|
+
`);
|
|
27
|
+
}
|
|
28
|
+
function runScript(scriptName) {
|
|
29
|
+
log("");
|
|
30
|
+
log(`Running: ${COLORS.blue}bun run ${scriptName}${COLORS.reset}`);
|
|
31
|
+
const result = Bun.spawnSync(["bun", "run", scriptName], {
|
|
32
|
+
stdio: ["inherit", "inherit", "inherit"]
|
|
33
|
+
});
|
|
34
|
+
return result.exitCode === 0;
|
|
35
|
+
}
|
|
36
|
+
function maybeSkipForRedPhase(reason, branch) {
|
|
37
|
+
const changedFiles = getChangedFilesForPush();
|
|
38
|
+
if (!changedFiles.deterministic) {
|
|
39
|
+
log(`${COLORS.yellow}RED-phase bypass denied${COLORS.reset}: could not determine full push diff range`);
|
|
40
|
+
log("Running strict verification.");
|
|
41
|
+
log("");
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
if (!canBypassRedPhaseByChangedFiles(changedFiles)) {
|
|
45
|
+
log(`${COLORS.yellow}RED-phase bypass denied${COLORS.reset}: changed files are not test-only`);
|
|
46
|
+
if (changedFiles.files.length > 0) {
|
|
47
|
+
log(`Changed files (${changedFiles.source}): ${changedFiles.files.join(", ")}`);
|
|
48
|
+
} else {
|
|
49
|
+
log(`No changed files detected in ${changedFiles.source} range. Running strict verification.`);
|
|
50
|
+
}
|
|
51
|
+
log("");
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
if (reason === "branch") {
|
|
55
|
+
log(`${COLORS.yellow}TDD RED phase${COLORS.reset} detected: ${COLORS.blue}${branch}${COLORS.reset}`);
|
|
56
|
+
} else {
|
|
57
|
+
log(`${COLORS.yellow}Scaffold branch${COLORS.reset} with RED phase branch in context: ${COLORS.blue}${branch}${COLORS.reset}`);
|
|
58
|
+
}
|
|
59
|
+
log(`${COLORS.yellow}Skipping strict verification${COLORS.reset} - changed files are test-only`);
|
|
60
|
+
log(`Diff source: ${changedFiles.source}`);
|
|
61
|
+
log("");
|
|
62
|
+
log("Remember: GREEN phase (implementation) must make these tests pass!");
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
async function runPrePush(options = {}) {
|
|
66
|
+
log(`${COLORS.blue}Pre-push verify${COLORS.reset} (TDD-aware)`);
|
|
67
|
+
log("");
|
|
68
|
+
if (options.force) {
|
|
69
|
+
log(`${COLORS.yellow}Force flag set${COLORS.reset} - skipping strict verification`);
|
|
70
|
+
process.exitCode = 0;
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const versionCheck = checkBunVersion();
|
|
74
|
+
if (!versionCheck.matches) {
|
|
75
|
+
log(`${COLORS.red}Bun version mismatch${COLORS.reset}: running ${versionCheck.actual}, pinned ${versionCheck.expected}`);
|
|
76
|
+
log("Fix: bunx @outfitter/tooling upgrade-bun");
|
|
77
|
+
log("");
|
|
78
|
+
process.exitCode = 1;
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const branch = getCurrentBranch();
|
|
82
|
+
if (isReleaseBranch(branch)) {
|
|
83
|
+
log(`${COLORS.yellow}Release branch detected${COLORS.reset}: ${COLORS.blue}${branch}${COLORS.reset}`);
|
|
84
|
+
log(`${COLORS.yellow}Skipping strict verification${COLORS.reset} for automated changeset release push`);
|
|
85
|
+
process.exitCode = 0;
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (isRedPhaseBranch(branch)) {
|
|
89
|
+
if (maybeSkipForRedPhase("branch", branch)) {
|
|
90
|
+
process.exitCode = 0;
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (isScaffoldBranch(branch)) {
|
|
95
|
+
if (hasRedPhaseBranchInContext(branch)) {
|
|
96
|
+
if (maybeSkipForRedPhase("context", branch)) {
|
|
97
|
+
process.exitCode = 0;
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const plan = createVerificationPlan(readPackageScripts());
|
|
103
|
+
if (!plan.ok) {
|
|
104
|
+
log(`${COLORS.red}Strict pre-push verification is not configured${COLORS.reset}`);
|
|
105
|
+
log(plan.error);
|
|
106
|
+
log("");
|
|
107
|
+
log("Add one of:");
|
|
108
|
+
log(" - verify:push");
|
|
109
|
+
log(" - verify:ci");
|
|
110
|
+
log(" - typecheck + (check or lint) + build + test");
|
|
111
|
+
process.exitCode = 1;
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
log(`Running strict verification for branch: ${COLORS.blue}${branch}${COLORS.reset}`);
|
|
115
|
+
if (plan.source === "verify:push" || plan.source === "verify:ci") {
|
|
116
|
+
log(`Using \`${plan.source}\` script.`);
|
|
117
|
+
} else {
|
|
118
|
+
log(`Using fallback scripts: ${plan.scripts.join(" -> ")}`);
|
|
119
|
+
}
|
|
120
|
+
for (const scriptName of plan.scripts) {
|
|
121
|
+
if (runScript(scriptName)) {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
log("");
|
|
125
|
+
log(`${COLORS.red}Verification failed${COLORS.reset} on script: ${scriptName}`);
|
|
126
|
+
log("");
|
|
127
|
+
log("If this is intentional TDD RED phase work, name your branch:");
|
|
128
|
+
log(" - feature-tests");
|
|
129
|
+
log(" - feature/tests");
|
|
130
|
+
log(" - feature_tests");
|
|
131
|
+
process.exitCode = 1;
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const changedFiles = getChangedFilesForPush();
|
|
135
|
+
if (hasPackageSourceChanges(changedFiles)) {
|
|
136
|
+
try {
|
|
137
|
+
await printTsdocSummary(log);
|
|
138
|
+
} catch {}
|
|
139
|
+
}
|
|
140
|
+
log("");
|
|
141
|
+
log(`${COLORS.green}Strict verification passed${COLORS.reset}`);
|
|
142
|
+
process.exitCode = 0;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export { runPrePush };
|