@bensandee/tooling 0.2.0 → 0.4.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 +142 -114
- package/package.json +5 -9
- package/dist/bin.d.mts +0 -1
- package/dist/index.d.mts +0 -102
package/dist/bin.mjs
CHANGED
|
@@ -48,18 +48,6 @@ const RenovateSchema = z.object({
|
|
|
48
48
|
$schema: z.string().optional(),
|
|
49
49
|
extends: z.array(z.string()).optional()
|
|
50
50
|
}).loose();
|
|
51
|
-
const KnipWorkspaceConfig = z.object({
|
|
52
|
-
entry: z.array(z.string()).optional(),
|
|
53
|
-
project: z.array(z.string()).optional(),
|
|
54
|
-
ignore: z.array(z.string()).optional()
|
|
55
|
-
});
|
|
56
|
-
const KnipSchema = z.object({
|
|
57
|
-
$schema: z.string().optional(),
|
|
58
|
-
entry: z.array(z.string()).optional(),
|
|
59
|
-
project: z.array(z.string()).optional(),
|
|
60
|
-
ignore: z.array(z.string()).optional(),
|
|
61
|
-
workspaces: z.record(z.string(), KnipWorkspaceConfig).optional()
|
|
62
|
-
}).loose();
|
|
63
51
|
/** Parse a JSONC string as a tsconfig.json. Returns a typed object with `{}` fallback on failure. */
|
|
64
52
|
function parseTsconfig(raw) {
|
|
65
53
|
const result = TsconfigSchema.safeParse(parse(raw));
|
|
@@ -70,11 +58,6 @@ function parseRenovateJson(raw) {
|
|
|
70
58
|
const result = RenovateSchema.safeParse(parse(raw));
|
|
71
59
|
return result.success ? result.data : {};
|
|
72
60
|
}
|
|
73
|
-
/** Parse a JSONC string as a knip.json. Returns a typed object with `{}` fallback on failure. */
|
|
74
|
-
function parseKnipJson(raw) {
|
|
75
|
-
const result = KnipSchema.safeParse(parse(raw));
|
|
76
|
-
return result.success ? result.data : {};
|
|
77
|
-
}
|
|
78
61
|
/** Parse a JSON string as a package.json. Returns `undefined` on failure. */
|
|
79
62
|
function parsePackageJson(raw) {
|
|
80
63
|
try {
|
|
@@ -208,11 +191,11 @@ async function runInitPrompts(targetDir) {
|
|
|
208
191
|
p.cancel("Cancelled.");
|
|
209
192
|
process.exit(0);
|
|
210
193
|
}
|
|
211
|
-
const
|
|
212
|
-
message: "Include @bensandee/
|
|
194
|
+
const useEslintPlugin = await p.confirm({
|
|
195
|
+
message: "Include @bensandee/eslint-plugin?",
|
|
213
196
|
initialValue: true
|
|
214
197
|
});
|
|
215
|
-
if (isCancelled(
|
|
198
|
+
if (isCancelled(useEslintPlugin)) {
|
|
216
199
|
p.cancel("Cancelled.");
|
|
217
200
|
process.exit(0);
|
|
218
201
|
}
|
|
@@ -361,7 +344,7 @@ async function runInitPrompts(targetDir) {
|
|
|
361
344
|
name,
|
|
362
345
|
isNew: !isExisting,
|
|
363
346
|
structure,
|
|
364
|
-
|
|
347
|
+
useEslintPlugin,
|
|
365
348
|
formatter,
|
|
366
349
|
setupVitest,
|
|
367
350
|
ci,
|
|
@@ -380,7 +363,7 @@ function buildDefaultConfig(targetDir, flags) {
|
|
|
380
363
|
name: existingPkg?.name ?? path.basename(targetDir),
|
|
381
364
|
isNew: !detected.hasPackageJson,
|
|
382
365
|
structure: detected.hasPnpmWorkspace ? "monorepo" : "single",
|
|
383
|
-
|
|
366
|
+
useEslintPlugin: flags.eslintPlugin ?? true,
|
|
384
367
|
formatter: detected.legacyConfigs.some((l) => l.tool === "prettier") ? "prettier" : "oxfmt",
|
|
385
368
|
setupVitest: !detected.hasVitestConfig,
|
|
386
369
|
ci: flags.noCi ? "none" : DEFAULT_CI,
|
|
@@ -526,7 +509,7 @@ async function generatePackageJson(ctx) {
|
|
|
526
509
|
if (!isMonorepo) Object.assign(devDeps, PER_PACKAGE_DEV_DEPS);
|
|
527
510
|
devDeps["@bensandee/config"] = isWorkspacePackage(ctx, "@bensandee/config") ? "workspace:*" : "latest";
|
|
528
511
|
devDeps["@bensandee/tooling"] = isWorkspacePackage(ctx, "@bensandee/tooling") ? "workspace:*" : "latest";
|
|
529
|
-
if (ctx.config.
|
|
512
|
+
if (ctx.config.useEslintPlugin) devDeps["@bensandee/eslint-plugin"] = isWorkspacePackage(ctx, "@bensandee/eslint-plugin") ? "workspace:*" : "latest";
|
|
530
513
|
if (ctx.config.formatter === "oxfmt") devDeps["oxfmt"] = "0.35.0";
|
|
531
514
|
if (ctx.config.formatter === "prettier") devDeps["prettier"] = "3.8.1";
|
|
532
515
|
addReleaseDeps(devDeps, ctx.config);
|
|
@@ -709,7 +692,7 @@ function generateMigratePrompt(results, config, detected) {
|
|
|
709
692
|
sections.push("");
|
|
710
693
|
sections.push("- **Lint errors**: fix the code rather than adding disable comments or rule exceptions");
|
|
711
694
|
sections.push("- **Test failures**: update the test or fix the underlying bug rather than skipping or deleting the test");
|
|
712
|
-
sections.push("- **Knip findings**: remove genuinely unused code/exports/dependencies rather than adding ignores to `knip.
|
|
695
|
+
sections.push("- **Knip findings**: remove genuinely unused code/exports/dependencies rather than adding ignores to `knip.config.ts`");
|
|
713
696
|
sections.push("- **Type errors**: add proper types rather than using `any` or `@ts-expect-error`");
|
|
714
697
|
sections.push("");
|
|
715
698
|
sections.push("Only suppress an issue if there is a clear, documented reason why the fix is not feasible (e.g. a third-party type mismatch). Leave a comment explaining why.");
|
|
@@ -941,7 +924,7 @@ export default defineConfig({
|
|
|
941
924
|
`;
|
|
942
925
|
async function generateOxlint(ctx) {
|
|
943
926
|
const filePath = "oxlint.config.ts";
|
|
944
|
-
const content = ctx.config.
|
|
927
|
+
const content = ctx.config.useEslintPlugin ? CONFIG_WITH_LINT_RULES : CONFIG_PRESET_ONLY;
|
|
945
928
|
const existing = ctx.read(filePath);
|
|
946
929
|
if (existing) {
|
|
947
930
|
if (existing === content) return {
|
|
@@ -1051,12 +1034,19 @@ const STANDARD_ENTRIES = [
|
|
|
1051
1034
|
"!.env.example",
|
|
1052
1035
|
".tooling-migrate.md"
|
|
1053
1036
|
];
|
|
1037
|
+
/** Normalize a gitignore entry for comparison: strip leading `/` and trailing `/`. */
|
|
1038
|
+
function normalizeEntry(entry) {
|
|
1039
|
+
let s = entry.trim();
|
|
1040
|
+
if (s.startsWith("/")) s = s.slice(1);
|
|
1041
|
+
if (s.endsWith("/")) s = s.slice(0, -1);
|
|
1042
|
+
return s;
|
|
1043
|
+
}
|
|
1054
1044
|
async function generateGitignore(ctx) {
|
|
1055
1045
|
const filePath = ".gitignore";
|
|
1056
1046
|
const existing = ctx.read(filePath);
|
|
1057
1047
|
if (existing) {
|
|
1058
|
-
const
|
|
1059
|
-
const missing = STANDARD_ENTRIES.filter((entry) => !
|
|
1048
|
+
const existingNormalized = new Set(existing.split("\n").map(normalizeEntry).filter((line) => line.length > 0));
|
|
1049
|
+
const missing = STANDARD_ENTRIES.filter((entry) => !existingNormalized.has(normalizeEntry(entry)));
|
|
1060
1050
|
if (missing.length === 0) return {
|
|
1061
1051
|
filePath,
|
|
1062
1052
|
action: "skipped",
|
|
@@ -1138,71 +1128,71 @@ async function generateCi(ctx) {
|
|
|
1138
1128
|
|
|
1139
1129
|
//#endregion
|
|
1140
1130
|
//#region src/generators/knip.ts
|
|
1141
|
-
const KNIP_CONFIG_SINGLE = {
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1131
|
+
const KNIP_CONFIG_SINGLE = `import type { KnipConfig } from "knip";
|
|
1132
|
+
|
|
1133
|
+
export default {
|
|
1134
|
+
entry: ["src/index.ts"],
|
|
1135
|
+
project: ["src/**/*.ts"],
|
|
1136
|
+
ignore: ["dist/**"],
|
|
1137
|
+
} satisfies KnipConfig;
|
|
1138
|
+
`;
|
|
1139
|
+
const KNIP_CONFIG_MONOREPO = `import type { KnipConfig } from "knip";
|
|
1140
|
+
|
|
1141
|
+
export default {
|
|
1142
|
+
workspaces: {
|
|
1143
|
+
".": {
|
|
1144
|
+
entry: [],
|
|
1145
|
+
project: [],
|
|
1146
|
+
},
|
|
1147
|
+
"packages/*": {
|
|
1148
|
+
entry: ["src/index.ts", "src/bin.ts"],
|
|
1149
|
+
project: ["src/**/*.ts"],
|
|
1150
|
+
ignore: ["dist/**"],
|
|
1151
|
+
},
|
|
1152
|
+
},
|
|
1153
|
+
} satisfies KnipConfig;
|
|
1154
|
+
`;
|
|
1155
|
+
/** All known knip config file locations, in priority order. */
|
|
1156
|
+
const KNIP_CONFIG_PATHS = [
|
|
1157
|
+
"knip.config.ts",
|
|
1158
|
+
"knip.config.mts",
|
|
1159
|
+
"knip.json",
|
|
1160
|
+
"knip.jsonc",
|
|
1161
|
+
"knip.ts",
|
|
1162
|
+
"knip.mts"
|
|
1163
|
+
];
|
|
1161
1164
|
async function generateKnip(ctx) {
|
|
1162
|
-
const filePath = "knip.
|
|
1163
|
-
const existing = ctx.read(filePath);
|
|
1165
|
+
const filePath = "knip.config.ts";
|
|
1164
1166
|
const isMonorepo = ctx.config.structure === "monorepo";
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
}
|
|
1172
|
-
if (!isMonorepo) {
|
|
1173
|
-
if (!parsed.entry) {
|
|
1174
|
-
parsed.entry = KNIP_CONFIG_SINGLE.entry;
|
|
1175
|
-
changes.push("added entry patterns");
|
|
1176
|
-
}
|
|
1177
|
-
if (!parsed.project) {
|
|
1178
|
-
parsed.project = KNIP_CONFIG_SINGLE.project;
|
|
1179
|
-
changes.push("added project patterns");
|
|
1180
|
-
}
|
|
1181
|
-
}
|
|
1182
|
-
if (changes.length === 0) return {
|
|
1183
|
-
filePath,
|
|
1184
|
-
action: "skipped",
|
|
1185
|
-
description: "Already configured"
|
|
1186
|
-
};
|
|
1187
|
-
ctx.write(filePath, JSON.stringify(parsed, null, 2) + "\n");
|
|
1188
|
-
return {
|
|
1189
|
-
filePath,
|
|
1190
|
-
action: "updated",
|
|
1191
|
-
description: changes.join(", ")
|
|
1192
|
-
};
|
|
1193
|
-
}
|
|
1167
|
+
const existingPath = KNIP_CONFIG_PATHS.find((p) => ctx.exists(p));
|
|
1168
|
+
if (existingPath) return {
|
|
1169
|
+
filePath: existingPath,
|
|
1170
|
+
action: "skipped",
|
|
1171
|
+
description: existingPath === filePath ? "Already configured" : `Existing config found at ${existingPath}`
|
|
1172
|
+
};
|
|
1194
1173
|
const config = isMonorepo ? KNIP_CONFIG_MONOREPO : KNIP_CONFIG_SINGLE;
|
|
1195
|
-
ctx.write(filePath,
|
|
1174
|
+
ctx.write(filePath, config);
|
|
1196
1175
|
return {
|
|
1197
1176
|
filePath,
|
|
1198
1177
|
action: "created",
|
|
1199
|
-
description: "Generated knip.
|
|
1178
|
+
description: "Generated knip.config.ts for dead code analysis"
|
|
1200
1179
|
};
|
|
1201
1180
|
}
|
|
1202
1181
|
|
|
1203
1182
|
//#endregion
|
|
1204
1183
|
//#region src/generators/renovate.ts
|
|
1205
|
-
const SHARED_PRESET = "
|
|
1184
|
+
const SHARED_PRESET = "local>bensandee/tooling";
|
|
1185
|
+
/** Deprecated npm-based preset to migrate away from. */
|
|
1186
|
+
const LEGACY_PRESET = "@bensandee/config";
|
|
1187
|
+
/** All known renovate config file locations, in priority order. */
|
|
1188
|
+
const RENOVATE_CONFIG_PATHS = [
|
|
1189
|
+
"renovate.json",
|
|
1190
|
+
"renovate.json5",
|
|
1191
|
+
".renovaterc",
|
|
1192
|
+
".renovaterc.json",
|
|
1193
|
+
".github/renovate.json",
|
|
1194
|
+
".github/renovate.json5"
|
|
1195
|
+
];
|
|
1206
1196
|
async function generateRenovate(ctx) {
|
|
1207
1197
|
const filePath = "renovate.json";
|
|
1208
1198
|
if (!ctx.config.setupRenovate) return {
|
|
@@ -1210,26 +1200,45 @@ async function generateRenovate(ctx) {
|
|
|
1210
1200
|
action: "skipped",
|
|
1211
1201
|
description: "Renovate not requested"
|
|
1212
1202
|
};
|
|
1213
|
-
const
|
|
1214
|
-
if (
|
|
1215
|
-
const
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
existingExtends.
|
|
1219
|
-
|
|
1220
|
-
|
|
1203
|
+
const existingPath = RENOVATE_CONFIG_PATHS.find((p) => ctx.exists(p));
|
|
1204
|
+
if (existingPath === filePath) {
|
|
1205
|
+
const existing = ctx.read(filePath);
|
|
1206
|
+
if (existing) {
|
|
1207
|
+
const parsed = parseRenovateJson(existing);
|
|
1208
|
+
const existingExtends = parsed.extends ?? [];
|
|
1209
|
+
const legacyIndex = existingExtends.indexOf(LEGACY_PRESET);
|
|
1210
|
+
if (legacyIndex !== -1) {
|
|
1211
|
+
existingExtends[legacyIndex] = SHARED_PRESET;
|
|
1212
|
+
parsed.extends = existingExtends;
|
|
1213
|
+
ctx.write(filePath, JSON.stringify(parsed, null, 2) + "\n");
|
|
1214
|
+
return {
|
|
1215
|
+
filePath,
|
|
1216
|
+
action: "updated",
|
|
1217
|
+
description: `Migrated extends: ${LEGACY_PRESET} → ${SHARED_PRESET}`
|
|
1218
|
+
};
|
|
1219
|
+
}
|
|
1220
|
+
if (!existingExtends.includes(SHARED_PRESET)) {
|
|
1221
|
+
existingExtends.unshift(SHARED_PRESET);
|
|
1222
|
+
parsed.extends = existingExtends;
|
|
1223
|
+
ctx.write(filePath, JSON.stringify(parsed, null, 2) + "\n");
|
|
1224
|
+
return {
|
|
1225
|
+
filePath,
|
|
1226
|
+
action: "updated",
|
|
1227
|
+
description: `Added extends: ${SHARED_PRESET}`
|
|
1228
|
+
};
|
|
1229
|
+
}
|
|
1221
1230
|
return {
|
|
1222
1231
|
filePath,
|
|
1223
|
-
action: "
|
|
1224
|
-
description:
|
|
1232
|
+
action: "skipped",
|
|
1233
|
+
description: "Already extends shared config"
|
|
1225
1234
|
};
|
|
1226
1235
|
}
|
|
1227
|
-
return {
|
|
1228
|
-
filePath,
|
|
1229
|
-
action: "skipped",
|
|
1230
|
-
description: "Already extends shared config"
|
|
1231
|
-
};
|
|
1232
1236
|
}
|
|
1237
|
+
if (existingPath) return {
|
|
1238
|
+
filePath: existingPath,
|
|
1239
|
+
action: "skipped",
|
|
1240
|
+
description: `Existing config found at ${existingPath}`
|
|
1241
|
+
};
|
|
1233
1242
|
const config = {
|
|
1234
1243
|
$schema: "https://docs.renovatebot.com/renovate-schema.json",
|
|
1235
1244
|
extends: [SHARED_PRESET]
|
|
@@ -1670,31 +1679,50 @@ async function generateReleaseCi(ctx) {
|
|
|
1670
1679
|
//#endregion
|
|
1671
1680
|
//#region src/generators/lint-staged.ts
|
|
1672
1681
|
function buildConfig(formatter) {
|
|
1673
|
-
return `export default {\n "*": "${formatter === "prettier" ? "prettier --write" : "oxfmt"}",\n};\n`;
|
|
1682
|
+
return `export default {\n "*": "${formatter === "prettier" ? "prettier --write" : "oxfmt --no-error-on-unmatched-pattern"}",\n};\n`;
|
|
1674
1683
|
}
|
|
1675
1684
|
const HUSKY_PRE_COMMIT = "pnpm exec lint-staged\n";
|
|
1685
|
+
/** All known lint-staged config file locations, in priority order. */
|
|
1686
|
+
const LINT_STAGED_CONFIG_PATHS = [
|
|
1687
|
+
"lint-staged.config.mjs",
|
|
1688
|
+
"lint-staged.config.js",
|
|
1689
|
+
"lint-staged.config.cjs",
|
|
1690
|
+
".lintstagedrc",
|
|
1691
|
+
".lintstagedrc.json",
|
|
1692
|
+
".lintstagedrc.yaml",
|
|
1693
|
+
".lintstagedrc.yml",
|
|
1694
|
+
".lintstagedrc.mjs",
|
|
1695
|
+
".lintstagedrc.cjs"
|
|
1696
|
+
];
|
|
1676
1697
|
async function generateLintStaged(ctx) {
|
|
1677
1698
|
const filePath = "lint-staged.config.mjs";
|
|
1678
1699
|
const huskyPath = ".husky/pre-commit";
|
|
1679
1700
|
const content = buildConfig(ctx.config.formatter);
|
|
1680
|
-
const existing = ctx.read(filePath);
|
|
1681
1701
|
if (ctx.read(huskyPath) !== HUSKY_PRE_COMMIT) ctx.write(huskyPath, HUSKY_PRE_COMMIT);
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1702
|
+
const existingPath = LINT_STAGED_CONFIG_PATHS.find((p) => ctx.exists(p));
|
|
1703
|
+
if (existingPath === filePath) {
|
|
1704
|
+
const existing = ctx.read(filePath);
|
|
1705
|
+
if (existing) {
|
|
1706
|
+
if (existing === content) return {
|
|
1707
|
+
filePath,
|
|
1708
|
+
action: "skipped",
|
|
1709
|
+
description: "Already configured"
|
|
1710
|
+
};
|
|
1711
|
+
if (await ctx.confirmOverwrite(filePath) === "skip") return {
|
|
1712
|
+
filePath,
|
|
1713
|
+
action: "skipped",
|
|
1714
|
+
description: "Existing lint-staged config preserved"
|
|
1715
|
+
};
|
|
1716
|
+
}
|
|
1717
|
+
} else if (existingPath) return {
|
|
1718
|
+
filePath: existingPath,
|
|
1719
|
+
action: "skipped",
|
|
1720
|
+
description: `Existing config found at ${existingPath}`
|
|
1721
|
+
};
|
|
1694
1722
|
ctx.write(filePath, content);
|
|
1695
1723
|
return {
|
|
1696
1724
|
filePath,
|
|
1697
|
-
action:
|
|
1725
|
+
action: existingPath === filePath ? "updated" : "created",
|
|
1698
1726
|
description: "Generated lint-staged config and husky pre-commit hook"
|
|
1699
1727
|
};
|
|
1700
1728
|
}
|
|
@@ -1717,9 +1745,9 @@ const initCommand = defineCommand({
|
|
|
1717
1745
|
alias: "y",
|
|
1718
1746
|
description: "Accept all defaults (non-interactive)"
|
|
1719
1747
|
},
|
|
1720
|
-
"
|
|
1748
|
+
"eslint-plugin": {
|
|
1721
1749
|
type: "boolean",
|
|
1722
|
-
description: "Include @bensandee/
|
|
1750
|
+
description: "Include @bensandee/eslint-plugin (default: true)"
|
|
1723
1751
|
},
|
|
1724
1752
|
"no-ci": {
|
|
1725
1753
|
type: "boolean",
|
|
@@ -1733,7 +1761,7 @@ const initCommand = defineCommand({
|
|
|
1733
1761
|
async run({ args }) {
|
|
1734
1762
|
const targetDir = path.resolve(args.dir ?? ".");
|
|
1735
1763
|
await runInit(args.yes ? buildDefaultConfig(targetDir, {
|
|
1736
|
-
|
|
1764
|
+
eslintPlugin: args["eslint-plugin"] === true ? true : void 0,
|
|
1737
1765
|
noCi: args["no-ci"] === true ? true : void 0
|
|
1738
1766
|
}) : await runInitPrompts(targetDir), args["no-prompt"] === true ? { noPrompt: true } : {});
|
|
1739
1767
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bensandee/tooling",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "CLI tool to bootstrap and maintain standardized TypeScript project tooling",
|
|
5
5
|
"bin": {
|
|
6
6
|
"tooling": "./dist/bin.mjs"
|
|
@@ -9,17 +9,13 @@
|
|
|
9
9
|
"dist"
|
|
10
10
|
],
|
|
11
11
|
"type": "module",
|
|
12
|
-
"main": "./dist/index.mjs",
|
|
13
|
-
"module": "./dist/index.mjs",
|
|
14
|
-
"types": "./dist/index.d.mts",
|
|
15
12
|
"imports": {
|
|
16
13
|
"#src/*": "./src/*.ts"
|
|
17
14
|
},
|
|
18
15
|
"exports": {
|
|
19
|
-
".":
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
16
|
+
".": "./dist/index.mjs",
|
|
17
|
+
"./bin": "./dist/bin.mjs",
|
|
18
|
+
"./package.json": "./package.json"
|
|
23
19
|
},
|
|
24
20
|
"publishConfig": {
|
|
25
21
|
"access": "public"
|
|
@@ -35,7 +31,7 @@
|
|
|
35
31
|
"tsdown": "0.20.3",
|
|
36
32
|
"typescript": "5.9.3",
|
|
37
33
|
"vitest": "4.0.18",
|
|
38
|
-
"@bensandee/config": "0.
|
|
34
|
+
"@bensandee/config": "0.4.0"
|
|
39
35
|
},
|
|
40
36
|
"scripts": {
|
|
41
37
|
"build": "tsdown",
|
package/dist/bin.d.mts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { };
|
package/dist/index.d.mts
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
|
|
3
|
-
//#region src/utils/json.d.ts
|
|
4
|
-
declare const PackageJsonSchema: z.ZodObject<{
|
|
5
|
-
name: z.ZodOptional<z.ZodString>;
|
|
6
|
-
version: z.ZodOptional<z.ZodString>;
|
|
7
|
-
private: z.ZodOptional<z.ZodBoolean>;
|
|
8
|
-
type: z.ZodOptional<z.ZodString>;
|
|
9
|
-
scripts: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
10
|
-
dependencies: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
11
|
-
devDependencies: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
12
|
-
bin: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
|
|
13
|
-
exports: z.ZodOptional<z.ZodUnknown>;
|
|
14
|
-
main: z.ZodOptional<z.ZodString>;
|
|
15
|
-
types: z.ZodOptional<z.ZodString>;
|
|
16
|
-
typings: z.ZodOptional<z.ZodString>;
|
|
17
|
-
engines: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
18
|
-
}, z.core.$loose>;
|
|
19
|
-
type PackageJson = z.infer<typeof PackageJsonSchema>;
|
|
20
|
-
//#endregion
|
|
21
|
-
//#region src/types.d.ts
|
|
22
|
-
type CiPlatform = "github" | "forgejo" | "none";
|
|
23
|
-
type ReleaseStrategy = "release-it" | "commit-and-tag-version" | "changesets" | "none";
|
|
24
|
-
/** User's answers from the interactive prompt or CLI flags. */
|
|
25
|
-
interface ProjectConfig {
|
|
26
|
-
/** Project name (from package.json name or user input) */
|
|
27
|
-
name: string;
|
|
28
|
-
/** Whether this is a new project or existing */
|
|
29
|
-
isNew: boolean;
|
|
30
|
-
/** Project structure */
|
|
31
|
-
structure: "single" | "monorepo";
|
|
32
|
-
/** Include @bensandee/lint-rules oxlint plugin */
|
|
33
|
-
useLintRules: boolean;
|
|
34
|
-
/** Formatter choice */
|
|
35
|
-
formatter: "oxfmt" | "prettier";
|
|
36
|
-
/** Set up vitest with a starter test */
|
|
37
|
-
setupVitest: boolean;
|
|
38
|
-
/** CI platform choice */
|
|
39
|
-
ci: CiPlatform;
|
|
40
|
-
/** Set up Renovate for automated dependency updates */
|
|
41
|
-
setupRenovate: boolean;
|
|
42
|
-
/** Release management strategy */
|
|
43
|
-
releaseStrategy: ReleaseStrategy;
|
|
44
|
-
/** Project type determines tsconfig base configuration */
|
|
45
|
-
projectType: "default" | "node" | "react" | "library";
|
|
46
|
-
/** Auto-detect and configure tsconfig bases for monorepo packages */
|
|
47
|
-
detectPackageTypes: boolean;
|
|
48
|
-
/** Target directory (default: cwd) */
|
|
49
|
-
targetDir: string;
|
|
50
|
-
}
|
|
51
|
-
/** Result from a single generator: what file was written and how. */
|
|
52
|
-
interface GeneratorResult {
|
|
53
|
-
filePath: string;
|
|
54
|
-
action: "created" | "updated" | "skipped";
|
|
55
|
-
/** Human-readable description of what changed */
|
|
56
|
-
description: string;
|
|
57
|
-
}
|
|
58
|
-
/** Context passed to each generator function. */
|
|
59
|
-
interface GeneratorContext {
|
|
60
|
-
config: ProjectConfig;
|
|
61
|
-
/** Absolute path to target directory */
|
|
62
|
-
targetDir: string;
|
|
63
|
-
/** Pre-parsed package.json from the target directory, or undefined if missing/invalid */
|
|
64
|
-
packageJson: PackageJson | undefined;
|
|
65
|
-
/** Check whether a file exists in the target directory */
|
|
66
|
-
exists: (relativePath: string) => boolean;
|
|
67
|
-
/** Read an existing file from the target directory, returns undefined if not found */
|
|
68
|
-
read: (relativePath: string) => string | undefined;
|
|
69
|
-
/** Write a file to the target directory (creating directories as needed) */
|
|
70
|
-
write: (relativePath: string, content: string) => void;
|
|
71
|
-
/** Prompt user for conflict resolution on non-mergeable files */
|
|
72
|
-
confirmOverwrite: (relativePath: string) => Promise<"overwrite" | "skip">;
|
|
73
|
-
}
|
|
74
|
-
/** Generator function signature. */
|
|
75
|
-
type Generator = (ctx: GeneratorContext) => Promise<GeneratorResult>;
|
|
76
|
-
/** State detected from an existing project directory. */
|
|
77
|
-
interface DetectedProjectState {
|
|
78
|
-
hasPackageJson: boolean;
|
|
79
|
-
hasTsconfig: boolean;
|
|
80
|
-
hasOxlintConfig: boolean;
|
|
81
|
-
/** Legacy .oxlintrc.json found (should be migrated to oxlint.config.ts) */
|
|
82
|
-
hasLegacyOxlintJson: boolean;
|
|
83
|
-
hasGitignore: boolean;
|
|
84
|
-
hasVitestConfig: boolean;
|
|
85
|
-
hasTsdownConfig: boolean;
|
|
86
|
-
hasPnpmWorkspace: boolean;
|
|
87
|
-
hasKnipConfig: boolean;
|
|
88
|
-
hasRenovateConfig: boolean;
|
|
89
|
-
hasReleaseItConfig: boolean;
|
|
90
|
-
hasCommitAndTagVersionConfig: boolean;
|
|
91
|
-
hasChangesetsConfig: boolean;
|
|
92
|
-
/** Legacy tooling configs found */
|
|
93
|
-
legacyConfigs: LegacyConfig[];
|
|
94
|
-
}
|
|
95
|
-
declare const LEGACY_TOOLS: readonly ["eslint", "prettier", "jest", "webpack", "rollup"];
|
|
96
|
-
type LegacyTool = (typeof LEGACY_TOOLS)[number];
|
|
97
|
-
interface LegacyConfig {
|
|
98
|
-
tool: LegacyTool;
|
|
99
|
-
files: string[];
|
|
100
|
-
}
|
|
101
|
-
//#endregion
|
|
102
|
-
export { type DetectedProjectState, type Generator, type GeneratorContext, type GeneratorResult, type LegacyConfig, type ProjectConfig };
|