commit-sheriff 1.2.0 → 1.3.1

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
@@ -20,7 +20,7 @@ npx commit-sheriff add-eslint-react # optional — TypeScript/React + module-b
20
20
  - [What `init` does](#what-init-does)
21
21
  - [Commit message format](#commit-message-format)
22
22
  - [Branch name format](#branch-name-format)
23
- - [Configuration (`commitGuard`)](#configuration-commitguard)
23
+ - [Configuration (`.commitsheriffrc.json`)](#configuration-commitsheriffrcjson)
24
24
  - [`useModules`](#usemodules)
25
25
  - [`project`](#project)
26
26
  - [`modules`](#modules)
@@ -38,7 +38,7 @@ npx commit-sheriff add-eslint-react # optional — TypeScript/React + module-b
38
38
 
39
39
  ## Why
40
40
 
41
- Different repos tend to drift into different commit-message and branch-naming conventions, which makes changelogs, ticket tracing, and code review harder. `commit-sheriff` gives every project the same two git hooks (`commit-msg`, `pre-commit`) driven by one small config block in `package.json`, so:
41
+ Different repos tend to drift into different commit-message and branch-naming conventions, which makes changelogs, ticket tracing, and code review harder. `commit-sheriff` gives every project the same two git hooks (`commit-msg`, `pre-commit`) driven by one small root-level `.commitsheriffrc.json` config file, so:
42
42
 
43
43
  - Every commit message references a ticket and a change type.
44
44
  - Every branch name reflects the same ticket and change type.
@@ -74,12 +74,12 @@ Run this once per repo, from the repo root (where `package.json` lives). This wo
74
74
 
75
75
  1. Initializes Husky if it isn't already set up (runs `npx husky init` when `.husky/_/husky.sh` is missing).
76
76
  2. Copies the hook scripts into `.husky/commit-msg` and `.husky/pre-commit` (overwriting any existing files with those exact names) and makes them executable.
77
- 3. Adds a default `commitGuard` block to `package.json` **only if one doesn't already exist** — re-running `init` never overwrites your customized config.
77
+ 3. Creates a default `.commitsheriffrc.json` file at the repo root **only if one doesn't already exist** (and only if there's no legacy `package.json` → `commitGuard` block either see [Configuration](#configuration-commitsheriffrcjson)) — re-running `init` never overwrites your customized config.
78
78
  4. Adds a default `lint-staged` block to `package.json` **only if one doesn't already exist**.
79
79
  5. Adds a `"prepare": "husky"` npm script if missing, so hooks are (re)installed automatically after `npm install`.
80
80
  6. If `react` and `typescript` are both present in your `dependencies`/`devDependencies`, also runs [`add-eslint-react`](#advanced-eslint-module-boundary-template-typescriptreact) automatically. Otherwise it's skipped and you can add it manually later.
81
81
 
82
- Re-running `npx commit-sheriff init` later (e.g. after updating the package) is safe: it refreshes the two hook scripts to the latest version but leaves your `commitGuard` / `lint-staged` config alone.
82
+ Re-running `npx commit-sheriff init` later (e.g. after updating the package) is safe: it refreshes the two hook scripts to the latest version but leaves your `.commitsheriffrc.json` / `lint-staged` config alone.
83
83
 
84
84
  ## Commit message format
85
85
 
@@ -140,46 +140,47 @@ improvement/PRO-PROJ-609-correct-husky-pre-commit-validation # only with useMo
140
140
 
141
141
  The check is skipped on a detached `HEAD` (e.g. mid-rebase, mid-cherry-pick), so it never blocks those operations.
142
142
 
143
- ## Configuration (`commitGuard`)
143
+ ## Configuration (`.commitsheriffrc.json`)
144
144
 
145
- Add/edit this block in your project's `package.json` (the `init` command adds a default one for you):
145
+ Add/edit this file at your project's **root** (next to `package.json`) the `init` command creates a default one for you:
146
146
 
147
147
  ```json
148
148
  {
149
- "commitGuard": {
150
- "useModules": false,
151
- "project": "PROJ",
152
- "branchTypes": [
153
- "feature",
154
- "bugfix",
155
- "hotfix",
156
- "improvement",
157
- "refactor",
158
- "release",
159
- "chore",
160
- "docs",
161
- "test",
162
- "spike"
163
- ],
164
- "types": [
165
- "feat",
166
- "feature",
167
- "fix",
168
- "docs",
169
- "style",
170
- "refactor",
171
- "test",
172
- "chore",
173
- "perf",
174
- "ci",
175
- "build",
176
- "revert"
177
- ]
178
- }
149
+ "useModules": false,
150
+ "project": "PROJ",
151
+ "branchTypes": [
152
+ "feature",
153
+ "bugfix",
154
+ "hotfix",
155
+ "improvement",
156
+ "refactor",
157
+ "release",
158
+ "chore",
159
+ "docs",
160
+ "test",
161
+ "spike"
162
+ ],
163
+ "types": [
164
+ "feat",
165
+ "fix",
166
+ "docs",
167
+ "style",
168
+ "refactor",
169
+ "test",
170
+ "chore",
171
+ "perf",
172
+ "ci",
173
+ "build",
174
+ "revert"
175
+ ]
179
176
  }
180
177
  ```
181
178
 
182
- All keys are optional; anything you omit falls back to the default shown above.
179
+ All keys are optional; anything you omit falls back to the default shown above. Edit this file whenever you like — the hooks read it fresh on every commit, so changes take effect immediately, no reinstall or `init` re-run needed.
180
+
181
+ ### Legacy `package.json` → `commitGuard` (backward compatibility)
182
+
183
+ Versions of `commit-sheriff` before this config-file change stored the same settings under a `commitGuard` key in `package.json` instead. That still works: if no `.commitsheriffrc.json` file exists, the hooks (and the `add-eslint-react` template) fall back to reading `package.json`'s `commitGuard` block automatically, so existing installs keep working without any changes required. New installs (and any project without an existing `commitGuard` block) get the new `.commitsheriffrc.json` file instead — this is the recommended location going forward. To migrate an existing project by hand, move the contents of `commitGuard` out into a new root-level `.commitsheriffrc.json` file and delete the `commitGuard` key from `package.json`.
183
184
 
184
185
  ### `useModules`
185
186
 
@@ -200,7 +201,7 @@ The project key used in the **branch name** check (`<type>/<PROJECT>-<NUMBER>-..
200
201
  Only relevant when `useModules: true`. If you want to restrict commits/branches to a specific, known set of module codes, list them explicitly:
201
202
 
202
203
  ```json
203
- "commitGuard": {
204
+ {
204
205
  "useModules": true,
205
206
  "project": "PROJ",
206
207
  "modules": ["AUTH", "BILLING", "REPORTS"]
@@ -217,7 +218,7 @@ Allowed prefixes for **branch names**. This is intentionally a coarser, workflow
217
218
 
218
219
  ### `types`
219
220
 
220
- `string[]`, default: `["feat", "feature", "fix", "docs", "style", "refactor", "test", "chore", "perf", "ci", "build", "revert"]`.
221
+ `string[]`, default: `["feat", "fix", "docs", "style", "refactor", "test", "chore", "perf", "ci", "build", "revert"]`.
221
222
 
222
223
  Allowed `type` words in **commit messages**, following [Conventional Commits](https://www.conventionalcommits.org/) style.
223
224
 
@@ -262,11 +263,12 @@ This copies two templates into your repo root, **without overwriting** anything
262
263
  testing-library/jest/vitest rules) with a module-boundary `no-restricted-imports` rule.
263
264
  - `.prettierrc.js` — matching Prettier config.
264
265
 
265
- The module list for the boundary rule isn't hardcoded — it's read straight from your
266
- `commitGuard.modules` array in `package.json` (the same list the commit-msg/branch-name hooks
267
- use for the `[MODULE]` tag), lowercased. If you haven't set `commitGuard.modules`, a small
268
- illustrative default (`auth`, `billing`, `reports`, `settings`) is used open `.eslintrc.js`
269
- and replace it, or just fill in `commitGuard.modules` and it picks it up automatically.
266
+ The module list for the boundary rule isn't hardcoded — it's read straight from the `modules`
267
+ array in your root `.commitsheriffrc.json` (the same list the commit-msg/branch-name hooks use
268
+ for the `[MODULE]` tag), lowercased. For older installs still using `package.json`'s
269
+ `commitGuard.modules`, that's used as a fallback. If neither is set, a small illustrative default
270
+ (`auth`, `billing`, `reports`, `settings`) is used open `.eslintrc.js` and replace it, or just
271
+ fill in `modules` in `.commitsheriffrc.json` and it picks it up automatically.
270
272
 
271
273
  This template assumes a `src/app/<module>/...` folder layout with `<module>.public.ts` barrel
272
274
  files; adjust the paths inside `.eslintrc.js` if your structure differs.
@@ -282,13 +284,24 @@ npm install --save-dev typescript @typescript-eslint/parser @typescript-eslint/e
282
284
 
283
285
  ## Examples
284
286
 
285
- **Minimal project (no modules):**
287
+ **Minimal project (no modules)** — `.commitsheriffrc.json`:
286
288
 
287
289
  ```json
288
- "commitGuard": {
290
+ {
289
291
  "useModules": false,
290
292
  "project": "PROJ",
291
- "branchTypes": ["feature", "bugfix", "hotfix", "improvement", "refactor", "release", "chore", "docs", "test", "spike"],
293
+ "branchTypes": [
294
+ "feature",
295
+ "bugfix",
296
+ "hotfix",
297
+ "improvement",
298
+ "refactor",
299
+ "release",
300
+ "chore",
301
+ "docs",
302
+ "test",
303
+ "spike"
304
+ ],
292
305
  "types": ["feat", "fix", "docs", "chore", "test"]
293
306
  }
294
307
  ```
@@ -298,14 +311,25 @@ git checkout -b bugfix/PROJ-1093-fix-validation
298
311
  git commit -m "(PROJ-1093) fix: correct validation on empty input"
299
312
  ```
300
313
 
301
- **Project split into modules, with a locked list:**
314
+ **Project split into modules, with a locked list** — `.commitsheriffrc.json`:
302
315
 
303
316
  ```json
304
- "commitGuard": {
317
+ {
305
318
  "useModules": true,
306
319
  "project": "ACME",
307
320
  "modules": ["AUTH", "BILLING", "REPORTS"],
308
- "branchTypes": ["feature", "bugfix", "hotfix", "improvement", "refactor", "release", "chore", "docs", "test", "spike"],
321
+ "branchTypes": [
322
+ "feature",
323
+ "bugfix",
324
+ "hotfix",
325
+ "improvement",
326
+ "refactor",
327
+ "release",
328
+ "chore",
329
+ "docs",
330
+ "test",
331
+ "spike"
332
+ ],
309
333
  "types": ["feat", "fix", "docs", "chore", "test"]
310
334
  }
311
335
  ```
@@ -324,7 +348,7 @@ npm install commit-sheriff@latest --save-dev
324
348
  npx commit-sheriff init
325
349
  ```
326
350
 
327
- `npm install` updates the package; `npx commit-sheriff init` re-copies the (possibly changed) `.husky/commit-msg` and `.husky/pre-commit` scripts. It will **not** touch your existing `commitGuard` or `lint-staged` config in `package.json`.
351
+ `npm install` updates the package; `npx commit-sheriff init` re-copies the (possibly changed) `.husky/commit-msg` and `.husky/pre-commit` scripts. It will **not** touch your existing `.commitsheriffrc.json` (or legacy `commitGuard` in `package.json`) or `lint-staged` config.
328
352
 
329
353
  ## Skipping / bypassing hooks
330
354
 
@@ -339,7 +363,7 @@ Merge, revert, `fixup!`, and `squash!` commits are already exempted from the com
339
363
  ## Troubleshooting
340
364
 
341
365
  **"Could not load commitGuard config — is Node.js installed and is this running from the repo root?"**
342
- The hook runs `node -e "..."` against `./package.json`. Make sure Node.js is on your `PATH` and that you're committing from the repository root (or that your Git client runs hooks with the repo root as the working directory — some GUI clients get this wrong).
366
+ The hook runs `node -e "..."` against `./.commitsheriffrc.json` (falling back to `./package.json`'s `commitGuard` block for older installs). Make sure Node.js is on your `PATH` and that you're committing from the repository root (or that your Git client runs hooks with the repo root as the working directory — some GUI clients get this wrong).
343
367
 
344
368
  **A commit is accepted even though the message looks wrong**
345
369
  Check which branch/tool you actually committed from — hooks only run for the local Git client that has Husky's `core.hooksPath` configured (`git config core.hooksPath` should print `.husky/_`). GUI clients or CI systems that bypass local hooks (or commit via the GitHub/GitLab API) will not trigger them.
@@ -348,7 +372,7 @@ Check which branch/tool you actually committed from — hooks only run for the l
348
372
  Detached HEAD is exempt, but a normal new branch is checked immediately on first commit — rename it with `git branch -m <valid-name>` and try again.
349
373
 
350
374
  **`useModules: true` but every module code is accepted**
351
- That's expected if `commitGuard.modules` is empty/omitted — see [`modules`](#modules). Add an explicit list to restrict it.
375
+ That's expected if `modules` is empty/omitted in `.commitsheriffrc.json` — see [`modules`](#modules). Add an explicit list to restrict it.
352
376
 
353
377
  ## Releasing this package
354
378
 
@@ -24,7 +24,6 @@ const DEFAULT_COMMIT_GUARD = {
24
24
  ],
25
25
  types: [
26
26
  "feat",
27
- "feature",
28
27
  "fix",
29
28
  "docs",
30
29
  "style",
@@ -43,6 +42,8 @@ const DEFAULT_LINT_STAGED = {
43
42
  "*.{json,md,css,scss,html}": ["prettier --write"],
44
43
  };
45
44
 
45
+ const CONFIG_FILE_NAME = ".commitsheriffrc.json";
46
+
46
47
  function readPackageJson() {
47
48
  const pkgPath = path.join(cwd, "package.json");
48
49
  if (!fs.existsSync(pkgPath)) {
@@ -70,17 +71,31 @@ function copyHook(name) {
70
71
  console.log(`✔ .husky/${name} yazıldı`);
71
72
  }
72
73
 
74
+ function ensureConfigFile() {
75
+ const configPath = path.join(cwd, CONFIG_FILE_NAME);
76
+ const { pkg } = readPackageJson();
77
+
78
+ if (fs.existsSync(configPath)) {
79
+ console.log(`• ${CONFIG_FILE_NAME} artıq var, toxunulmadı`);
80
+ return;
81
+ }
82
+
83
+ if (pkg.commitGuard) {
84
+ console.log(
85
+ `• 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.`,
86
+ );
87
+ return;
88
+ }
89
+
90
+ fs.writeFileSync(configPath, JSON.stringify(DEFAULT_COMMIT_GUARD, null, 2) + "\n");
91
+ console.log(`✔ ${CONFIG_FILE_NAME} yazıldı`);
92
+ }
93
+
73
94
  function mergeConfig() {
74
95
  const { pkgPath, pkg } = readPackageJson();
75
96
  let changed = false;
76
97
 
77
- if (!pkg.commitGuard) {
78
- pkg.commitGuard = DEFAULT_COMMIT_GUARD;
79
- changed = true;
80
- console.log("✔ package.json → commitGuard konfiqurasiyası əlavə olundu");
81
- } else {
82
- console.log("• package.json-da commitGuard artıq var, toxunulmadı");
83
- }
98
+ ensureConfigFile();
84
99
 
85
100
  if (!pkg["lint-staged"]) {
86
101
  pkg["lint-staged"] = DEFAULT_LINT_STAGED;
@@ -125,7 +140,7 @@ function init() {
125
140
  }
126
141
 
127
142
  console.log(
128
- "\nHazırdır. package.json commitGuard bölməsində 'project' və lazım olsa 'modules' dəyərlərini tənzimləyin.\n",
143
+ `\nHazırdır. ${CONFIG_FILE_NAME} faylında 'project' və lazım olsa 'modules' dəyərlərini tənzimləyin.\n`,
129
144
  );
130
145
  }
131
146
 
@@ -162,7 +177,7 @@ function addEslintReact() {
162
177
  copyTemplateIfMissing("prettier.module-boundaries.js", ".prettierrc.js");
163
178
  console.log(
164
179
  "\nBu şablon TypeScript/React + modul-sərhəd (module-boundary) qaydaları üçündür.\n" +
165
- "Modul siyahısı package.json → commitGuard.modules-dən avtomatik oxunur (boşdursa nümunə\n" +
180
+ `Modul siyahısı ${CONFIG_FILE_NAME}.modules-dən avtomatik oxunur (boşdursa nümunə\n` +
166
181
  "siyahı istifadə olunur — .eslintrc.js içindəki şərhi oxuyun).\n\n" +
167
182
  "Lazımi devDependencies (özünüz quraşdırın, layihənizin real versiyalarına uyğun):\n" +
168
183
  ` npm install --save-dev ${ESLINT_REACT_DEV_DEPS.join(" ")}\n`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "commit-sheriff",
3
- "version": "1.2.0",
3
+ "version": "1.3.1",
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": {
@@ -50,35 +50,5 @@
50
50
  "*.{json,md,css,scss,html}": [
51
51
  "prettier --write"
52
52
  ]
53
- },
54
- "commitGuard": {
55
- "useModules": false,
56
- "project": "PROJ",
57
- "branchTypes": [
58
- "feature",
59
- "bugfix",
60
- "hotfix",
61
- "improvement",
62
- "refactor",
63
- "release",
64
- "chore",
65
- "docs",
66
- "test",
67
- "spike"
68
- ],
69
- "types": [
70
- "feat",
71
- "feature",
72
- "fix",
73
- "docs",
74
- "style",
75
- "refactor",
76
- "test",
77
- "chore",
78
- "perf",
79
- "ci",
80
- "build",
81
- "revert"
82
- ]
83
53
  }
84
54
  }
@@ -8,15 +8,20 @@ case "$commit_msg" in
8
8
  ;;
9
9
  esac
10
10
 
11
- # ── Load config from package.json "commitGuard" (falls back to sane defaults) ──
12
- # commitGuard: { "useModules": true, "modules": [...], "types": [...] }
11
+ # ── Load config: .commitsheriffrc.json at repo root (falls back to package.json "commitGuard"
12
+ # ── for older installs, then to sane defaults) ──────────────────────────────
13
+ # .commitsheriffrc.json: { "useModules": true, "modules": [...], "types": [...] }
13
14
  eval "$(node -e "
14
15
  const fs = require('fs');
15
16
  let cfg = {};
16
- try { cfg = (JSON.parse(fs.readFileSync('package.json', 'utf8')).commitGuard) || {}; } catch (e) {}
17
+ try {
18
+ cfg = JSON.parse(fs.readFileSync('.commitsheriffrc.json', 'utf8'));
19
+ } catch (e) {
20
+ try { cfg = (JSON.parse(fs.readFileSync('package.json', 'utf8')).commitGuard) || {}; } catch (e2) {}
21
+ }
17
22
  const useModules = cfg.useModules !== undefined ? cfg.useModules : true;
18
23
  const modules = (cfg.modules && cfg.modules.length ? cfg.modules.join('|') : '[A-Z]+');
19
- const types = (cfg.types || ['feat','feature','fix','docs','style','refactor','test','chore','perf','ci','build','revert']).join('|');
24
+ const types = (cfg.types || ['feat','fix','docs','style','refactor','test','chore','perf','ci','build','revert']).join('|');
20
25
  console.log('USE_MODULES=' + useModules);
21
26
  console.log('MODULES=\"' + modules + '\"');
22
27
  console.log('TYPES=\"' + types + '\"');
@@ -16,17 +16,28 @@
16
16
  * are the one place allowed to import every module directly (that's the whole point of a
17
17
  * router/registry), so the boundary rule is turned off just for those two files.
18
18
  *
19
- * Module list: instead of hardcoding module names, this reads `commitGuard.modules` from
20
- * package.json (the same list used by the commit-sheriff commit-msg/branch-name hooks — see
21
- * README), so the two conventions (module tags in commits/branches, and module boundaries in
22
- * imports) stay in sync automatically. If `commitGuard.modules` is empty/absent, a small
23
- * illustrative default is used instead replace it with your real module names.
19
+ * Module list: instead of hardcoding module names, this reads `modules` from the repo's
20
+ * `.commitsheriffrc.json` (the same list used by the commit-sheriff commit-msg/branch-name
21
+ * hooks — see README), so the two conventions (module tags in commits/branches, and module
22
+ * boundaries in imports) stay in sync automatically. For older installs that still keep their
23
+ * config under `package.json`'s `commitGuard` block, that location is used as a fallback. If
24
+ * neither is found, a small illustrative default is used instead — replace it with your real
25
+ * module names.
24
26
  */
25
27
 
26
28
  const fs = require("fs");
27
29
  const path = require("path");
28
30
 
29
31
  function loadAppModules() {
32
+ try {
33
+ const rcPath = path.join(__dirname, ".commitsheriffrc.json");
34
+ const rc = JSON.parse(fs.readFileSync(rcPath, "utf8"));
35
+ if (Array.isArray(rc.modules) && rc.modules.length) {
36
+ return rc.modules.map((m) => String(m).toLowerCase());
37
+ }
38
+ } catch {
39
+ // fall through to the legacy package.json lookup below
40
+ }
30
41
  try {
31
42
  const pkgPath = path.join(__dirname, "package.json");
32
43
  const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
@@ -1,11 +1,16 @@
1
1
  local_branch="$(git rev-parse --abbrev-ref HEAD 2>/dev/null)" || true
2
2
 
3
- # ── Load config from package.json "commitGuard" (falls back to sane defaults) ──
4
- # commitGuard: { "useModules": true, "project": "PROJ", "modules": [...], "branchTypes": [...] }
3
+ # ── Load config: .commitsheriffrc.json at repo root (falls back to package.json "commitGuard"
4
+ # ── for older installs, then to sane defaults) ──────────────────────────────
5
+ # .commitsheriffrc.json: { "useModules": true, "project": "PROJ", "modules": [...], "branchTypes": [...] }
5
6
  eval "$(node -e "
6
7
  const fs = require('fs');
7
8
  let cfg = {};
8
- try { cfg = (JSON.parse(fs.readFileSync('package.json', 'utf8')).commitGuard) || {}; } catch (e) {}
9
+ try {
10
+ cfg = JSON.parse(fs.readFileSync('.commitsheriffrc.json', 'utf8'));
11
+ } catch (e) {
12
+ try { cfg = (JSON.parse(fs.readFileSync('package.json', 'utf8')).commitGuard) || {}; } catch (e2) {}
13
+ }
9
14
  const useModules = cfg.useModules !== undefined ? cfg.useModules : true;
10
15
  const project = cfg.project || 'PROJ';
11
16
  const modules = (cfg.modules && cfg.modules.length ? cfg.modules.join('|') : '[A-Z]+');