commit-sheriff 1.7.1 → 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 CHANGED
@@ -251,19 +251,19 @@ The default config added by `init` (only if you don't already have one):
251
251
  ```json
252
252
  {
253
253
  "lint-staged": {
254
- "*.{js,jsx,ts,tsx}": ["eslint --fix --max-warnings=0", "prettier --write"],
254
+ "*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"],
255
255
  "*.{json,md,css,scss,html}": ["prettier --write"]
256
256
  }
257
257
  }
258
258
  ```
259
259
 
260
- `--max-warnings=0` is there on purpose: ESLint only fails (non-zero exit) on errors by default, so
261
- a rule set to `"warn"` (e.g. many "recommended" configs' default severity for unused imports) would
262
- otherwise print in the terminal but still let the commit through looking clean.
260
+ Warnings are printed but never block the commit — only actual errors (ESLint's non-zero exit) do.
261
+ That's the default ESLint behavior (`--fix` alone exits 0 on warning-only results), so a rule set to
262
+ `"warn"` (e.g. an unused import under some "recommended" configs) shows up in the terminal as a
263
+ heads-up but doesn't stop you from committing.
263
264
 
264
- Adjust freely — `commit-sheriff` doesn't overwrite it once present. That also means this default
265
- only lands on **new** setups; if your project already has a `lint-staged` block from before this
266
- change, add `--max-warnings=0` to it by hand to get the same behavior.
265
+ Adjust freely — `commit-sheriff` doesn't overwrite it once present. If you want warnings to block
266
+ the commit too, add `--max-warnings=0` to the `eslint --fix` command by hand.
267
267
 
268
268
  ## Advanced: ESLint module-boundary template (TypeScript/React)
269
269
 
@@ -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. It always writes two files, no manual
280
- merge step required, ever:
281
-
282
- - `eslint.config.commit-sheriff.mjs` — the actual ruleset (module-boundary rules + all the
283
- recommended plugins: TypeScript, React, hooks, a11y, sonarjs, import, testing-library, vitest,
284
- jest, prettier).
285
- - `eslint.config.mjs` the entry point ESLint actually reads, generated fresh every run to import
286
- the file above. **If you already had an `eslint.config.mjs` (e.g. Next.js's own
287
- `create-next-app`-generated one), it is fully overwritten** not merged, not left alone. If
288
- `next` is detected in your `dependencies`, the generated entry point folds in Next's own ESLint
289
- rules automatically so you don't lose Next-specific linting; `eslint-config-next` is
290
- auto-installed too if it's missing. Any other hand-written customizations in your previous
291
- `eslint.config.mjs` are not preserved — back it up under a different name first if you need them.
292
-
293
- `eslint-config-next` changed shape between major versions, so the generated entry point checks
294
- the actually-installed version and picks the matching pattern automatically:
295
- - **v16+** (current): ships a native flat-config array
296
- (`import nextCoreWebVitals from "eslint-config-next/core-web-vitals"`, spread directly). Mixing
297
- this with our own module-boundary plugins (which resolve the same plugin _names_ `react`,
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. Since frameworks like Next.js already scaffold their own
352
- `eslint.config.mjs`, a legacy-only `.eslintrc.js` template would sit right next to it and never
353
- actually run (this was a real bug: an unused `useState` import went completely unflagged because
354
- the module-boundary config was silently dead). The flat config here uses `FlatCompat` (the same
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
- at all, but the CLI no longer writes it by default.
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
 
@@ -38,10 +38,9 @@ const DEFAULT_COMMIT_GUARD = {
38
38
  };
39
39
 
40
40
  const DEFAULT_LINT_STAGED = {
41
- // --max-warnings=0: without this, ESLint only fails (non-zero exit) on errors a warning
42
- // (e.g. many "recommended" configs' default severity for unused imports) prints in the
43
- // terminal but still lets `eslint --fix` exit 0, so the commit goes through looking clean.
44
- "*.{js,jsx,ts,tsx}": ["eslint --fix --max-warnings=0", "prettier --write"],
41
+ // No --max-warnings=0 here on purpose: warnings should be visible (ESLint prints them to the
42
+ // terminal either way) but must NOT block the commit only actual errors (non-zero exit) do.
43
+ "*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"],
45
44
  "*.{json,md,css,scss,html}": ["prettier --write"],
46
45
  };
47
46
 
@@ -50,7 +49,7 @@ const CONFIG_FILE_NAME = ".commitsheriffrc.json";
50
49
  function readPackageJson() {
51
50
  const pkgPath = path.join(cwd, "package.json");
52
51
  if (!fs.existsSync(pkgPath)) {
53
- console.error(" package.json tapılmadı. Əvvəlcə `npm init` işlədin.");
52
+ console.error(" package.json not found. Run `npm init` first.");
54
53
  process.exit(1);
55
54
  }
56
55
  return { pkgPath, pkg: JSON.parse(fs.readFileSync(pkgPath, "utf8")) };
@@ -60,7 +59,7 @@ function ensureHusky() {
60
59
  const huskyDir = path.join(cwd, ".husky");
61
60
  const shimPath = path.join(huskyDir, "_", "husky.sh");
62
61
  if (!fs.existsSync(shimPath)) {
63
- console.log("→ husky quraşdırılır (npx husky init)...");
62
+ console.log("→ setting up husky (npx husky init)...");
64
63
  execSync("npx husky init", { cwd, stdio: "inherit" });
65
64
  }
66
65
  }
@@ -71,7 +70,7 @@ function copyHook(name) {
71
70
  fs.mkdirSync(path.dirname(dest), { recursive: true });
72
71
  fs.copyFileSync(src, dest);
73
72
  fs.chmodSync(dest, 0o755);
74
- console.log(`✔ .husky/${name} yazıldı`);
73
+ console.log(`✔ .husky/${name} written`);
75
74
  }
76
75
 
77
76
  function ensureConfigFile() {
@@ -79,19 +78,19 @@ function ensureConfigFile() {
79
78
  const { pkg } = readPackageJson();
80
79
 
81
80
  if (fs.existsSync(configPath)) {
82
- console.log(`• ${CONFIG_FILE_NAME} artıq var, toxunulmadı`);
81
+ console.log(`• ${CONFIG_FILE_NAME} already exists, left untouched`);
83
82
  return;
84
83
  }
85
84
 
86
85
  if (pkg.commitGuard) {
87
86
  console.log(
88
- `• package.json-da köhnə tərzli 'commitGuard' bloku tapıldı — ${CONFIG_FILE_NAME} yaradılmadı, köhnə konfiqurasiya işləməyə davam edir.`,
87
+ `• Found a legacy-style 'commitGuard' block in package.json — ${CONFIG_FILE_NAME} not created, the old configuration keeps working.`,
89
88
  );
90
89
  return;
91
90
  }
92
91
 
93
92
  fs.writeFileSync(configPath, JSON.stringify(DEFAULT_COMMIT_GUARD, null, 2) + "\n");
94
- console.log(`✔ ${CONFIG_FILE_NAME} yazıldı`);
93
+ console.log(`✔ ${CONFIG_FILE_NAME} written`);
95
94
  }
96
95
 
97
96
  // Installs whatever in `packages` isn't already a listed dependency/devDependency, so the hooks
@@ -112,10 +111,10 @@ function ensureDevDeps(packages) {
112
111
  const deps = Object.assign({}, pkg.dependencies, pkg.devDependencies);
113
112
  const missing = packages.filter((spec) => !deps[packageNameOf(spec)]);
114
113
  if (!missing.length) {
115
- console.log("• Lazımi devDependencies artıq quraşdırılıb, toxunulmadı");
114
+ console.log("• Required devDependencies are already installed, nothing to do");
116
115
  return;
117
116
  }
118
- console.log(`→ Quraşdırılır: ${missing.join(" ")}`);
117
+ console.log(`→ Installing: ${missing.join(" ")}`);
119
118
  try {
120
119
  // --legacy-peer-deps: some ESLint plugins lag behind the newest ESLint major in their
121
120
  // declared peerDependencies (e.g. eslint-plugin-import still capping at ^9 while npm already
@@ -125,10 +124,10 @@ function ensureDevDeps(packages) {
125
124
  cwd,
126
125
  stdio: "inherit",
127
126
  });
128
- console.log("✔ devDependencies quraşdırıldı");
127
+ console.log("✔ devDependencies installed");
129
128
  } catch {
130
129
  console.log(
131
- "\n Avtomatik quraşdırma alınmadı (şəbəkə/npm xətası ola bilər) — özünüz işlədin:\n" +
130
+ "\n Automatic install failed (could be a network/npm error) — run it yourself:\n" +
132
131
  ` npm install --save-dev ${missing.join(" ")}\n`,
133
132
  );
134
133
  }
@@ -143,9 +142,9 @@ function mergeConfig() {
143
142
  if (!pkg["lint-staged"]) {
144
143
  pkg["lint-staged"] = DEFAULT_LINT_STAGED;
145
144
  changed = true;
146
- console.log("✔ package.json → lint-staged konfiqurasiyası əlavə olundu");
145
+ console.log("✔ package.json → lint-staged configuration added");
147
146
  } else {
148
- console.log("• package.json-da lint-staged artıq var, toxunulmadı");
147
+ console.log("• package.json already has a lint-staged block, left untouched");
149
148
  }
150
149
 
151
150
  if (!pkg.scripts) pkg.scripts = {};
@@ -185,18 +184,16 @@ function init() {
185
184
 
186
185
  if (isReactTypescriptProject()) {
187
186
  console.log(
188
- "\n• react + typescript aşkarlandı → TS/React modul-sərhəd ESLint şablonu da əlavə olunur:",
187
+ "\n• react + typescript detectedalso adding the TS/React module-boundary ESLint template:",
189
188
  );
190
189
  addEslintReact();
191
190
  } else {
192
191
  console.log(
193
- "\n(Layihə TS/React kimi aşkarlanmadı istəsəniz `npx commit-sheriff add-eslint-react` ilə əlavə edə bilərsiniz.)",
192
+ "\n(Project not detected as TS/React you can add it manually with `npx commit-sheriff add-eslint-react` if needed.)",
194
193
  );
195
194
  }
196
195
 
197
- console.log(
198
- `\nHazırdır. ${CONFIG_FILE_NAME} faylında 'project' və lazım olsa 'modules' dəyərlərini tənzimləyin.\n`,
199
- );
196
+ console.log(`\nDone. Adjust 'project' and, if needed, 'modules' in ${CONFIG_FILE_NAME}.\n`);
200
197
  }
201
198
 
202
199
  const ESLINT_REACT_DEV_DEPS = [
@@ -241,7 +238,7 @@ function copyTemplateOverwrite(templateName, destName) {
241
238
  const dest = path.join(cwd, destName);
242
239
  const existed = fs.existsSync(dest);
243
240
  fs.copyFileSync(src, dest);
244
- console.log(existed ? `✔ ${destName} yeniləndi (üzərinə yazıldı)` : `✔ ${destName} yazıldı`);
241
+ console.log(existed ? `✔ ${destName} updated (overwritten)` : `✔ ${destName} written`);
245
242
  return dest;
246
243
  }
247
244
 
@@ -249,7 +246,7 @@ function writeFileOverwrite(destName, content) {
249
246
  const dest = path.join(cwd, destName);
250
247
  const existed = fs.existsSync(dest);
251
248
  fs.writeFileSync(dest, content);
252
- console.log(existed ? `✔ ${destName} yeniləndi (üzərinə yazıldı)` : `✔ ${destName} yazıldı`);
249
+ console.log(existed ? `✔ ${destName} updated (overwritten)` : `✔ ${destName} written`);
253
250
  return dest;
254
251
  }
255
252
 
@@ -349,25 +346,26 @@ function addEslintReact() {
349
346
  copyTemplateOverwrite("prettier.module-boundaries.js", ".prettierrc.js");
350
347
 
351
348
  console.log(
352
- "\nBu şablon TypeScript/React + modul-sərhəd (module-boundary) qaydaları üçündür.\n" +
353
- `Modul siyahısı ${CONFIG_FILE_NAME}.modules-dən avtomatik oxunur (boşdursa nümunə\n` +
354
- "siyahı istifadə olunurşablon faylı içindəki şərhi oxuyun).\n\n" +
355
- "Diqqət: eslint.config.mjs, " +
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, " +
356
353
  RULES_FILE +
357
- " .prettierrc.js hər dəfə\n" +
358
- "şablonun ən son versiyası ilə üzərinə yazılır (commit-msg/pre-commit hook-ları kimi) —\n" +
359
- "manual dəyişiklik lazım deyil, amma özünüzə uyğun etdiyiniz dəyişiklikləri qorumaq\n" +
360
- "istəsəniz, əvvəlcə fərqli adla backup götürün.\n" +
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" +
361
358
  (hasNext
362
- ? "\nNext.js aşkarlandı → next/core-web-vitals next/typescript qaydaları da\n" +
363
- "avtomatik daxil edildi.\n"
359
+ ? "\nNext.js detected → next/core-web-vitals and next/typescript rules are folded in\n" +
360
+ "automatically too.\n"
364
361
  : ""),
365
362
  );
366
363
 
367
364
  if (fs.existsSync(path.join(cwd, ".eslintrc.js"))) {
368
365
  console.log(
369
- "\nDiqqət: layihənizdə hələ köhnə .eslintrc.js var silin. Flat config varkən\n" +
370
- "ESLint onu oxumur, üstəlik özü indi adi .js fayl kimi lint xətaları törədə bilər.\n",
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",
371
369
  );
372
370
  }
373
371
  }
@@ -377,9 +375,9 @@ if (command === "init") {
377
375
  } else if (command === "add-eslint-react") {
378
376
  addEslintReact();
379
377
  } else {
380
- console.log("İstifadə: npx commit-sheriff init");
378
+ console.log("Usage: npx commit-sheriff init");
381
379
  console.log(
382
- " : npx commit-sheriff add-eslint-react (opsional, TS/React modul-sərhəd ESLint şablonu)",
380
+ " : npx commit-sheriff add-eslint-react (optional, TS/React module-boundary ESLint template)",
383
381
  );
384
382
  process.exit(command ? 1 : 0);
385
383
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "commit-sheriff",
3
- "version": "1.7.1",
3
+ "version": "1.7.3",
4
4
  "description": "Shared husky commit-msg / pre-commit hooks enforcing ticket-based commit messages and branch naming",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -77,13 +77,17 @@ HAS_LINT_CONFIG=$(node -e "
77
77
  " 2>/dev/null)
78
78
  if [ "$HAS_LINT_CONFIG" = "1" ]; then
79
79
  if node -e "require.resolve('lint-staged')" 2>/dev/null; then
80
- npx lint-staged || exit 1
80
+ # --verbose: lint-staged only prints a task's output when it *fails* by default, so a
81
+ # warning-only ESLint result (exit 0, commit still allowed through) would otherwise show
82
+ # nothing at all. --verbose keeps it printing on success too, so warnings stay visible as
83
+ # a heads-up without blocking the commit.
84
+ npx lint-staged --verbose || exit 1
81
85
  else
82
86
  echo ""
83
- echo " package.json-da 'lint-staged' konfiqurasiyası var, amma paketin özü"
84
- echo " node_modules-də tapılmadı — lint/format yoxlanışı keçirilə bilmədi."
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."
85
89
  echo ""
86
- echo " Düzəltmək üçün:"
90
+ echo " To fix:"
87
91
  echo " npm install --save-dev lint-staged"
88
92
  echo ""
89
93
  exit 1