eslint-plugin-conventions 5.0.0 → 5.2.0

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
@@ -1,9 +1,9 @@
1
1
  <p align="center">
2
- <a href="https://eslint.interlace.tools/?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-conventions" target="blank"><img src="https://eslint.interlace.tools/logos/interlace.svg" alt="Interlace" height="90" /></a>
2
+ <a href="https://eslint.interlace.tools/?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-conventions" target="blank"><picture><source media="(prefers-color-scheme: dark)" srcset="https://eslint.interlace.tools/logos/interlace-dark.svg"><img src="https://eslint.interlace.tools/logos/interlace-light.svg" alt="Interlace" height="90" /></picture></a>
3
3
  &nbsp;&nbsp;
4
- <a href="https://oxc.rs" target="_blank"><img src="https://eslint.interlace.tools/logos/oxlint.svg" alt="oxlint" height="90" /></a>
4
+ <a href="https://oxc.rs" target="_blank"><picture><source media="(prefers-color-scheme: dark)" srcset="https://eslint.interlace.tools/logos/oxlint-dark.svg"><img src="https://eslint.interlace.tools/logos/oxlint-light.svg" alt="oxlint" height="90" /></picture></a>
5
5
  &nbsp;&nbsp;
6
- <a href="https://eslint.org" target="_blank"><img src="https://eslint.interlace.tools/logos/eslint.svg" alt="ESLint" height="90" /></a>
6
+ <a href="https://eslint.org" target="_blank"><picture><source media="(prefers-color-scheme: dark)" srcset="https://eslint.interlace.tools/logos/eslint-dark.svg"><img src="https://eslint.interlace.tools/logos/eslint-light.svg" alt="ESLint" height="90" /></picture></a>
7
7
  </p>
8
8
 
9
9
  <p align="center">
@@ -25,57 +25,25 @@
25
25
 
26
26
  This plugin provides Project conventions: naming, file structure, and code style consistency.
27
27
 
28
- ## Philosophy
29
-
30
- **Interlace** fosters **strength through integration**. Instead of stacking isolated rules, we **interlace** security directly into your workflow to create a resilient fabric of code. We believe tools should **guide rather than gatekeep**, providing educational feedback that strengthens the developer with every interaction.
31
-
32
28
  <!-- AUTO-GENERATED:DOCTRINE:START - Do not edit manually -->
33
29
 
34
- ## Why these rules are quiet
35
-
36
- **Noise creates apathy, and apathy is not a security posture.** A linter that reports
37
- a thousand things a week gets switched off in a month, and the real finding goes with
38
- it. So every rule here is built to be worth reading: we would rather miss a finding
39
- than spend your attention on one that was never real.
40
-
41
- That is a trade, and it is made deliberately. It costs recall, and we measure what it
42
- costs rather than assuming it is free.
43
-
44
- ## How the rules decide
45
-
46
- **Evidence, not names.** A rule fires on what the code *does*, resolved through the
47
- AST and ESLint's own scope analysis — not on an identifier that happens to contain
48
- `query`, a method called `setItem`, or a file whose path contains `key`. Every one of
49
- those was a real false positive in this ecosystem, found by reading our own output on
50
- open-source projects and fixed with a test that fails on the unfixed rule.
51
-
52
- Where a rule has known false-positive shapes, its page carries a **Not a finding**
53
- section: what it deliberately stays quiet on, and what to check first when it fires
54
- and you disagree.
55
-
56
- ## What you get
30
+ - **Why** a linter nobody reads protects nothing. We would rather miss a finding
31
+ than spend your attention on one that was never real.
32
+ - **How** evidence, not names. A rule fires on what the code *does*, resolved
33
+ through the AST and ESLint's own scope analysis.
34
+ - **What** every finding carries its fix, in prose for a human and as structured
35
+ JSON for an agent. Security rules add a CWE mapping and, where assigned, a CVSS score.
57
36
 
