eslint-plugin-conventions 4.3.0 → 5.1.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 +59 -58
- package/package.json +2 -2
- package/src/index.js +1 -1
- package/src/rules/conventions/analytics-event-naming.js +1 -1
- package/src/rules/conventions/no-commented-code.js +1 -1
- package/src/rules/conventions/no-magic-numbers.js +1 -1
- package/src/rules/conventions/prefer-dom-node-text-content.js +1 -1
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
|
|
|
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
|
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
**
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
|
@@ -124,6 +92,7 @@ See the [ESLint Version Support Policy](../../docs/ESLINT_VERSION_SUPPORT.md)
|
|
|
124
92
|
<!-- AUTO-GENERATED:RULES_TABLE:START - Do not edit manually -->
|
|
125
93
|
| Rule | CWE | OWASP | CVSS | Description | 🧠 | 💼 | ⚠️ | 🔧 | 💡 | 🚫 |
|
|
126
94
|
| :--- | :---: | :---: | :---: | :--- | :---: | :---: | :---: | :---: | :---: | :---: |
|
|
95
|
+
| [analytics-event-naming](https://eslint.interlace.tools/docs/quality/plugin-conventions/rules/analytics-event-naming?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-conventions) | | | | Analytics event names (track/capture) must follow ANALYTICS_PHILOSOPHY.md principle 4: lowercase snake_case… | 🟢 | 💼 | | | | |
|
|
127
96
|
| [consistent-existence-index-check](https://eslint.interlace.tools/docs/quality/plugin-conventions/rules/consistent-existence-index-check?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-conventions) | | | | Enforce consistent style for checking if an element exists in an array | 🟢 | | | | 💡 | |
|
|
128
97
|
| [expiring-todo-comments](https://eslint.interlace.tools/docs/quality/plugin-conventions/rules/expiring-todo-comments?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-conventions) | | | | Add expiration conditions to TODO comments to prevent forgotten tasks. This rule is part of eslint-plugin-c… | 🟢 | | ⚠️ | | 💡 | |
|
|
129
98
|
| [filename-case](https://eslint.interlace.tools/docs/quality/plugin-conventions/rules/filename-case?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-conventions) | | | | Enforce filename case conventions for consistency across your codebase | 🟢 | | | | 💡 | |
|
|
@@ -131,28 +100,60 @@ See the [ESLint Version Support Policy](../../docs/ESLINT_VERSION_SUPPORT.md)
|
|
|
131
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. | 🟢 | | | | 💡 | |
|
|
132
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… | 🟢 | | ⚠️ | | 💡 | |
|
|
133
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. | 🟢 | | | | 💡 | |
|
|
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) | 🟢 | | | | 💡 | |
|
|
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 | 🟢 | 💼 | | | | |
|
|
134
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. | 🟢 | | | | 💡 | |
|
|
135
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.… | 🟢 | | | | 💡 | |
|
|
136
107
|
| [prefer-dom-node-text-content](https://eslint.interlace.tools/docs/quality/plugin-conventions/rules/prefer-dom-node-text-content?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-conventions) | | | | Prefer textContent over innerText. This rule is part of eslint-plugin-conventions. | 🟢 | | | | 💡 | |
|
|
137
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 | 🟢 | | | | 💡 | |
|
|
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 | 🟢 | 💼 | | | | |
|
|
138
110
|
<!-- AUTO-GENERATED:RULES_TABLE:END -->
|
|
111
|
+
<!-- AUTO-GENERATED:ECOSYSTEM_TABLE:START - Do not edit manually -->
|
|
112
|
+
|
|
139
113
|
## 🔗 Related ESLint Plugins
|
|
140
114
|
|
|
141
|
-
Part of the **Interlace ESLint
|
|
115
|
+
Part of the **Interlace ESLint ecosystem** — AI-native rules with LLM-optimized error messages:
|
|
116
|
+
|
|
117
|
+
**Security**
|
|
142
118
|
|
|
143
119
|
| Plugin | Downloads | Description |
|
|
144
120
|
| :--- | :---: | :--- |
|
|
145
|
-
| [`eslint-plugin-
|
|
146
|
-
| [`eslint-plugin-
|
|
147
|
-
| [`eslint-plugin-
|
|
148
|
-
| [`eslint-plugin-
|
|
149
|
-
| [`eslint-plugin-
|
|
150
|
-
| [`eslint-plugin-
|
|
151
|
-
| [`eslint-plugin-
|
|
152
|
-
| [`eslint-plugin-
|
|
153
|
-
| [`eslint-plugin-
|
|
154
|
-
| [`eslint-plugin-
|
|
155
|
-
| [`eslint-plugin-
|
|
121
|
+
| [`eslint-plugin-anthropic-security`](https://www.npmjs.com/package/eslint-plugin-anthropic-security) | [](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) | [](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) | [](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) | [](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) | [](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) | [](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) | [](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) | [](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) | [](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) | [](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) | [](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) | [](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) | [](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) | [](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) | [](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) | [](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) | [](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) | [](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) | [](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) | [](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) | [](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) | [](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) | [](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) | [](https://www.npmjs.com/package/eslint-plugin-modernization) | ESNext migration + syntax evolution. |
|
|
150
|
+
| [`eslint-plugin-modularity`](https://www.npmjs.com/package/eslint-plugin-modularity) | [](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) | [](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) | [](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) | [](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) | [](https://www.npmjs.com/package/eslint-plugin-reliability) | Runtime stability and error safety. |
|
|
155
|
+
|
|
156
|
+
<!-- AUTO-GENERATED:ECOSYSTEM_TABLE:END -->
|
|
156
157
|
|
|
157
158
|
<!-- INTERLACE:STAR_CTA:START -->
|
|
158
159
|
## ⭐ Support & follow
|
|
@@ -172,5 +173,5 @@ MIT © [Ofri Peretz](https://github.com/ofri-peretz)
|
|
|
172
173
|
</p>
|
|
173
174
|
|
|
174
175
|
<p align="center">
|
|
175
|
-
<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/
|
|
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>
|
|
176
177
|
</p>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-conventions",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.1.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.
|
|
53
|
+
"@interlace/eslint-devkit": "^1.17.4"
|
|
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;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}};exports.plugin={meta:{name:"eslint-plugin-conventions",version:"
|
|
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.1.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];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 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noCommentedCode=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");function looksLikeCode(comment,isBlockComment){if(isBlockComment&&comment.startsWith("*")){return false}let text=comment;if(isBlockComment){text=text.replace(/^\s*\/\*+/,"").replace(/\*+\/\s*$/,"");text=text.split("\n").map(line=>line.replace(/^\s*\*+\s*/,"")).join("\n")}else{text=text.split("\n").map(line=>line.replace(/^\s*\/\/+\s*/,"")).join("\n")}const lines=text.split("\n").map(line=>line.trim()).filter(line=>line.length>0);if(lines.length===0){return false}const STRUCTURAL_CODE=[/^(const|let|var)\s+[A-Za-z_$][\w$]*(\s*:[^=]+)?\s*=/,/^import\s+[^;]*\bfrom\b/,/^import\s+["']/,/^export\s+(default|const|let|var|function|class|\{|\*)/,/^throw\s+new\s+[A-Za-z_$]/,/^(throw|await|yield)\s+[A-Za-z_$][\w$]*\s*[.(]/,/^return\s+(new|await)\s/,/^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*)+\s*\(/,/=>/,/===|!==/,/^@[A-Za-z_$][\w$]*\s*\(/,/^<[A-Z][\w$]*[\s/>]/];const codePatterns=[/^(const|let|var|function|class|if|for|while|return|import|export)\s+/,/^[a-zA-Z_$][a-zA-Z0-9_$]*\s*[=:]\s*/,/^[a-zA-Z_$][a-zA-Z0-9_$]*\(/,/^[{}[\]]/];const URL_LINE=/^[a-z][a-z0-9+.-]*:\/\//i;const ENDS_LIKE_CODE=/[;{},()[\]]$|=>$/;let codeLikeLines=0;for(const line of lines){if(/^(TODO|FIXME|HACK|XXX)/i.test(line)){continue}if(URL_LINE.test(line)){continue}if(!STRUCTURAL_CODE.some(pattern=>pattern.test(line))&&!ENDS_LIKE_CODE.test(line)){continue}if(STRUCTURAL_CODE.some(pattern=>pattern.test(line))){codeLikeLines++;continue}for(const pattern of codePatterns){if(pattern.test(line)){codeLikeLines++;break}}}return codeLikeLines>0}function countCommentLines(comment){if(!comment)return 0;const newlineCount=(comment.match(/\n/g)||[]).length;return newlineCount>0?newlineCount+1:1}exports.noCommentedCode=(0,eslint_devkit_2.createRule)({name:"no-commented-code",meta:{type:"suggestion",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-conventions/docs/rules/no-commented-code.md",description:"Detects commented-out code blocks"},hasSuggestions:true,messages:{commentedCode:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.WARNING,issueName:"Commented code",description:"Commented-out code detected ({{lines}} lines)",severity:"LOW",fix:"Remove commented code or use version control for history",documentationLink:"https://rules.sonarsource.com/javascript/RSPEC-125/"}),removeCode:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Remove Code",description:"Remove commented code block",severity:"LOW",fix:"Delete the commented code block",documentationLink:"https://rules.sonarsource.com/javascript/RSPEC-125/"}),useVersionControl:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use Git",description:"Use version control for code history",severity:"LOW",fix:"Delete commented code and use git history instead",documentationLink:"https://git-scm.com/docs/git-log"})},schema:[{type:"object",properties:{ignoreSingleLine:{type:"boolean",default:false,description:"Ignore single-line comments"},ignoreInTests:{type:"boolean",default:true,description:"Ignore comments in test files"},minLines:{type:"number",default:1,minimum:1,description:"Minimum lines of commented code to trigger"}},additionalProperties:false}]},defaultOptions:[{ignoreSingleLine:false,ignoreInTests:true,minLines:1}],create(context,[options={}]){const{ignoreSingleLine=false,ignoreInTests=true,minLines=1}=options||{};const filename=context.filename;const isTestFile=ignoreInTests&&/\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);if(isTestFile){return{}}const sourceCode=context.sourceCode;function checkComment(node){try{const commentText=node.value||"";const isBlockComment=node.type==="Block";const lines=countCommentLines(commentText);if(ignoreSingleLine&&lines===1&&!isBlockComment){return}if(lines<minLines){return}if(looksLikeCode(commentText,isBlockComment)){context.report({node,messageId:"commentedCode",data:{lines:String(lines)},suggest:[{messageId:"removeCode",fix:fixer=>{try{return fixer.remove(node)}catch{return null}}},{messageId:"useVersionControl",fix:()=>null}]})}}catch{return}}return{Program(){const comments=sourceCode.getAllComments();const groupedComments=[];let currentGroup=[];for(let i=0;i<comments.length;i++){const comment=comments[i];const commentText=comment.value||"";const isBlockComment=comment.type==="Block";if(looksLikeCode(commentText,isBlockComment)){currentGroup.push(comment)}else{if(currentGroup.length>0){groupedComments.push([...currentGroup]);currentGroup=[]}}}if(currentGroup.length>0){groupedComments.push(currentGroup)}groupedComments.forEach(group=>{if(group.length===1){checkComment(group[0])}else{const firstComment=group[0];const totalLines=group.reduce((sum,comment)=>{const commentText=comment.value||"";return sum+countCommentLines(commentText)},0);if(totalLines>=minLines){context.report({node:firstComment,messageId:"commentedCode",data:{lines:String(totalLines)},suggest:[{messageId:"removeCode",fix:fixer=>{try{return fixer.removeRange([group[0].range[0],group[group.length-1].range[1]])}catch{return null}}},{messageId:"useVersionControl",fix:()=>null}]})}}})}}}});
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noCommentedCode=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");function looksLikeCode(comment,isBlockComment){if(isBlockComment&&comment.startsWith("*")){return false}let text=comment;if(isBlockComment){text=text.replace(/^\s*\/\*+/,"").replace(/\*+\/\s*$/,"");text=text.split("\n").map(line=>line.replace(/^\s*\*+\s*/,"")).join("\n")}else{text=text.split("\n").map(line=>line.replace(/^\s*\/\/+\s*/,"")).join("\n")}const lines=text.split("\n").map(line=>line.trim()).filter(line=>line.length>0);if(lines.length===0){return false}const STRUCTURAL_CODE=[/^(const|let|var)\s+[A-Za-z_$][\w$]*(\s*:[^=]+)?\s*=/,/^import\s+[^;]*\bfrom\b/,/^import\s+["']/,/^export\s+(default|const|let|var|function|class|\{|\*)/,/^throw\s+new\s+[A-Za-z_$]/,/^(throw|await|yield)\s+[A-Za-z_$][\w$]*\s*[.(]/,/^return\s+(new|await)\s/,/^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*)+\s*\(/,/=>/,/===|!==/,/^@[A-Za-z_$][\w$]*\s*\(/,/^<[A-Z][\w$]*[\s/>]/];const codePatterns=[/^(const|let|var|function|class|if|for|while|return|import|export)\s+/,/^[a-zA-Z_$][a-zA-Z0-9_$]*\s*[=:]\s*/,/^[a-zA-Z_$][a-zA-Z0-9_$]*\(/,/^[{}[\]]/];const URL_LINE=/^[a-z][a-z0-9+.-]*:\/\//i;const ENDS_LIKE_CODE=/[;{},()[\]]$|=>$/;let codeLikeLines=0;for(const line of lines){if(/^(TODO|FIXME|HACK|XXX)/i.test(line)){continue}if(URL_LINE.test(line)){continue}if(!STRUCTURAL_CODE.some(pattern=>pattern.test(line))&&!ENDS_LIKE_CODE.test(line)){continue}if(STRUCTURAL_CODE.some(pattern=>pattern.test(line))){codeLikeLines++;continue}for(const pattern of codePatterns){if(pattern.test(line)){codeLikeLines++;break}}}return codeLikeLines>0}function countCommentLines(comment){if(!comment)return 0;const newlineCount=(comment.match(/\n/g)||[]).length;return newlineCount>0?newlineCount+1:1}exports.noCommentedCode=(0,eslint_devkit_2.createRule)({name:"no-commented-code",skipGeneratedFiles:true,meta:{type:"suggestion",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-conventions/docs/rules/no-commented-code.md",description:"Detects commented-out code blocks"},hasSuggestions:true,messages:{commentedCode:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.WARNING,issueName:"Commented code",description:"Commented-out code detected ({{lines}} lines)",severity:"LOW",fix:"Remove commented code or use version control for history",documentationLink:"https://rules.sonarsource.com/javascript/RSPEC-125/"}),removeCode:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Remove Code",description:"Remove commented code block",severity:"LOW",fix:"Delete the commented code block",documentationLink:"https://rules.sonarsource.com/javascript/RSPEC-125/"}),useVersionControl:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use Git",description:"Use version control for code history",severity:"LOW",fix:"Delete commented code and use git history instead",documentationLink:"https://git-scm.com/docs/git-log"})},schema:[{type:"object",properties:{ignoreSingleLine:{type:"boolean",default:false,description:"Ignore single-line comments"},ignoreInTests:{type:"boolean",default:true,description:"Ignore comments in test files"},minLines:{type:"number",default:1,minimum:1,description:"Minimum lines of commented code to trigger"}},additionalProperties:false}]},defaultOptions:[{ignoreSingleLine:false,ignoreInTests:true,minLines:1}],create(context,[options={}]){const{ignoreSingleLine=false,ignoreInTests=true,minLines=1}=options||{};const filename=context.filename;const isTestFile=ignoreInTests&&/\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);if(isTestFile){return{}}const sourceCode=context.sourceCode;function checkComment(node){try{const commentText=node.value||"";const isBlockComment=node.type==="Block";const lines=countCommentLines(commentText);if(ignoreSingleLine&&lines===1&&!isBlockComment){return}if(lines<minLines){return}if(looksLikeCode(commentText,isBlockComment)){context.report({node,messageId:"commentedCode",data:{lines:String(lines)},suggest:[{messageId:"removeCode",fix:fixer=>{try{return fixer.remove(node)}catch{return null}}},{messageId:"useVersionControl",fix:()=>null}]})}}catch{return}}return{Program(){const comments=sourceCode.getAllComments();const groupedComments=[];let currentGroup=[];for(let i=0;i<comments.length;i++){const comment=comments[i];const commentText=comment.value||"";const isBlockComment=comment.type==="Block";if(looksLikeCode(commentText,isBlockComment)){currentGroup.push(comment)}else{if(currentGroup.length>0){groupedComments.push([...currentGroup]);currentGroup=[]}}}if(currentGroup.length>0){groupedComments.push(currentGroup)}groupedComments.forEach(group=>{if(group.length===1){checkComment(group[0])}else{const firstComment=group[0];const totalLines=group.reduce((sum,comment)=>{const commentText=comment.value||"";return sum+countCommentLines(commentText)},0);if(totalLines>=minLines){context.report({node:firstComment,messageId:"commentedCode",data:{lines:String(totalLines)},suggest:[{messageId:"removeCode",fix:fixer=>{try{return fixer.removeRange([group[0].range[0],group[group.length-1].range[1]])}catch{return null}}},{messageId:"useVersionControl",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",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},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};
|
|
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
|
|
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}}]})}}}}});
|