@bensandee/tooling 0.10.1 → 0.12.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 +153 -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,13 @@ 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"
|
|
530
|
+
};
|
|
531
|
+
/** Scripts that tooling owns — map from script name to keyword that must appear in the value. */
|
|
532
|
+
const MANAGED_SCRIPTS = {
|
|
533
|
+
check: "repo:run-checks",
|
|
534
|
+
"tooling:check": "repo:check"
|
|
528
535
|
};
|
|
529
536
|
/** DevDeps that belong in every project (single repo) or per-package (monorepo). */
|
|
530
537
|
const PER_PACKAGE_DEV_DEPS = {
|
|
@@ -581,7 +588,7 @@ function getAddedDevDepNames(config) {
|
|
|
581
588
|
const deps = { ...ROOT_DEV_DEPS };
|
|
582
589
|
if (config.structure !== "monorepo") Object.assign(deps, PER_PACKAGE_DEV_DEPS);
|
|
583
590
|
deps["@bensandee/config"] = "0.7.1";
|
|
584
|
-
deps["@bensandee/tooling"] = "0.
|
|
591
|
+
deps["@bensandee/tooling"] = "0.12.0";
|
|
585
592
|
if (config.formatter === "oxfmt") deps["oxfmt"] = "0.35.0";
|
|
586
593
|
if (config.formatter === "prettier") deps["prettier"] = "3.8.1";
|
|
587
594
|
addReleaseDeps(deps, config);
|
|
@@ -598,11 +605,11 @@ async function generatePackageJson(ctx) {
|
|
|
598
605
|
format: formatScript
|
|
599
606
|
};
|
|
600
607
|
if (ctx.config.releaseStrategy === "changesets") allScripts["changeset"] = "changeset";
|
|
601
|
-
if (ctx.config.releaseStrategy !== "none") allScripts["trigger-release"] = "pnpm exec tooling release:trigger";
|
|
608
|
+
if (ctx.config.releaseStrategy !== "none" && ctx.config.releaseStrategy !== "changesets") allScripts["trigger-release"] = "pnpm exec tooling release:trigger";
|
|
602
609
|
const devDeps = { ...ROOT_DEV_DEPS };
|
|
603
610
|
if (!isMonorepo) Object.assign(devDeps, PER_PACKAGE_DEV_DEPS);
|
|
604
611
|
devDeps["@bensandee/config"] = isWorkspacePackage(ctx, "@bensandee/config") ? "workspace:*" : "0.7.1";
|
|
605
|
-
devDeps["@bensandee/tooling"] = isWorkspacePackage(ctx, "@bensandee/tooling") ? "workspace:*" : "0.
|
|
612
|
+
devDeps["@bensandee/tooling"] = isWorkspacePackage(ctx, "@bensandee/tooling") ? "workspace:*" : "0.12.0";
|
|
606
613
|
if (ctx.config.useEslintPlugin) devDeps["@bensandee/eslint-plugin"] = isWorkspacePackage(ctx, "@bensandee/eslint-plugin") ? "workspace:*" : "0.9.0";
|
|
607
614
|
if (ctx.config.formatter === "oxfmt") devDeps["oxfmt"] = "0.35.0";
|
|
608
615
|
if (ctx.config.formatter === "prettier") devDeps["prettier"] = "3.8.1";
|
|
@@ -623,6 +630,9 @@ async function generatePackageJson(ctx) {
|
|
|
623
630
|
for (const [key, value] of Object.entries(allScripts)) if (!(key in existingScripts)) {
|
|
624
631
|
existingScripts[key] = value;
|
|
625
632
|
changes.push(`added script: ${key}`);
|
|
633
|
+
} else if (key in MANAGED_SCRIPTS && !existingScripts[key]?.includes(MANAGED_SCRIPTS[key])) {
|
|
634
|
+
existingScripts[key] = value;
|
|
635
|
+
changes.push(`updated script: ${key}`);
|
|
626
636
|
}
|
|
627
637
|
pkg.scripts = existingScripts;
|
|
628
638
|
const existingDevDeps = pkg.devDependencies ?? {};
|
|
@@ -1193,6 +1203,7 @@ async function generateTsdown(ctx) {
|
|
|
1193
1203
|
/** Entries that every project should have — repo:check flags these as missing. */
|
|
1194
1204
|
const REQUIRED_ENTRIES = [
|
|
1195
1205
|
"node_modules/",
|
|
1206
|
+
".pnpm-store/",
|
|
1196
1207
|
"dist/",
|
|
1197
1208
|
"*.tsbuildinfo",
|
|
1198
1209
|
".env",
|
|
@@ -1249,6 +1260,12 @@ const FORGEJO_SCHEMA_COMMENT = "# yaml-language-server: $schema=../../.vscode/fo
|
|
|
1249
1260
|
function workflowSchemaComment(ci) {
|
|
1250
1261
|
return ci === "forgejo" ? FORGEJO_SCHEMA_COMMENT : "";
|
|
1251
1262
|
}
|
|
1263
|
+
/** Prepend the Forgejo schema comment if it's not already present. No-op for GitHub. */
|
|
1264
|
+
function ensureSchemaComment(content, ci) {
|
|
1265
|
+
if (ci !== "forgejo") return content;
|
|
1266
|
+
if (content.includes("yaml-language-server")) return content;
|
|
1267
|
+
return FORGEJO_SCHEMA_COMMENT + content;
|
|
1268
|
+
}
|
|
1252
1269
|
/** Check if a YAML file has an opt-out comment in the first 10 lines. */
|
|
1253
1270
|
function isToolingIgnored(content) {
|
|
1254
1271
|
return content.split("\n", 10).some((line) => line.includes(IGNORE_PATTERN));
|
|
@@ -1348,10 +1365,7 @@ function hasEnginesNode$1(ctx) {
|
|
|
1348
1365
|
if (!raw) return false;
|
|
1349
1366
|
return typeof parsePackageJson(raw)?.engines?.["node"] === "string";
|
|
1350
1367
|
}
|
|
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";
|
|
1368
|
+
function ciWorkflow(nodeVersionYaml, isForgejo) {
|
|
1355
1369
|
const emailNotifications = isForgejo ? "\nenable-email-notifications: true\n" : "";
|
|
1356
1370
|
return `${workflowSchemaComment(isForgejo ? "forgejo" : "github")}name: CI
|
|
1357
1371
|
${emailNotifications}on:
|
|
@@ -1371,19 +1385,11 @@ jobs:
|
|
|
1371
1385
|
${nodeVersionYaml}
|
|
1372
1386
|
cache: pnpm
|
|
1373
1387
|
- 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
|
|
1388
|
+
- name: Run all checks
|
|
1389
|
+
run: pnpm check
|
|
1381
1390
|
`;
|
|
1382
1391
|
}
|
|
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";
|
|
1392
|
+
function requiredCheckSteps(nodeVersionYaml) {
|
|
1387
1393
|
return [
|
|
1388
1394
|
{
|
|
1389
1395
|
match: { uses: "actions/checkout" },
|
|
@@ -1408,32 +1414,11 @@ function requiredCheckSteps(isMonorepo, nodeVersionYaml) {
|
|
|
1408
1414
|
step: { run: "pnpm install --frozen-lockfile" }
|
|
1409
1415
|
},
|
|
1410
1416
|
{
|
|
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" }
|
|
1417
|
+
match: { run: "check" },
|
|
1418
|
+
step: {
|
|
1419
|
+
name: "Run all checks",
|
|
1420
|
+
run: "pnpm check"
|
|
1421
|
+
}
|
|
1437
1422
|
}
|
|
1438
1423
|
];
|
|
1439
1424
|
}
|
|
@@ -1443,17 +1428,17 @@ async function generateCi(ctx) {
|
|
|
1443
1428
|
action: "skipped",
|
|
1444
1429
|
description: "CI workflow not requested"
|
|
1445
1430
|
};
|
|
1446
|
-
const isMonorepo = ctx.config.structure === "monorepo";
|
|
1447
1431
|
const isGitHub = ctx.config.ci === "github";
|
|
1448
1432
|
const nodeVersionYaml = hasEnginesNode$1(ctx) ? "node-version-file: package.json" : "node-version: \"24\"";
|
|
1449
1433
|
const filePath = isGitHub ? ".github/workflows/check.yml" : ".forgejo/workflows/check.yml";
|
|
1450
|
-
const content = ciWorkflow(
|
|
1434
|
+
const content = ciWorkflow(nodeVersionYaml, !isGitHub);
|
|
1451
1435
|
if (ctx.exists(filePath)) {
|
|
1452
1436
|
const existing = ctx.read(filePath);
|
|
1453
1437
|
if (existing) {
|
|
1454
|
-
const merged = mergeWorkflowSteps(existing, "check", requiredCheckSteps(
|
|
1455
|
-
|
|
1456
|
-
|
|
1438
|
+
const merged = mergeWorkflowSteps(existing, "check", requiredCheckSteps(nodeVersionYaml));
|
|
1439
|
+
const withComment = ensureSchemaComment(merged.content, isGitHub ? "github" : "forgejo");
|
|
1440
|
+
if (merged.changed || withComment !== merged.content) {
|
|
1441
|
+
ctx.write(filePath, withComment);
|
|
1457
1442
|
return {
|
|
1458
1443
|
filePath,
|
|
1459
1444
|
action: "updated",
|
|
@@ -2135,8 +2120,9 @@ async function generateReleaseCi(ctx) {
|
|
|
2135
2120
|
const existing = ctx.read(workflowPath);
|
|
2136
2121
|
if (existing) {
|
|
2137
2122
|
const merged = mergeWorkflowSteps(existing, "release", requiredReleaseSteps(ctx.config.releaseStrategy, nodeVersionYaml));
|
|
2138
|
-
|
|
2139
|
-
|
|
2123
|
+
const withComment = ensureSchemaComment(merged.content, ctx.config.ci);
|
|
2124
|
+
if (merged.changed || withComment !== merged.content) {
|
|
2125
|
+
ctx.write(workflowPath, withComment);
|
|
2140
2126
|
return {
|
|
2141
2127
|
filePath: workflowPath,
|
|
2142
2128
|
action: "updated",
|
|
@@ -3424,24 +3410,136 @@ function mergeGitHub(dryRun) {
|
|
|
3424
3410
|
p.log.info(`Merged changesets PR and deleted branch ${HEAD_BRANCH}`);
|
|
3425
3411
|
}
|
|
3426
3412
|
//#endregion
|
|
3413
|
+
//#region src/commands/repo-run-checks.ts
|
|
3414
|
+
const CHECKS = [
|
|
3415
|
+
{
|
|
3416
|
+
name: "build",
|
|
3417
|
+
cmd: "pnpm run --if-present build"
|
|
3418
|
+
},
|
|
3419
|
+
{
|
|
3420
|
+
name: "typecheck",
|
|
3421
|
+
cmd: "pnpm run --if-present typecheck"
|
|
3422
|
+
},
|
|
3423
|
+
{
|
|
3424
|
+
name: "lint",
|
|
3425
|
+
cmd: "pnpm run --if-present lint"
|
|
3426
|
+
},
|
|
3427
|
+
{
|
|
3428
|
+
name: "test",
|
|
3429
|
+
cmd: "pnpm run --if-present test"
|
|
3430
|
+
},
|
|
3431
|
+
{
|
|
3432
|
+
name: "format",
|
|
3433
|
+
cmd: "pnpm run --if-present format -- --check"
|
|
3434
|
+
},
|
|
3435
|
+
{
|
|
3436
|
+
name: "knip",
|
|
3437
|
+
cmd: "pnpm run --if-present knip"
|
|
3438
|
+
},
|
|
3439
|
+
{
|
|
3440
|
+
name: "tooling:check",
|
|
3441
|
+
cmd: "pnpm run --if-present tooling:check"
|
|
3442
|
+
},
|
|
3443
|
+
{
|
|
3444
|
+
name: "image:check",
|
|
3445
|
+
cmd: "pnpm run --if-present image:check"
|
|
3446
|
+
}
|
|
3447
|
+
];
|
|
3448
|
+
function defaultExecCommand(cmd, cwd) {
|
|
3449
|
+
try {
|
|
3450
|
+
execSync(cmd, {
|
|
3451
|
+
cwd,
|
|
3452
|
+
stdio: "inherit"
|
|
3453
|
+
});
|
|
3454
|
+
return 0;
|
|
3455
|
+
} catch (err) {
|
|
3456
|
+
if (isExecSyncError(err)) return err.status;
|
|
3457
|
+
return 1;
|
|
3458
|
+
}
|
|
3459
|
+
}
|
|
3460
|
+
const ciLog = (msg) => console.log(msg);
|
|
3461
|
+
function runRunChecks(targetDir, options = {}) {
|
|
3462
|
+
const exec = options.execCommand ?? defaultExecCommand;
|
|
3463
|
+
const skip = options.skip ?? /* @__PURE__ */ new Set();
|
|
3464
|
+
const add = options.add ?? [];
|
|
3465
|
+
const isCI = Boolean(process.env["CI"]);
|
|
3466
|
+
const allChecks = [...CHECKS, ...add.map((name) => ({
|
|
3467
|
+
name,
|
|
3468
|
+
cmd: `pnpm run --if-present ${name}`
|
|
3469
|
+
}))];
|
|
3470
|
+
const failures = [];
|
|
3471
|
+
for (const check of allChecks) {
|
|
3472
|
+
if (skip.has(check.name)) {
|
|
3473
|
+
p.log.info(`${check.name} (skipped)`);
|
|
3474
|
+
continue;
|
|
3475
|
+
}
|
|
3476
|
+
if (isCI) ciLog(`::group::${check.name}`);
|
|
3477
|
+
const exitCode = exec(check.cmd, targetDir);
|
|
3478
|
+
if (isCI) ciLog("::endgroup::");
|
|
3479
|
+
if (exitCode === 0) p.log.success(check.name);
|
|
3480
|
+
else {
|
|
3481
|
+
if (isCI) ciLog(`::error::${check.name} failed`);
|
|
3482
|
+
p.log.error(`${check.name} failed`);
|
|
3483
|
+
failures.push(check.name);
|
|
3484
|
+
}
|
|
3485
|
+
}
|
|
3486
|
+
if (failures.length > 0) {
|
|
3487
|
+
p.log.error(`Failed checks: ${failures.join(", ")}`);
|
|
3488
|
+
return 1;
|
|
3489
|
+
}
|
|
3490
|
+
p.log.success("All checks passed");
|
|
3491
|
+
return 0;
|
|
3492
|
+
}
|
|
3493
|
+
const runChecksCommand = defineCommand({
|
|
3494
|
+
meta: {
|
|
3495
|
+
name: "repo:run-checks",
|
|
3496
|
+
description: "Run all standard checks (build, typecheck, lint, test, format, knip, tooling:check, image:check)"
|
|
3497
|
+
},
|
|
3498
|
+
args: {
|
|
3499
|
+
dir: {
|
|
3500
|
+
type: "positional",
|
|
3501
|
+
description: "Target directory (default: current directory)",
|
|
3502
|
+
required: false
|
|
3503
|
+
},
|
|
3504
|
+
skip: {
|
|
3505
|
+
type: "string",
|
|
3506
|
+
description: "Comma-separated list of checks to skip (build, typecheck, lint, test, format, knip, tooling:check, image:check)",
|
|
3507
|
+
required: false
|
|
3508
|
+
},
|
|
3509
|
+
add: {
|
|
3510
|
+
type: "string",
|
|
3511
|
+
description: "Comma-separated list of additional check names to run (uses pnpm run --if-present <name>)",
|
|
3512
|
+
required: false
|
|
3513
|
+
}
|
|
3514
|
+
},
|
|
3515
|
+
run({ args }) {
|
|
3516
|
+
const exitCode = runRunChecks(path.resolve(args.dir ?? "."), {
|
|
3517
|
+
skip: args.skip ? new Set(args.skip.split(",").map((s) => s.trim())) : void 0,
|
|
3518
|
+
add: args.add ? args.add.split(",").map((s) => s.trim()) : void 0
|
|
3519
|
+
});
|
|
3520
|
+
process.exitCode = exitCode;
|
|
3521
|
+
}
|
|
3522
|
+
});
|
|
3523
|
+
//#endregion
|
|
3427
3524
|
//#region src/bin.ts
|
|
3428
3525
|
const main = defineCommand({
|
|
3429
3526
|
meta: {
|
|
3430
3527
|
name: "tooling",
|
|
3431
|
-
version: "0.
|
|
3528
|
+
version: "0.12.0",
|
|
3432
3529
|
description: "Bootstrap and maintain standardized TypeScript project tooling"
|
|
3433
3530
|
},
|
|
3434
3531
|
subCommands: {
|
|
3435
3532
|
"repo:init": initCommand,
|
|
3436
3533
|
"repo:update": updateCommand,
|
|
3437
3534
|
"repo:check": checkCommand,
|
|
3535
|
+
"repo:run-checks": runChecksCommand,
|
|
3438
3536
|
"release:changesets": releaseForgejoCommand,
|
|
3439
3537
|
"release:trigger": releaseTriggerCommand,
|
|
3440
3538
|
"release:create-forgejo-release": createForgejoReleaseCommand,
|
|
3441
3539
|
"release:merge": releaseMergeCommand
|
|
3442
3540
|
}
|
|
3443
3541
|
});
|
|
3444
|
-
console.log(`@bensandee/tooling v0.
|
|
3542
|
+
console.log(`@bensandee/tooling v0.12.0`);
|
|
3445
3543
|
runMain(main);
|
|
3446
3544
|
//#endregion
|
|
3447
3545
|
export {};
|