{"_id":"@amashukov/eslint-plugin-mess-detector","_rev":"3-48b927ff4a2e5af232dcce345c709d5a","name":"@amashukov/eslint-plugin-mess-detector","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@amashukov/eslint-plugin-mess-detector","version":"0.1.0","keywords":["eslint","eslintplugin","eslint-plugin","typescript","code-quality","static-analysis"],"author":{"name":"Andrei Mashukov","email":"a.mashukoff@gmail.com"},"license":"MIT","_id":"@amashukov/eslint-plugin-mess-detector@0.1.0","maintainers":[{"name":"mashuk","email":"a.mashukov@yandex.ru"}],"homepage":"https://github.com/AndreyMashukov/eslint-plugin-mess-detector#readme","bugs":{"url":"https://github.com/AndreyMashukov/eslint-plugin-mess-detector/issues"},"dist":{"shasum":"9d4d047ff93ed28a77076e06f74e049d8ee60dac","tarball":"https://registry.npmjs.org/@amashukov/eslint-plugin-mess-detector/-/eslint-plugin-mess-detector-0.1.0.tgz","fileCount":22,"integrity":"sha512-D+7jD6zxJc77Ob78UybobdpUrd6HCTqJ7tkp+Kr3/9MO7MCKb/mQtimQL1KYnrzmVmN0pkMaotokJpZdStJv6w==","signatures":[{"sig":"MEUCIHR9bLDNwEaPkQKVZ2yWwt3uJjxf3ifRfmYxzGHOTLTIAiEAlwflTDjzpiLVlQ3pJSSDv00o3B+/dhSnYUug+tV6QzI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":252057},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=22.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js","require":"./dist/index.cjs"},"./package.json":"./package.json"},"gitHead":"826d2afeb3def827c127a1380d7372c4a21d0bab","scripts":{"lint":"eslint src","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run build"},"_npmUser":{"name":"mashuk","email":"a.mashukov@yandex.ru"},"repository":{"url":"git+https://github.com/AndreyMashukov/eslint-plugin-mess-detector.git","type":"git"},"_npmVersion":"10.9.8","description":"ESLint plugin that fails the build on low-signal code patterns: inline narration, suppression directives, defensive nullish guards, tautological JSDoc, banal error wrappers, env branching, direct Date.now, type-only test assertions, TODO markers.","directories":{},"_nodeVersion":"22.22.3","dependencies":{"minimatch":"^10.0.1","@typescript-eslint/utils":"^8.18.0"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.5","eslint":"^9.17.0","vitest":"^2.1.8","typescript":"^5.7.2","@types/node":"^22.10.2","@typescript-eslint/parser":"^8.18.0","@typescript-eslint/rule-tester":"^8.18.0"},"peerDependencies":{"eslint":"^9.0.0","typescript":">=5.0.0"},"peerDependenciesMeta":{"typescript":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/eslint-plugin-mess-detector_0.1.0_1779896302646_0.8249868016202602","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@amashukov/eslint-plugin-mess-detector","version":"0.1.1","keywords":["eslint","eslintplugin","eslint-plugin","typescript","code-quality","static-analysis"],"author":{"name":"Andrei Mashukov","email":"a.mashukoff@gmail.com"},"license":"MIT","_id":"@amashukov/eslint-plugin-mess-detector@0.1.1","maintainers":[{"name":"mashuk","email":"a.mashukov@yandex.ru"}],"homepage":"https://github.com/AndreyMashukov/eslint-plugin-mess-detector#readme","bugs":{"url":"https://github.com/AndreyMashukov/eslint-plugin-mess-detector/issues"},"dist":{"shasum":"b681271f985764e7f07863296380ccbade075665","tarball":"https://registry.npmjs.org/@amashukov/eslint-plugin-mess-detector/-/eslint-plugin-mess-detector-0.1.1.tgz","fileCount":22,"integrity":"sha512-/CI2Wn/uH3gXBDTzvuYqHSJlQKXY9JcXkal+QmlfHS+qD6mRZxa+1KLCWV2xV3yAJFmg+hOmMkMbwIIyoumynw==","signatures":[{"sig":"MEQCIClQD3KlWULeXS3VvtF35XAPZYPQUtQUs/FfC3xEg0bZAiBTRppW+Y+R2WYG2dsFiP3BM23zGhlJjBr9hyUYITUNQQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":254016},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=22.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js","require":"./dist/index.cjs"},"./package.json":"./package.json"},"gitHead":"911c801b3d954b3316b28deeddd82a3545a1f8cd","scripts":{"lint":"eslint src","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"npm run build"},"_npmUser":{"name":"mashuk","email":"a.mashukov@yandex.ru"},"repository":{"url":"git+https://github.com/AndreyMashukov/eslint-plugin-mess-detector.git","type":"git"},"_npmVersion":"10.9.8","description":"ESLint plugin that fails the build on low-signal code patterns: inline narration, suppression directives, defensive nullish guards, tautological JSDoc, banal error wrappers, env branching, direct Date.now, type-only test assertions, TODO markers.","directories":{},"_nodeVersion":"22.22.3","dependencies":{"minimatch":"^10.0.1","@typescript-eslint/utils":"^8.18.0"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.5","eslint":"^9.17.0","vitest":"^2.1.8","typescript":"^5.7.2","@types/node":"^22.10.2","@typescript-eslint/parser":"^8.18.0","@typescript-eslint/rule-tester":"^8.18.0"},"peerDependencies":{"eslint":"^9.0.0","typescript":">=5.0.0"},"peerDependenciesMeta":{"typescript":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/eslint-plugin-mess-detector_0.1.1_1779898221430_0.7545072322350526","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@amashukov/eslint-plugin-mess-detector","version":"0.2.0","description":"ESLint plugin that fails the build on low-signal code patterns: inline narration, suppression directives, defensive nullish guards, tautological JSDoc, banal error wrappers, env branching, direct Date.now, type-only test assertions, TODO markers.","keywords":["eslint","eslintplugin","eslint-plugin","typescript","code-quality","static-analysis"],"homepage":"https://github.com/AndreyMashukov/eslint-plugin-mess-detector#readme","bugs":{"url":"https://github.com/AndreyMashukov/eslint-plugin-mess-detector/issues"},"repository":{"type":"git","url":"git+https://github.com/AndreyMashukov/eslint-plugin-mess-detector.git"},"license":"MIT","author":{"name":"Andrei Mashukov","email":"a.mashukoff@gmail.com"},"type":"module","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs","default":"./dist/index.js"},"./package.json":"./package.json"},"main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","scripts":{"build":"tsup","test":"vitest run","test:watch":"vitest","typecheck":"tsc --noEmit","lint":"eslint src","prepublishOnly":"npm run build"},"dependencies":{"@typescript-eslint/utils":"^8.18.0","minimatch":"^10.0.1"},"devDependencies":{"@types/node":"^22.10.2","@typescript-eslint/parser":"^8.18.0","@typescript-eslint/rule-tester":"^8.18.0","eslint":"^9.17.0","tsup":"^8.3.5","typescript":"^5.7.2","vitest":"^2.1.8"},"peerDependencies":{"eslint":"^9.0.0","typescript":">=5.0.0"},"peerDependenciesMeta":{"typescript":{"optional":true}},"engines":{"node":">=22.0.0"},"_id":"@amashukov/eslint-plugin-mess-detector@0.2.0","gitHead":"8779df36d53ce118f79beeb933ed7b03c7760921","_nodeVersion":"22.22.3","_npmVersion":"10.9.8","dist":{"integrity":"sha512-70l/lf8RUUlQSUlxz6MkdJe6seC9+vy2+KAc60f82VDHeZxvbuIH4PwqVSfLotvlr8imyIGwJzgkeJD+9iSQYQ==","shasum":"d7561c8ca9ac0d35901ed6383bd224c5b6058550","tarball":"https://registry.npmjs.org/@amashukov/eslint-plugin-mess-detector/-/eslint-plugin-mess-detector-0.2.0.tgz","fileCount":23,"unpackedSize":272094,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCLM70uQUVOJCPwWMWcavRyaEvvxm6ZElPq2Jbl4uV1/QIhAN6bvSJMxp7yHu2bhikUZvH7slDZRfPT7z09bxS3YNpJ"}]},"_npmUser":{"name":"mashuk","email":"a.mashukov@yandex.ru"},"directories":{},"maintainers":[{"name":"mashuk","email":"a.mashukov@yandex.ru"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/eslint-plugin-mess-detector_0.2.0_1779905301039_0.6433401006918311"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-27T15:38:22.499Z","modified":"2026-05-27T18:08:21.374Z","0.1.0":"2026-05-27T15:38:22.784Z","0.1.1":"2026-05-27T16:10:21.597Z","0.2.0":"2026-05-27T18:08:21.247Z"},"bugs":{"url":"https://github.com/AndreyMashukov/eslint-plugin-mess-detector/issues"},"author":{"name":"Andrei Mashukov","email":"a.mashukoff@gmail.com"},"license":"MIT","homepage":"https://github.com/AndreyMashukov/eslint-plugin-mess-detector#readme","keywords":["eslint","eslintplugin","eslint-plugin","typescript","code-quality","static-analysis"],"repository":{"type":"git","url":"git+https://github.com/AndreyMashukov/eslint-plugin-mess-detector.git"},"description":"ESLint plugin that fails the build on low-signal code patterns: inline narration, suppression directives, defensive nullish guards, tautological JSDoc, banal error wrappers, env branching, direct Date.now, type-only test assertions, TODO markers.","maintainers":[{"name":"mashuk","email":"a.mashukov@yandex.ru"}],"readme":"# @amashukov/eslint-plugin-mess-detector\n\n[![CI](https://img.shields.io/github/actions/workflow/status/AndreyMashukov/eslint-plugin-mess-detector/ci.yml?branch=main&label=CI)](https://github.com/AndreyMashukov/eslint-plugin-mess-detector/actions)\n[![npm](https://img.shields.io/npm/v/@amashukov/eslint-plugin-mess-detector)](https://www.npmjs.com/package/@amashukov/eslint-plugin-mess-detector)\n[![License](https://img.shields.io/npm/l/@amashukov/eslint-plugin-mess-detector)](LICENSE)\n[![Node](https://img.shields.io/node/v/@amashukov/eslint-plugin-mess-detector)](package.json)\n\nESLint plugin that **fails the build** on the low-signal patterns that bloat a TypeScript / JavaScript codebase: inline narration, suppression directives, defensive nullish guards, tautological JSDoc, banal `throw new Error(...)` wrappers, runtime environment branching, scattered `process.env` reads, direct `Date.now()`, type-only test assertions, and `TODO` / `FIXME` markers.\n\nCounterpart of [`go-lint`](https://github.com/AndreyMashukov/go-lint) for Go and [`rector-php-rules`](https://github.com/AndreyMashukov/rector-php-rules) for PHP. Same philosophy, different syntax tree.\n\n---\n\n## Why\n\nLow-effort code tends to drift in the same direction every time:\n\n- Each line gets an inline `//` comment that paraphrases the line itself.\n- Every exported function gets a JSDoc that restates its signature in English (`/** Add adds two numbers */`).\n- `x == null` checks appear on values that, by their TypeScript type, can never be `null` or `undefined`.\n- Tests assert `toBeDefined()`, `toBeTruthy()`, or `toBeInstanceOf(...)` — checks the type system already does — instead of pinning the actual value.\n- Errors get wrapped in `throw new Error(\"failed to read: \" + err.message)` — strictly worse than re-throwing the original because it lengthens the chain and drops the stack.\n- `if (process.env.NODE_ENV === \"production\")` branches sneak into production code, creating divergent test- and prod-only paths.\n- `// eslint-disable-next-line` appears next to anything the linter complained about.\n\nThis plugin is a single hard gate that flags every one of these in one pass. It does not autofix. The point is to make the human re-think the code, not regex it.\n\n---\n\n## Install\n\n```bash\nnpm install --save-dev @amashukov/eslint-plugin-mess-detector\n```\n\nRequires ESLint v9+ and Node 22+. For the type-aware rules (`no-dead-nullish-guard`, `no-redundant-optional-chain`) you also need `@typescript-eslint/parser` with `parserOptions.project` set.\n\n---\n\n## Prerequisites — parsers\n\nESLint parses every file into an AST **before** any plugin rule runs. For source that is not plain JavaScript, the host project must register the matching parser in flat config — otherwise lint exits with `Parsing error: Unexpected token` and the plugin never gets a chance to inspect the code. This is the standard ESLint contract; mess-detector follows it just like every other plugin in the ecosystem.\n\nThe three common setups:\n\n**Plain TypeScript.** Install `@typescript-eslint/parser` and register it for `.ts` files:\n\n```js\nimport mess from \"@amashukov/eslint-plugin-mess-detector\";\nimport tsParser from \"@typescript-eslint/parser\";\n\nexport default [\n  { files: [\"**/*.ts\"], languageOptions: { parser: tsParser } },\n  { files: [\"**/*.ts\"], plugins: { \"mess-detector\": mess }, rules: mess.configs.recommended.rules },\n];\n```\n\n**Vue single-file components.** Install `vue-eslint-parser` (which delegates `<script lang=\"ts\">` blocks to `@typescript-eslint/parser`):\n\n```js\nimport mess from \"@amashukov/eslint-plugin-mess-detector\";\nimport vueParser from \"vue-eslint-parser\";\nimport tsParser from \"@typescript-eslint/parser\";\n\nexport default [\n  {\n    files: [\"**/*.vue\"],\n    languageOptions: { parser: vueParser, parserOptions: { parser: tsParser } },\n  },\n  { plugins: { \"mess-detector\": mess }, rules: mess.configs.recommended.rules },\n];\n```\n\n**Nuxt.** Add `@nuxt/eslint` to the `modules` list in `nuxt.config.ts`; `nuxt prepare` then emits `.nuxt/eslint.config.mjs` with TS + Vue parsers already wired. Wrap your config with `withNuxt(...)`:\n\n```js\nimport mess from \"@amashukov/eslint-plugin-mess-detector\";\nimport withNuxt from \"./.nuxt/eslint.config.mjs\";\n\nexport default withNuxt({\n  files: [\"**/*.{js,mjs,cjs,ts,vue}\"],\n  plugins: { \"mess-detector\": mess },\n  rules: mess.configs.recommended.rules,\n});\n```\n\nIf `yarn lint` reports `Parsing error` from any rule, the fix is a missing parser, not a plugin bug.\n\n---\n\n## Usage (flat config, ESLint v9)\n\n### Plain (no type information)\n\n```js\n// eslint.config.js\nimport mess from \"@amashukov/eslint-plugin-mess-detector\";\n\nexport default [\n  mess.configs.recommended,\n];\n```\n\nThis enables the 11 non-type-aware rules.\n\n### With type information\n\n```js\n// eslint.config.js\nimport mess from \"@amashukov/eslint-plugin-mess-detector\";\nimport tseslint from \"typescript-eslint\";\n\nexport default [\n  ...tseslint.configs.recommendedTypeChecked,\n  {\n    languageOptions: {\n      parserOptions: {\n        project: \"./tsconfig.json\",\n        tsconfigRootDir: import.meta.dirname,\n      },\n    },\n  },\n  mess.configs[\"recommended-typed\"],\n];\n```\n\nThis adds the two type-aware rules on top.\n\n---\n\n## Rules\n\n| # | Rule | Type-aware | Catches |\n|---|---|---|---|\n| 1 | [`no-todo`](docs/rules/no-todo.md) | no | `TODO` / `FIXME` / `XXX` / `HACK` markers (owned or not) |\n| 2 | [`no-suppression-comments`](docs/rules/no-suppression-comments.md) | no | `// eslint-disable*`, `// @ts-ignore`, `// @ts-expect-error`, `// @ts-nocheck` |\n| 3 | [`no-inline-narration`](docs/rules/no-inline-narration.md) | no | comments inside function bodies |\n| 4 | [`no-process-env-outside-config`](docs/rules/no-process-env-outside-config.md) | no | `process.env.X` outside `config/` and `*.config.*` files |\n| 5 | [`no-env-branch`](docs/rules/no-env-branch.md) | no | runtime branching on `\"prod\"` / `\"dev\"` / `\"test\"` strings |\n| 6 | [`no-direct-date-now`](docs/rules/no-direct-date-now.md) | no | `Date.now()`, `new Date()`, `performance.now()` outside `clock/` |\n| 7 | [`no-redundant-bool-return`](docs/rules/no-redundant-bool-return.md) | no | `if (c) return true; return false;` |\n| 8 | [`no-banal-error-wrap`](docs/rules/no-banal-error-wrap.md) | no | `throw new Error(\"failed to X: \" + err.message)` |\n| 9 | [`no-catch-rethrow-banal`](docs/rules/no-catch-rethrow-banal.md) | no | `catch (e) { throw new Error(e.message); }` |\n| 10 | [`no-type-only-assertion`](docs/rules/no-type-only-assertion.md) | no | `expect(x).toBeDefined()` / `toBeInstanceOf(...)` etc. |\n| 11 | [`no-tautological-jsdoc`](docs/rules/no-tautological-jsdoc.md) | no | JSDoc that restates the function name |\n| 12 | [`no-silent-fallback`](docs/rules/no-silent-fallback.md) | no | `??`, `??=`, and `\\|\\|` with a literal default — silent fallbacks for missing values |\n| 13 | [`no-dead-nullish-guard`](docs/rules/no-dead-nullish-guard.md) | **yes** | `x === null` on a type that admits neither `null` nor `undefined` |\n| 14 | [`no-redundant-optional-chain`](docs/rules/no-redundant-optional-chain.md) | **yes** | `?.` on a type that admits neither `null` nor `undefined` |\n\n---\n\n## Configs\n\nTwo flat-config presets:\n\n- `mess.configs.recommended` — rules 1–12. Works on plain ESLint without `parserOptions.project`.\n- `mess.configs[\"recommended-typed\"]` — `recommended` plus the two type-aware rules. Requires `@typescript-eslint/parser` with `parserOptions.project`.\n\n---\n\n## Overlap with widely-used plugins\n\nSeveral of these patterns are partly covered elsewhere. This plugin keeps all 13 anyway because the value is \"single hard gate, no plugin sprawl\":\n\n| Concern | Overlapping plugin / rule | This plugin |\n|---|---|---|\n| `TODO` / `FIXME` markers | core ESLint `no-warning-comments` (off by default, configurable) | `no-todo` (strict, no carve-outs) |\n| `// eslint-disable*` directives | `eslint-plugin-eslint-comments/no-use` | `no-suppression-comments` (strict, also `@ts-*`) |\n| Inline comments | core `no-inline-comments` (only same-line) | `no-inline-narration` (whole function body) |\n| `process.env` | core `no-process-env` | `no-process-env-outside-config` (glob carve-out) |\n| `if (cond) return true` | `eslint-plugin-sonarjs/prefer-single-boolean-return` | `no-redundant-bool-return` |\n| `new Error(...)` content | `eslint-plugin-unicorn/error-message` | `no-banal-error-wrap` (banal-verb regex) |\n| `// @ts-ignore` | `@typescript-eslint/ban-ts-comment` | rolled into `no-suppression-comments` |\n| `?.` on non-nullable | `@typescript-eslint/no-unnecessary-condition` | `no-redundant-optional-chain` |\n| `catch { throw ... }` | `@typescript-eslint/no-useless-catch` | `no-catch-rethrow-banal` (also flags message-only wrap) |\n\n---\n\n## Comparison with sibling repos\n\n| Concern | [go-lint](https://github.com/AndreyMashukov/go-lint) | [rector-php-rules](https://github.com/AndreyMashukov/rector-php-rules) | eslint-plugin-mess-detector |\n|---|---|---|---|\n| Inline narration | `noinlinecomment` | `NoCommentsOutsideInterfaceMethodDocBlockRector` | `no-inline-narration` |\n| Tautological doc | `norobotgodoc` | — | `no-tautological-jsdoc` |\n| Suppression directives | `nolintdirective` | `NoPhpstanIgnoreRector` | `no-suppression-comments` |\n| Env access outside config | `nogetenv` | `NoSuperglobalAccessRector` | `no-process-env-outside-config` |\n| Env branching in src | `noenvbranch` | `NoEnvironmentCheckInSrcRector` | `no-env-branch` |\n| Real-clock injection | `notimenow` | `RequirePsrClockInterfaceRector` | `no-direct-date-now` |\n| Type-only test assertions | `notypeonlyassert` | `NoTypeOnlyAssertionsInTestsRector` | `no-type-only-assertion` |\n| Banal error wrapping | `noerrorwrapbanality` | — | `no-banal-error-wrap` + `no-catch-rethrow-banal` |\n| `TODO` / `FIXME` markers | `notodo` | `NoTodoCommentRector` | `no-todo` |\n| `if cond return true` | `noredundantif` | — | `no-redundant-bool-return` |\n| Nullish guard on non-nullable | `nodeadguard` | — | `no-dead-nullish-guard` |\n| Redundant optional chain | — | — | `no-redundant-optional-chain` |\n\n---\n\n## Design notes\n\n- **No configuration file beyond ESLint's own.** Each rule is either on or off via the flat-config block. Policy lives in `eslint.config.js`, not a side-car YAML.\n- **No autofixers.** Most findings need restructuring, not a regex. The point is to make the human re-think the code.\n- **No per-line waiver.** If a rule is wrong for your project, drop it from the config. Per-line `// eslint-disable` waivers turn into silent debt — and `no-suppression-comments` flags them anyway.\n- **Self-hosted.** The plugin's own source is linted by its own `recommended-typed` config with zero findings.\n\n---\n\n## Development\n\n```bash\nmake install   # docker-driven npm install\nmake build     # tsup → dist/index.{js,cjs,d.ts}\nmake test      # vitest\nmake lint      # self-host gate (eslint on src/)\nmake typecheck # tsc --noEmit\n```\n\nAll targets run inside `node:22-alpine` via docker — no host Node required.\n\n---\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n","readmeFilename":"README.md"}