@bensandee/tooling 0.9.0 → 0.10.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 +289 -38
- package/package.json +2 -1
package/dist/bin.mjs
CHANGED
|
@@ -7,6 +7,7 @@ import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync
|
|
|
7
7
|
import JSON5 from "json5";
|
|
8
8
|
import { parse } from "jsonc-parser";
|
|
9
9
|
import { z } from "zod";
|
|
10
|
+
import { isMap, isSeq, parseDocument } from "yaml";
|
|
10
11
|
import { FatalError, TransientError, UnexpectedError } from "@bensandee/common";
|
|
11
12
|
//#region src/types.ts
|
|
12
13
|
/** Default CI platform when not explicitly chosen. */
|
|
@@ -414,6 +415,19 @@ function buildDefaultConfig(targetDir, flags) {
|
|
|
414
415
|
}
|
|
415
416
|
//#endregion
|
|
416
417
|
//#region src/utils/fs.ts
|
|
418
|
+
/**
|
|
419
|
+
* Compare file content, using parsed comparison for JSON files to ignore
|
|
420
|
+
* formatting differences. Falls back to exact string comparison for JSONC
|
|
421
|
+
* files (with comments/trailing commas) that fail JSON.parse.
|
|
422
|
+
*/
|
|
423
|
+
function contentEqual(relativePath, a, b) {
|
|
424
|
+
if (relativePath.endsWith(".json")) try {
|
|
425
|
+
return JSON.stringify(JSON.parse(a)) === JSON.stringify(JSON.parse(b));
|
|
426
|
+
} catch {
|
|
427
|
+
return a === b;
|
|
428
|
+
}
|
|
429
|
+
return a === b;
|
|
430
|
+
}
|
|
417
431
|
/** Check whether a file exists at the given path. */
|
|
418
432
|
function fileExists(targetDir, relativePath) {
|
|
419
433
|
return existsSync(path.join(targetDir, relativePath));
|
|
@@ -446,7 +460,7 @@ function createContext(config, confirmOverwrite) {
|
|
|
446
460
|
write: (rel, content) => {
|
|
447
461
|
if (!rel.startsWith(".tooling-archived/")) {
|
|
448
462
|
const existing = readFile(config.targetDir, rel);
|
|
449
|
-
if (existing !== void 0 && existing
|
|
463
|
+
if (existing !== void 0 && !contentEqual(rel, existing, content)) {
|
|
450
464
|
writeFile(config.targetDir, `.tooling-archived/${rel}`, existing);
|
|
451
465
|
archivedFiles.push(rel);
|
|
452
466
|
}
|
|
@@ -567,7 +581,7 @@ function getAddedDevDepNames(config) {
|
|
|
567
581
|
const deps = { ...ROOT_DEV_DEPS };
|
|
568
582
|
if (config.structure !== "monorepo") Object.assign(deps, PER_PACKAGE_DEV_DEPS);
|
|
569
583
|
deps["@bensandee/config"] = "0.7.1";
|
|
570
|
-
deps["@bensandee/tooling"] = "0.
|
|
584
|
+
deps["@bensandee/tooling"] = "0.10.0";
|
|
571
585
|
if (config.formatter === "oxfmt") deps["oxfmt"] = "0.35.0";
|
|
572
586
|
if (config.formatter === "prettier") deps["prettier"] = "3.8.1";
|
|
573
587
|
addReleaseDeps(deps, config);
|
|
@@ -588,7 +602,7 @@ async function generatePackageJson(ctx) {
|
|
|
588
602
|
const devDeps = { ...ROOT_DEV_DEPS };
|
|
589
603
|
if (!isMonorepo) Object.assign(devDeps, PER_PACKAGE_DEV_DEPS);
|
|
590
604
|
devDeps["@bensandee/config"] = isWorkspacePackage(ctx, "@bensandee/config") ? "workspace:*" : "0.7.1";
|
|
591
|
-
devDeps["@bensandee/tooling"] = isWorkspacePackage(ctx, "@bensandee/tooling") ? "workspace:*" : "0.
|
|
605
|
+
devDeps["@bensandee/tooling"] = isWorkspacePackage(ctx, "@bensandee/tooling") ? "workspace:*" : "0.10.0";
|
|
592
606
|
if (ctx.config.useEslintPlugin) devDeps["@bensandee/eslint-plugin"] = isWorkspacePackage(ctx, "@bensandee/eslint-plugin") ? "workspace:*" : "0.9.0";
|
|
593
607
|
if (ctx.config.formatter === "oxfmt") devDeps["oxfmt"] = "0.35.0";
|
|
594
608
|
if (ctx.config.formatter === "prettier") devDeps["prettier"] = "3.8.1";
|
|
@@ -1228,6 +1242,101 @@ async function generateGitignore(ctx) {
|
|
|
1228
1242
|
};
|
|
1229
1243
|
}
|
|
1230
1244
|
//#endregion
|
|
1245
|
+
//#region src/utils/yaml-merge.ts
|
|
1246
|
+
const IGNORE_PATTERN = "@bensandee/tooling:ignore";
|
|
1247
|
+
/** Check if a YAML file has an opt-out comment in the first 10 lines. */
|
|
1248
|
+
function isToolingIgnored(content) {
|
|
1249
|
+
return content.split("\n", 10).some((line) => line.includes(IGNORE_PATTERN));
|
|
1250
|
+
}
|
|
1251
|
+
/**
|
|
1252
|
+
* Ensure required commands exist under `pre-commit.commands` in a lefthook config.
|
|
1253
|
+
* Only adds missing commands — never modifies existing ones.
|
|
1254
|
+
* Returns unchanged content if the file has an opt-out comment or can't be parsed.
|
|
1255
|
+
*/
|
|
1256
|
+
function mergeLefthookCommands(existing, requiredCommands) {
|
|
1257
|
+
if (isToolingIgnored(existing)) return {
|
|
1258
|
+
content: existing,
|
|
1259
|
+
changed: false
|
|
1260
|
+
};
|
|
1261
|
+
try {
|
|
1262
|
+
const doc = parseDocument(existing);
|
|
1263
|
+
let changed = false;
|
|
1264
|
+
if (!doc.hasIn(["pre-commit", "commands"])) {
|
|
1265
|
+
doc.setIn(["pre-commit", "commands"], requiredCommands);
|
|
1266
|
+
return {
|
|
1267
|
+
content: doc.toString(),
|
|
1268
|
+
changed: true
|
|
1269
|
+
};
|
|
1270
|
+
}
|
|
1271
|
+
const commands = doc.getIn(["pre-commit", "commands"]);
|
|
1272
|
+
if (!isMap(commands)) return {
|
|
1273
|
+
content: existing,
|
|
1274
|
+
changed: false
|
|
1275
|
+
};
|
|
1276
|
+
for (const [name, config] of Object.entries(requiredCommands)) if (!commands.has(name)) {
|
|
1277
|
+
commands.set(name, config);
|
|
1278
|
+
changed = true;
|
|
1279
|
+
}
|
|
1280
|
+
return {
|
|
1281
|
+
content: changed ? doc.toString() : existing,
|
|
1282
|
+
changed
|
|
1283
|
+
};
|
|
1284
|
+
} catch {
|
|
1285
|
+
return {
|
|
1286
|
+
content: existing,
|
|
1287
|
+
changed: false
|
|
1288
|
+
};
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
/**
|
|
1292
|
+
* Ensure required steps exist in a workflow job's steps array.
|
|
1293
|
+
* Only adds missing steps at the end — never modifies existing ones.
|
|
1294
|
+
* Returns unchanged content if the file has an opt-out comment or can't be parsed.
|
|
1295
|
+
*/
|
|
1296
|
+
function mergeWorkflowSteps(existing, jobName, requiredSteps) {
|
|
1297
|
+
if (isToolingIgnored(existing)) return {
|
|
1298
|
+
content: existing,
|
|
1299
|
+
changed: false
|
|
1300
|
+
};
|
|
1301
|
+
try {
|
|
1302
|
+
const doc = parseDocument(existing);
|
|
1303
|
+
const steps = doc.getIn([
|
|
1304
|
+
"jobs",
|
|
1305
|
+
jobName,
|
|
1306
|
+
"steps"
|
|
1307
|
+
]);
|
|
1308
|
+
if (!isSeq(steps)) return {
|
|
1309
|
+
content: existing,
|
|
1310
|
+
changed: false
|
|
1311
|
+
};
|
|
1312
|
+
let changed = false;
|
|
1313
|
+
for (const { match, step } of requiredSteps) if (!steps.items.some((item) => {
|
|
1314
|
+
if (!isMap(item)) return false;
|
|
1315
|
+
if (match.run) {
|
|
1316
|
+
const run = item.get("run");
|
|
1317
|
+
return typeof run === "string" && run.includes(match.run);
|
|
1318
|
+
}
|
|
1319
|
+
if (match.uses) {
|
|
1320
|
+
const uses = item.get("uses");
|
|
1321
|
+
return typeof uses === "string" && uses.startsWith(match.uses);
|
|
1322
|
+
}
|
|
1323
|
+
return false;
|
|
1324
|
+
})) {
|
|
1325
|
+
steps.add(doc.createNode(step));
|
|
1326
|
+
changed = true;
|
|
1327
|
+
}
|
|
1328
|
+
return {
|
|
1329
|
+
content: changed ? doc.toString() : existing,
|
|
1330
|
+
changed
|
|
1331
|
+
};
|
|
1332
|
+
} catch {
|
|
1333
|
+
return {
|
|
1334
|
+
content: existing,
|
|
1335
|
+
changed: false
|
|
1336
|
+
};
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
//#endregion
|
|
1231
1340
|
//#region src/generators/ci.ts
|
|
1232
1341
|
function hasEnginesNode$1(ctx) {
|
|
1233
1342
|
const raw = ctx.read("package.json");
|
|
@@ -1262,23 +1371,62 @@ jobs:
|
|
|
1262
1371
|
- run: pnpm exec tooling repo:check
|
|
1263
1372
|
`;
|
|
1264
1373
|
}
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1374
|
+
function requiredCheckSteps(isMonorepo, nodeVersionYaml) {
|
|
1375
|
+
const buildCmd = isMonorepo ? "pnpm -r build" : "pnpm build";
|
|
1376
|
+
const testCmd = isMonorepo ? "pnpm -r test" : "pnpm test";
|
|
1377
|
+
const typecheckCmd = isMonorepo ? "pnpm -r --parallel run typecheck" : "pnpm typecheck";
|
|
1378
|
+
return [
|
|
1379
|
+
{
|
|
1380
|
+
match: { uses: "actions/checkout" },
|
|
1381
|
+
step: { uses: "actions/checkout@v4" }
|
|
1382
|
+
},
|
|
1383
|
+
{
|
|
1384
|
+
match: { uses: "pnpm/action-setup" },
|
|
1385
|
+
step: { uses: "pnpm/action-setup@v4" }
|
|
1386
|
+
},
|
|
1387
|
+
{
|
|
1388
|
+
match: { uses: "actions/setup-node" },
|
|
1389
|
+
step: {
|
|
1390
|
+
uses: "actions/setup-node@v4",
|
|
1391
|
+
with: {
|
|
1392
|
+
...nodeVersionYaml.startsWith("node-version-file") ? { "node-version-file": "package.json" } : { "node-version": "24" },
|
|
1393
|
+
cache: "pnpm"
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
},
|
|
1397
|
+
{
|
|
1398
|
+
match: { run: "pnpm install" },
|
|
1399
|
+
step: { run: "pnpm install --frozen-lockfile" }
|
|
1400
|
+
},
|
|
1401
|
+
{
|
|
1402
|
+
match: { run: "typecheck" },
|
|
1403
|
+
step: { run: typecheckCmd }
|
|
1404
|
+
},
|
|
1405
|
+
{
|
|
1406
|
+
match: { run: "lint" },
|
|
1407
|
+
step: { run: "pnpm lint" }
|
|
1408
|
+
},
|
|
1409
|
+
{
|
|
1410
|
+
match: { run: "build" },
|
|
1411
|
+
step: { run: buildCmd }
|
|
1412
|
+
},
|
|
1413
|
+
{
|
|
1414
|
+
match: { run: "test" },
|
|
1415
|
+
step: { run: testCmd }
|
|
1416
|
+
},
|
|
1417
|
+
{
|
|
1418
|
+
match: { run: "format" },
|
|
1419
|
+
step: { run: "pnpm format --check" }
|
|
1420
|
+
},
|
|
1421
|
+
{
|
|
1422
|
+
match: { run: "knip" },
|
|
1423
|
+
step: { run: "pnpm knip" }
|
|
1424
|
+
},
|
|
1425
|
+
{
|
|
1426
|
+
match: { run: "repo:check" },
|
|
1427
|
+
step: { run: "pnpm exec tooling repo:check" }
|
|
1428
|
+
}
|
|
1429
|
+
];
|
|
1282
1430
|
}
|
|
1283
1431
|
async function generateCi(ctx) {
|
|
1284
1432
|
if (ctx.config.ci === "none") return {
|
|
@@ -1293,14 +1441,14 @@ async function generateCi(ctx) {
|
|
|
1293
1441
|
const content = ciWorkflow(isMonorepo, nodeVersionYaml, !isGitHub);
|
|
1294
1442
|
if (ctx.exists(filePath)) {
|
|
1295
1443
|
const existing = ctx.read(filePath);
|
|
1296
|
-
if (existing
|
|
1297
|
-
const
|
|
1298
|
-
if (
|
|
1299
|
-
ctx.write(filePath,
|
|
1444
|
+
if (existing) {
|
|
1445
|
+
const merged = mergeWorkflowSteps(existing, "check", requiredCheckSteps(isMonorepo, nodeVersionYaml));
|
|
1446
|
+
if (merged.changed) {
|
|
1447
|
+
ctx.write(filePath, merged.content);
|
|
1300
1448
|
return {
|
|
1301
1449
|
filePath,
|
|
1302
1450
|
action: "updated",
|
|
1303
|
-
description: "Added
|
|
1451
|
+
description: "Added missing steps to CI workflow"
|
|
1304
1452
|
};
|
|
1305
1453
|
}
|
|
1306
1454
|
}
|
|
@@ -1706,7 +1854,7 @@ async function generateReleaseIt(ctx) {
|
|
|
1706
1854
|
const content = JSON.stringify(buildConfig$2(ctx.config.ci, ctx.config.structure === "monorepo"), null, 2) + "\n";
|
|
1707
1855
|
const existing = ctx.read(filePath);
|
|
1708
1856
|
if (existing) {
|
|
1709
|
-
if (existing
|
|
1857
|
+
if (contentEqual(filePath, existing, content)) return {
|
|
1710
1858
|
filePath,
|
|
1711
1859
|
action: "skipped",
|
|
1712
1860
|
description: "Already configured"
|
|
@@ -1893,6 +2041,62 @@ ${commonSteps(nodeVersionYaml)}
|
|
|
1893
2041
|
run: pnpm exec tooling release:changesets
|
|
1894
2042
|
`;
|
|
1895
2043
|
}
|
|
2044
|
+
function requiredReleaseSteps(strategy, nodeVersionYaml) {
|
|
2045
|
+
const isNodeVersionFile = nodeVersionYaml.startsWith("node-version-file");
|
|
2046
|
+
const steps = [
|
|
2047
|
+
{
|
|
2048
|
+
match: { uses: "actions/checkout" },
|
|
2049
|
+
step: {
|
|
2050
|
+
uses: "actions/checkout@v4",
|
|
2051
|
+
with: { "fetch-depth": 0 }
|
|
2052
|
+
}
|
|
2053
|
+
},
|
|
2054
|
+
{
|
|
2055
|
+
match: { uses: "pnpm/action-setup" },
|
|
2056
|
+
step: { uses: "pnpm/action-setup@v4" }
|
|
2057
|
+
},
|
|
2058
|
+
{
|
|
2059
|
+
match: { uses: "actions/setup-node" },
|
|
2060
|
+
step: {
|
|
2061
|
+
uses: "actions/setup-node@v4",
|
|
2062
|
+
with: {
|
|
2063
|
+
...isNodeVersionFile ? { "node-version-file": "package.json" } : { "node-version": "24" },
|
|
2064
|
+
cache: "pnpm",
|
|
2065
|
+
"registry-url": "https://registry.npmjs.org"
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
},
|
|
2069
|
+
{
|
|
2070
|
+
match: { run: "pnpm install" },
|
|
2071
|
+
step: { run: "pnpm install --frozen-lockfile" }
|
|
2072
|
+
},
|
|
2073
|
+
{
|
|
2074
|
+
match: { run: "build" },
|
|
2075
|
+
step: { run: "pnpm build" }
|
|
2076
|
+
}
|
|
2077
|
+
];
|
|
2078
|
+
switch (strategy) {
|
|
2079
|
+
case "release-it":
|
|
2080
|
+
steps.push({
|
|
2081
|
+
match: { run: "release-it" },
|
|
2082
|
+
step: { run: "pnpm release-it --ci" }
|
|
2083
|
+
});
|
|
2084
|
+
break;
|
|
2085
|
+
case "commit-and-tag-version":
|
|
2086
|
+
steps.push({
|
|
2087
|
+
match: { run: "commit-and-tag-version" },
|
|
2088
|
+
step: { run: "pnpm exec commit-and-tag-version" }
|
|
2089
|
+
});
|
|
2090
|
+
break;
|
|
2091
|
+
case "changesets":
|
|
2092
|
+
steps.push({
|
|
2093
|
+
match: { run: "changeset" },
|
|
2094
|
+
step: { run: "pnpm exec tooling release:changesets" }
|
|
2095
|
+
});
|
|
2096
|
+
break;
|
|
2097
|
+
}
|
|
2098
|
+
return steps;
|
|
2099
|
+
}
|
|
1896
2100
|
function buildWorkflow(strategy, ci, nodeVersionYaml) {
|
|
1897
2101
|
switch (strategy) {
|
|
1898
2102
|
case "release-it": return releaseItWorkflow(ci, nodeVersionYaml);
|
|
@@ -1917,11 +2121,25 @@ async function generateReleaseCi(ctx) {
|
|
|
1917
2121
|
action: "skipped",
|
|
1918
2122
|
description: "Release CI workflow not applicable"
|
|
1919
2123
|
};
|
|
1920
|
-
if (ctx.exists(workflowPath))
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
2124
|
+
if (ctx.exists(workflowPath)) {
|
|
2125
|
+
const existing = ctx.read(workflowPath);
|
|
2126
|
+
if (existing) {
|
|
2127
|
+
const merged = mergeWorkflowSteps(existing, "release", requiredReleaseSteps(ctx.config.releaseStrategy, nodeVersionYaml));
|
|
2128
|
+
if (merged.changed) {
|
|
2129
|
+
ctx.write(workflowPath, merged.content);
|
|
2130
|
+
return {
|
|
2131
|
+
filePath: workflowPath,
|
|
2132
|
+
action: "updated",
|
|
2133
|
+
description: "Added missing steps to release workflow"
|
|
2134
|
+
};
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
return {
|
|
2138
|
+
filePath: workflowPath,
|
|
2139
|
+
action: "skipped",
|
|
2140
|
+
description: "Release workflow already up to date"
|
|
2141
|
+
};
|
|
2142
|
+
}
|
|
1925
2143
|
ctx.write(workflowPath, content);
|
|
1926
2144
|
return {
|
|
1927
2145
|
filePath: workflowPath,
|
|
@@ -1931,6 +2149,15 @@ async function generateReleaseCi(ctx) {
|
|
|
1931
2149
|
}
|
|
1932
2150
|
//#endregion
|
|
1933
2151
|
//#region src/generators/lefthook.ts
|
|
2152
|
+
function requiredCommands(formatter) {
|
|
2153
|
+
return {
|
|
2154
|
+
lint: { run: "pnpm exec oxlint {staged_files}" },
|
|
2155
|
+
format: {
|
|
2156
|
+
run: formatter === "prettier" ? "pnpm exec prettier --write {staged_files}" : "pnpm exec oxfmt --no-error-on-unmatched-pattern {staged_files}",
|
|
2157
|
+
stage_fixed: true
|
|
2158
|
+
}
|
|
2159
|
+
};
|
|
2160
|
+
}
|
|
1934
2161
|
function buildConfig(formatter) {
|
|
1935
2162
|
return [
|
|
1936
2163
|
"pre-commit:",
|
|
@@ -2042,10 +2269,25 @@ async function generateLefthook(ctx) {
|
|
|
2042
2269
|
cleanPackageJson(ctx, results);
|
|
2043
2270
|
const existingPath = LEFTHOOK_CONFIG_PATHS.find((p) => ctx.exists(p));
|
|
2044
2271
|
if (existingPath) {
|
|
2045
|
-
|
|
2272
|
+
const existing = ctx.read(existingPath);
|
|
2273
|
+
if (existing) {
|
|
2274
|
+
const merged = mergeLefthookCommands(existing, requiredCommands(ctx.config.formatter));
|
|
2275
|
+
if (merged.changed) {
|
|
2276
|
+
ctx.write(existingPath, merged.content);
|
|
2277
|
+
results.push({
|
|
2278
|
+
filePath: existingPath,
|
|
2279
|
+
action: "updated",
|
|
2280
|
+
description: "Added missing pre-commit commands"
|
|
2281
|
+
});
|
|
2282
|
+
} else results.push({
|
|
2283
|
+
filePath: existingPath,
|
|
2284
|
+
action: "skipped",
|
|
2285
|
+
description: "Lefthook config already up to date"
|
|
2286
|
+
});
|
|
2287
|
+
} else results.push({
|
|
2046
2288
|
filePath: existingPath,
|
|
2047
2289
|
action: "skipped",
|
|
2048
|
-
description: "
|
|
2290
|
+
description: "Could not read existing lefthook config"
|
|
2049
2291
|
});
|
|
2050
2292
|
return results;
|
|
2051
2293
|
}
|
|
@@ -2183,7 +2425,7 @@ async function generateVscodeSettings(ctx) {
|
|
|
2183
2425
|
return results;
|
|
2184
2426
|
}
|
|
2185
2427
|
const existingSchema = ctx.read(SCHEMA_LOCAL_PATH);
|
|
2186
|
-
if (existingSchema
|
|
2428
|
+
if (existingSchema !== void 0 && contentEqual(SCHEMA_LOCAL_PATH, existingSchema, schemaContent)) results.push({
|
|
2187
2429
|
filePath: SCHEMA_LOCAL_PATH,
|
|
2188
2430
|
action: "skipped",
|
|
2189
2431
|
description: "Schema already up to date"
|
|
@@ -2279,7 +2521,7 @@ function saveToolingConfig(ctx, config) {
|
|
|
2279
2521
|
};
|
|
2280
2522
|
const content = JSON.stringify(saved, null, 2) + "\n";
|
|
2281
2523
|
const existing = ctx.exists(CONFIG_FILE) ? ctx.read(CONFIG_FILE) : void 0;
|
|
2282
|
-
if (existing
|
|
2524
|
+
if (existing !== void 0 && contentEqual(CONFIG_FILE, existing, content)) return {
|
|
2283
2525
|
filePath: CONFIG_FILE,
|
|
2284
2526
|
action: "skipped",
|
|
2285
2527
|
description: "Already up to date"
|
|
@@ -2482,7 +2724,16 @@ async function runCheck(targetDir) {
|
|
|
2482
2724
|
return 1;
|
|
2483
2725
|
}
|
|
2484
2726
|
const { ctx, pendingWrites } = createDryRunContext(mergeWithSavedConfig(buildDefaultConfig(targetDir, {}), saved));
|
|
2485
|
-
const actionable = (await runGenerators(ctx)).filter((r) =>
|
|
2727
|
+
const actionable = (await runGenerators(ctx)).filter((r) => {
|
|
2728
|
+
if (r.action !== "created" && r.action !== "updated") return false;
|
|
2729
|
+
const newContent = pendingWrites.get(r.filePath);
|
|
2730
|
+
if (newContent && r.action === "updated") {
|
|
2731
|
+
const existingPath = path.join(targetDir, r.filePath);
|
|
2732
|
+
const existing = existsSync(existingPath) ? readFileSync(existingPath, "utf-8") : void 0;
|
|
2733
|
+
if (existing && contentEqual(r.filePath, existing, newContent)) return false;
|
|
2734
|
+
}
|
|
2735
|
+
return true;
|
|
2736
|
+
});
|
|
2486
2737
|
if (actionable.length === 0) {
|
|
2487
2738
|
p.log.success("Repository is up to date.");
|
|
2488
2739
|
return 0;
|
|
@@ -3207,7 +3458,7 @@ function mergeGitHub(dryRun) {
|
|
|
3207
3458
|
const main = defineCommand({
|
|
3208
3459
|
meta: {
|
|
3209
3460
|
name: "tooling",
|
|
3210
|
-
version: "0.
|
|
3461
|
+
version: "0.10.0",
|
|
3211
3462
|
description: "Bootstrap and maintain standardized TypeScript project tooling"
|
|
3212
3463
|
},
|
|
3213
3464
|
subCommands: {
|
|
@@ -3220,7 +3471,7 @@ const main = defineCommand({
|
|
|
3220
3471
|
"release:merge": releaseMergeCommand
|
|
3221
3472
|
}
|
|
3222
3473
|
});
|
|
3223
|
-
console.log(`@bensandee/tooling v0.
|
|
3474
|
+
console.log(`@bensandee/tooling v0.10.0`);
|
|
3224
3475
|
runMain(main);
|
|
3225
3476
|
//#endregion
|
|
3226
3477
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bensandee/tooling",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "CLI tool to bootstrap and maintain standardized TypeScript project tooling",
|
|
5
5
|
"bin": {
|
|
6
6
|
"tooling": "./dist/bin.mjs"
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"citty": "^0.2.1",
|
|
26
26
|
"json5": "^2.2.3",
|
|
27
27
|
"jsonc-parser": "^3.3.1",
|
|
28
|
+
"yaml": "^2.8.2",
|
|
28
29
|
"zod": "^4.3.6",
|
|
29
30
|
"@bensandee/common": "0.1.0"
|
|
30
31
|
},
|