@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.
Files changed (2) hide show
  1. package/dist/bin.mjs +128 -55
  2. 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 typecheck && pnpm build && pnpm lint && pnpm knip"
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 typecheck && pnpm build && pnpm lint && pnpm knip"
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.10.1";
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.10.1";
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(isMonorepo, nodeVersionYaml, isForgejo) {
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
- - run: ${typecheckCmd}
1375
- - run: pnpm lint
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(isMonorepo, nodeVersionYaml) {
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: "typecheck" },
1412
- step: { run: typecheckCmd }
1413
- },
1414
- {
1415
- match: { run: "lint" },
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(isMonorepo, nodeVersionYaml, !isGitHub);
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(isMonorepo, nodeVersionYaml));
1455
- if (merged.changed) {
1456
- ctx.write(filePath, merged.content);
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
- if (merged.changed) {
2139
- ctx.write(workflowPath, merged.content);
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.10.1",
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.10.1`);
3517
+ console.log(`@bensandee/tooling v0.11.0`);
3445
3518
  runMain(main);
3446
3519
  //#endregion
3447
3520
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bensandee/tooling",
3
- "version": "0.10.1",
3
+ "version": "0.11.0",
4
4
  "description": "CLI tool to bootstrap and maintain standardized TypeScript project tooling",
5
5
  "bin": {
6
6
  "tooling": "./dist/bin.mjs"