@outfitter/tooling 0.2.3 → 0.3.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/README.md +69 -1
- package/biome.json +1 -1
- package/dist/cli/check-changeset.d.ts +12 -10
- package/dist/cli/check-changeset.js +7 -1
- package/dist/cli/check-exports.d.ts +2 -2
- package/dist/cli/check-exports.js +3 -1
- package/dist/cli/check-readme-imports.d.ts +3 -2
- package/dist/cli/check-readme-imports.js +5 -1
- package/dist/cli/check-tsdoc.d.ts +2 -0
- package/dist/cli/check-tsdoc.js +36 -0
- package/dist/cli/index.js +208 -51
- package/dist/cli/pre-push.d.ts +8 -1
- package/dist/cli/pre-push.js +6 -1
- package/dist/index.d.ts +79 -4
- package/dist/index.js +17 -6
- package/dist/registry/build.d.ts +1 -1
- package/dist/registry/build.js +8 -5
- package/dist/registry/index.d.ts +2 -2
- package/dist/registry/index.js +1 -1
- package/dist/registry/schema.d.ts +1 -1
- package/dist/registry/schema.js +1 -1
- package/dist/shared/@outfitter/{tooling-8sd32ts6.js → tooling-2n2dpsaa.js} +48 -2
- package/dist/shared/@outfitter/tooling-cj5vsa9k.js +184 -0
- package/dist/shared/@outfitter/{tooling-tf22zt9p.js → tooling-enjcenja.js} +5 -2
- package/dist/shared/@outfitter/tooling-njw4z34x.d.ts +140 -0
- package/dist/shared/@outfitter/tooling-qk5xgmxr.js +405 -0
- package/dist/shared/@outfitter/{tooling-q0d60xb3.d.ts → tooling-wesswf21.d.ts} +2 -1
- package/dist/shared/@outfitter/{tooling-g83d0kjv.js → tooling-wv09k6hr.js} +3 -3
- package/dist/shared/{chunk-6a7tjcgm.js → chunk-7tdgbqb0.js} +5 -1
- package/dist/shared/chunk-cmde0fwx.js +421 -0
- package/dist/version.d.ts +1 -1
- package/package.json +14 -6
- package/registry/registry.json +6 -6
- package/dist/shared/@outfitter/tooling-3w8vr2w3.js +0 -94
- package/dist/shared/chunk-8aenrm6f.js +0 -18
package/dist/cli/index.js
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import {
|
|
3
|
-
VERSION
|
|
4
|
-
|
|
3
|
+
VERSION,
|
|
4
|
+
analyzeSourceFile,
|
|
5
|
+
calculateCoverage,
|
|
6
|
+
runCheckTsdoc
|
|
7
|
+
} from "../shared/chunk-cmde0fwx.js";
|
|
5
8
|
import {
|
|
6
9
|
__require
|
|
7
10
|
} from "../shared/chunk-3s189drz.js";
|
|
8
11
|
|
|
9
12
|
// src/cli/index.ts
|
|
13
|
+
import { exitWithError } from "@outfitter/cli";
|
|
14
|
+
import { createCLI } from "@outfitter/cli/command";
|
|
10
15
|
import { Command } from "commander";
|
|
11
16
|
|
|
12
17
|
// src/cli/check.ts
|
|
@@ -203,6 +208,8 @@ Add the missing entries to ${COLORS.blue}bunup.config.ts${COLORS.reset} defineWo
|
|
|
203
208
|
}
|
|
204
209
|
|
|
205
210
|
// src/cli/check-changeset.ts
|
|
211
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
212
|
+
import { join } from "node:path";
|
|
206
213
|
function getChangedPackagePaths(files) {
|
|
207
214
|
const packageNames = new Set;
|
|
208
215
|
const pattern = /^packages\/([^/]+)\/src\//;
|
|
@@ -234,6 +241,73 @@ function checkChangesetRequired(changedPackages, changesetFiles) {
|
|
|
234
241
|
}
|
|
235
242
|
return { ok: false, missingFor: changedPackages };
|
|
236
243
|
}
|
|
244
|
+
function parseIgnoredPackagesFromChangesetConfig(jsonContent) {
|
|
245
|
+
try {
|
|
246
|
+
const parsed = JSON.parse(jsonContent);
|
|
247
|
+
if (!Array.isArray(parsed.ignore)) {
|
|
248
|
+
return [];
|
|
249
|
+
}
|
|
250
|
+
return parsed.ignore.filter((entry) => typeof entry === "string");
|
|
251
|
+
} catch {
|
|
252
|
+
return [];
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
function parseChangesetFrontmatterPackageNames(markdownContent) {
|
|
256
|
+
const frontmatterMatch = /^---\r?\n([\s\S]*?)\r?\n---/.exec(markdownContent);
|
|
257
|
+
if (!frontmatterMatch?.[1]) {
|
|
258
|
+
return [];
|
|
259
|
+
}
|
|
260
|
+
const packages = new Set;
|
|
261
|
+
for (const line of frontmatterMatch[1].split(/\r?\n/)) {
|
|
262
|
+
const trimmed = line.trim();
|
|
263
|
+
const match = /^(["']?)(@[^"':\s]+\/[^"':\s]+)\1\s*:/.exec(trimmed);
|
|
264
|
+
if (match?.[2]) {
|
|
265
|
+
packages.add(match[2]);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return [...packages].sort();
|
|
269
|
+
}
|
|
270
|
+
function findIgnoredPackageReferences(input) {
|
|
271
|
+
if (input.ignoredPackages.length === 0 || input.changesetFiles.length === 0) {
|
|
272
|
+
return [];
|
|
273
|
+
}
|
|
274
|
+
const ignored = new Set(input.ignoredPackages);
|
|
275
|
+
const results = [];
|
|
276
|
+
for (const file of input.changesetFiles) {
|
|
277
|
+
const content = input.readChangesetFile(file);
|
|
278
|
+
const referencedPackages = parseChangesetFrontmatterPackageNames(content);
|
|
279
|
+
const invalidReferences = referencedPackages.filter((pkg) => ignored.has(pkg));
|
|
280
|
+
if (invalidReferences.length > 0) {
|
|
281
|
+
results.push({ file, packages: invalidReferences.sort() });
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return results.sort((a, b) => a.file.localeCompare(b.file));
|
|
285
|
+
}
|
|
286
|
+
function loadIgnoredPackages(cwd) {
|
|
287
|
+
const configPath = join(cwd, ".changeset", "config.json");
|
|
288
|
+
if (!existsSync(configPath)) {
|
|
289
|
+
return [];
|
|
290
|
+
}
|
|
291
|
+
try {
|
|
292
|
+
return parseIgnoredPackagesFromChangesetConfig(readFileSync(configPath, "utf-8"));
|
|
293
|
+
} catch {
|
|
294
|
+
return [];
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
function getIgnoredReferencesForChangedChangesets(cwd, changesetFiles) {
|
|
298
|
+
const ignoredPackages = loadIgnoredPackages(cwd);
|
|
299
|
+
return findIgnoredPackageReferences({
|
|
300
|
+
changesetFiles,
|
|
301
|
+
ignoredPackages,
|
|
302
|
+
readChangesetFile: (filename) => {
|
|
303
|
+
try {
|
|
304
|
+
return readFileSync(join(cwd, ".changeset", filename), "utf-8");
|
|
305
|
+
} catch {
|
|
306
|
+
return "";
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
}
|
|
237
311
|
var COLORS2 = {
|
|
238
312
|
reset: "\x1B[0m",
|
|
239
313
|
red: "\x1B[31m",
|
|
@@ -273,25 +347,46 @@ async function runCheckChangeset(options = {}) {
|
|
|
273
347
|
}
|
|
274
348
|
const changesetFiles = getChangedChangesetFiles(changedFiles);
|
|
275
349
|
const check = checkChangesetRequired(changedPackages, changesetFiles);
|
|
276
|
-
if (check.ok) {
|
|
277
|
-
process.
|
|
350
|
+
if (!check.ok) {
|
|
351
|
+
process.stderr.write(`${COLORS2.red}Missing changeset!${COLORS2.reset}
|
|
352
|
+
|
|
278
353
|
`);
|
|
279
|
-
process.
|
|
354
|
+
process.stderr.write(`The following packages have source changes but no changeset:
|
|
355
|
+
|
|
356
|
+
`);
|
|
357
|
+
for (const pkg of check.missingFor) {
|
|
358
|
+
process.stderr.write(` ${COLORS2.yellow}@outfitter/${pkg}${COLORS2.reset}
|
|
359
|
+
`);
|
|
360
|
+
}
|
|
361
|
+
process.stderr.write(`
|
|
362
|
+
Run ${COLORS2.blue}bun run changeset${COLORS2.reset} to add a changeset, ` + `or add the ${COLORS2.blue}no-changeset${COLORS2.reset} label to skip.
|
|
363
|
+
`);
|
|
364
|
+
process.exit(1);
|
|
280
365
|
}
|
|
281
|
-
|
|
366
|
+
const ignoredReferences = getIgnoredReferencesForChangedChangesets(cwd, changesetFiles);
|
|
367
|
+
if (ignoredReferences.length > 0) {
|
|
368
|
+
process.stderr.write(`${COLORS2.red}Invalid changeset package reference(s).${COLORS2.reset}
|
|
282
369
|
|
|
283
370
|
`);
|
|
284
|
-
|
|
371
|
+
process.stderr.write(`Changesets must not reference packages listed in .changeset/config.json ignore:
|
|
285
372
|
|
|
286
373
|
`);
|
|
287
|
-
|
|
288
|
-
|
|
374
|
+
for (const reference of ignoredReferences) {
|
|
375
|
+
process.stderr.write(` ${COLORS2.yellow}${reference.file}${COLORS2.reset}
|
|
289
376
|
`);
|
|
377
|
+
for (const pkg of reference.packages) {
|
|
378
|
+
process.stderr.write(` - ${pkg}
|
|
379
|
+
`);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
process.stderr.write(`
|
|
383
|
+
Update the affected changeset files to remove ignored packages before merging.
|
|
384
|
+
`);
|
|
385
|
+
process.exit(1);
|
|
290
386
|
}
|
|
291
|
-
process.
|
|
292
|
-
Run ${COLORS2.blue}bun run changeset${COLORS2.reset} to add a changeset, ` + `or add the ${COLORS2.blue}no-changeset${COLORS2.reset} label to skip.
|
|
387
|
+
process.stdout.write(`${COLORS2.green}Changeset found for ${changedPackages.length} changed package(s).${COLORS2.reset}
|
|
293
388
|
`);
|
|
294
|
-
process.exit(
|
|
389
|
+
process.exit(0);
|
|
295
390
|
}
|
|
296
391
|
|
|
297
392
|
// src/cli/check-clean-tree.ts
|
|
@@ -501,6 +596,9 @@ var COLORS4 = {
|
|
|
501
596
|
blue: "\x1B[34m",
|
|
502
597
|
dim: "\x1B[2m"
|
|
503
598
|
};
|
|
599
|
+
function resolveJsonMode(options = {}) {
|
|
600
|
+
return options.json ?? process.env["OUTFITTER_JSON"] === "1";
|
|
601
|
+
}
|
|
504
602
|
async function runCheckExports(options = {}) {
|
|
505
603
|
const cwd = process.cwd();
|
|
506
604
|
const configPath = resolve3(cwd, "bunup.config.ts");
|
|
@@ -543,7 +641,7 @@ async function runCheckExports(options = {}) {
|
|
|
543
641
|
ok: results.every((r) => r.status === "ok"),
|
|
544
642
|
packages: results
|
|
545
643
|
};
|
|
546
|
-
if (options
|
|
644
|
+
if (resolveJsonMode(options)) {
|
|
547
645
|
process.stdout.write(`${JSON.stringify(checkResult, null, 2)}
|
|
548
646
|
`);
|
|
549
647
|
} else {
|
|
@@ -669,8 +767,9 @@ async function runInit(cwd = process.cwd()) {
|
|
|
669
767
|
}
|
|
670
768
|
|
|
671
769
|
// src/cli/pre-push.ts
|
|
672
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
673
|
-
import { join } from "node:path";
|
|
770
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
771
|
+
import { join as join2, resolve as resolve4 } from "node:path";
|
|
772
|
+
import ts from "typescript";
|
|
674
773
|
var COLORS5 = {
|
|
675
774
|
reset: "\x1B[0m",
|
|
676
775
|
red: "\x1B[31m",
|
|
@@ -707,6 +806,9 @@ function isRedPhaseBranch(branch) {
|
|
|
707
806
|
function isScaffoldBranch(branch) {
|
|
708
807
|
return branch.endsWith("-scaffold") || branch.endsWith("/scaffold") || branch.endsWith("_scaffold");
|
|
709
808
|
}
|
|
809
|
+
function isReleaseBranch(branch) {
|
|
810
|
+
return branch.startsWith("changeset-release/");
|
|
811
|
+
}
|
|
710
812
|
var TEST_PATH_PATTERNS = [
|
|
711
813
|
/(^|\/)__tests__\//,
|
|
712
814
|
/(^|\/)__snapshots__\//,
|
|
@@ -726,6 +828,32 @@ function areFilesTestOnly(paths) {
|
|
|
726
828
|
function canBypassRedPhaseByChangedFiles(changedFiles) {
|
|
727
829
|
return changedFiles.deterministic && areFilesTestOnly(changedFiles.files);
|
|
728
830
|
}
|
|
831
|
+
function hasPackageSourceChanges(changedFiles) {
|
|
832
|
+
const packageSrcPattern = /^packages\/[^/]+\/src\//;
|
|
833
|
+
return changedFiles.files.some((f) => packageSrcPattern.test(f));
|
|
834
|
+
}
|
|
835
|
+
async function printTsdocSummary() {
|
|
836
|
+
const glob = new Bun.Glob("packages/*/src/index.ts");
|
|
837
|
+
const cwd = process.cwd();
|
|
838
|
+
const allDeclarations = [];
|
|
839
|
+
for (const entry of glob.scanSync({ cwd })) {
|
|
840
|
+
const filePath = resolve4(cwd, entry);
|
|
841
|
+
const content = await Bun.file(filePath).text();
|
|
842
|
+
const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
|
|
843
|
+
allDeclarations.push(...analyzeSourceFile(sourceFile));
|
|
844
|
+
}
|
|
845
|
+
if (allDeclarations.length === 0)
|
|
846
|
+
return;
|
|
847
|
+
const coverage = calculateCoverage(allDeclarations);
|
|
848
|
+
const parts = [];
|
|
849
|
+
if (coverage.documented > 0)
|
|
850
|
+
parts.push(`${coverage.documented} documented`);
|
|
851
|
+
if (coverage.partial > 0)
|
|
852
|
+
parts.push(`${coverage.partial} partial`);
|
|
853
|
+
if (coverage.undocumented > 0)
|
|
854
|
+
parts.push(`${coverage.undocumented} undocumented`);
|
|
855
|
+
log(`${COLORS5.blue}TSDoc${COLORS5.reset}: ${coverage.percentage}% coverage (${parts.join(", ")} of ${coverage.total} total)`);
|
|
856
|
+
}
|
|
729
857
|
function resolveBaseRef() {
|
|
730
858
|
const candidates = [
|
|
731
859
|
"origin/main",
|
|
@@ -863,12 +991,12 @@ function createVerificationPlan(scripts) {
|
|
|
863
991
|
};
|
|
864
992
|
}
|
|
865
993
|
function readPackageScripts(cwd = process.cwd()) {
|
|
866
|
-
const packageJsonPath =
|
|
867
|
-
if (!
|
|
994
|
+
const packageJsonPath = join2(cwd, "package.json");
|
|
995
|
+
if (!existsSync2(packageJsonPath)) {
|
|
868
996
|
return {};
|
|
869
997
|
}
|
|
870
998
|
try {
|
|
871
|
-
const parsed = JSON.parse(
|
|
999
|
+
const parsed = JSON.parse(readFileSync2(packageJsonPath, "utf-8"));
|
|
872
1000
|
const scripts = parsed.scripts ?? {};
|
|
873
1001
|
const normalized = {};
|
|
874
1002
|
for (const [name, value] of Object.entries(scripts)) {
|
|
@@ -893,6 +1021,11 @@ async function runPrePush(options = {}) {
|
|
|
893
1021
|
log(`${COLORS5.blue}Pre-push verify${COLORS5.reset} (TDD-aware)`);
|
|
894
1022
|
log("");
|
|
895
1023
|
const branch = getCurrentBranch();
|
|
1024
|
+
if (isReleaseBranch(branch)) {
|
|
1025
|
+
log(`${COLORS5.yellow}Release branch detected${COLORS5.reset}: ${COLORS5.blue}${branch}${COLORS5.reset}`);
|
|
1026
|
+
log(`${COLORS5.yellow}Skipping strict verification${COLORS5.reset} for automated changeset release push`);
|
|
1027
|
+
process.exit(0);
|
|
1028
|
+
}
|
|
896
1029
|
if (isRedPhaseBranch(branch)) {
|
|
897
1030
|
if (maybeSkipForRedPhase("branch", branch)) {
|
|
898
1031
|
process.exit(0);
|
|
@@ -938,14 +1071,20 @@ async function runPrePush(options = {}) {
|
|
|
938
1071
|
log(" - feature_tests");
|
|
939
1072
|
process.exit(1);
|
|
940
1073
|
}
|
|
1074
|
+
const changedFiles = getChangedFilesForPush();
|
|
1075
|
+
if (hasPackageSourceChanges(changedFiles)) {
|
|
1076
|
+
try {
|
|
1077
|
+
await printTsdocSummary();
|
|
1078
|
+
} catch {}
|
|
1079
|
+
}
|
|
941
1080
|
log("");
|
|
942
1081
|
log(`${COLORS5.green}Strict verification passed${COLORS5.reset}`);
|
|
943
1082
|
process.exit(0);
|
|
944
1083
|
}
|
|
945
1084
|
|
|
946
1085
|
// src/cli/upgrade-bun.ts
|
|
947
|
-
import { existsSync as
|
|
948
|
-
import { join as
|
|
1086
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync } from "node:fs";
|
|
1087
|
+
import { join as join3 } from "node:path";
|
|
949
1088
|
var COLORS6 = {
|
|
950
1089
|
reset: "\x1B[0m",
|
|
951
1090
|
red: "\x1B[31m",
|
|
@@ -983,13 +1122,13 @@ function findPackageJsonFiles(dir) {
|
|
|
983
1122
|
const glob = new Bun.Glob("**/package.json");
|
|
984
1123
|
for (const path of glob.scanSync({ cwd: dir })) {
|
|
985
1124
|
if (!path.includes("node_modules")) {
|
|
986
|
-
results.push(
|
|
1125
|
+
results.push(join3(dir, path));
|
|
987
1126
|
}
|
|
988
1127
|
}
|
|
989
1128
|
return results;
|
|
990
1129
|
}
|
|
991
1130
|
function updateEnginesBun(filePath, version) {
|
|
992
|
-
const content =
|
|
1131
|
+
const content = readFileSync3(filePath, "utf-8");
|
|
993
1132
|
const pattern = /"bun":\s*">=[\d.]+"/;
|
|
994
1133
|
if (!pattern.test(content)) {
|
|
995
1134
|
return false;
|
|
@@ -1002,7 +1141,7 @@ function updateEnginesBun(filePath, version) {
|
|
|
1002
1141
|
return false;
|
|
1003
1142
|
}
|
|
1004
1143
|
function updateTypesBun(filePath, version) {
|
|
1005
|
-
const content =
|
|
1144
|
+
const content = readFileSync3(filePath, "utf-8");
|
|
1006
1145
|
const pattern = /"@types\/bun":\s*"\^[\d.]+"/;
|
|
1007
1146
|
if (!pattern.test(content)) {
|
|
1008
1147
|
return false;
|
|
@@ -1016,14 +1155,14 @@ function updateTypesBun(filePath, version) {
|
|
|
1016
1155
|
}
|
|
1017
1156
|
async function runUpgradeBun(targetVersion, options = {}) {
|
|
1018
1157
|
const cwd = process.cwd();
|
|
1019
|
-
const bunVersionFile =
|
|
1158
|
+
const bunVersionFile = join3(cwd, ".bun-version");
|
|
1020
1159
|
let version = targetVersion;
|
|
1021
1160
|
if (!version) {
|
|
1022
1161
|
info("Fetching latest Bun version...");
|
|
1023
1162
|
version = await fetchLatestVersion();
|
|
1024
1163
|
log2(`Latest version: ${version}`);
|
|
1025
1164
|
}
|
|
1026
|
-
const currentVersion =
|
|
1165
|
+
const currentVersion = existsSync3(bunVersionFile) ? readFileSync3(bunVersionFile, "utf-8").trim() : "unknown";
|
|
1027
1166
|
log2(`Current version: ${currentVersion}`);
|
|
1028
1167
|
if (currentVersion === version) {
|
|
1029
1168
|
success(`Already on version ${version}`);
|
|
@@ -1088,40 +1227,58 @@ async function runUpgradeBun(targetVersion, options = {}) {
|
|
|
1088
1227
|
}
|
|
1089
1228
|
|
|
1090
1229
|
// src/cli/index.ts
|
|
1091
|
-
var
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1230
|
+
var cli = createCLI({
|
|
1231
|
+
name: "tooling",
|
|
1232
|
+
version: VERSION,
|
|
1233
|
+
description: "Dev tooling configuration management for Outfitter projects",
|
|
1234
|
+
onError: (error) => {
|
|
1235
|
+
const err = error instanceof Error ? error : new Error("An unexpected error occurred");
|
|
1236
|
+
exitWithError(err);
|
|
1237
|
+
}
|
|
1095
1238
|
});
|
|
1096
|
-
|
|
1239
|
+
function register(command) {
|
|
1240
|
+
cli.register(command);
|
|
1241
|
+
}
|
|
1242
|
+
register(new Command("init").description("Initialize tooling config in the current project").action(async () => {
|
|
1243
|
+
await runInit();
|
|
1244
|
+
}));
|
|
1245
|
+
register(new Command("check").description("Run linting checks (wraps ultracite)").argument("[paths...]", "Paths to check").action(async (paths) => {
|
|
1097
1246
|
await runCheck(paths);
|
|
1098
|
-
});
|
|
1099
|
-
|
|
1247
|
+
}));
|
|
1248
|
+
register(new Command("fix").description("Fix linting issues (wraps ultracite)").argument("[paths...]", "Paths to fix").action(async (paths) => {
|
|
1100
1249
|
await runFix(paths);
|
|
1101
|
-
});
|
|
1102
|
-
|
|
1250
|
+
}));
|
|
1251
|
+
register(new Command("upgrade-bun").description("Upgrade Bun version across the project").argument("[version]", "Target version (defaults to latest)").option("--no-install", "Skip installing Bun and updating lockfile").action(async (version, options) => {
|
|
1103
1252
|
await runUpgradeBun(version, options);
|
|
1104
|
-
});
|
|
1105
|
-
|
|
1253
|
+
}));
|
|
1254
|
+
register(new Command("pre-push").description("TDD-aware pre-push strict verification hook").option("-f, --force", "Skip strict verification entirely").action(async (options) => {
|
|
1106
1255
|
await runPrePush(options);
|
|
1107
|
-
});
|
|
1108
|
-
|
|
1256
|
+
}));
|
|
1257
|
+
register(new Command("check-bunup-registry").description("Validate packages with bunup --filter are registered in bunup.config.ts").action(async () => {
|
|
1109
1258
|
await runCheckBunupRegistry();
|
|
1110
|
-
});
|
|
1111
|
-
|
|
1259
|
+
}));
|
|
1260
|
+
register(new Command("check-changeset").description("Validate PRs touching package source include a changeset").option("-s, --skip", "Skip changeset check").action(async (options) => {
|
|
1112
1261
|
await runCheckChangeset(options);
|
|
1113
|
-
});
|
|
1114
|
-
|
|
1262
|
+
}));
|
|
1263
|
+
register(new Command("check-exports").description("Validate package.json exports match source entry points").option("--json", "Output results as JSON").action(async (options) => {
|
|
1115
1264
|
await runCheckExports(options);
|
|
1116
|
-
});
|
|
1117
|
-
|
|
1265
|
+
}));
|
|
1266
|
+
register(new Command("check-tsdoc").description("Check TSDoc coverage on exported declarations").argument("[paths...]", "Specific package paths to check").option("--strict", "Exit non-zero when coverage is below threshold").option("--json", "Output results as JSON").option("--min-coverage <percentage>", "Minimum coverage percentage", "0").action(async (paths, options) => {
|
|
1267
|
+
await runCheckTsdoc({
|
|
1268
|
+
paths: paths.length > 0 ? paths : undefined,
|
|
1269
|
+
strict: options.strict,
|
|
1270
|
+
json: options.json,
|
|
1271
|
+
minCoverage: options.minCoverage ? Number.parseInt(options.minCoverage, 10) : undefined
|
|
1272
|
+
});
|
|
1273
|
+
}));
|
|
1274
|
+
register(new Command("check-clean-tree").description("Assert working tree is clean (no modified or untracked files)").option("--paths <paths...>", "Limit check to specific paths").action(async (options) => {
|
|
1118
1275
|
await runCheckCleanTree(options);
|
|
1119
|
-
});
|
|
1120
|
-
|
|
1121
|
-
const { runCheckReadmeImports } = await import("../shared/chunk-
|
|
1276
|
+
}));
|
|
1277
|
+
register(new Command("check-readme-imports").description("Validate README import examples match package exports").option("--json", "Output results as JSON").action(async (options) => {
|
|
1278
|
+
const { runCheckReadmeImports } = await import("../shared/chunk-7tdgbqb0.js");
|
|
1122
1279
|
await runCheckReadmeImports(options);
|
|
1123
|
-
});
|
|
1124
|
-
|
|
1280
|
+
}));
|
|
1281
|
+
register(new Command("check-boundary-invocations").description("Validate root/app scripts do not execute packages/*/src entrypoints directly").action(async () => {
|
|
1125
1282
|
await runCheckBoundaryInvocations();
|
|
1126
|
-
});
|
|
1127
|
-
|
|
1283
|
+
}));
|
|
1284
|
+
await cli.parse();
|
package/dist/cli/pre-push.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ declare function isRedPhaseBranch(branch: string): boolean;
|
|
|
6
6
|
* Check if branch is a scaffold branch
|
|
7
7
|
*/
|
|
8
8
|
declare function isScaffoldBranch(branch: string): boolean;
|
|
9
|
+
declare function isReleaseBranch(branch: string): boolean;
|
|
9
10
|
declare function isTestOnlyPath(path: string): boolean;
|
|
10
11
|
declare function areFilesTestOnly(paths: readonly string[]): boolean;
|
|
11
12
|
interface PushChangedFiles {
|
|
@@ -14,6 +15,12 @@ interface PushChangedFiles {
|
|
|
14
15
|
readonly source: "upstream" | "baseRef" | "undetermined";
|
|
15
16
|
}
|
|
16
17
|
declare function canBypassRedPhaseByChangedFiles(changedFiles: PushChangedFiles): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Check whether any changed files are package source files.
|
|
20
|
+
*
|
|
21
|
+
* Matches files under "packages/PKGNAME/src/" (any depth).
|
|
22
|
+
*/
|
|
23
|
+
declare function hasPackageSourceChanges(changedFiles: PushChangedFiles): boolean;
|
|
17
24
|
type ScriptMap = Readonly<Record<string, string | undefined>>;
|
|
18
25
|
type VerificationPlan = {
|
|
19
26
|
readonly ok: true;
|
|
@@ -38,4 +45,4 @@ interface PrePushOptions {
|
|
|
38
45
|
* Main pre-push command
|
|
39
46
|
*/
|
|
40
47
|
declare function runPrePush(options?: PrePushOptions): Promise<void>;
|
|
41
|
-
export { runPrePush, isTestOnlyPath, isScaffoldBranch, isRedPhaseBranch, createVerificationPlan, canBypassRedPhaseByChangedFiles, areFilesTestOnly, VerificationPlan, PushChangedFiles, PrePushOptions };
|
|
48
|
+
export { runPrePush, isTestOnlyPath, isScaffoldBranch, isReleaseBranch, isRedPhaseBranch, hasPackageSourceChanges, createVerificationPlan, canBypassRedPhaseByChangedFiles, areFilesTestOnly, VerificationPlan, PushChangedFiles, PrePushOptions };
|
package/dist/cli/pre-push.js
CHANGED
|
@@ -3,17 +3,22 @@ import {
|
|
|
3
3
|
areFilesTestOnly,
|
|
4
4
|
canBypassRedPhaseByChangedFiles,
|
|
5
5
|
createVerificationPlan,
|
|
6
|
+
hasPackageSourceChanges,
|
|
6
7
|
isRedPhaseBranch,
|
|
8
|
+
isReleaseBranch,
|
|
7
9
|
isScaffoldBranch,
|
|
8
10
|
isTestOnlyPath,
|
|
9
11
|
runPrePush
|
|
10
|
-
} from "../shared/@outfitter/tooling-
|
|
12
|
+
} from "../shared/@outfitter/tooling-2n2dpsaa.js";
|
|
13
|
+
import"../shared/@outfitter/tooling-qk5xgmxr.js";
|
|
11
14
|
import"../shared/@outfitter/tooling-dvwh9qve.js";
|
|
12
15
|
export {
|
|
13
16
|
runPrePush,
|
|
14
17
|
isTestOnlyPath,
|
|
15
18
|
isScaffoldBranch,
|
|
19
|
+
isReleaseBranch,
|
|
16
20
|
isRedPhaseBranch,
|
|
21
|
+
hasPackageSourceChanges,
|
|
17
22
|
createVerificationPlan,
|
|
18
23
|
canBypassRedPhaseByChangedFiles,
|
|
19
24
|
areFilesTestOnly
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,79 @@
|
|
|
1
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 PackageCoverage}. */
|
|
42
|
+
declare const packageCoverageSchema: ZodType<PackageCoverage>;
|
|
43
|
+
/** Zod schema for {@link TsDocCheckResult}. */
|
|
44
|
+
declare const tsDocCheckResultSchema: ZodType<TsDocCheckResult>;
|
|
45
|
+
/** Options for the check-tsdoc command. */
|
|
46
|
+
interface CheckTsDocOptions {
|
|
47
|
+
readonly strict?: boolean | undefined;
|
|
48
|
+
readonly json?: boolean | undefined;
|
|
49
|
+
readonly minCoverage?: number | undefined;
|
|
50
|
+
readonly cwd?: string | undefined;
|
|
51
|
+
readonly paths?: readonly string[] | undefined;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Analyze TSDoc coverage across workspace packages.
|
|
55
|
+
*
|
|
56
|
+
* Pure function that discovers packages, analyzes TSDoc coverage on exported
|
|
57
|
+
* declarations, and returns the aggregated result. Does not print output or
|
|
58
|
+
* call `process.exit()`.
|
|
59
|
+
*
|
|
60
|
+
* @param options - Analysis options (paths, strict mode, coverage threshold)
|
|
61
|
+
* @returns Aggregated coverage result across all packages, or `null` if no packages found
|
|
62
|
+
*/
|
|
63
|
+
declare function analyzeCheckTsdoc(options?: CheckTsDocOptions): TsDocCheckResult | null;
|
|
64
|
+
/**
|
|
65
|
+
* Print a TSDoc coverage result in human-readable format.
|
|
66
|
+
*
|
|
67
|
+
* Renders a bar chart per package with summary statistics. Writes to stdout/stderr.
|
|
68
|
+
*
|
|
69
|
+
* @param result - The coverage result to print
|
|
70
|
+
* @param options - Display options (strict mode, coverage threshold for warning)
|
|
71
|
+
*/
|
|
72
|
+
declare function printCheckTsdocHuman(result: TsDocCheckResult, options?: {
|
|
73
|
+
strict?: boolean | undefined;
|
|
74
|
+
minCoverage?: number | undefined;
|
|
75
|
+
}): void;
|
|
76
|
+
import { ZodType as ZodType2 } from "zod";
|
|
2
77
|
/**
|
|
3
78
|
* File entry in a block.
|
|
4
79
|
*/
|
|
@@ -16,7 +91,7 @@ interface FileEntry {
|
|
|
16
91
|
* Schema for a file entry in a block.
|
|
17
92
|
* Represents a file that will be copied to the user's project.
|
|
18
93
|
*/
|
|
19
|
-
declare const FileEntrySchema:
|
|
94
|
+
declare const FileEntrySchema: ZodType2<FileEntry>;
|
|
20
95
|
/**
|
|
21
96
|
* Block in the registry.
|
|
22
97
|
*/
|
|
@@ -38,7 +113,7 @@ interface Block {
|
|
|
38
113
|
* Schema for a block in the registry.
|
|
39
114
|
* A block is a collection of related files that can be added together.
|
|
40
115
|
*/
|
|
41
|
-
declare const BlockSchema:
|
|
116
|
+
declare const BlockSchema: ZodType2<Block>;
|
|
42
117
|
/**
|
|
43
118
|
* Complete registry structure.
|
|
44
119
|
*/
|
|
@@ -52,7 +127,7 @@ interface Registry {
|
|
|
52
127
|
* Schema for the complete registry.
|
|
53
128
|
* Contains all available blocks with their files and metadata.
|
|
54
129
|
*/
|
|
55
|
-
declare const RegistrySchema:
|
|
130
|
+
declare const RegistrySchema: ZodType2<Registry>;
|
|
56
131
|
/**
|
|
57
132
|
* Block definition used in the build script.
|
|
58
133
|
* Specifies how to collect source files into a block.
|
|
@@ -108,4 +183,4 @@ interface AddBlockOptions {
|
|
|
108
183
|
}
|
|
109
184
|
/** Package version, read from package.json at load time. */
|
|
110
185
|
declare const VERSION: string;
|
|
111
|
-
export { VERSION, RegistrySchema, RegistryBuildConfig, Registry, FileEntrySchema, FileEntry, BlockSchema, BlockDefinition, Block, AddBlockResult, AddBlockOptions };
|
|
186
|
+
export { tsDocCheckResultSchema, printCheckTsdocHuman, packageCoverageSchema, declarationCoverageSchema, coverageLevelSchema, analyzeCheckTsdoc, VERSION, TsDocCheckResult, RegistrySchema, RegistryBuildConfig, Registry, PackageCoverage, FileEntrySchema, FileEntry, DeclarationCoverage, CoverageLevel, CheckTsDocOptions, BlockSchema, BlockDefinition, Block, AddBlockResult, AddBlockOptions };
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
|
-
VERSION
|
|
3
|
-
|
|
2
|
+
VERSION,
|
|
3
|
+
analyzeCheckTsdoc,
|
|
4
|
+
coverageLevelSchema,
|
|
5
|
+
declarationCoverageSchema,
|
|
6
|
+
packageCoverageSchema,
|
|
7
|
+
printCheckTsdocHuman,
|
|
8
|
+
tsDocCheckResultSchema
|
|
9
|
+
} from "./shared/chunk-cmde0fwx.js";
|
|
4
10
|
import"./shared/chunk-3s189drz.js";
|
|
5
|
-
|
|
6
11
|
// src/registry/schema.ts
|
|
7
12
|
import { z } from "zod";
|
|
8
13
|
var FileEntrySchema = z.object({
|
|
@@ -15,15 +20,21 @@ var BlockSchema = z.object({
|
|
|
15
20
|
name: z.string().min(1),
|
|
16
21
|
description: z.string().min(1),
|
|
17
22
|
files: z.array(FileEntrySchema).optional(),
|
|
18
|
-
dependencies: z.record(z.string()).optional(),
|
|
19
|
-
devDependencies: z.record(z.string()).optional(),
|
|
23
|
+
dependencies: z.record(z.string(), z.string()).optional(),
|
|
24
|
+
devDependencies: z.record(z.string(), z.string()).optional(),
|
|
20
25
|
extends: z.array(z.string()).optional()
|
|
21
26
|
});
|
|
22
27
|
var RegistrySchema = z.object({
|
|
23
28
|
version: z.string(),
|
|
24
|
-
blocks: z.record(BlockSchema)
|
|
29
|
+
blocks: z.record(z.string(), BlockSchema)
|
|
25
30
|
});
|
|
26
31
|
export {
|
|
32
|
+
tsDocCheckResultSchema,
|
|
33
|
+
printCheckTsdocHuman,
|
|
34
|
+
packageCoverageSchema,
|
|
35
|
+
declarationCoverageSchema,
|
|
36
|
+
coverageLevelSchema,
|
|
37
|
+
analyzeCheckTsdoc,
|
|
27
38
|
VERSION,
|
|
28
39
|
RegistrySchema,
|
|
29
40
|
FileEntrySchema,
|
package/dist/registry/build.d.ts
CHANGED
package/dist/registry/build.js
CHANGED
|
@@ -93,16 +93,16 @@ var REGISTRY_CONFIG = {
|
|
|
93
93
|
description: "Biome linter/formatter configuration via Ultracite",
|
|
94
94
|
files: ["packages/tooling/biome.json"],
|
|
95
95
|
remap: { "packages/tooling/biome.json": "biome.json" },
|
|
96
|
-
devDependencies: { ultracite: "^7.
|
|
96
|
+
devDependencies: { ultracite: "^7.2.3" }
|
|
97
97
|
},
|
|
98
98
|
lefthook: {
|
|
99
99
|
description: "Git hooks via Lefthook for pre-commit and pre-push",
|
|
100
100
|
files: ["packages/tooling/lefthook.yml"],
|
|
101
101
|
remap: { "packages/tooling/lefthook.yml": ".lefthook.yml" },
|
|
102
102
|
devDependencies: {
|
|
103
|
-
"@outfitter/tooling": "^0.2.
|
|
104
|
-
lefthook: "^2.
|
|
105
|
-
ultracite: "^7.
|
|
103
|
+
"@outfitter/tooling": "^0.2.4",
|
|
104
|
+
lefthook: "^2.1.1",
|
|
105
|
+
ultracite: "^7.2.3"
|
|
106
106
|
}
|
|
107
107
|
},
|
|
108
108
|
markdownlint: {
|
|
@@ -114,7 +114,10 @@ var REGISTRY_CONFIG = {
|
|
|
114
114
|
},
|
|
115
115
|
bootstrap: {
|
|
116
116
|
description: "Project bootstrap script for installing tools and dependencies",
|
|
117
|
-
files: ["
|
|
117
|
+
files: ["packages/tooling/templates/bootstrap.sh"],
|
|
118
|
+
remap: {
|
|
119
|
+
"packages/tooling/templates/bootstrap.sh": "scripts/bootstrap.sh"
|
|
120
|
+
}
|
|
118
121
|
},
|
|
119
122
|
scaffolding: {
|
|
120
123
|
description: "Full starter kit: Claude settings, Biome, Lefthook, markdownlint, and bootstrap script",
|
package/dist/registry/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import "../shared/@outfitter/tooling-xqwn46sx";
|
|
2
|
-
import { AddBlockOptions, AddBlockResult, Block, BlockDefinition, BlockSchema, FileEntry, FileEntrySchema, Registry, RegistryBuildConfig, RegistrySchema } from "../shared/@outfitter/tooling-sjm8nebx";
|
|
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
3
|
export { RegistrySchema, RegistryBuildConfig, Registry, FileEntrySchema, FileEntry, BlockSchema, BlockDefinition, Block, AddBlockResult, AddBlockOptions };
|
package/dist/registry/index.js
CHANGED