commit-sheriff 1.7.0 → 1.7.2
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 -10
- package/bin/commit-sheriff.js +67 -10
- package/package.json +1 -1
- package/templates/pre-commit +5 -1
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
|
|
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
|
-
|
|
261
|
-
|
|
262
|
-
|
|
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.
|
|
265
|
-
|
|
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
|
|
|
@@ -285,12 +285,27 @@ merge step required, ever:
|
|
|
285
285
|
- `eslint.config.mjs` — the entry point ESLint actually reads, generated fresh every run to import
|
|
286
286
|
the file above. **If you already had an `eslint.config.mjs` (e.g. Next.js's own
|
|
287
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
|
|
289
|
-
|
|
290
|
-
own generated config uses) so you don't lose Next-specific linting; `eslint-config-next` is
|
|
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
|
|
291
290
|
auto-installed too if it's missing. Any other hand-written customizations in your previous
|
|
292
291
|
`eslint.config.mjs` are not preserved — back it up under a different name first if you need them.
|
|
293
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
|
+
|
|
294
309
|
`.prettierrc.js` is written/refreshed the same way — **always overwritten** with the latest
|
|
295
310
|
template (same behavior as the `commit-msg`/`pre-commit` hooks). This trade-off is intentional:
|
|
296
311
|
zero manual steps for the common case, at the cost of not preserving bespoke prior ESLint/Prettier
|
package/bin/commit-sheriff.js
CHANGED
|
@@ -38,10 +38,9 @@ const DEFAULT_COMMIT_GUARD = {
|
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
const DEFAULT_LINT_STAGED = {
|
|
41
|
-
// --max-warnings=0
|
|
42
|
-
//
|
|
43
|
-
|
|
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
|
|
|
@@ -253,6 +252,25 @@ function writeFileOverwrite(destName, content) {
|
|
|
253
252
|
return dest;
|
|
254
253
|
}
|
|
255
254
|
|
|
255
|
+
// eslint-config-next changed shape across majors: up through v15 it ships an eslintrc-style
|
|
256
|
+
// object (`module.exports = { extends: [...] }`) meant to be pulled in via FlatCompat's legacy
|
|
257
|
+
// `compat.extends("next/core-web-vitals", "next/typescript")` bridge (exactly what
|
|
258
|
+
// `create-next-app` itself generates for v15 projects). From v16 on it ships a native flat-config
|
|
259
|
+
// array as the default export of `eslint-config-next/core-web-vitals` / `/typescript` instead —
|
|
260
|
+
// running the *old* FlatCompat-string pattern against a v16 install crashes with
|
|
261
|
+
// "TypeError: Converting circular structure to JSON" deep inside `@eslint/eslintrc`'s config
|
|
262
|
+
// validator (confirmed by reproducing it in a scratch project — the crash reproduces with only
|
|
263
|
+
// Next's own extends, no module-boundary rules involved at all). `create-next-app@latest` itself
|
|
264
|
+
// already generates the new native-import form for v16, so we mirror that here rather than the
|
|
265
|
+
// older FlatCompat form once eslint-config-next resolves to 16+.
|
|
266
|
+
function installedVersion(pkgName) {
|
|
267
|
+
try {
|
|
268
|
+
return require(path.join(cwd, "node_modules", pkgName, "package.json")).version;
|
|
269
|
+
} catch {
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
256
274
|
function buildEslintEntryConfig(hasNext) {
|
|
257
275
|
if (!hasNext) {
|
|
258
276
|
return (
|
|
@@ -260,9 +278,44 @@ function buildEslintEntryConfig(hasNext) {
|
|
|
260
278
|
`export default [...moduleBoundaries];\n`
|
|
261
279
|
);
|
|
262
280
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
281
|
+
|
|
282
|
+
const version = installedVersion("eslint-config-next");
|
|
283
|
+
const major = version ? parseInt(version.split(".")[0], 10) : null;
|
|
284
|
+
// Default to the modern (16+) form when the version can't be determined (e.g. install failed
|
|
285
|
+
// offline) — that's what any newly-scaffolded Next project will have going forward.
|
|
286
|
+
const useNativeFlatExport = major === null || major >= 16;
|
|
287
|
+
|
|
288
|
+
if (useNativeFlatExport) {
|
|
289
|
+
// eslint-config-next's native flat array already registers plugins like "react",
|
|
290
|
+
// "react-hooks", "import", "jsx-a11y" and "@typescript-eslint". Our own module-boundary
|
|
291
|
+
// template is built through @eslint/eslintrc's FlatCompat, which independently resolves
|
|
292
|
+
// those same plugin *names* into separate object instances — even though they load the same
|
|
293
|
+
// underlying package, ESLint's flat config throws "Cannot redefine plugin ..." when a plugin
|
|
294
|
+
// name is registered twice with two different object references (confirmed by reproducing it
|
|
295
|
+
// in a scratch Next 16 project). Only one registration per plugin name is actually needed, so
|
|
296
|
+
// any config entry from our own template that re-declares a plugin Next.js already registered
|
|
297
|
+
// has that redundant "plugins" key stripped below — its "rules" keep working against the
|
|
298
|
+
// plugin instance Next.js already loaded.
|
|
299
|
+
return (
|
|
300
|
+
`import nextCoreWebVitals from "eslint-config-next/core-web-vitals";\n` +
|
|
301
|
+
`import nextTypescript from "eslint-config-next/typescript";\n` +
|
|
302
|
+
`import moduleBoundaries from "./${RULES_FILE}";\n\n` +
|
|
303
|
+
`const nextConfigs = [...nextCoreWebVitals, ...nextTypescript];\n` +
|
|
304
|
+
`const pluginsNextAlreadyRegisters = new Set(\n` +
|
|
305
|
+
` nextConfigs.flatMap((entry) => (entry.plugins ? Object.keys(entry.plugins) : [])),\n` +
|
|
306
|
+
`);\n` +
|
|
307
|
+
`const dedupedModuleBoundaries = moduleBoundaries.map((entry) => {\n` +
|
|
308
|
+
` if (!entry.plugins) return entry;\n` +
|
|
309
|
+
` const ownPlugins = Object.fromEntries(\n` +
|
|
310
|
+
` Object.entries(entry.plugins).filter(([name]) => !pluginsNextAlreadyRegisters.has(name)),\n` +
|
|
311
|
+
` );\n` +
|
|
312
|
+
` const { plugins, ...rest } = entry;\n` +
|
|
313
|
+
` return Object.keys(ownPlugins).length ? { ...rest, plugins: ownPlugins } : rest;\n` +
|
|
314
|
+
`});\n\n` +
|
|
315
|
+
`export default [...nextConfigs, ...dedupedModuleBoundaries];\n`
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
|
|
266
319
|
return (
|
|
267
320
|
`import { fileURLToPath } from "node:url";\n` +
|
|
268
321
|
`import path from "node:path";\n` +
|
|
@@ -284,6 +337,13 @@ function addEslintReact() {
|
|
|
284
337
|
const hasNext = Boolean(deps.next);
|
|
285
338
|
|
|
286
339
|
copyTemplateOverwrite("eslint.config.module-boundaries.mjs", RULES_FILE);
|
|
340
|
+
|
|
341
|
+
// eslint-config-next must actually be installed (and its version readable) *before* generating
|
|
342
|
+
// the entry config below, so the version check above sees real, resolved data instead of "not
|
|
343
|
+
// installed yet".
|
|
344
|
+
const extraDeps = hasNext && !deps["eslint-config-next"] ? ["eslint-config-next"] : [];
|
|
345
|
+
ensureDevDeps(ESLINT_REACT_DEV_DEPS.concat(extraDeps));
|
|
346
|
+
|
|
287
347
|
writeFileOverwrite("eslint.config.mjs", buildEslintEntryConfig(hasNext));
|
|
288
348
|
copyTemplateOverwrite("prettier.module-boundaries.js", ".prettierrc.js");
|
|
289
349
|
|
|
@@ -303,9 +363,6 @@ function addEslintReact() {
|
|
|
303
363
|
: ""),
|
|
304
364
|
);
|
|
305
365
|
|
|
306
|
-
const extraDeps = hasNext && !deps["eslint-config-next"] ? ["eslint-config-next"] : [];
|
|
307
|
-
ensureDevDeps(ESLINT_REACT_DEV_DEPS.concat(extraDeps));
|
|
308
|
-
|
|
309
366
|
if (fs.existsSync(path.join(cwd, ".eslintrc.js"))) {
|
|
310
367
|
console.log(
|
|
311
368
|
"\nDiqqət: layihənizdə hələ də köhnə .eslintrc.js var — silin. Flat config varkən\n" +
|
package/package.json
CHANGED
package/templates/pre-commit
CHANGED
|
@@ -77,7 +77,11 @@ 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
|
-
|
|
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
87
|
echo " package.json-da 'lint-staged' konfiqurasiyası var, amma paketin özü"
|