@bensandee/tooling 0.5.0 → 0.6.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 +92 -22
- package/package.json +2 -2
package/dist/bin.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { defineCommand, runMain } from "citty";
|
|
3
3
|
import * as p from "@clack/prompts";
|
|
4
|
+
import { execSync } from "node:child_process";
|
|
4
5
|
import path from "node:path";
|
|
5
6
|
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
6
7
|
import { parse } from "jsonc-parser";
|
|
7
8
|
import { z } from "zod";
|
|
8
|
-
import { execSync } from "node:child_process";
|
|
9
9
|
|
|
10
10
|
//#region src/types.ts
|
|
11
11
|
/** Default CI platform when not explicitly chosen. */
|
|
@@ -41,7 +41,7 @@ const TsconfigSchema = z.object({
|
|
|
41
41
|
include: z.array(z.string()).optional(),
|
|
42
42
|
exclude: z.array(z.string()).optional(),
|
|
43
43
|
files: z.array(z.string()).optional(),
|
|
44
|
-
references: z.array(z.object({ path: z.string() })).optional(),
|
|
44
|
+
references: z.array(z.object({ path: z.string() }).passthrough()).optional(),
|
|
45
45
|
compilerOptions: z.record(z.string(), z.unknown()).optional()
|
|
46
46
|
}).loose();
|
|
47
47
|
const RenovateSchema = z.object({
|
|
@@ -656,9 +656,16 @@ function generateMigratePrompt(results, config, detected) {
|
|
|
656
656
|
sections.push("");
|
|
657
657
|
for (const r of archived) sections.push(`- \`${r.filePath}\` → \`.tooling-archived/${r.filePath}\``);
|
|
658
658
|
sections.push("");
|
|
659
|
-
sections.push("
|
|
660
|
-
sections.push("
|
|
661
|
-
sections.push("
|
|
659
|
+
sections.push("For each archived file, **diff the old version against the new one** and look for features, categories, or modules that were enabled in the original but are missing from the replacement. Focus on broad capability gaps rather than individual rule strictness (in general, being stricter is fine). Examples of what to look for:");
|
|
660
|
+
sections.push("");
|
|
661
|
+
sections.push("- **Lint configs**: enabled plugin categories (e.g. `jsx-a11y`, `import`, `react`, `nextjs`), custom `plugins` or `overrides`, file-scoped rule blocks");
|
|
662
|
+
sections.push("- **TypeScript configs**: compiler features like `jsx`, `paths`, `baseUrl`, or `references` that affect build behavior");
|
|
663
|
+
sections.push("- **Other configs**: feature flags, custom presets, or integrations that go beyond the default template");
|
|
664
|
+
sections.push("");
|
|
665
|
+
sections.push("If the old config had capabilities the new one lacks, port them into the new file. Then:");
|
|
666
|
+
sections.push("");
|
|
667
|
+
sections.push("1. If the project previously used `husky` and `lint-staged`, remove them from `devDependencies`");
|
|
668
|
+
sections.push("2. Delete the `.tooling-archived/` directory when migration is complete");
|
|
662
669
|
sections.push("");
|
|
663
670
|
}
|
|
664
671
|
const oxlintWasSkipped = results.find((r) => r.filePath === "oxlint.config.ts")?.action === "skipped";
|
|
@@ -1731,6 +1738,15 @@ function buildConfig(formatter) {
|
|
|
1731
1738
|
].join("\n");
|
|
1732
1739
|
}
|
|
1733
1740
|
const ARCHIVE_DIR = ".tooling-archived";
|
|
1741
|
+
/** Common client-side git hooks that husky may have configured. */
|
|
1742
|
+
const HUSKY_HOOK_NAMES = [
|
|
1743
|
+
"pre-commit",
|
|
1744
|
+
"commit-msg",
|
|
1745
|
+
"pre-push",
|
|
1746
|
+
"post-merge",
|
|
1747
|
+
"post-checkout",
|
|
1748
|
+
"prepare-commit-msg"
|
|
1749
|
+
];
|
|
1734
1750
|
/** All known lint-staged config file locations to archive. */
|
|
1735
1751
|
const LINT_STAGED_CONFIG_PATHS = [
|
|
1736
1752
|
"lint-staged.config.mjs",
|
|
@@ -1745,21 +1761,28 @@ const LINT_STAGED_CONFIG_PATHS = [
|
|
|
1745
1761
|
];
|
|
1746
1762
|
/** All known lefthook config file locations, in priority order. */
|
|
1747
1763
|
const LEFTHOOK_CONFIG_PATHS = ["lefthook.yml", ".lefthook.yml"];
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
const
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1764
|
+
/** Archive all husky hook files found in .husky/ */
|
|
1765
|
+
function archiveHuskyHooks(ctx, results) {
|
|
1766
|
+
let found = false;
|
|
1767
|
+
for (const hook of HUSKY_HOOK_NAMES) {
|
|
1768
|
+
const huskyPath = `.husky/${hook}`;
|
|
1769
|
+
const existing = ctx.read(huskyPath);
|
|
1770
|
+
if (existing !== void 0) {
|
|
1771
|
+
ctx.write(`${ARCHIVE_DIR}/${huskyPath}`, existing);
|
|
1772
|
+
ctx.remove(huskyPath);
|
|
1773
|
+
results.push({
|
|
1774
|
+
filePath: huskyPath,
|
|
1775
|
+
action: "archived",
|
|
1776
|
+
description: `Moved to ${ARCHIVE_DIR}/${huskyPath}`
|
|
1777
|
+
});
|
|
1778
|
+
found = true;
|
|
1779
|
+
}
|
|
1762
1780
|
}
|
|
1781
|
+
return found;
|
|
1782
|
+
}
|
|
1783
|
+
/** Archive all lint-staged config files. */
|
|
1784
|
+
function archiveLintStagedConfigs(ctx, results) {
|
|
1785
|
+
let found = false;
|
|
1763
1786
|
for (const lsPath of LINT_STAGED_CONFIG_PATHS) {
|
|
1764
1787
|
const existing = ctx.read(lsPath);
|
|
1765
1788
|
if (existing !== void 0) {
|
|
@@ -1770,8 +1793,47 @@ async function generateLefthook(ctx) {
|
|
|
1770
1793
|
action: "archived",
|
|
1771
1794
|
description: `Moved to ${ARCHIVE_DIR}/${lsPath}`
|
|
1772
1795
|
});
|
|
1796
|
+
found = true;
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
return found;
|
|
1800
|
+
}
|
|
1801
|
+
/** Remove husky/lint-staged from package.json devDependencies and fix prepare script. */
|
|
1802
|
+
function cleanPackageJson(ctx, results) {
|
|
1803
|
+
const raw = ctx.read("package.json");
|
|
1804
|
+
if (!raw) return;
|
|
1805
|
+
const pkg = parsePackageJson(raw);
|
|
1806
|
+
if (!pkg) return;
|
|
1807
|
+
const changes = [];
|
|
1808
|
+
if (pkg.devDependencies) {
|
|
1809
|
+
if ("husky" in pkg.devDependencies) {
|
|
1810
|
+
delete pkg.devDependencies["husky"];
|
|
1811
|
+
changes.push("removed devDependency: husky");
|
|
1812
|
+
}
|
|
1813
|
+
if ("lint-staged" in pkg.devDependencies) {
|
|
1814
|
+
delete pkg.devDependencies["lint-staged"];
|
|
1815
|
+
changes.push("removed devDependency: lint-staged");
|
|
1773
1816
|
}
|
|
1774
1817
|
}
|
|
1818
|
+
if (pkg.scripts?.["prepare"] && /\bhusky\b/.test(pkg.scripts["prepare"])) {
|
|
1819
|
+
pkg.scripts["prepare"] = "lefthook install";
|
|
1820
|
+
changes.push("replaced prepare script: husky → lefthook install");
|
|
1821
|
+
}
|
|
1822
|
+
if (changes.length === 0) return;
|
|
1823
|
+
ctx.write("package.json", JSON.stringify(pkg, null, 2) + "\n");
|
|
1824
|
+
results.push({
|
|
1825
|
+
filePath: "package.json",
|
|
1826
|
+
action: "updated",
|
|
1827
|
+
description: changes.join(", ")
|
|
1828
|
+
});
|
|
1829
|
+
}
|
|
1830
|
+
async function generateLefthook(ctx) {
|
|
1831
|
+
const filePath = "lefthook.yml";
|
|
1832
|
+
const content = buildConfig(ctx.config.formatter);
|
|
1833
|
+
const results = [];
|
|
1834
|
+
archiveHuskyHooks(ctx, results);
|
|
1835
|
+
archiveLintStagedConfigs(ctx, results);
|
|
1836
|
+
cleanPackageJson(ctx, results);
|
|
1775
1837
|
const existingPath = LEFTHOOK_CONFIG_PATHS.find((p) => ctx.exists(p));
|
|
1776
1838
|
if (existingPath === filePath) {
|
|
1777
1839
|
const existing = ctx.read(filePath);
|
|
@@ -1892,6 +1954,12 @@ async function runInit(config, options = {}) {
|
|
|
1892
1954
|
description: `Original saved to .tooling-archived/${rel}`
|
|
1893
1955
|
});
|
|
1894
1956
|
s.stop("Done!");
|
|
1957
|
+
if (results.some((r) => r.action === "archived" && r.filePath.startsWith(".husky/"))) try {
|
|
1958
|
+
execSync("git config --unset core.hooksPath", {
|
|
1959
|
+
cwd: config.targetDir,
|
|
1960
|
+
stdio: "ignore"
|
|
1961
|
+
});
|
|
1962
|
+
} catch (_error) {}
|
|
1895
1963
|
const created = results.filter((r) => r.action === "created");
|
|
1896
1964
|
const updated = results.filter((r) => r.action === "updated");
|
|
1897
1965
|
const skipped = results.filter((r) => r.action === "skipped");
|
|
@@ -2630,10 +2698,10 @@ function mergeGitHub(dryRun) {
|
|
|
2630
2698
|
|
|
2631
2699
|
//#endregion
|
|
2632
2700
|
//#region src/bin.ts
|
|
2633
|
-
|
|
2701
|
+
const main = defineCommand({
|
|
2634
2702
|
meta: {
|
|
2635
2703
|
name: "tooling",
|
|
2636
|
-
version: "0.
|
|
2704
|
+
version: "0.6.0",
|
|
2637
2705
|
description: "Bootstrap and maintain standardized TypeScript project tooling"
|
|
2638
2706
|
},
|
|
2639
2707
|
subCommands: {
|
|
@@ -2644,7 +2712,9 @@ runMain(defineCommand({
|
|
|
2644
2712
|
"release:create-forgejo-release": createForgejoReleaseCommand,
|
|
2645
2713
|
"release:merge": releaseMergeCommand
|
|
2646
2714
|
}
|
|
2647
|
-
})
|
|
2715
|
+
});
|
|
2716
|
+
console.log(`@bensandee/tooling v0.6.0`);
|
|
2717
|
+
runMain(main);
|
|
2648
2718
|
|
|
2649
2719
|
//#endregion
|
|
2650
2720
|
export { };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bensandee/tooling",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "CLI tool to bootstrap and maintain standardized TypeScript project tooling",
|
|
5
5
|
"bin": {
|
|
6
6
|
"tooling": "./dist/bin.mjs"
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"tsdown": "0.20.3",
|
|
32
32
|
"typescript": "5.9.3",
|
|
33
33
|
"vitest": "4.0.18",
|
|
34
|
-
"@bensandee/config": "0.
|
|
34
|
+
"@bensandee/config": "0.6.0"
|
|
35
35
|
},
|
|
36
36
|
"scripts": {
|
|
37
37
|
"build": "tsdown",
|