commit-sheriff 1.7.2 → 1.7.3
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/README.md +25 -42
- package/bin/commit-sheriff.js +32 -33
- package/package.json +1 -1
- package/templates/pre-commit +3 -3
package/README.md
CHANGED
|
@@ -276,40 +276,25 @@ npx commit-sheriff add-eslint-react
|
|
|
276
276
|
```
|
|
277
277
|
|
|
278
278
|
This ships as a **flat config** (`eslint.config.*`, ESLint 9+ format), not the legacy
|
|
279
|
-
`.eslintrc.js` format — see [why](#why-flat-config) below.
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
`jsx-a11y`, `import`, etc. — through a separate loader) would otherwise throw `Cannot redefine
|
|
299
|
-
plugin ...`; the generated config strips those redundant re-registrations automatically so both
|
|
300
|
-
rule sets apply cleanly.
|
|
301
|
-
- **v15 and earlier**: still ships the legacy eslintrc-style object, so the generated config uses
|
|
302
|
-
`FlatCompat.extends("next/core-web-vitals", "next/typescript")` instead — the same bridge
|
|
303
|
-
`create-next-app@15` itself generates.
|
|
304
|
-
|
|
305
|
-
(An earlier version of this tool hardcoded the v15-style pattern unconditionally, which crashed
|
|
306
|
-
with `TypeError: Converting circular structure to JSON` on projects using `eslint-config-next@16+`
|
|
307
|
-
— fixed by detecting the installed version instead of assuming one.)
|
|
308
|
-
|
|
309
|
-
`.prettierrc.js` is written/refreshed the same way — **always overwritten** with the latest
|
|
310
|
-
template (same behavior as the `commit-msg`/`pre-commit` hooks). This trade-off is intentional:
|
|
311
|
-
zero manual steps for the common case, at the cost of not preserving bespoke prior ESLint/Prettier
|
|
312
|
-
setups.
|
|
279
|
+
`.eslintrc.js` format — see [why](#why-flat-config) below.
|
|
280
|
+
|
|
281
|
+
It writes/refreshes three files every run, **always overwriting** whatever was there before (same
|
|
282
|
+
as the `commit-msg`/`pre-commit` hooks) — no manual merge step, ever. If you've hand-edited any of
|
|
283
|
+
these, back them up under a different name first:
|
|
284
|
+
|
|
285
|
+
| File | What it is |
|
|
286
|
+
| ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
287
|
+
| `eslint.config.commit-sheriff.mjs` | The actual ruleset: module-boundary rules + all the recommended plugins (TypeScript, React, hooks, a11y, sonarjs, import, testing-library, vitest, jest, prettier). |
|
|
288
|
+
| `eslint.config.mjs` | The entry point ESLint actually reads. Just imports the file above — or, if `next` is detected in your dependencies, also folds in Next.js's own ESLint rules so you don't lose Next-specific linting (`eslint-config-next` is auto-installed if missing). |
|
|
289
|
+
| `.prettierrc.js` | Matching Prettier config for the same ruleset. |
|
|
290
|
+
|
|
291
|
+
If your project already had its own `eslint.config.mjs` (e.g. Next.js's own
|
|
292
|
+
`create-next-app`-generated one), it gets replaced by the one above, not merged with it.
|
|
293
|
+
|
|
294
|
+
Next.js support is version-aware: `eslint-config-next` changed its internal shape between major
|
|
295
|
+
versions (a native flat-config array from v16 on, the older eslintrc-style object before that), and
|
|
296
|
+
the generated `eslint.config.mjs` detects which one is actually installed and uses the matching
|
|
297
|
+
pattern automatically — you don't need to think about this at all, it's handled for you.
|
|
313
298
|
|
|
314
299
|
The module list for the boundary rule isn't hardcoded — it's read straight from the `modules`
|
|
315
300
|
array in your root `.commitsheriffrc.json` (the same list the commit-msg/branch-name hooks use
|
|
@@ -348,16 +333,14 @@ it yourself.
|
|
|
348
333
|
#### Why flat config?
|
|
349
334
|
|
|
350
335
|
ESLint 9+ looks for `eslint.config.*` first and, if one is found, **ignores `.eslintrc.js`
|
|
351
|
-
entirely** — there's no fallback.
|
|
352
|
-
`eslint.config.mjs
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
official migration helper Next.js's own generated config uses internally) so the exact same rule
|
|
356
|
-
set works natively under ESLint 9+, whether or not the project already had its own flat config.
|
|
336
|
+
entirely** — there's no fallback. A legacy-only `.eslintrc.js` template sitting next to a
|
|
337
|
+
framework-generated `eslint.config.mjs` (Next.js scaffolds one by default) would simply never run —
|
|
338
|
+
this used to be a real bug here, where an unused import went completely unflagged because the
|
|
339
|
+
module-boundary rules were silently dead. Flat config avoids that trap entirely.
|
|
357
340
|
|
|
358
341
|
A legacy `.eslintrc.js` version of this template (`eslintrc.module-boundaries.js`) still ships
|
|
359
|
-
inside the package for reference/manual use on pure ESLint 8 projects with no flat config support
|
|
360
|
-
|
|
342
|
+
inside the package for reference/manual use on pure ESLint 8 projects with no flat config support,
|
|
343
|
+
but the CLI no longer writes it by default.
|
|
361
344
|
|
|
362
345
|
## Examples
|
|
363
346
|
|
package/bin/commit-sheriff.js
CHANGED
|
@@ -49,7 +49,7 @@ const CONFIG_FILE_NAME = ".commitsheriffrc.json";
|
|
|
49
49
|
function readPackageJson() {
|
|
50
50
|
const pkgPath = path.join(cwd, "package.json");
|
|
51
51
|
if (!fs.existsSync(pkgPath)) {
|
|
52
|
-
console.error(" package.json
|
|
52
|
+
console.error(" package.json not found. Run `npm init` first.");
|
|
53
53
|
process.exit(1);
|
|
54
54
|
}
|
|
55
55
|
return { pkgPath, pkg: JSON.parse(fs.readFileSync(pkgPath, "utf8")) };
|
|
@@ -59,7 +59,7 @@ function ensureHusky() {
|
|
|
59
59
|
const huskyDir = path.join(cwd, ".husky");
|
|
60
60
|
const shimPath = path.join(huskyDir, "_", "husky.sh");
|
|
61
61
|
if (!fs.existsSync(shimPath)) {
|
|
62
|
-
console.log("→ husky
|
|
62
|
+
console.log("→ setting up husky (npx husky init)...");
|
|
63
63
|
execSync("npx husky init", { cwd, stdio: "inherit" });
|
|
64
64
|
}
|
|
65
65
|
}
|
|
@@ -70,7 +70,7 @@ function copyHook(name) {
|
|
|
70
70
|
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
71
71
|
fs.copyFileSync(src, dest);
|
|
72
72
|
fs.chmodSync(dest, 0o755);
|
|
73
|
-
console.log(`✔ .husky/${name}
|
|
73
|
+
console.log(`✔ .husky/${name} written`);
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
function ensureConfigFile() {
|
|
@@ -78,19 +78,19 @@ function ensureConfigFile() {
|
|
|
78
78
|
const { pkg } = readPackageJson();
|
|
79
79
|
|
|
80
80
|
if (fs.existsSync(configPath)) {
|
|
81
|
-
console.log(`• ${CONFIG_FILE_NAME}
|
|
81
|
+
console.log(`• ${CONFIG_FILE_NAME} already exists, left untouched`);
|
|
82
82
|
return;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
if (pkg.commitGuard) {
|
|
86
86
|
console.log(
|
|
87
|
-
`•
|
|
87
|
+
`• Found a legacy-style 'commitGuard' block in package.json — ${CONFIG_FILE_NAME} not created, the old configuration keeps working.`,
|
|
88
88
|
);
|
|
89
89
|
return;
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
fs.writeFileSync(configPath, JSON.stringify(DEFAULT_COMMIT_GUARD, null, 2) + "\n");
|
|
93
|
-
console.log(`✔ ${CONFIG_FILE_NAME}
|
|
93
|
+
console.log(`✔ ${CONFIG_FILE_NAME} written`);
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
// Installs whatever in `packages` isn't already a listed dependency/devDependency, so the hooks
|
|
@@ -111,10 +111,10 @@ function ensureDevDeps(packages) {
|
|
|
111
111
|
const deps = Object.assign({}, pkg.dependencies, pkg.devDependencies);
|
|
112
112
|
const missing = packages.filter((spec) => !deps[packageNameOf(spec)]);
|
|
113
113
|
if (!missing.length) {
|
|
114
|
-
console.log("•
|
|
114
|
+
console.log("• Required devDependencies are already installed, nothing to do");
|
|
115
115
|
return;
|
|
116
116
|
}
|
|
117
|
-
console.log(`→
|
|
117
|
+
console.log(`→ Installing: ${missing.join(" ")}`);
|
|
118
118
|
try {
|
|
119
119
|
// --legacy-peer-deps: some ESLint plugins lag behind the newest ESLint major in their
|
|
120
120
|
// declared peerDependencies (e.g. eslint-plugin-import still capping at ^9 while npm already
|
|
@@ -124,10 +124,10 @@ function ensureDevDeps(packages) {
|
|
|
124
124
|
cwd,
|
|
125
125
|
stdio: "inherit",
|
|
126
126
|
});
|
|
127
|
-
console.log("✔ devDependencies
|
|
127
|
+
console.log("✔ devDependencies installed");
|
|
128
128
|
} catch {
|
|
129
129
|
console.log(
|
|
130
|
-
"\n
|
|
130
|
+
"\n Automatic install failed (could be a network/npm error) — run it yourself:\n" +
|
|
131
131
|
` npm install --save-dev ${missing.join(" ")}\n`,
|
|
132
132
|
);
|
|
133
133
|
}
|
|
@@ -142,9 +142,9 @@ function mergeConfig() {
|
|
|
142
142
|
if (!pkg["lint-staged"]) {
|
|
143
143
|
pkg["lint-staged"] = DEFAULT_LINT_STAGED;
|
|
144
144
|
changed = true;
|
|
145
|
-
console.log("✔ package.json → lint-staged
|
|
145
|
+
console.log("✔ package.json → lint-staged configuration added");
|
|
146
146
|
} else {
|
|
147
|
-
console.log("• package.json
|
|
147
|
+
console.log("• package.json already has a lint-staged block, left untouched");
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
if (!pkg.scripts) pkg.scripts = {};
|
|
@@ -184,18 +184,16 @@ function init() {
|
|
|
184
184
|
|
|
185
185
|
if (isReactTypescriptProject()) {
|
|
186
186
|
console.log(
|
|
187
|
-
"\n• react + typescript
|
|
187
|
+
"\n• react + typescript detected → also adding the TS/React module-boundary ESLint template:",
|
|
188
188
|
);
|
|
189
189
|
addEslintReact();
|
|
190
190
|
} else {
|
|
191
191
|
console.log(
|
|
192
|
-
"\n(
|
|
192
|
+
"\n(Project not detected as TS/React — you can add it manually with `npx commit-sheriff add-eslint-react` if needed.)",
|
|
193
193
|
);
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
-
console.log(
|
|
197
|
-
`\nHazırdır. ${CONFIG_FILE_NAME} faylında 'project' və lazım olsa 'modules' dəyərlərini tənzimləyin.\n`,
|
|
198
|
-
);
|
|
196
|
+
console.log(`\nDone. Adjust 'project' and, if needed, 'modules' in ${CONFIG_FILE_NAME}.\n`);
|
|
199
197
|
}
|
|
200
198
|
|
|
201
199
|
const ESLINT_REACT_DEV_DEPS = [
|
|
@@ -240,7 +238,7 @@ function copyTemplateOverwrite(templateName, destName) {
|
|
|
240
238
|
const dest = path.join(cwd, destName);
|
|
241
239
|
const existed = fs.existsSync(dest);
|
|
242
240
|
fs.copyFileSync(src, dest);
|
|
243
|
-
console.log(existed ? `✔ ${destName}
|
|
241
|
+
console.log(existed ? `✔ ${destName} updated (overwritten)` : `✔ ${destName} written`);
|
|
244
242
|
return dest;
|
|
245
243
|
}
|
|
246
244
|
|
|
@@ -248,7 +246,7 @@ function writeFileOverwrite(destName, content) {
|
|
|
248
246
|
const dest = path.join(cwd, destName);
|
|
249
247
|
const existed = fs.existsSync(dest);
|
|
250
248
|
fs.writeFileSync(dest, content);
|
|
251
|
-
console.log(existed ? `✔ ${destName}
|
|
249
|
+
console.log(existed ? `✔ ${destName} updated (overwritten)` : `✔ ${destName} written`);
|
|
252
250
|
return dest;
|
|
253
251
|
}
|
|
254
252
|
|
|
@@ -348,25 +346,26 @@ function addEslintReact() {
|
|
|
348
346
|
copyTemplateOverwrite("prettier.module-boundaries.js", ".prettierrc.js");
|
|
349
347
|
|
|
350
348
|
console.log(
|
|
351
|
-
"\
|
|
352
|
-
`
|
|
353
|
-
"
|
|
354
|
-
"
|
|
349
|
+
"\nThis template is for TypeScript/React + module-boundary rules.\n" +
|
|
350
|
+
`The module list is read automatically from ${CONFIG_FILE_NAME}'s "modules" (falls back\n` +
|
|
351
|
+
"to an illustrative default if empty — read the comment inside the template file).\n\n" +
|
|
352
|
+
"Note: eslint.config.mjs, " +
|
|
355
353
|
RULES_FILE +
|
|
356
|
-
"
|
|
357
|
-
"
|
|
358
|
-
"
|
|
359
|
-
"
|
|
354
|
+
", and .prettierrc.js are overwritten\n" +
|
|
355
|
+
"with the latest template every run (same as the commit-msg/pre-commit hooks) — no manual\n" +
|
|
356
|
+
"merge step needed, but if you want to keep your own customizations, back them up under a\n" +
|
|
357
|
+
"different name first.\n" +
|
|
360
358
|
(hasNext
|
|
361
|
-
? "\nNext.js
|
|
362
|
-
"
|
|
359
|
+
? "\nNext.js detected → next/core-web-vitals and next/typescript rules are folded in\n" +
|
|
360
|
+
"automatically too.\n"
|
|
363
361
|
: ""),
|
|
364
362
|
);
|
|
365
363
|
|
|
366
364
|
if (fs.existsSync(path.join(cwd, ".eslintrc.js"))) {
|
|
367
365
|
console.log(
|
|
368
|
-
"\
|
|
369
|
-
"ESLint
|
|
366
|
+
"\nNote: your project still has a legacy .eslintrc.js — delete it. With a flat config\n" +
|
|
367
|
+
"present, ESLint never reads it, and it can itself start producing lint errors as a\n" +
|
|
368
|
+
"plain .js file.\n",
|
|
370
369
|
);
|
|
371
370
|
}
|
|
372
371
|
}
|
|
@@ -376,9 +375,9 @@ if (command === "init") {
|
|
|
376
375
|
} else if (command === "add-eslint-react") {
|
|
377
376
|
addEslintReact();
|
|
378
377
|
} else {
|
|
379
|
-
console.log("
|
|
378
|
+
console.log("Usage: npx commit-sheriff init");
|
|
380
379
|
console.log(
|
|
381
|
-
"
|
|
380
|
+
" : npx commit-sheriff add-eslint-react (optional, TS/React module-boundary ESLint template)",
|
|
382
381
|
);
|
|
383
382
|
process.exit(command ? 1 : 0);
|
|
384
383
|
}
|
package/package.json
CHANGED
package/templates/pre-commit
CHANGED
|
@@ -84,10 +84,10 @@ if [ "$HAS_LINT_CONFIG" = "1" ]; then
|
|
|
84
84
|
npx lint-staged --verbose || exit 1
|
|
85
85
|
else
|
|
86
86
|
echo ""
|
|
87
|
-
echo " package.json
|
|
88
|
-
echo " node_modules
|
|
87
|
+
echo " package.json has a 'lint-staged' configuration, but the package itself"
|
|
88
|
+
echo " was not found in node_modules — the lint/format check could not run."
|
|
89
89
|
echo ""
|
|
90
|
-
echo "
|
|
90
|
+
echo " To fix:"
|
|
91
91
|
echo " npm install --save-dev lint-staged"
|
|
92
92
|
echo ""
|
|
93
93
|
exit 1
|