@bensandee/tooling 0.10.1 → 0.11.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/dist/bin.mjs +128 -55
- package/package.json +1 -1
package/dist/bin.mjs
CHANGED
|
@@ -516,7 +516,8 @@ const STANDARD_SCRIPTS_SINGLE = {
|
|
|
516
516
|
test: "vitest run",
|
|
517
517
|
lint: "oxlint",
|
|
518
518
|
knip: "knip",
|
|
519
|
-
check: "pnpm
|
|
519
|
+
check: "pnpm exec tooling repo:run-checks",
|
|
520
|
+
"tooling:check": "pnpm exec tooling repo:check"
|
|
520
521
|
};
|
|
521
522
|
const STANDARD_SCRIPTS_MONOREPO = {
|
|
522
523
|
build: "pnpm -r build",
|
|
@@ -524,7 +525,8 @@ const STANDARD_SCRIPTS_MONOREPO = {
|
|
|
524
525
|
typecheck: "pnpm -r --parallel run typecheck",
|
|
525
526
|
lint: "oxlint",
|
|
526
527
|
knip: "knip",
|
|
527
|
-
check: "pnpm
|
|
528
|
+
check: "pnpm exec tooling repo:run-checks",
|
|
529
|
+
"tooling:check": "pnpm exec tooling repo:check"
|
|
528
530
|
};
|
|
529
531
|
/** DevDeps that belong in every project (single repo) or per-package (monorepo). */
|
|
530
532
|
const PER_PACKAGE_DEV_DEPS = {
|
|
@@ -581,7 +583,7 @@ function getAddedDevDepNames(config) {
|
|
|
581
583
|
const deps = { ...ROOT_DEV_DEPS };
|
|
582
584
|
if (config.structure !== "monorepo") Object.assign(deps, PER_PACKAGE_DEV_DEPS);
|
|
583
585
|
deps["@bensandee/config"] = "0.7.1";
|
|
584
|
-
deps["@bensandee/tooling"] = "0.
|
|
586
|
+
deps["@bensandee/tooling"] = "0.11.0";
|
|
585
587
|
if (config.formatter === "oxfmt") deps["oxfmt"] = "0.35.0";
|
|
586
588
|
if (config.formatter === "prettier") deps["prettier"] = "3.8.1";
|
|
587
589
|
addReleaseDeps(deps, config);
|
|
@@ -598,11 +600,11 @@ async function generatePackageJson(ctx) {
|
|
|
598
600
|
format: formatScript
|
|
599
601
|
};
|
|
600
602
|
if (ctx.config.releaseStrategy === "changesets") allScripts["changeset"] = "changeset";
|
|
601
|
-
if (ctx.config.releaseStrategy !== "none") allScripts["trigger-release"] = "pnpm exec tooling release:trigger";
|
|
603
|
+
if (ctx.config.releaseStrategy !== "none" && ctx.config.releaseStrategy !== "changesets") allScripts["trigger-release"] = "pnpm exec tooling release:trigger";
|
|
602
604
|
const devDeps = { ...ROOT_DEV_DEPS };
|
|
603
605
|
if (!isMonorepo) Object.assign(devDeps, PER_PACKAGE_DEV_DEPS);
|
|
604
606
|
devDeps["@bensandee/config"] = isWorkspacePackage(ctx, "@bensandee/config") ? "workspace:*" : "0.7.1";
|
|
605
|
-
devDeps["@bensandee/tooling"] = isWorkspacePackage(ctx, "@bensandee/tooling") ? "workspace:*" : "0.
|
|
607
|
+
devDeps["@bensandee/tooling"] = isWorkspacePackage(ctx, "@bensandee/tooling") ? "workspace:*" : "0.11.0";
|
|
606
608
|
if (ctx.config.useEslintPlugin) devDeps["@bensandee/eslint-plugin"] = isWorkspacePackage(ctx, "@bensandee/eslint-plugin") ? "workspace:*" : "0.9.0";
|
|
607
609
|
if (ctx.config.formatter === "oxfmt") devDeps["oxfmt"] = "0.35.0";
|
|
608
610
|
if (ctx.config.formatter === "prettier") devDeps["prettier"] = "3.8.1";
|
|
@@ -1193,6 +1195,7 @@ async function generateTsdown(ctx) {
|
|
|
1193
1195
|
/** Entries that every project should have — repo:check flags these as missing. */
|
|
1194
1196
|
const REQUIRED_ENTRIES = [
|
|
1195
1197
|
"node_modules/",
|
|
1198
|
+
".pnpm-store/",
|
|
1196
1199
|
"dist/",
|
|
1197
1200
|
"*.tsbuildinfo",
|
|
1198
1201
|
".env",
|
|
@@ -1249,6 +1252,12 @@ const FORGEJO_SCHEMA_COMMENT = "# yaml-language-server: $schema=../../.vscode/fo
|
|
|
1249
1252
|
function workflowSchemaComment(ci) {
|
|
1250
1253
|
return ci === "forgejo" ? FORGEJO_SCHEMA_COMMENT : "";
|
|
1251
1254
|
}
|
|
1255
|
+
/** Prepend the Forgejo schema comment if it's not already present. No-op for GitHub. */
|
|
1256
|
+
function ensureSchemaComment(content, ci) {
|
|
1257
|
+
if (ci !== "forgejo") return content;
|
|
1258
|
+
if (content.includes("yaml-language-server")) return content;
|
|
1259
|
+
return FORGEJO_SCHEMA_COMMENT + content;
|
|
1260
|
+
}
|
|
1252
1261
|
/** Check if a YAML file has an opt-out comment in the first 10 lines. */
|
|
1253
1262
|
function isToolingIgnored(content) {
|
|
1254
1263
|
return content.split("\n", 10).some((line) => line.includes(IGNORE_PATTERN));
|
|
@@ -1348,10 +1357,7 @@ function hasEnginesNode$1(ctx) {
|
|
|
1348
1357
|
if (!raw) return false;
|
|
1349
1358
|
return typeof parsePackageJson(raw)?.engines?.["node"] === "string";
|
|
1350
1359
|
}
|
|
1351
|
-
function ciWorkflow(
|
|
1352
|
-
const buildCmd = isMonorepo ? "pnpm -r build" : "pnpm build";
|
|
1353
|
-
const testCmd = isMonorepo ? "pnpm -r test" : "pnpm test";
|
|
1354
|
-
const typecheckCmd = isMonorepo ? "pnpm -r --parallel run typecheck" : "pnpm typecheck";
|
|
1360
|
+
function ciWorkflow(nodeVersionYaml, isForgejo) {
|
|
1355
1361
|
const emailNotifications = isForgejo ? "\nenable-email-notifications: true\n" : "";
|
|
1356
1362
|
return `${workflowSchemaComment(isForgejo ? "forgejo" : "github")}name: CI
|
|
1357
1363
|
${emailNotifications}on:
|
|
@@ -1371,19 +1377,11 @@ jobs:
|
|
|
1371
1377
|
${nodeVersionYaml}
|
|
1372
1378
|
cache: pnpm
|
|
1373
1379
|
- run: pnpm install --frozen-lockfile
|
|
1374
|
-
-
|
|
1375
|
-
|
|
1376
|
-
- run: ${buildCmd}
|
|
1377
|
-
- run: ${testCmd}
|
|
1378
|
-
- run: pnpm format --check
|
|
1379
|
-
- run: pnpm knip
|
|
1380
|
-
- run: pnpm exec tooling repo:check
|
|
1380
|
+
- name: Run all checks
|
|
1381
|
+
run: pnpm check
|
|
1381
1382
|
`;
|
|
1382
1383
|
}
|
|
1383
|
-
function requiredCheckSteps(
|
|
1384
|
-
const buildCmd = isMonorepo ? "pnpm -r build" : "pnpm build";
|
|
1385
|
-
const testCmd = isMonorepo ? "pnpm -r test" : "pnpm test";
|
|
1386
|
-
const typecheckCmd = isMonorepo ? "pnpm -r --parallel run typecheck" : "pnpm typecheck";
|
|
1384
|
+
function requiredCheckSteps(nodeVersionYaml) {
|
|
1387
1385
|
return [
|
|
1388
1386
|
{
|
|
1389
1387
|
match: { uses: "actions/checkout" },
|
|
@@ -1408,32 +1406,11 @@ function requiredCheckSteps(isMonorepo, nodeVersionYaml) {
|
|
|
1408
1406
|
step: { run: "pnpm install --frozen-lockfile" }
|
|
1409
1407
|
},
|
|
1410
1408
|
{
|
|
1411
|
-
match: { run: "
|
|
1412
|
-
step: {
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
step: { run: "pnpm lint" }
|
|
1417
|
-
},
|
|
1418
|
-
{
|
|
1419
|
-
match: { run: "build" },
|
|
1420
|
-
step: { run: buildCmd }
|
|
1421
|
-
},
|
|
1422
|
-
{
|
|
1423
|
-
match: { run: "test" },
|
|
1424
|
-
step: { run: testCmd }
|
|
1425
|
-
},
|
|
1426
|
-
{
|
|
1427
|
-
match: { run: "format" },
|
|
1428
|
-
step: { run: "pnpm format --check" }
|
|
1429
|
-
},
|
|
1430
|
-
{
|
|
1431
|
-
match: { run: "knip" },
|
|
1432
|
-
step: { run: "pnpm knip" }
|
|
1433
|
-
},
|
|
1434
|
-
{
|
|
1435
|
-
match: { run: "repo:check" },
|
|
1436
|
-
step: { run: "pnpm exec tooling repo:check" }
|
|
1409
|
+
match: { run: "check" },
|
|
1410
|
+
step: {
|
|
1411
|
+
name: "Run all checks",
|
|
1412
|
+
run: "pnpm check"
|
|
1413
|
+
}
|
|
1437
1414
|
}
|
|
1438
1415
|
];
|
|
1439
1416
|
}
|
|
@@ -1443,17 +1420,17 @@ async function generateCi(ctx) {
|
|
|
1443
1420
|
action: "skipped",
|
|
1444
1421
|
description: "CI workflow not requested"
|
|
1445
1422
|
};
|
|
1446
|
-
const isMonorepo = ctx.config.structure === "monorepo";
|
|
1447
1423
|
const isGitHub = ctx.config.ci === "github";
|
|
1448
1424
|
const nodeVersionYaml = hasEnginesNode$1(ctx) ? "node-version-file: package.json" : "node-version: \"24\"";
|
|
1449
1425
|
const filePath = isGitHub ? ".github/workflows/check.yml" : ".forgejo/workflows/check.yml";
|
|
1450
|
-
const content = ciWorkflow(
|
|
1426
|
+
const content = ciWorkflow(nodeVersionYaml, !isGitHub);
|
|
1451
1427
|
if (ctx.exists(filePath)) {
|
|
1452
1428
|
const existing = ctx.read(filePath);
|
|
1453
1429
|
if (existing) {
|
|
1454
|
-
const merged = mergeWorkflowSteps(existing, "check", requiredCheckSteps(
|
|
1455
|
-
|
|
1456
|
-
|
|
1430
|
+
const merged = mergeWorkflowSteps(existing, "check", requiredCheckSteps(nodeVersionYaml));
|
|
1431
|
+
const withComment = ensureSchemaComment(merged.content, isGitHub ? "github" : "forgejo");
|
|
1432
|
+
if (merged.changed || withComment !== merged.content) {
|
|
1433
|
+
ctx.write(filePath, withComment);
|
|
1457
1434
|
return {
|
|
1458
1435
|
filePath,
|
|
1459
1436
|
action: "updated",
|
|
@@ -2135,8 +2112,9 @@ async function generateReleaseCi(ctx) {
|
|
|
2135
2112
|
const existing = ctx.read(workflowPath);
|
|
2136
2113
|
if (existing) {
|
|
2137
2114
|
const merged = mergeWorkflowSteps(existing, "release", requiredReleaseSteps(ctx.config.releaseStrategy, nodeVersionYaml));
|
|
2138
|
-
|
|
2139
|
-
|
|
2115
|
+
const withComment = ensureSchemaComment(merged.content, ctx.config.ci);
|
|
2116
|
+
if (merged.changed || withComment !== merged.content) {
|
|
2117
|
+
ctx.write(workflowPath, withComment);
|
|
2140
2118
|
return {
|
|
2141
2119
|
filePath: workflowPath,
|
|
2142
2120
|
action: "updated",
|
|
@@ -3424,24 +3402,119 @@ function mergeGitHub(dryRun) {
|
|
|
3424
3402
|
p.log.info(`Merged changesets PR and deleted branch ${HEAD_BRANCH}`);
|
|
3425
3403
|
}
|
|
3426
3404
|
//#endregion
|
|
3405
|
+
//#region src/commands/repo-run-checks.ts
|
|
3406
|
+
const CHECKS = [
|
|
3407
|
+
{
|
|
3408
|
+
name: "build",
|
|
3409
|
+
cmd: "pnpm run --if-present build"
|
|
3410
|
+
},
|
|
3411
|
+
{
|
|
3412
|
+
name: "typecheck",
|
|
3413
|
+
cmd: "pnpm run --if-present typecheck"
|
|
3414
|
+
},
|
|
3415
|
+
{
|
|
3416
|
+
name: "lint",
|
|
3417
|
+
cmd: "pnpm run --if-present lint"
|
|
3418
|
+
},
|
|
3419
|
+
{
|
|
3420
|
+
name: "test",
|
|
3421
|
+
cmd: "pnpm run --if-present test"
|
|
3422
|
+
},
|
|
3423
|
+
{
|
|
3424
|
+
name: "format",
|
|
3425
|
+
cmd: "pnpm run --if-present format -- --check"
|
|
3426
|
+
},
|
|
3427
|
+
{
|
|
3428
|
+
name: "knip",
|
|
3429
|
+
cmd: "pnpm run --if-present knip"
|
|
3430
|
+
},
|
|
3431
|
+
{
|
|
3432
|
+
name: "tooling:check",
|
|
3433
|
+
cmd: "pnpm run --if-present tooling:check"
|
|
3434
|
+
}
|
|
3435
|
+
];
|
|
3436
|
+
function defaultExecCommand(cmd, cwd) {
|
|
3437
|
+
try {
|
|
3438
|
+
execSync(cmd, {
|
|
3439
|
+
cwd,
|
|
3440
|
+
stdio: "inherit"
|
|
3441
|
+
});
|
|
3442
|
+
return 0;
|
|
3443
|
+
} catch (err) {
|
|
3444
|
+
if (isExecSyncError(err)) return err.status;
|
|
3445
|
+
return 1;
|
|
3446
|
+
}
|
|
3447
|
+
}
|
|
3448
|
+
const ciLog = (msg) => console.log(msg);
|
|
3449
|
+
function runRunChecks(targetDir, options = {}) {
|
|
3450
|
+
const exec = options.execCommand ?? defaultExecCommand;
|
|
3451
|
+
const skip = options.skip ?? /* @__PURE__ */ new Set();
|
|
3452
|
+
const isCI = Boolean(process.env["CI"]);
|
|
3453
|
+
const failures = [];
|
|
3454
|
+
for (const check of CHECKS) {
|
|
3455
|
+
if (skip.has(check.name)) {
|
|
3456
|
+
p.log.info(`${check.name} (skipped)`);
|
|
3457
|
+
continue;
|
|
3458
|
+
}
|
|
3459
|
+
if (isCI) ciLog(`::group::${check.name}`);
|
|
3460
|
+
const exitCode = exec(check.cmd, targetDir);
|
|
3461
|
+
if (isCI) ciLog("::endgroup::");
|
|
3462
|
+
if (exitCode === 0) p.log.success(check.name);
|
|
3463
|
+
else {
|
|
3464
|
+
if (isCI) ciLog(`::error::${check.name} failed`);
|
|
3465
|
+
p.log.error(`${check.name} failed`);
|
|
3466
|
+
failures.push(check.name);
|
|
3467
|
+
}
|
|
3468
|
+
}
|
|
3469
|
+
if (failures.length > 0) {
|
|
3470
|
+
p.log.error(`Failed checks: ${failures.join(", ")}`);
|
|
3471
|
+
return 1;
|
|
3472
|
+
}
|
|
3473
|
+
p.log.success("All checks passed");
|
|
3474
|
+
return 0;
|
|
3475
|
+
}
|
|
3476
|
+
const runChecksCommand = defineCommand({
|
|
3477
|
+
meta: {
|
|
3478
|
+
name: "repo:run-checks",
|
|
3479
|
+
description: "Run all standard checks (build, typecheck, lint, test, format, knip, tooling:check)"
|
|
3480
|
+
},
|
|
3481
|
+
args: {
|
|
3482
|
+
dir: {
|
|
3483
|
+
type: "positional",
|
|
3484
|
+
description: "Target directory (default: current directory)",
|
|
3485
|
+
required: false
|
|
3486
|
+
},
|
|
3487
|
+
skip: {
|
|
3488
|
+
type: "string",
|
|
3489
|
+
description: "Comma-separated list of checks to skip (build, typecheck, lint, test, format, knip, tooling:check)",
|
|
3490
|
+
required: false
|
|
3491
|
+
}
|
|
3492
|
+
},
|
|
3493
|
+
run({ args }) {
|
|
3494
|
+
const exitCode = runRunChecks(path.resolve(args.dir ?? "."), { skip: args.skip ? new Set(args.skip.split(",").map((s) => s.trim())) : void 0 });
|
|
3495
|
+
process.exitCode = exitCode;
|
|
3496
|
+
}
|
|
3497
|
+
});
|
|
3498
|
+
//#endregion
|
|
3427
3499
|
//#region src/bin.ts
|
|
3428
3500
|
const main = defineCommand({
|
|
3429
3501
|
meta: {
|
|
3430
3502
|
name: "tooling",
|
|
3431
|
-
version: "0.
|
|
3503
|
+
version: "0.11.0",
|
|
3432
3504
|
description: "Bootstrap and maintain standardized TypeScript project tooling"
|
|
3433
3505
|
},
|
|
3434
3506
|
subCommands: {
|
|
3435
3507
|
"repo:init": initCommand,
|
|
3436
3508
|
"repo:update": updateCommand,
|
|
3437
3509
|
"repo:check": checkCommand,
|
|
3510
|
+
"repo:run-checks": runChecksCommand,
|
|
3438
3511
|
"release:changesets": releaseForgejoCommand,
|
|
3439
3512
|
"release:trigger": releaseTriggerCommand,
|
|
3440
3513
|
"release:create-forgejo-release": createForgejoReleaseCommand,
|
|
3441
3514
|
"release:merge": releaseMergeCommand
|
|
3442
3515
|
}
|
|
3443
3516
|
});
|
|
3444
|
-
console.log(`@bensandee/tooling v0.
|
|
3517
|
+
console.log(`@bensandee/tooling v0.11.0`);
|
|
3445
3518
|
runMain(main);
|
|
3446
3519
|
//#endregion
|
|
3447
3520
|
export {};
|