@outfitter/tooling 0.2.4 → 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 +1 -1
- package/dist/cli/check-readme-imports.d.ts +1 -1
- package/dist/cli/check-tsdoc.d.ts +2 -0
- package/dist/cli/check-tsdoc.js +36 -0
- package/dist/cli/index.js +166 -24
- 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-njw4z34x.d.ts +140 -0
- package/dist/shared/@outfitter/tooling-qk5xgmxr.js +405 -0
- package/dist/shared/@outfitter/{tooling-g83d0kjv.js → tooling-wv09k6hr.js} +3 -3
- package/dist/shared/chunk-cmde0fwx.js +421 -0
- package/dist/version.d.ts +1 -1
- package/package.json +12 -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/README.md
CHANGED
|
@@ -98,6 +98,75 @@ bunx @outfitter/tooling check-boundary-invocations
|
|
|
98
98
|
When this fails, replace direct source execution with canonical command surfaces
|
|
99
99
|
(`outfitter repo ...` in monorepo scripts, or package bins for standalone use).
|
|
100
100
|
|
|
101
|
+
### `tooling check-bunup-registry`
|
|
102
|
+
|
|
103
|
+
Validate that packages using `bunup --filter` are registered in `bunup.config.ts`.
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
bunx @outfitter/tooling check-bunup-registry
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### `tooling check-changeset`
|
|
110
|
+
|
|
111
|
+
Validate that PRs touching package source include a changeset.
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
bunx @outfitter/tooling check-changeset
|
|
115
|
+
|
|
116
|
+
# Skip the check (e.g. for non-package changes)
|
|
117
|
+
bunx @outfitter/tooling check-changeset --skip
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### `tooling check-exports`
|
|
121
|
+
|
|
122
|
+
Validate that `package.json` exports match source entry points.
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
bunx @outfitter/tooling check-exports
|
|
126
|
+
|
|
127
|
+
# Machine-readable output
|
|
128
|
+
bunx @outfitter/tooling check-exports --json
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### `tooling check-tsdoc`
|
|
132
|
+
|
|
133
|
+
Check TSDoc coverage on exported declarations.
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
bunx @outfitter/tooling check-tsdoc
|
|
137
|
+
|
|
138
|
+
# Check specific packages
|
|
139
|
+
bunx @outfitter/tooling check-tsdoc packages/cli packages/contracts
|
|
140
|
+
|
|
141
|
+
# Strict mode with coverage threshold
|
|
142
|
+
bunx @outfitter/tooling check-tsdoc --strict --min-coverage 80
|
|
143
|
+
|
|
144
|
+
# JSON output
|
|
145
|
+
bunx @outfitter/tooling check-tsdoc --json
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### `tooling check-clean-tree`
|
|
149
|
+
|
|
150
|
+
Assert the working tree is clean (no modified or untracked files).
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
bunx @outfitter/tooling check-clean-tree
|
|
154
|
+
|
|
155
|
+
# Check specific paths only
|
|
156
|
+
bunx @outfitter/tooling check-clean-tree --paths packages/cli packages/contracts
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### `tooling check-readme-imports`
|
|
160
|
+
|
|
161
|
+
Validate that README import examples match package exports.
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
bunx @outfitter/tooling check-readme-imports
|
|
165
|
+
|
|
166
|
+
# Machine-readable output
|
|
167
|
+
bunx @outfitter/tooling check-readme-imports --json
|
|
168
|
+
```
|
|
169
|
+
|
|
101
170
|
## Configuration Presets
|
|
102
171
|
|
|
103
172
|
### Biome
|
|
@@ -188,7 +257,6 @@ bun run apps/outfitter/src/cli.ts repo check boundary-invocations --cwd .
|
|
|
188
257
|
|
|
189
258
|
- [@outfitter/contracts](../contracts/README.md) — Result types and error patterns
|
|
190
259
|
- [@outfitter/cli](../cli/README.md) — CLI framework
|
|
191
|
-
- [@outfitter/kit](../kit/README.md) — Version coordination
|
|
192
260
|
|
|
193
261
|
## License
|
|
194
262
|
|
package/biome.json
CHANGED
|
@@ -1,17 +1,12 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Check-changeset command — validates PRs touching package source include a changeset.
|
|
3
|
-
*
|
|
4
|
-
* Pure core functions for detecting changed packages and verifying changeset
|
|
5
|
-
* presence. The CLI runner in {@link runCheckChangeset} handles git discovery
|
|
6
|
-
* and output.
|
|
7
|
-
*
|
|
8
|
-
* @packageDocumentation
|
|
9
|
-
*/
|
|
10
1
|
/** Result of checking whether changesets are required */
|
|
11
2
|
interface ChangesetCheckResult {
|
|
12
3
|
readonly ok: boolean;
|
|
13
4
|
readonly missingFor: string[];
|
|
14
5
|
}
|
|
6
|
+
interface ChangesetIgnoredReference {
|
|
7
|
+
readonly file: string;
|
|
8
|
+
readonly packages: string[];
|
|
9
|
+
}
|
|
15
10
|
/**
|
|
16
11
|
* Extract unique package names from changed file paths.
|
|
17
12
|
*
|
|
@@ -44,6 +39,13 @@ declare function getChangedChangesetFiles(files: string[]): string[];
|
|
|
44
39
|
* @param changesetFiles - Changeset filenames found in `.changeset/`
|
|
45
40
|
*/
|
|
46
41
|
declare function checkChangesetRequired(changedPackages: string[], changesetFiles: string[]): ChangesetCheckResult;
|
|
42
|
+
declare function parseIgnoredPackagesFromChangesetConfig(jsonContent: string): string[];
|
|
43
|
+
declare function parseChangesetFrontmatterPackageNames(markdownContent: string): string[];
|
|
44
|
+
declare function findIgnoredPackageReferences(input: {
|
|
45
|
+
readonly changesetFiles: readonly string[];
|
|
46
|
+
readonly ignoredPackages: readonly string[];
|
|
47
|
+
readonly readChangesetFile: (filename: string) => string;
|
|
48
|
+
}): ChangesetIgnoredReference[];
|
|
47
49
|
interface CheckChangesetOptions {
|
|
48
50
|
readonly skip?: boolean;
|
|
49
51
|
}
|
|
@@ -61,4 +63,4 @@ interface CheckChangesetOptions {
|
|
|
61
63
|
* - Git diff fails (local dev without origin)
|
|
62
64
|
*/
|
|
63
65
|
declare function runCheckChangeset(options?: CheckChangesetOptions): Promise<void>;
|
|
64
|
-
export { runCheckChangeset, getChangedPackagePaths, getChangedChangesetFiles, checkChangesetRequired, CheckChangesetOptions, ChangesetCheckResult };
|
|
66
|
+
export { runCheckChangeset, parseIgnoredPackagesFromChangesetConfig, parseChangesetFrontmatterPackageNames, getChangedPackagePaths, getChangedChangesetFiles, findIgnoredPackageReferences, checkChangesetRequired, CheckChangesetOptions, ChangesetIgnoredReference, ChangesetCheckResult };
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
3
|
checkChangesetRequired,
|
|
4
|
+
findIgnoredPackageReferences,
|
|
4
5
|
getChangedChangesetFiles,
|
|
5
6
|
getChangedPackagePaths,
|
|
7
|
+
parseChangesetFrontmatterPackageNames,
|
|
8
|
+
parseIgnoredPackagesFromChangesetConfig,
|
|
6
9
|
runCheckChangeset
|
|
7
|
-
} from "../shared/@outfitter/tooling-
|
|
10
|
+
} from "../shared/@outfitter/tooling-cj5vsa9k.js";
|
|
8
11
|
import"../shared/@outfitter/tooling-dvwh9qve.js";
|
|
9
12
|
export {
|
|
10
13
|
runCheckChangeset,
|
|
14
|
+
parseIgnoredPackagesFromChangesetConfig,
|
|
15
|
+
parseChangesetFrontmatterPackageNames,
|
|
11
16
|
getChangedPackagePaths,
|
|
12
17
|
getChangedChangesetFiles,
|
|
18
|
+
findIgnoredPackageReferences,
|
|
13
19
|
checkChangesetRequired
|
|
14
20
|
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { CheckExportsOptions, CheckResult, CompareInput, ExportDrift, ExportMap, PackageResult, compareExports, entryToSubpath, resolveJsonMode, runCheckExports } from "../shared/@outfitter/tooling-wesswf21";
|
|
1
|
+
import { CheckExportsOptions, CheckResult, CompareInput, ExportDrift, ExportMap, PackageResult, compareExports, entryToSubpath, resolveJsonMode, runCheckExports } from "../shared/@outfitter/tooling-wesswf21.js";
|
|
2
2
|
export { runCheckExports, resolveJsonMode, entryToSubpath, compareExports, PackageResult, ExportMap, ExportDrift, CompareInput, CheckResult, CheckExportsOptions };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ExportMap } from "../shared/@outfitter/tooling-wesswf21";
|
|
1
|
+
import { ExportMap } from "../shared/@outfitter/tooling-wesswf21.js";
|
|
2
2
|
/** An import example extracted from a markdown code block */
|
|
3
3
|
interface ImportExample {
|
|
4
4
|
/** Package name, e.g. "@outfitter/cli" */
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { CheckTsDocOptions, CoverageLevel, CoverageSummary, DeclarationCoverage, PackageCoverage, TsDocCheckResult, analyzeCheckTsdoc, analyzeSourceFile, calculateCoverage, classifyDeclaration, coverageLevelSchema, coverageSummarySchema, declarationCoverageSchema, getDeclarationKind, getDeclarationName, isExportedDeclaration, packageCoverageSchema, printCheckTsdocHuman, resolveJsonMode, runCheckTsdoc, tsDocCheckResultSchema } from "../shared/@outfitter/tooling-njw4z34x.js";
|
|
2
|
+
export { tsDocCheckResultSchema, runCheckTsdoc, resolveJsonMode, printCheckTsdocHuman, packageCoverageSchema, isExportedDeclaration, getDeclarationName, getDeclarationKind, declarationCoverageSchema, coverageSummarySchema, coverageLevelSchema, classifyDeclaration, calculateCoverage, analyzeSourceFile, analyzeCheckTsdoc, TsDocCheckResult, PackageCoverage, DeclarationCoverage, CoverageSummary, CoverageLevel, CheckTsDocOptions };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
analyzeCheckTsdoc,
|
|
4
|
+
analyzeSourceFile,
|
|
5
|
+
calculateCoverage,
|
|
6
|
+
classifyDeclaration,
|
|
7
|
+
coverageLevelSchema,
|
|
8
|
+
coverageSummarySchema,
|
|
9
|
+
declarationCoverageSchema,
|
|
10
|
+
getDeclarationKind,
|
|
11
|
+
getDeclarationName,
|
|
12
|
+
isExportedDeclaration,
|
|
13
|
+
packageCoverageSchema,
|
|
14
|
+
printCheckTsdocHuman,
|
|
15
|
+
resolveJsonMode,
|
|
16
|
+
runCheckTsdoc,
|
|
17
|
+
tsDocCheckResultSchema
|
|
18
|
+
} from "../shared/@outfitter/tooling-qk5xgmxr.js";
|
|
19
|
+
import"../shared/@outfitter/tooling-dvwh9qve.js";
|
|
20
|
+
export {
|
|
21
|
+
tsDocCheckResultSchema,
|
|
22
|
+
runCheckTsdoc,
|
|
23
|
+
resolveJsonMode,
|
|
24
|
+
printCheckTsdocHuman,
|
|
25
|
+
packageCoverageSchema,
|
|
26
|
+
isExportedDeclaration,
|
|
27
|
+
getDeclarationName,
|
|
28
|
+
getDeclarationKind,
|
|
29
|
+
declarationCoverageSchema,
|
|
30
|
+
coverageSummarySchema,
|
|
31
|
+
coverageLevelSchema,
|
|
32
|
+
classifyDeclaration,
|
|
33
|
+
calculateCoverage,
|
|
34
|
+
analyzeSourceFile,
|
|
35
|
+
analyzeCheckTsdoc
|
|
36
|
+
};
|
package/dist/cli/index.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
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";
|
|
@@ -205,6 +208,8 @@ Add the missing entries to ${COLORS.blue}bunup.config.ts${COLORS.reset} defineWo
|
|
|
205
208
|
}
|
|
206
209
|
|
|
207
210
|
// src/cli/check-changeset.ts
|
|
211
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
212
|
+
import { join } from "node:path";
|
|
208
213
|
function getChangedPackagePaths(files) {
|
|
209
214
|
const packageNames = new Set;
|
|
210
215
|
const pattern = /^packages\/([^/]+)\/src\//;
|
|
@@ -236,6 +241,73 @@ function checkChangesetRequired(changedPackages, changesetFiles) {
|
|
|
236
241
|
}
|
|
237
242
|
return { ok: false, missingFor: changedPackages };
|
|
238
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
|
+
}
|
|
239
311
|
var COLORS2 = {
|
|
240
312
|
reset: "\x1B[0m",
|
|
241
313
|
red: "\x1B[31m",
|
|
@@ -275,25 +347,46 @@ async function runCheckChangeset(options = {}) {
|
|
|
275
347
|
}
|
|
276
348
|
const changesetFiles = getChangedChangesetFiles(changedFiles);
|
|
277
349
|
const check = checkChangesetRequired(changedPackages, changesetFiles);
|
|
278
|
-
if (check.ok) {
|
|
279
|
-
process.
|
|
350
|
+
if (!check.ok) {
|
|
351
|
+
process.stderr.write(`${COLORS2.red}Missing changeset!${COLORS2.reset}
|
|
352
|
+
|
|
280
353
|
`);
|
|
281
|
-
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);
|
|
282
365
|
}
|
|
283
|
-
|
|
366
|
+
const ignoredReferences = getIgnoredReferencesForChangedChangesets(cwd, changesetFiles);
|
|
367
|
+
if (ignoredReferences.length > 0) {
|
|
368
|
+
process.stderr.write(`${COLORS2.red}Invalid changeset package reference(s).${COLORS2.reset}
|
|
284
369
|
|
|
285
370
|
`);
|
|
286
|
-
|
|
371
|
+
process.stderr.write(`Changesets must not reference packages listed in .changeset/config.json ignore:
|
|
287
372
|
|
|
288
373
|
`);
|
|
289
|
-
|
|
290
|
-
|
|
374
|
+
for (const reference of ignoredReferences) {
|
|
375
|
+
process.stderr.write(` ${COLORS2.yellow}${reference.file}${COLORS2.reset}
|
|
376
|
+
`);
|
|
377
|
+
for (const pkg of reference.packages) {
|
|
378
|
+
process.stderr.write(` - ${pkg}
|
|
291
379
|
`);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
process.stderr.write(`
|
|
383
|
+
Update the affected changeset files to remove ignored packages before merging.
|
|
384
|
+
`);
|
|
385
|
+
process.exit(1);
|
|
292
386
|
}
|
|
293
|
-
process.
|
|
294
|
-
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}
|
|
295
388
|
`);
|
|
296
|
-
process.exit(
|
|
389
|
+
process.exit(0);
|
|
297
390
|
}
|
|
298
391
|
|
|
299
392
|
// src/cli/check-clean-tree.ts
|
|
@@ -674,8 +767,9 @@ async function runInit(cwd = process.cwd()) {
|
|
|
674
767
|
}
|
|
675
768
|
|
|
676
769
|
// src/cli/pre-push.ts
|
|
677
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
678
|
-
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";
|
|
679
773
|
var COLORS5 = {
|
|
680
774
|
reset: "\x1B[0m",
|
|
681
775
|
red: "\x1B[31m",
|
|
@@ -712,6 +806,9 @@ function isRedPhaseBranch(branch) {
|
|
|
712
806
|
function isScaffoldBranch(branch) {
|
|
713
807
|
return branch.endsWith("-scaffold") || branch.endsWith("/scaffold") || branch.endsWith("_scaffold");
|
|
714
808
|
}
|
|
809
|
+
function isReleaseBranch(branch) {
|
|
810
|
+
return branch.startsWith("changeset-release/");
|
|
811
|
+
}
|
|
715
812
|
var TEST_PATH_PATTERNS = [
|
|
716
813
|
/(^|\/)__tests__\//,
|
|
717
814
|
/(^|\/)__snapshots__\//,
|
|
@@ -731,6 +828,32 @@ function areFilesTestOnly(paths) {
|
|
|
731
828
|
function canBypassRedPhaseByChangedFiles(changedFiles) {
|
|
732
829
|
return changedFiles.deterministic && areFilesTestOnly(changedFiles.files);
|
|
733
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
|
+
}
|
|
734
857
|
function resolveBaseRef() {
|
|
735
858
|
const candidates = [
|
|
736
859
|
"origin/main",
|
|
@@ -868,12 +991,12 @@ function createVerificationPlan(scripts) {
|
|
|
868
991
|
};
|
|
869
992
|
}
|
|
870
993
|
function readPackageScripts(cwd = process.cwd()) {
|
|
871
|
-
const packageJsonPath =
|
|
872
|
-
if (!
|
|
994
|
+
const packageJsonPath = join2(cwd, "package.json");
|
|
995
|
+
if (!existsSync2(packageJsonPath)) {
|
|
873
996
|
return {};
|
|
874
997
|
}
|
|
875
998
|
try {
|
|
876
|
-
const parsed = JSON.parse(
|
|
999
|
+
const parsed = JSON.parse(readFileSync2(packageJsonPath, "utf-8"));
|
|
877
1000
|
const scripts = parsed.scripts ?? {};
|
|
878
1001
|
const normalized = {};
|
|
879
1002
|
for (const [name, value] of Object.entries(scripts)) {
|
|
@@ -898,6 +1021,11 @@ async function runPrePush(options = {}) {
|
|
|
898
1021
|
log(`${COLORS5.blue}Pre-push verify${COLORS5.reset} (TDD-aware)`);
|
|
899
1022
|
log("");
|
|
900
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
|
+
}
|
|
901
1029
|
if (isRedPhaseBranch(branch)) {
|
|
902
1030
|
if (maybeSkipForRedPhase("branch", branch)) {
|
|
903
1031
|
process.exit(0);
|
|
@@ -943,14 +1071,20 @@ async function runPrePush(options = {}) {
|
|
|
943
1071
|
log(" - feature_tests");
|
|
944
1072
|
process.exit(1);
|
|
945
1073
|
}
|
|
1074
|
+
const changedFiles = getChangedFilesForPush();
|
|
1075
|
+
if (hasPackageSourceChanges(changedFiles)) {
|
|
1076
|
+
try {
|
|
1077
|
+
await printTsdocSummary();
|
|
1078
|
+
} catch {}
|
|
1079
|
+
}
|
|
946
1080
|
log("");
|
|
947
1081
|
log(`${COLORS5.green}Strict verification passed${COLORS5.reset}`);
|
|
948
1082
|
process.exit(0);
|
|
949
1083
|
}
|
|
950
1084
|
|
|
951
1085
|
// src/cli/upgrade-bun.ts
|
|
952
|
-
import { existsSync as
|
|
953
|
-
import { join as
|
|
1086
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync } from "node:fs";
|
|
1087
|
+
import { join as join3 } from "node:path";
|
|
954
1088
|
var COLORS6 = {
|
|
955
1089
|
reset: "\x1B[0m",
|
|
956
1090
|
red: "\x1B[31m",
|
|
@@ -988,13 +1122,13 @@ function findPackageJsonFiles(dir) {
|
|
|
988
1122
|
const glob = new Bun.Glob("**/package.json");
|
|
989
1123
|
for (const path of glob.scanSync({ cwd: dir })) {
|
|
990
1124
|
if (!path.includes("node_modules")) {
|
|
991
|
-
results.push(
|
|
1125
|
+
results.push(join3(dir, path));
|
|
992
1126
|
}
|
|
993
1127
|
}
|
|
994
1128
|
return results;
|
|
995
1129
|
}
|
|
996
1130
|
function updateEnginesBun(filePath, version) {
|
|
997
|
-
const content =
|
|
1131
|
+
const content = readFileSync3(filePath, "utf-8");
|
|
998
1132
|
const pattern = /"bun":\s*">=[\d.]+"/;
|
|
999
1133
|
if (!pattern.test(content)) {
|
|
1000
1134
|
return false;
|
|
@@ -1007,7 +1141,7 @@ function updateEnginesBun(filePath, version) {
|
|
|
1007
1141
|
return false;
|
|
1008
1142
|
}
|
|
1009
1143
|
function updateTypesBun(filePath, version) {
|
|
1010
|
-
const content =
|
|
1144
|
+
const content = readFileSync3(filePath, "utf-8");
|
|
1011
1145
|
const pattern = /"@types\/bun":\s*"\^[\d.]+"/;
|
|
1012
1146
|
if (!pattern.test(content)) {
|
|
1013
1147
|
return false;
|
|
@@ -1021,14 +1155,14 @@ function updateTypesBun(filePath, version) {
|
|
|
1021
1155
|
}
|
|
1022
1156
|
async function runUpgradeBun(targetVersion, options = {}) {
|
|
1023
1157
|
const cwd = process.cwd();
|
|
1024
|
-
const bunVersionFile =
|
|
1158
|
+
const bunVersionFile = join3(cwd, ".bun-version");
|
|
1025
1159
|
let version = targetVersion;
|
|
1026
1160
|
if (!version) {
|
|
1027
1161
|
info("Fetching latest Bun version...");
|
|
1028
1162
|
version = await fetchLatestVersion();
|
|
1029
1163
|
log2(`Latest version: ${version}`);
|
|
1030
1164
|
}
|
|
1031
|
-
const currentVersion =
|
|
1165
|
+
const currentVersion = existsSync3(bunVersionFile) ? readFileSync3(bunVersionFile, "utf-8").trim() : "unknown";
|
|
1032
1166
|
log2(`Current version: ${currentVersion}`);
|
|
1033
1167
|
if (currentVersion === version) {
|
|
1034
1168
|
success(`Already on version ${version}`);
|
|
@@ -1129,6 +1263,14 @@ register(new Command("check-changeset").description("Validate PRs touching packa
|
|
|
1129
1263
|
register(new Command("check-exports").description("Validate package.json exports match source entry points").option("--json", "Output results as JSON").action(async (options) => {
|
|
1130
1264
|
await runCheckExports(options);
|
|
1131
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
|
+
}));
|
|
1132
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) => {
|
|
1133
1275
|
await runCheckCleanTree(options);
|
|
1134
1276
|
}));
|
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
|