58
- The rules below. Security rules carry a CWE mapping and, where one is assigned, a
59
- CVSS score; every rule carries a fix on its message — in prose for a human and as
60
- structured JSON for an agent. Install it, enable
61
- `recommended`, and read the findings. If one of them is wrong,
62
- [open an issue](https://github.com/ofri-peretz/eslint/issues) — a false positive is a
63
- bug here, not a tuning exercise for you.
64
-
65
- How that is measured, on which projects, and where it falls short:
66
- [benchmark methodology](https://github.com/ofri-peretz/eslint/blob/main/BENCHMARK-METHODOLOGY.md)
67
- and [results](https://github.com/ofri-peretz/eslint/blob/main/BENCHMARK-RESULTS.md).
37
+ That trade costs recall, and we measure it:
38
+ [methodology](https://github.com/ofri-peretz/eslint/blob/main/BENCHMARK-METHODOLOGY.md)
39
+ · [results](https://github.com/ofri-peretz/eslint/blob/main/BENCHMARK-RESULTS.md)
40
+ · [a false positive is a bug](https://github.com/ofri-peretz/eslint/issues).
68
41
 
69
42
  <!-- AUTO-GENERATED:DOCTRINE:END -->
70
43
 
71
44
  ## Getting Started
72
45
 
73
46
  - To check out the [guide](https://eslint.interlace.tools/docs/quality/plugin-conventions?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-conventions), visit [eslint.interlace.tools](https://eslint.interlace.tools/?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-conventions). 📚
74
- - 要查看中文 [指南](https://eslint.interlace.tools/docs/quality/plugin-conventions?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-conventions), 请访问 [eslint.interlace.tools](https://eslint.interlace.tools/?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-conventions). 📚
75
- - [가이드](https://eslint.interlace.tools/docs/quality/plugin-conventions?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-conventions) 문서는 [eslint.interlace.tools](https://eslint.interlace.tools/?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-conventions)에서 확인하실 수 있습니다. 📚
76
- - [ガイド](https://eslint.interlace.tools/docs/quality/plugin-conventions?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-conventions)は [eslint.interlace.tools](https://eslint.interlace.tools/?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-conventions)でご確認ください。 📚
77
- - Para ver la [guía](https://eslint.interlace.tools/docs/quality/plugin-conventions?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-conventions), visita [eslint.interlace.tools](https://eslint.interlace.tools/?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-conventions). 📚
78
- - للاطلاع على [الدليل](https://eslint.interlace.tools/docs/quality/plugin-conventions?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-conventions)، قم بزيارة [eslint.interlace.tools](https://eslint.interlace.tools/?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-conventions). 📚
79
47
 
80
48
  ```bash
81
49
  npm install eslint-plugin-conventions --save-dev
@@ -132,7 +100,7 @@ See the [ESLint Version Support Policy](../../docs/ESLINT_VERSION_SUPPORT.md)
132
100
  | [no-console-spaces](https://eslint.interlace.tools/docs/quality/plugin-conventions/rules/no-console-spaces?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-conventions) | | | | Disallow leading/trailing whitespace in console arguments. This rule is part of eslint-plugin-conventions. | 🟢 | | | | 💡 | |
133
101
  | [no-deprecated-api](https://eslint.interlace.tools/docs/quality/plugin-conventions/rules/no-deprecated-api?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-conventions) | CWE-1078 | | | Prevent usage of deprecated APIs with migration context and timeline. This rule is part of eslint-plugin-co… | 🟢 | | ⚠️ | | 💡 | |
134
102
  | [no-json-schema-tags](https://eslint.interlace.tools/docs/quality/plugin-conventions/rules/no-json-schema-tags?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-conventions) | | | | Disallow JSON Schema keywords (e.g. @minimum, @maximum, @pattern, @format) used as JSDoc tags. | 🟢 | | | | 💡 | |
135
- | [no-magic-numbers](https://eslint.interlace.tools/docs/quality/plugin-conventions/rules/no-magic-numbers?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-conventions) | | | | Disallow magic numbers (numeric literals without a named constant) | 🟢 | | ⚠️ | | 💡 | |
103
+ | [no-magic-numbers](https://eslint.interlace.tools/docs/quality/plugin-conventions/rules/no-magic-numbers?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-conventions) | | | | Disallow magic numbers (numeric literals without a named constant) | 🟢 | | | | 💡 | |
136
104
  | [no-raw-cross-property-href](https://eslint.interlace.tools/docs/quality/plugin-conventions/rules/no-raw-cross-property-href?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-conventions) | | | | Forbid hand-written cross-property hrefs; use buildUtmHref() from lib/utm.ts | 🟢 | 💼 | | | | |
137
105
  | [prefer-code-point](https://eslint.interlace.tools/docs/quality/plugin-conventions/rules/prefer-code-point?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-conventions) | | | | Prefer String.codePointAt() over String.charCodeAt(). This rule is part of eslint-plugin-conventions. | 🟢 | | | | 💡 | |
138
106
  | [prefer-dependency-version-strategy](https://eslint.interlace.tools/docs/quality/plugin-conventions/rules/prefer-dependency-version-strategy?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-conventions) | | | | Enforce consistent version strategy (caret ^, tilde ~, exact, range, or any) for package.json dependencies.… | 🟢 | | | | 💡 | |
@@ -140,23 +108,52 @@ See the [ESLint Version Support Policy](../../docs/ESLINT_VERSION_SUPPORT.md)
140
108
  | [require-data-testid](https://eslint.interlace.tools/docs/quality/plugin-conventions/rules/require-data-testid?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-conventions) | | | | Require stable data-testid attributes on interactive elements for end-to-end test reliability | 🟢 | | | | 💡 | |
141
109
  | [utm-taxonomy](https://eslint.interlace.tools/docs/quality/plugin-conventions/rules/utm-taxonomy?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-conventions) | | | | Validate utm_source and utm_medium values against the fixed taxonomy in UTM_PHILOSOPHY.md | 🟢 | 💼 | | | | |
142
110
  <!-- AUTO-GENERATED:RULES_TABLE:END -->
111
+ <!-- AUTO-GENERATED:ECOSYSTEM_TABLE:START - Do not edit manually -->
112
+
143
113
  ## 🔗 Related ESLint Plugins
144
114
 
145
- Part of the **Interlace ESLint Ecosystem** — AI-native security plugins with LLM-optimized error messages:
115
+ Part of the **Interlace ESLint ecosystem** — AI-native rules with LLM-optimized error messages:
116
+
117
+ **Security**
146
118
 
147
119
  | Plugin | Downloads | Description |
148
120
  | :--- | :---: | :--- |
149
- | [`eslint-plugin-secure-coding`](https://www.npmjs.com/package/eslint-plugin-secure-coding) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-secure-coding.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-secure-coding) | General security rules & OWASP guidelines. |
150
- | [`eslint-plugin-pg`](https://www.npmjs.com/package/eslint-plugin-pg) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-pg.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-pg) | PostgreSQL security & best practices. |
151
- | [`eslint-plugin-node-security`](https://www.npmjs.com/package/eslint-plugin-node-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-node-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-node-security) | Node.js core-module security (fs, child_process, vm, crypto, Buffer). |
152
- | [`eslint-plugin-jwt`](https://www.npmjs.com/package/eslint-plugin-jwt) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-jwt.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-jwt) | JWT security & best practices. |
153
- | [`eslint-plugin-browser-security`](https://www.npmjs.com/package/eslint-plugin-browser-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-browser-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-browser-security) | Browser-specific security & XSS prevention. |
154
- | [`eslint-plugin-express-security`](https://www.npmjs.com/package/eslint-plugin-express-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-express-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-express-security) | Express.js security hardening rules. |
155
- | [`eslint-plugin-lambda-security`](https://www.npmjs.com/package/eslint-plugin-lambda-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-lambda-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-lambda-security) | AWS Lambda security best practices. |
156
- | [`eslint-plugin-nestjs-security`](https://www.npmjs.com/package/eslint-plugin-nestjs-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-nestjs-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-nestjs-security) | NestJS security rules & patterns. |
157
- | [`eslint-plugin-mongodb-security`](https://www.npmjs.com/package/eslint-plugin-mongodb-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-mongodb-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-mongodb-security) | MongoDB security best practices. |
158
- | [`eslint-plugin-vercel-ai-security`](https://www.npmjs.com/package/eslint-plugin-vercel-ai-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-vercel-ai-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-vercel-ai-security) | Vercel AI SDK security hardening. |
159
- | [`eslint-plugin-import-next`](https://www.npmjs.com/package/eslint-plugin-import-next) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-import-next.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-import-next) | Next-gen import sorting & architecture. |
121
+ | [`eslint-plugin-anthropic-security`](https://www.npmjs.com/package/eslint-plugin-anthropic-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-anthropic-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-anthropic-security) | Anthropic SDK security. |
122
+ | [`eslint-plugin-browser-security`](https://www.npmjs.com/package/eslint-plugin-browser-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-browser-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-browser-security) | XSS, DOM security. |
123
+ | [`eslint-plugin-drizzle-security`](https://www.npmjs.com/package/eslint-plugin-drizzle-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-drizzle-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-drizzle-security) | Drizzle security. |
124
+ | [`eslint-plugin-express-security`](https://www.npmjs.com/package/eslint-plugin-express-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-express-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-express-security) | Express middleware hardening. |
125
+ | [`eslint-plugin-gemini-security`](https://www.npmjs.com/package/eslint-plugin-gemini-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-gemini-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-gemini-security) | Google Gemini SDK security. |
126
+ | [`eslint-plugin-jwt-security`](https://www.npmjs.com/package/eslint-plugin-jwt-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-jwt-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-jwt-security) | Token security. |
127
+ | [`eslint-plugin-knex-security`](https://www.npmjs.com/package/eslint-plugin-knex-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-knex-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-knex-security) | Knex security. |
128
+ | [`eslint-plugin-lambda-security`](https://www.npmjs.com/package/eslint-plugin-lambda-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-lambda-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-lambda-security) | AWS Lambda hardening. |
129
+ | [`eslint-plugin-mcp-sdk-security`](https://www.npmjs.com/package/eslint-plugin-mcp-sdk-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-mcp-sdk-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-mcp-sdk-security) | MCP SDK security. |
130
+ | [`eslint-plugin-mongodb-security`](https://www.npmjs.com/package/eslint-plugin-mongodb-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-mongodb-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-mongodb-security) | MongoDB injection. |
131
+ | [`eslint-plugin-mysql-security`](https://www.npmjs.com/package/eslint-plugin-mysql-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-mysql-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-mysql-security) | MySQL security. |
132
+ | [`eslint-plugin-nestjs-security`](https://www.npmjs.com/package/eslint-plugin-nestjs-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-nestjs-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-nestjs-security) | NestJS framework hardening. |
133
+ | [`eslint-plugin-node-security`](https://www.npmjs.com/package/eslint-plugin-node-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-node-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-node-security) | Server-side patterns. |
134
+ | [`eslint-plugin-openai-security`](https://www.npmjs.com/package/eslint-plugin-openai-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-openai-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-openai-security) | OpenAI SDK security. |
135
+ | [`eslint-plugin-postgresql-security`](https://www.npmjs.com/package/eslint-plugin-postgresql-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-postgresql-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-postgresql-security) | PostgreSQL security. |
136
+ | [`eslint-plugin-prisma-security`](https://www.npmjs.com/package/eslint-plugin-prisma-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-prisma-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-prisma-security) | Prisma security. |
137
+ | [`eslint-plugin-secure-coding`](https://www.npmjs.com/package/eslint-plugin-secure-coding) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-secure-coding.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-secure-coding) | Injection prevention. |
138
+ | [`eslint-plugin-sequelize-security`](https://www.npmjs.com/package/eslint-plugin-sequelize-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-sequelize-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-sequelize-security) | Sequelize ORM security. |
139
+ | [`eslint-plugin-sqlite-security`](https://www.npmjs.com/package/eslint-plugin-sqlite-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-sqlite-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-sqlite-security) | SQLite security. |
140
+ | [`eslint-plugin-typeorm-security`](https://www.npmjs.com/package/eslint-plugin-typeorm-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-typeorm-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-typeorm-security) | TypeORM security. |
141
+ | [`eslint-plugin-vercel-ai-security`](https://www.npmjs.com/package/eslint-plugin-vercel-ai-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-vercel-ai-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-vercel-ai-security) | AI SDK security. |
142
+
143
+ **Code quality**
144
+
145
+ | Plugin | Downloads | Description |
146
+ | :--- | :---: | :--- |
147
+ | [`eslint-plugin-import-next`](https://www.npmjs.com/package/eslint-plugin-import-next) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-import-next.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-import-next) | Fast cycle + import-graph analysis. |
148
+ | [`eslint-plugin-maintainability`](https://www.npmjs.com/package/eslint-plugin-maintainability) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-maintainability.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-maintainability) | Cognitive load and clean-code patterns. |
149
+ | [`eslint-plugin-modernization`](https://www.npmjs.com/package/eslint-plugin-modernization) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-modernization.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-modernization) | ESNext migration + syntax evolution. |
150
+ | [`eslint-plugin-modularity`](https://www.npmjs.com/package/eslint-plugin-modularity) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-modularity.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-modularity) | Structural integrity and DDD patterns. |
151
+ | [`eslint-plugin-operability`](https://www.npmjs.com/package/eslint-plugin-operability) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-operability.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-operability) | Production readiness and resource health. |
152
+ | [`eslint-plugin-react-a11y`](https://www.npmjs.com/package/eslint-plugin-react-a11y) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-react-a11y.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-react-a11y) | React accessibility / WCAG. |
153
+ | [`eslint-plugin-react-features`](https://www.npmjs.com/package/eslint-plugin-react-features) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-react-features.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-react-features) | React best practices and optimization. |
154
+ | [`eslint-plugin-reliability`](https://www.npmjs.com/package/eslint-plugin-reliability) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-reliability.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-reliability) | Runtime stability and error safety. |
155
+
156
+ <!-- AUTO-GENERATED:ECOSYSTEM_TABLE:END -->
160
157
 
161
158
  <!-- INTERLACE:STAR_CTA:START -->
162
159
  ## ⭐ Support & follow
@@ -176,5 +173,5 @@ MIT © [Ofri Peretz](https://github.com/ofri-peretz)
176
173
  </p>
177
174
 
178
175
  <p align="center">
179
- <a href="https://eslint.interlace.tools/?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-conventions" target="blank"><img src="https://eslint.interlace.tools/icon-light.svg" alt="Interlace" height="70" /></a>
176
+ <a href="https://eslint.interlace.tools/?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-conventions" target="blank"><picture><source media="(prefers-color-scheme: dark)" srcset="https://eslint.interlace.tools/logos/interlace-dark.svg"><img src="https://eslint.interlace.tools/logos/interlace-light.svg" alt="Interlace" height="70" /></picture></a>
180
177
  </p>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-conventions",
3
- "version": "5.0.0",
3
+ "version": "5.2.0",
4
4
  "description": "ESLint plugin for team code conventions — enforces filename case, magic-number bans, commented-out code, expiring TODOs, and deprecated-API usage.",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -50,7 +50,7 @@
50
50
  "node": ">=18.0.0"
51
51
  },
52
52
  "dependencies": {
53
- "@interlace/eslint-devkit": "^1.17.2"
53
+ "@interlace/eslint-devkit": "^1.18.0"
54
54
  },
55
55
  "repository": {
56
56
  "type": "git",
package/src/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.configs=exports.plugin=exports.rules=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");exports.rules={get"no-commented-code"(){return require("./rules/conventions/no-commented-code").noCommentedCode},get"expiring-todo-comments"(){return require("./rules/conventions/expiring-todo-comments").expiringTodoComments},get"prefer-code-point"(){return require("./rules/conventions/prefer-code-point").preferCodePoint},get"prefer-dom-node-text-content"(){return require("./rules/conventions/prefer-dom-node-text-content").preferDomNodeTextContent},get"no-console-spaces"(){return require("./rules/conventions/no-console-spaces").noConsoleSpaces},get"no-deprecated-api"(){return require("./rules/deprecation/no-deprecated-api").noDeprecatedApi},get"prefer-dependency-version-strategy"(){return require("./rules/conventions/prefer-dependency-version-strategy").preferDependencyVersionStrategy},get"filename-case"(){return require("./rules/conventions/filename-case").filenameCase},get"consistent-existence-index-check"(){return require("./rules/conventions/consistent-existence-index-check").consistentExistenceIndexCheck},get"no-json-schema-tags"(){return require("./rules/conventions/no-json-schema-tags").noJsonSchemaTags},get"require-data-testid"(){return require("./rules/conventions/require-data-testid").requireDataTestId},get"utm-taxonomy"(){return require("./rules/conventions/utm-taxonomy").utmTaxonomy},get"no-raw-cross-property-href"(){return require("./rules/conventions/no-raw-cross-property-href").noRawCrossPropertyHref},get"analytics-event-naming"(){return require("./rules/conventions/analytics-event-naming").analyticsEventNaming},get"no-magic-numbers"(){return require("./rules/conventions/no-magic-numbers").noMagicNumbers}};(0,eslint_devkit_1.withCanonicalDocsUrls)("plugin-conventions",exports.rules);exports.plugin={meta:{name:"eslint-plugin-conventions",version:"5.0.0"},rules:exports.rules};exports.configs={recommended:{plugins:{conventions:exports.plugin},rules:{"conventions/no-commented-code":"warn","conventions/expiring-todo-comments":"warn","conventions/no-deprecated-api":"warn","conventions/utm-taxonomy":"error","conventions/no-raw-cross-property-href":"error","conventions/analytics-event-naming":"error"}}};exports.default=exports.plugin;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.configs=exports.plugin=exports.rules=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");exports.rules={get"no-commented-code"(){return require("./rules/conventions/no-commented-code").noCommentedCode},get"expiring-todo-comments"(){return require("./rules/conventions/expiring-todo-comments").expiringTodoComments},get"prefer-code-point"(){return require("./rules/conventions/prefer-code-point").preferCodePoint},get"prefer-dom-node-text-content"(){return require("./rules/conventions/prefer-dom-node-text-content").preferDomNodeTextContent},get"no-console-spaces"(){return require("./rules/conventions/no-console-spaces").noConsoleSpaces},get"no-deprecated-api"(){return require("./rules/deprecation/no-deprecated-api").noDeprecatedApi},get"prefer-dependency-version-strategy"(){return require("./rules/conventions/prefer-dependency-version-strategy").preferDependencyVersionStrategy},get"filename-case"(){return require("./rules/conventions/filename-case").filenameCase},get"consistent-existence-index-check"(){return require("./rules/conventions/consistent-existence-index-check").consistentExistenceIndexCheck},get"no-json-schema-tags"(){return require("./rules/conventions/no-json-schema-tags").noJsonSchemaTags},get"require-data-testid"(){return require("./rules/conventions/require-data-testid").requireDataTestId},get"utm-taxonomy"(){return require("./rules/conventions/utm-taxonomy").utmTaxonomy},get"no-raw-cross-property-href"(){return require("./rules/conventions/no-raw-cross-property-href").noRawCrossPropertyHref},get"analytics-event-naming"(){return require("./rules/conventions/analytics-event-naming").analyticsEventNaming},get"no-magic-numbers"(){return require("./rules/conventions/no-magic-numbers").noMagicNumbers}};(0,eslint_devkit_1.withCanonicalDocsUrls)("plugin-conventions",exports.rules);exports.plugin={meta:{name:"eslint-plugin-conventions",version:"5.2.0"},rules:exports.rules};exports.configs={recommended:{plugins:{conventions:exports.plugin},rules:{"conventions/no-commented-code":"warn","conventions/expiring-todo-comments":"warn","conventions/no-deprecated-api":"warn","conventions/utm-taxonomy":"error","conventions/no-raw-cross-property-href":"error","conventions/analytics-event-naming":"error"}}};exports.default=exports.plugin;
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.analyticsEventNaming=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");const ACTION_VERBS=["click","submit","view","add","remove","start","end","generate","send","cancel","fail","create","delete","update","invite"].join("|");const EVENT_NAME_RE=new RegExp(`^[a-z][a-z0-9_]*:[a-z][a-z0-9_]*_(${ACTION_VERBS})$|^\\$[a-z_]+$`);exports.analyticsEventNaming=(0,eslint_devkit_1.createRule)({name:"analytics-event-naming",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-conventions/docs/rules/analytics-event-naming.md",description:"Analytics event names (track/capture) must follow ANALYTICS_PHILOSOPHY.md principle 4: lowercase snake_case, category:object_action, fixed verb list"},fixable:void 0,hasSuggestions:false,messages:{invalidEventName:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.WARNING,issueName:"Invalid analytics event name",description:'Event "{{name}}" does not match category:object_action grammar (lowercase snake_case, action from fixed verb list).',severity:"HIGH",fix:"Rename to match `<category>:<object>_<verb>` where verb \u2208 {click, submit, view, add, remove, start, end, generate, send, cancel, fail, create, delete, update, invite}. Vendor reserved events ($pageview, $set, \u2026) are exempt.",documentationLink:"https://github.com/ofri-peretz/interlace/blob/main/docs/philosophies/ANALYTICS_PHILOSOPHY.md"}),interpolatedEventName:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.WARNING,issueName:"Interpolated analytics event name",description:"Event names must be static string literals. Template literals create an unbounded catalog (ANALYTICS_PHILOSOPHY.md principle 3).",severity:"HIGH",fix:"Move the dynamic part into an event property: track('category:object_view', { slug }).",documentationLink:"https://github.com/ofri-peretz/interlace/blob/main/docs/philosophies/ANALYTICS_PHILOSOPHY.md"})},schema:[]},defaultOptions:[],create(context){function isCaptureLike(node){if(node.callee.type==="MemberExpression"&&node.callee.property.type==="Identifier"&&(node.callee.property.name==="capture"||node.callee.property.name==="track")){return true}if(node.callee.type==="Identifier"&&node.callee.name==="track"){return true}return false}return{CallExpression(node){if(!isCaptureLike(node)||node.arguments.length===0)return;const first=node.arguments[0];if(first.type==="Literal"&&typeof first.value==="string"){if(!EVENT_NAME_RE.test(first.value)){context.report({node:first,messageId:"invalidEventName",data:{name:first.value}})}return}if(first.type==="TemplateLiteral"&&first.expressions.length>0){context.report({node:first,messageId:"interpolatedEventName"})}}}}});
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.analyticsEventNaming=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");const ACTION_VERBS=["click","submit","view","add","remove","start","end","generate","send","cancel","fail","create","delete","update","invite","copy"].join("|");const EVENT_NAME_RE=new RegExp(`^[a-z][a-z0-9_]*:[a-z][a-z0-9_]*_(${ACTION_VERBS})$|^\\$[a-z_]+$`);exports.analyticsEventNaming=(0,eslint_devkit_1.createRule)({name:"analytics-event-naming",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-conventions/docs/rules/analytics-event-naming.md",description:"Analytics event names (track/capture) must follow ANALYTICS_PHILOSOPHY.md principle 4: lowercase snake_case, category:object_action, fixed verb list"},fixable:void 0,hasSuggestions:false,messages:{invalidEventName:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.WARNING,issueName:"Invalid analytics event name",description:'Event "{{name}}" does not match category:object_action grammar (lowercase snake_case, action from fixed verb list).',severity:"HIGH",fix:"Rename to match `<category>:<object>_<verb>` where verb \u2208 {click, submit, view, add, remove, start, end, generate, send, cancel, fail, create, delete, update, invite, copy}. Vendor reserved events ($pageview, $set, \u2026) are exempt.",documentationLink:"https://github.com/ofri-peretz/interlace/blob/main/docs/philosophies/ANALYTICS_PHILOSOPHY.md"}),interpolatedEventName:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.WARNING,issueName:"Interpolated analytics event name",description:"Event names must be static string literals. Template literals create an unbounded catalog (ANALYTICS_PHILOSOPHY.md principle 3).",severity:"HIGH",fix:"Move the dynamic part into an event property: track('category:object_view', { slug }).",documentationLink:"https://github.com/ofri-peretz/interlace/blob/main/docs/philosophies/ANALYTICS_PHILOSOPHY.md"})},schema:[]},defaultOptions:[],create(context){function isCaptureLike(node){if(node.callee.type==="MemberExpression"&&node.callee.property.type==="Identifier"&&(node.callee.property.name==="capture"||node.callee.property.name==="track")){return true}if(node.callee.type==="Identifier"&&node.callee.name==="track"){return true}return false}return{CallExpression(node){if(!isCaptureLike(node)||node.arguments.length===0)return;const first=node.arguments[0];const staticText=(0,eslint_devkit_1.staticString)(first);if(staticText!==null){if(!EVENT_NAME_RE.test(staticText)){context.report({node:first,messageId:"invalidEventName",data:{name:staticText}})}return}if(first.type==="TemplateLiteral"&&first.expressions.length>0){context.report({node:first,messageId:"interpolatedEventName"})}}}}});
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noConsoleSpaces=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");exports.noConsoleSpaces=(0,eslint_devkit_1.createRule)({name:"no-console-spaces",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-conventions/docs/rules/no-console-spaces.md",description:"Prevent leading/trailing space between console.log parameters"},fixable:"code",messages:{noConsoleSpaces:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.WARNING,issueName:"Console Spaces",description:"Remove leading/trailing spaces in console method parameters",severity:"MEDIUM",fix:"Remove spaces from console method arguments",documentationLink:"https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-console-spaces.md"})},schema:[]},defaultOptions:[],create(context){const consoleMethods=new Set(["log","debug","info","warn","error","table","trace","group","groupCollapsed"]);function isInAllowedContext(){return false}function isConsoleMethodCall(node){if(node.callee.type==="MemberExpression"&&node.callee.object.type==="Identifier"&&node.callee.object.name==="console"&&node.callee.property.type==="Identifier"&&consoleMethods.has(node.callee.property.name)){return true}return false}function getConsoleMethodName(node){if(node.callee.type==="MemberExpression"&&node.callee.property.type==="Identifier"){return node.callee.property.name}return"console"}function hasLeadingOrTrailingSpaces(text){const trimmed=text.trim();return trimmed.length>0&&/^\s|\s$/.test(text)}function hasLeadingOrTrailingSpacesInTemplate(text){return/^\s|\s$/.test(text)}return{CallExpression(node){if(isConsoleMethodCall(node)&&!isInAllowedContext()){for(const arg of node.arguments){if(arg.type==="Literal"&&typeof arg.value==="string"){if(hasLeadingOrTrailingSpaces(arg.value)){context.report({node:arg,messageId:"noConsoleSpaces",data:{method:getConsoleMethodName(node),arg:arg.value},fix(fixer){const trimmed=arg.value.trim();return fixer.replaceText(arg,`'${trimmed}'`)}})}}else if(arg.type==="TemplateLiteral"){let hasSpacesInTemplate=false;for(const quasi of arg.quasis){if(hasLeadingOrTrailingSpacesInTemplate(quasi.value.raw)){hasSpacesInTemplate=true;break}}if(hasSpacesInTemplate){context.report({node:arg,messageId:"noConsoleSpaces",data:{method:getConsoleMethodName(node),arg:"template literal with spaces"},fix:null})}}}}}}}});
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noConsoleSpaces=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");exports.noConsoleSpaces=(0,eslint_devkit_1.createRule)({name:"no-console-spaces",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-conventions/docs/rules/no-console-spaces.md",description:"Prevent leading/trailing space between console.log parameters"},fixable:"code",messages:{noConsoleSpaces:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.WARNING,issueName:"Console Spaces",description:"Remove leading/trailing spaces in console method parameters",severity:"MEDIUM",fix:"Remove spaces from console method arguments",documentationLink:"https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-console-spaces.md"})},schema:[]},defaultOptions:[],create(context){const consoleMethods=new Set(["log","debug","info","warn","error","table","trace","group","groupCollapsed"]);function isInAllowedContext(){return false}function isConsoleMethodCall(node){if(node.callee.type==="MemberExpression"&&node.callee.object.type==="Identifier"&&node.callee.object.name==="console"&&node.callee.property.type==="Identifier"&&consoleMethods.has(node.callee.property.name)){return true}return false}function getConsoleMethodName(node){if(node.callee.type==="MemberExpression"&&node.callee.property.type==="Identifier"){return node.callee.property.name}return"console"}function hasLeadingOrTrailingSpaces(text){const trimmed=text.trim();return trimmed.length>0&&/^\s|\s$/.test(text)}function hasLeadingOrTrailingSpacesInTemplate(text){return/^\s|\s$/.test(text)}return{CallExpression(node){if(isConsoleMethodCall(node)&&!isInAllowedContext()){for(const arg of node.arguments){const staticText=arg.type==="TemplateLiteral"?null:(0,eslint_devkit_1.staticString)(arg);if(staticText!==null){if(hasLeadingOrTrailingSpaces(staticText)){context.report({node:arg,messageId:"noConsoleSpaces",data:{method:getConsoleMethodName(node),arg:staticText},fix(fixer){const trimmed=staticText.trim();return fixer.replaceText(arg,`'${trimmed}'`)}})}}else if(arg.type==="TemplateLiteral"){let hasSpacesInTemplate=false;for(const quasi of arg.quasis){if(hasLeadingOrTrailingSpacesInTemplate(quasi.value.raw)){hasSpacesInTemplate=true;break}}if(hasSpacesInTemplate){context.report({node:arg,messageId:"noConsoleSpaces",data:{method:getConsoleMethodName(node),arg:"template literal with spaces"},fix:null})}}}}}}}});
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noMagicNumbers=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");const DEFAULT_IGNORE=new Set([-1,0,1,2]);function constNameFor(value){const prefix=value<0?"MAGIC_NEG_":"MAGIC_";const digits=String(Math.abs(value)).toUpperCase().replaceAll(/[^0-9A-Z]+/g,"_").replace(/_+$/,"");return`${prefix}${digits}`}function nearestStatement(node){const STATEMENT_TYPES=new Set(["ExpressionStatement","VariableDeclaration","ReturnStatement","IfStatement","WhileStatement","ForStatement","ForInStatement","ForOfStatement","ThrowStatement","SwitchStatement"]);let current=node.parent;while(current){if(STATEMENT_TYPES.has(current.type))return current;current=current.parent}return null}exports.noMagicNumbers=(0,eslint_devkit_1.createRule)({name:"no-magic-numbers",skipGeneratedFiles:true,skipMinifiedFiles:true,meta:{type:"suggestion",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-conventions/docs/rules/no-magic-numbers.md",description:"Disallow magic numbers (numeric literals without a named constant)"},hasSuggestions:true,messages:{noMagicNumber:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.INFO,issueName:"Magic Number",description:"The number {{value}} is a magic literal. Extract it to a named constant to make the intent clear.",severity:"LOW",fix:"const TIMEOUT_MS = {{value}}; // use the named constant everywhere",documentationLink:"https://refactoring.guru/replace-magic-literal"}),extractConst:"Extract {{value}} to a named constant ({{constName}})"},schema:[{type:"object",properties:{ignore:{type:"array",items:{type:"number"},description:"Additional numbers to allow"},ignoreArrayIndexes:{type:"boolean",default:true},detectObjects:{type:"boolean",default:false},ignoreLoopBounds:{type:"boolean",default:true},ignoreLengthComparisons:{type:"boolean",default:true},ignoreDefaultValues:{type:"boolean",default:true},ignoreEnums:{type:"boolean",default:true},ignoreBitwiseExpressions:{type:"boolean",default:false}},additionalProperties:false}]},defaultOptions:[{ignore:[],ignoreArrayIndexes:true,detectObjects:false,ignoreLoopBounds:true,ignoreLengthComparisons:true,ignoreDefaultValues:true,ignoreEnums:true,ignoreBitwiseExpressions:false}],create(context){const[options={}]=context.options;const{ignore=[],ignoreArrayIndexes=true,detectObjects=false,ignoreLoopBounds=true,ignoreLengthComparisons=true,ignoreDefaultValues=true,ignoreEnums=true,ignoreBitwiseExpressions=false}=options;const ignoredValues=new Set([...DEFAULT_IGNORE,...ignore]);function isIgnoredNumber(value){return ignoredValues.has(value)}const MAX_ARRAY_INDEX=2**32-2;function isArrayIndex(node){if(!ignoreArrayIndexes)return false;const parent=node.parent;const inIndexPosition=parent?.type==="MemberExpression"&&parent.computed&&parent.property===node;if(!inIndexPosition)return false;const value=node.value;return typeof value==="number"&&Number.isInteger(value)&&value>=0&&value<=MAX_ARRAY_INDEX}function isObjectPropertyValue(node){if(detectObjects)return false;const parent=node.parent;return parent?.type==="Property"&&parent.value===node}function isLoopBound(node){if(!ignoreLoopBounds)return false;let current=node;let parent=current.parent;while(parent){if(parent.type==="ForStatement"){const loop=parent;return loop.test===current||loop.update===current||loop.init===current}if(parent.type==="BlockStatement"||parent.type==="Program")return false;current=parent;parent=current.parent}return false}function isLengthComparison(node){if(!ignoreLengthComparisons)return false;const parent=node.parent;if(parent?.type!=="BinaryExpression")return false;const comparison=parent;const EQUALITY=new Set(["===","!==","==","!="]);if(!EQUALITY.has(comparison.operator))return false;const other=comparison.left===node?comparison.right:comparison.left;return other.type==="MemberExpression"&&!other.computed&&other.property.type==="Identifier"&&other.property.name==="length"}function isDefaultValue(node){if(!ignoreDefaultValues)return false;const parent=node.parent;return parent?.type==="AssignmentPattern"&&parent.right===node}function isEnumMember(node){if(!ignoreEnums)return false;const parent=node.parent;return parent?.type==="TSEnumMember"}function isBitwiseContext(node){if(!ignoreBitwiseExpressions)return false;const parent=node.parent;const BITWISE_OPS=new Set(["&","|","^","<<",">>",">>>"]);return parent?.type==="BinaryExpression"&&BITWISE_OPS.has(parent.operator)}function isVariableDeclarator(node){const parent=node.parent;return parent?.type==="VariableDeclarator"}function isExportedConst(node){let current=node;while(current){if(current.type==="ExportNamedDeclaration")return true;if(current.type==="VariableDeclaration"||current.type==="VariableDeclarator"){current=current.parent;continue}break}return false}function isPropertyKey(node){const parent=node.parent;return parent?.type==="Property"&&parent.key===node}return{Literal(node){if(typeof node.value!=="number")return;const value=node.value;if(isIgnoredNumber(value))return;if(!isFinite(value))return;if(isVariableDeclarator(node))return;if(isExportedConst(node))return;if(isArrayIndex(node))return;if(isObjectPropertyValue(node))return;if(isLoopBound(node))return;if(isLengthComparison(node))return;if(isDefaultValue(node))return;if(isEnumMember(node))return;if(isBitwiseContext(node))return;if(isPropertyKey(node))return;const constName=constNameFor(value);const sourceCode=context.sourceCode;context.report({node,messageId:"noMagicNumber",data:{value:String(value)},suggest:[{messageId:"extractConst",data:{value:String(value),constName},fix(fixer){const stmt=nearestStatement(node);if(!stmt)return null;const firstToken=sourceCode.getFirstToken(stmt);if(!firstToken)return null;const col=firstToken.loc.start.column;const indent=" ".repeat(col);return[fixer.insertTextBefore(stmt,`const ${constName} = ${value};
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noMagicNumbers=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");const DEFAULT_IGNORE=new Set([-1,0,1,2]);function constNameFor(value){const prefix=value<0?"MAGIC_NEG_":"MAGIC_";const digits=String(Math.abs(value)).toUpperCase().replaceAll(/[^0-9A-Z]+/g,"_").replace(/_+$/,"");return`${prefix}${digits}`}function nearestStatement(node){const STATEMENT_TYPES=new Set(["ExpressionStatement","VariableDeclaration","ReturnStatement","IfStatement","WhileStatement","ForStatement","ForInStatement","ForOfStatement","ThrowStatement","SwitchStatement"]);let current=node.parent;while(current){if(STATEMENT_TYPES.has(current.type))return current;current=current.parent}return null}exports.noMagicNumbers=(0,eslint_devkit_1.createRule)({name:"no-magic-numbers",skipGeneratedFiles:true,skipMinifiedFiles:true,meta:{type:"suggestion",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-conventions/docs/rules/no-magic-numbers.md",description:"Disallow magic numbers (numeric literals without a named constant)"},hasSuggestions:true,messages:{noMagicNumber:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.INFO,issueName:"Magic Number",description:"The number {{value}} is a magic literal. Extract it to a named constant to make the intent clear.",severity:"LOW",fix:"const TIMEOUT_MS = {{value}}; // use the named constant everywhere",documentationLink:"https://refactoring.guru/replace-magic-literal"}),extractConst:"Extract {{value}} to a named constant ({{constName}})"},schema:[{type:"object",properties:{ignore:{type:"array",items:{type:"number"},description:"Additional numbers to allow"},ignoreArrayIndexes:{type:"boolean",default:true},detectObjects:{type:"boolean",default:false},ignoreLoopBounds:{type:"boolean",default:true},ignoreLengthComparisons:{type:"boolean",default:true},ignoreNumericArrays:{type:"boolean",default:true},namedConstArithmetic:{type:"boolean",default:true},ignoreDefaultValues:{type:"boolean",default:true},ignoreEnums:{type:"boolean",default:true},ignoreBitwiseExpressions:{type:"boolean",default:false}},additionalProperties:false}]},defaultOptions:[{ignore:[],ignoreArrayIndexes:true,detectObjects:false,ignoreLoopBounds:true,ignoreLengthComparisons:true,ignoreNumericArrays:true,namedConstArithmetic:true,ignoreDefaultValues:true,ignoreEnums:true,ignoreBitwiseExpressions:false}],create(context){const[options={}]=context.options;const{ignore=[],ignoreArrayIndexes=true,detectObjects=false,ignoreLoopBounds=true,ignoreLengthComparisons=true,ignoreNumericArrays=true,namedConstArithmetic=true,ignoreDefaultValues=true,ignoreEnums=true,ignoreBitwiseExpressions=false}=options;const ignoredValues=new Set([...DEFAULT_IGNORE,...ignore]);function isIgnoredNumber(value){return ignoredValues.has(value)}const MAX_ARRAY_INDEX=2**32-2;function isArrayIndex(node){if(!ignoreArrayIndexes)return false;const parent=node.parent;const inIndexPosition=parent?.type==="MemberExpression"&&parent.computed&&parent.property===node;if(!inIndexPosition)return false;const value=node.value;return typeof value==="number"&&Number.isInteger(value)&&value>=0&&value<=MAX_ARRAY_INDEX}function isObjectPropertyValue(node){if(detectObjects)return false;const parent=node.parent;return parent?.type==="Property"&&parent.value===node}function isLoopBound(node){if(!ignoreLoopBounds)return false;let current=node;let parent=current.parent;while(parent){if(parent.type==="ForStatement"){const loop=parent;return loop.test===current||loop.update===current||loop.init===current}if(parent.type==="BlockStatement"||parent.type==="Program")return false;current=parent;parent=current.parent}return false}function isLengthComparison(node){if(!ignoreLengthComparisons)return false;const parent=node.parent;if(parent?.type!=="BinaryExpression")return false;const comparison=parent;const EQUALITY=new Set(["===","!==","==","!="]);if(!EQUALITY.has(comparison.operator))return false;const other=comparison.left===node?comparison.right:comparison.left;return other.type==="MemberExpression"&&!other.computed&&other.property.type==="Identifier"&&other.property.name==="length"}function isDefaultValue(node){if(!ignoreDefaultValues)return false;const parent=node.parent;return parent?.type==="AssignmentPattern"&&parent.right===node}function isEnumMember(node){if(!ignoreEnums)return false;const parent=node.parent;return parent?.type==="TSEnumMember"}function isBitwiseContext(node){if(!ignoreBitwiseExpressions)return false;const parent=node.parent;const BITWISE_OPS=new Set(["&","|","^","<<",">>",">>>"]);return parent?.type==="BinaryExpression"&&BITWISE_OPS.has(parent.operator)}const allLiteralOperands=node=>{if(!node||typeof node!=="object")return false;if(node.type==="Literal")return typeof node.value==="number";if(node.type==="UnaryExpression")return allLiteralOperands(node.argument);if(node.type==="TSAsExpression"||node.type==="TSSatisfiesExpression"){return allLiteralOperands(node.expression)}if(node.type==="BinaryExpression"){return allLiteralOperands(node.left)&&allLiteralOperands(node.right)}return false};function isVariableDeclarator(node){let current=node;let parent=current.parent;while(parent&&(parent.type==="TSAsExpression"||parent.type==="TSSatisfiesExpression"||parent.type==="UnaryExpression"||parent.type==="BinaryExpression"&&namedConstArithmetic&&allLiteralOperands(parent))){current=parent;parent=current.parent}return parent?.type==="VariableDeclarator"&&parent.id.type==="Identifier"}function isNumericDataArray(node){if(!ignoreNumericArrays)return false;const parent=node.parent;const array=parent?.type==="ArrayExpression"?parent:parent?.type==="UnaryExpression"&&parent.parent?.type==="ArrayExpression"?parent.parent:null;if(array===null)return false;const numeric=element=>{if(element===null)return false;if(element.type==="Literal")return typeof element.value==="number";if(element.type==="UnaryExpression")return numeric(element.argument);return false};return array.elements.length>=2&&array.elements.every(numeric)}function isExportedConst(node){let current=node;while(current){if(current.type==="ExportNamedDeclaration")return true;if(current.type==="VariableDeclaration"||current.type==="VariableDeclarator"){current=current.parent;continue}break}return false}function isPropertyKey(node){const parent=node.parent;return parent?.type==="Property"&&parent.key===node}return{Literal(node){if(typeof node.value!=="number")return;const value=node.value;if(isIgnoredNumber(value))return;if(!isFinite(value))return;if(isVariableDeclarator(node))return;if(isExportedConst(node))return;if(isArrayIndex(node))return;if(isObjectPropertyValue(node))return;if(isLoopBound(node))return;if(isLengthComparison(node))return;if(isDefaultValue(node))return;if(isEnumMember(node))return;if(isBitwiseContext(node))return;if(isPropertyKey(node))return;if(isNumericDataArray(node))return;const constName=constNameFor(value);const sourceCode=context.sourceCode;context.report({node,messageId:"noMagicNumber",data:{value:String(value)},suggest:[{messageId:"extractConst",data:{value:String(value),constName},fix(fixer){const stmt=nearestStatement(node);if(!stmt)return null;const firstToken=sourceCode.getFirstToken(stmt);if(!firstToken)return null;const col=firstToken.loc.start.column;const indent=" ".repeat(col);return[fixer.insertTextBefore(stmt,`const ${constName} = ${value};
2
2
  ${indent}`),fixer.replaceText(node,constName)]}}]})}}}});
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.preferDomNodeTextContent=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");exports.preferDomNodeTextContent=(0,eslint_devkit_1.createRule)({name:"prefer-dom-node-text-content",meta:{type:"suggestion",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-conventions/docs/rules/prefer-dom-node-text-content.md",description:"Prefer textContent over innerText for better performance and reliability"},hasSuggestions:true,messages:{preferDomNodeTextContent:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.PERFORMANCE,issueName:"DOM Text Access",description:"Use textContent instead of innerText",severity:"MEDIUM",fix:"Replace innerText with textContent",documentationLink:"https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-dom-node-text-content.md"})},schema:[{type:"object",properties:{},additionalProperties:false}]},defaultOptions:[],create(context){function isInAllowedContext(){return false}function isLikelyDomElement(node){const obj=node.object;if(obj.type==="Identifier"){const name=obj.name;if(name.match(/^(element|el|div|span|node|ref|dom|elem)$/i)){return true}if(name.match(/(Element|Node|Ref)$/)){return true}}if(obj.type==="CallExpression"&&obj.callee.type==="MemberExpression"){const methodName=obj.callee.property?.name;if(methodName&&["querySelector","querySelectorAll","getElementById","getElementsByClassName","getElementsByTagName","createElement"].includes(methodName)){return true}}if(obj.type==="MemberExpression"){const propName=obj.property?.name;if(propName&&["current","children","childNodes","parentNode","element"].includes(propName)){return true}}return false}function isInnerTextAccess(node){if(node.property.type==="Identifier"&&node.property.name==="innerText"){return true}if(node.computed&&node.property.type==="Literal"&&node.property.value==="innerText"){return true}return false}return{MemberExpression(node){if(isInnerTextAccess(node)&&isLikelyDomElement(node)&&!isInAllowedContext()){context.report({node,messageId:"preferDomNodeTextContent",data:{current:"innerText",fix:"textContent"},suggest:[{messageId:"preferDomNodeTextContent",data:{replacement:"textContent",suggestion:"Replace with textContent"},fix(fixer){if(node.property.type==="Identifier"){return fixer.replaceText(node.property,"textContent")}else if(node.property.type==="Literal"&&node.property.value==="innerText"){return fixer.replaceText(node.property,'"textContent"')}return null}}]})}}}}});
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.preferDomNodeTextContent=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");exports.preferDomNodeTextContent=(0,eslint_devkit_1.createRule)({name:"prefer-dom-node-text-content",meta:{type:"suggestion",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-conventions/docs/rules/prefer-dom-node-text-content.md",description:"Prefer textContent over innerText for better performance and reliability"},hasSuggestions:true,messages:{preferDomNodeTextContent:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.PERFORMANCE,issueName:"DOM Text Access",description:"Use textContent instead of innerText",severity:"MEDIUM",fix:"Replace innerText with textContent",documentationLink:"https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-dom-node-text-content.md"})},schema:[{type:"object",properties:{},additionalProperties:false}]},defaultOptions:[],create(context){function isInAllowedContext(){return false}function isInnerTextAccess(node){if(node.property.type==="Identifier"&&node.property.name==="innerText"){return true}if(node.computed&&node.property.type==="Literal"&&node.property.value==="innerText"){return true}return false}return{MemberExpression(node){if(isInnerTextAccess(node)&&!isInAllowedContext()){context.report({node,messageId:"preferDomNodeTextContent",data:{current:"innerText",fix:"textContent"},suggest:[{messageId:"preferDomNodeTextContent",data:{replacement:"textContent",suggestion:"Replace with textContent"},fix(fixer){if(node.property.type==="Identifier"){return fixer.replaceText(node.property,"textContent")}else if(node.property.type==="Literal"&&node.property.value==="innerText"){return fixer.replaceText(node.property,'"textContent"')}return null}}]})}}}}});