eslint-plugin-conventions 4.2.2 → 4.2.4

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.
Files changed (56) hide show
  1. package/package.json +19 -19
  2. package/src/index.d.ts +15 -173
  3. package/src/index.js +1 -8
  4. package/src/lib/eslint-plugin-conventions.js +0 -1
  5. package/src/oxlint.d.ts +17 -20
  6. package/src/oxlint.js +0 -19
  7. package/src/rules/conventions/analytics-event-naming.js +0 -11
  8. package/src/rules/conventions/consistent-existence-index-check.js +0 -15
  9. package/src/rules/conventions/expiring-todo-comments.js +2 -35
  10. package/src/rules/conventions/filename-case.js +1 -40
  11. package/src/rules/conventions/no-commented-code.js +1 -43
  12. package/src/rules/conventions/no-console-spaces.js +0 -20
  13. package/src/rules/conventions/no-json-schema-tags.js +1 -28
  14. package/src/rules/conventions/no-magic-numbers.js +1 -21
  15. package/src/rules/conventions/no-raw-cross-property-href.js +0 -10
  16. package/src/rules/conventions/prefer-code-point.js +0 -18
  17. package/src/rules/conventions/prefer-dependency-version-strategy.js +0 -26
  18. package/src/rules/conventions/prefer-dom-node-text-content.js +0 -20
  19. package/src/rules/conventions/require-data-testid.js +1 -31
  20. package/src/rules/conventions/utm-taxonomy.js +0 -11
  21. package/src/rules/deprecation/no-deprecated-api.js +1 -19
  22. package/CHANGELOG.md +0 -164
  23. package/src/index.js.map +0 -1
  24. package/src/lib/eslint-plugin-conventions.d.ts +0 -1
  25. package/src/lib/eslint-plugin-conventions.js.map +0 -1
  26. package/src/oxlint.js.map +0 -1
  27. package/src/rules/conventions/analytics-event-naming.d.ts +0 -41
  28. package/src/rules/conventions/analytics-event-naming.js.map +0 -1
  29. package/src/rules/conventions/consistent-existence-index-check.d.ts +0 -19
  30. package/src/rules/conventions/consistent-existence-index-check.js.map +0 -1
  31. package/src/rules/conventions/expiring-todo-comments.d.ts +0 -65
  32. package/src/rules/conventions/expiring-todo-comments.js.map +0 -1
  33. package/src/rules/conventions/filename-case.d.ts +0 -32
  34. package/src/rules/conventions/filename-case.js.map +0 -1
  35. package/src/rules/conventions/no-commented-code.d.ts +0 -26
  36. package/src/rules/conventions/no-commented-code.js.map +0 -1
  37. package/src/rules/conventions/no-console-spaces.d.ts +0 -13
  38. package/src/rules/conventions/no-console-spaces.js.map +0 -1
  39. package/src/rules/conventions/no-json-schema-tags.d.ts +0 -29
  40. package/src/rules/conventions/no-json-schema-tags.js.map +0 -1
  41. package/src/rules/conventions/no-magic-numbers.d.ts +0 -41
  42. package/src/rules/conventions/no-magic-numbers.js.map +0 -1
  43. package/src/rules/conventions/no-raw-cross-property-href.d.ts +0 -24
  44. package/src/rules/conventions/no-raw-cross-property-href.js.map +0 -1
  45. package/src/rules/conventions/prefer-code-point.d.ts +0 -13
  46. package/src/rules/conventions/prefer-code-point.js.map +0 -1
  47. package/src/rules/conventions/prefer-dependency-version-strategy.d.ts +0 -29
  48. package/src/rules/conventions/prefer-dependency-version-strategy.js.map +0 -1
  49. package/src/rules/conventions/prefer-dom-node-text-content.d.ts +0 -13
  50. package/src/rules/conventions/prefer-dom-node-text-content.js.map +0 -1
  51. package/src/rules/conventions/require-data-testid.d.ts +0 -56
  52. package/src/rules/conventions/require-data-testid.js.map +0 -1
  53. package/src/rules/conventions/utm-taxonomy.d.ts +0 -25
  54. package/src/rules/conventions/utm-taxonomy.js.map +0 -1
  55. package/src/rules/deprecation/no-deprecated-api.d.ts +0 -30
  56. package/src/rules/deprecation/no-deprecated-api.js.map +0 -1
@@ -1,9 +1,4 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.preferDependencyVersionStrategy = void 0;
9
4
  const eslint_devkit_1 = require("@interlace/eslint-devkit");
@@ -84,7 +79,6 @@ exports.preferDependencyVersionStrategy = (0, eslint_devkit_2.createRule)({
84
79
  create(context) {
85
80
  const options = context.options[0] || {};
86
81
  const { strategy = 'caret', allowWorkspace = true, allowFile = true, allowLink = true, overrides = {}, } = options;
87
- // Validate strategy
88
82
  const validStrategies = [
89
83
  'caret',
90
84
  'tilde',
@@ -100,31 +94,23 @@ exports.preferDependencyVersionStrategy = (0, eslint_devkit_2.createRule)({
100
94
  });
101
95
  return {};
102
96
  }
103
- // If strategy is 'any', allow all versions
104
97
  if (strategy === 'any') {
105
98
  return {};
106
99
  }
107
100
  function checkVersion(node, depName, version) {
108
- // Skip special protocols
109
101
  if (allowWorkspace && version.startsWith('workspace:'))
110
102
  return;
111
103
  if (allowFile && version.startsWith('file:'))
112
104
  return;
113
105
  if (allowLink && version.startsWith('link:'))
114
106
  return;
115
- // Skip if not a semantic version (allow prefixes like ^, ~)
116
- // Match patterns like: 1.0.0, ^1.0.0, ~1.0.0, >=1.0.0, etc.
117
107
  if (!version.match(/^[\^~<>=]?\d+\.\d+\.\d+/))
118
108
  return;
119
- // Check for package-specific override
120
109
  const packageStrategy = overrides[depName] || strategy;
121
- // If override is 'any', skip this package
122
110
  if (packageStrategy === 'any')
123
111
  return;
124
112
  let expectedVersion = version;
125
113
  let needsFix = false;
126
- // Determine expected format based on strategy (package override or default)
127
- // First, extract the base version (remove any existing prefix)
128
114
  const baseVersion = version.replace(/^[\^~<>=]+/, '');
129
115
  switch (packageStrategy) {
130
116
  case 'caret':
@@ -140,7 +126,6 @@ exports.preferDependencyVersionStrategy = (0, eslint_devkit_2.createRule)({
140
126
  }
141
127
  break;
142
128
  case 'exact': {
143
- // Remove any prefix to get exact version
144
129
  if (version !== baseVersion) {
145
130
  expectedVersion = baseVersion;
146
131
  needsFix = true;
@@ -148,15 +133,12 @@ exports.preferDependencyVersionStrategy = (0, eslint_devkit_2.createRule)({
148
133
  break;
149
134
  }
150
135
  case 'range':
151
- // Allow ranges like ">=1.0.0 <2.0.0" or "1.0.0 || 2.0.0"
152
136
  if (!version.includes('||') &&
153
137
  !version.includes('>=') &&
154
138
  !version.includes('<=') &&
155
139
  !version.includes('>') &&
156
140
  !version.includes('<') &&
157
141
  !version.includes(' - ')) {
158
- // If it's just a version, suggest caret as default for ranges
159
- // Use baseVersion to avoid double-prefixing (e.g., ^^18.0.0)
160
142
  expectedVersion = `^${baseVersion}`;
161
143
  needsFix = true;
162
144
  }
@@ -178,9 +160,6 @@ exports.preferDependencyVersionStrategy = (0, eslint_devkit_2.createRule)({
178
160
  });
179
161
  }
180
162
  }
181
- /**
182
- * Check an object expression for dependency version violations
183
- */
184
163
  const checkObjectExpression = (node) => {
185
164
  for (const prop of node.properties) {
186
165
  if (prop.type === 'Property' &&
@@ -199,19 +178,15 @@ exports.preferDependencyVersionStrategy = (0, eslint_devkit_2.createRule)({
199
178
  }
200
179
  };
201
180
  return {
202
- // Check package.json dependencies properties
203
181
  'Property[key.value="dependencies"], Property[key.value="devDependencies"], Property[key.value="peerDependencies"]'(node) {
204
182
  if (node.value.type !== 'ObjectExpression')
205
183
  return;
206
184
  checkObjectExpression(node.value);
207
185
  },
208
- // Also check object literals (for testing and general use)
209
186
  ObjectExpression(node) {
210
- // Only check if it looks like a dependencies object (has string keys and version-like values)
211
187
  const hasVersionLikeValues = node.properties.some((prop) => {
212
188
  if (prop.type === 'Property' && prop.value.type === 'Literal') {
213
189
  const value = String(prop.value.value);
214
- // Check if value looks like a version (starts with ^, ~, or is a semantic version)
215
190
  return (/^[\^~]?\d+\.\d+\.\d+/.test(value) ||
216
191
  value.startsWith('workspace:'));
217
192
  }
@@ -224,4 +199,3 @@ exports.preferDependencyVersionStrategy = (0, eslint_devkit_2.createRule)({
224
199
  };
225
200
  },
226
201
  });
227
- //# sourceMappingURL=prefer-dependency-version-strategy.js.map
@@ -1,9 +1,4 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.preferDomNodeTextContent = void 0;
9
4
  const eslint_devkit_1 = require("@interlace/eslint-devkit");
@@ -37,26 +32,20 @@ exports.preferDomNodeTextContent = (0, eslint_devkit_1.createRule)({
37
32
  },
38
33
  defaultOptions: [],
39
34
  create(context) {
40
- // oxlint-disable-next-line consistent-function-scoping
41
35
  function isInAllowedContext() {
42
- // For simplicity, we'll skip the allow option for now
43
36
  return false;
44
37
  }
45
38
  function isLikelyDomElement(node) {
46
39
  const obj = node.object;
47
- // Check if it's a variable/identifier that might be a DOM element
48
40
  if (obj.type === 'Identifier') {
49
41
  const name = obj.name;
50
- // Common DOM element variable names
51
42
  if (name.match(/^(element|el|div|span|node|ref|dom|elem)$/i)) {
52
43
  return true;
53
44
  }
54
- // Variables that end with common DOM suffixes
55
45
  if (name.match(/(Element|Node|Ref)$/)) {
56
46
  return true;
57
47
  }
58
48
  }
59
- // Check for DOM method calls like document.querySelector, getElementById, etc.
60
49
  if (obj.type === 'CallExpression' &&
61
50
  obj.callee.type === 'MemberExpression') {
62
51
  const methodName = obj.callee.property?.name;
@@ -72,7 +61,6 @@ exports.preferDomNodeTextContent = (0, eslint_devkit_1.createRule)({
72
61
  return true;
73
62
  }
74
63
  }
75
- // Check for property access on known DOM objects
76
64
  if (obj.type === 'MemberExpression') {
77
65
  const propName = obj.property?.name;
78
66
  if (propName &&
@@ -86,20 +74,13 @@ exports.preferDomNodeTextContent = (0, eslint_devkit_1.createRule)({
86
74
  return true;
87
75
  }
88
76
  }
89
- // NOTE: a dedicated `this.element` check used to live here, but it was
90
- // unreachable: any `<obj>.element.innerText` access (including
91
- // `this.element.innerText`) is already accepted by the known-DOM-object
92
- // property list above ('element' is in that list).
93
77
  return false;
94
78
  }
95
- // oxlint-disable-next-line consistent-function-scoping
96
79
  function isInnerTextAccess(node) {
97
- // Check if this is accessing .innerText property
98
80
  if (node.property.type === 'Identifier' &&
99
81
  node.property.name === 'innerText') {
100
82
  return true;
101
83
  }
102
- // Also check computed property access like element["innerText"]
103
84
  if (node.computed &&
104
85
  node.property.type === 'Literal' &&
105
86
  node.property.value === 'innerText') {
@@ -144,4 +125,3 @@ exports.preferDomNodeTextContent = (0, eslint_devkit_1.createRule)({
144
125
  };
145
126
  },
146
127
  });
147
- //# sourceMappingURL=prefer-dom-node-text-content.js.map
@@ -1,16 +1,7 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.requireDataTestId = void 0;
9
4
  const eslint_devkit_1 = require("@interlace/eslint-devkit");
10
- /**
11
- * Native HTML elements that always need a stable selector — they're the
12
- * usual targets in Playwright / Cypress / Testing Library.
13
- */
14
5
  const ALWAYS_INTERACTIVE = new Set([
15
6
  'button',
16
7
  'input',
@@ -18,10 +9,6 @@ const ALWAYS_INTERACTIVE = new Set([
18
9
  'textarea',
19
10
  'form',
20
11
  ]);
21
- /**
22
- * `<a>` is interactive only when it has `href` or `onClick`. We don't want
23
- * to flag in-content anchor jumps (e.g. <a name="...">).
24
- */
25
12
  function isInteractiveAnchor(node) {
26
13
  return node.attributes.some((attr) => {
27
14
  if (attr.type !== 'JSXAttribute')
@@ -36,7 +23,6 @@ function hasEventHandler(node) {
36
23
  return false;
37
24
  if (attr.name.type !== 'JSXIdentifier')
38
25
  return false;
39
- // React `on*` handler convention: onClick, onChange, onSubmit, etc.
40
26
  return /^on[A-Z]/.test(attr.name.name);
41
27
  });
42
28
  }
@@ -44,7 +30,6 @@ function getJsxName(name) {
44
30
  if (name.type === 'JSXIdentifier')
45
31
  return name.name;
46
32
  if (name.type === 'JSXMemberExpression') {
47
- // Conservative: use the right-most segment. e.g. `<Card.Header />` → "Header"
48
33
  return name.property.type === 'JSXIdentifier'
49
34
  ? name.property.name
50
35
  : undefined;
@@ -61,14 +46,11 @@ function hasDataTestId(node) {
61
46
  return null;
62
47
  }
63
48
  function hasSpreadingAllProps(node) {
64
- // If the element receives `{...props}` we trust the parent is supplying
65
- // data-testid (or else the parent itself is responsible). Avoid double-
66
- // flagging in pure render-forward components.
67
49
  return node.attributes.some((a) => a.type === 'JSXSpreadAttribute');
68
50
  }
69
51
  function isStableValue(attr) {
70
52
  if (!attr.value)
71
- return false; // <... data-testid /> — boolean shorthand
53
+ return false;
72
54
  if (attr.value.type === 'Literal')
73
55
  return typeof attr.value.value === 'string';
74
56
  if (attr.value.type === 'JSXExpressionContainer') {
@@ -76,11 +58,8 @@ function isStableValue(attr) {
76
58
  if (expr.type === 'Literal')
77
59
  return typeof expr.value === 'string';
78
60
  if (expr.type === 'TemplateLiteral') {
79
- // Template literal is stable if all expressions are member/identifier
80
- // (no function calls / random values).
81
61
  return expr.expressions.every((e) => ['Identifier', 'MemberExpression', 'Literal'].includes(e.type));
82
62
  }
83
- // Identifier reference (likely a constant) — accept.
84
63
  if (expr.type === 'Identifier')
85
64
  return true;
86
65
  return false;
@@ -111,7 +90,6 @@ exports.requireDataTestId = (0, eslint_devkit_1.createRule)({
111
90
  issueName: 'Unstable data-testid value',
112
91
  description: '`data-testid` should be a string literal or a template literal of stable identifiers — values that change between renders make tests flaky.',
113
92
  severity: 'LOW',
114
- // oxlint-disable-next-line eslint/no-template-curly-in-string
115
93
  fix: 'Use a string literal or template like `pagination-page-${pageNumber}` instead of dynamic / computed values.',
116
94
  documentationLink: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-conventions/docs/rules/require-data-testid.md',
117
95
  }),
@@ -151,9 +129,6 @@ exports.requireDataTestId = (0, eslint_devkit_1.createRule)({
151
129
  {
152
130
  requireOn: [],
153
131
  ignore: [],
154
- // Default: only flag PascalCase components when they handle events.
155
- // Pure presentational wrappers (e.g. <Body />, <AnchorProvider>) are
156
- // not testing targets and would create noise.
157
132
  componentPattern: '^[A-Z]',
158
133
  componentRequiresHandler: true,
159
134
  enforceStableValues: true,
@@ -176,22 +151,18 @@ exports.requireDataTestId = (0, eslint_devkit_1.createRule)({
176
151
  return;
177
152
  if (ignore.has(name))
178
153
  return;
179
- // Decide whether this element is in-scope.
180
154
  let inScope = false;
181
155
  if (ALWAYS_INTERACTIVE.has(name))
182
156
  inScope = true;
183
157
  else if (name === 'a' && isInteractiveAnchor(node))
184
158
  inScope = true;
185
159
  else if (componentPattern && componentPattern.test(name)) {
186
- // PascalCase component. Only flag when it has an interactive
187
- // event handler, unless `componentRequiresHandler: false` is set.
188
160
  inScope = componentRequiresHandler ? hasEventHandler(node) : true;
189
161
  }
190
162
  else if (requireOn.has(name))
191
163
  inScope = true;
192
164
  if (!inScope)
193
165
  return;
194
- // Skip if a spread is forwarding all props — the parent owns testid.
195
166
  if (hasSpreadingAllProps(node))
196
167
  return;
197
168
  const existing = hasDataTestId(node);
@@ -206,4 +177,3 @@ exports.requireDataTestId = (0, eslint_devkit_1.createRule)({
206
177
  };
207
178
  },
208
179
  });
209
- //# sourceMappingURL=require-data-testid.js.map
@@ -1,15 +1,8 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.utmTaxonomy = void 0;
9
4
  const eslint_devkit_1 = require("@interlace/eslint-devkit");
10
5
  const eslint_devkit_2 = require("@interlace/eslint-devkit");
11
- // UTM_PHILOSOPHY.md → "The taxonomy" table.
12
- // Edit there and here together (the rule has no other source of truth).
13
6
  const VALID_UTM_SOURCES = new Set([
14
7
  'ofriperetz_dev',
15
8
  'interlace',
@@ -17,9 +10,6 @@ const VALID_UTM_SOURCES = new Set([
17
10
  'serverless_docs',
18
11
  'ds',
19
12
  'storybook',
20
- // `devto` (not `dev_to`): the blog's /go/ redirect handler routes by
21
- // `article_platforms.platform === utm_source`, and those rows are stored
22
- // as 'devto'. The value is load-bearing — see UTM_PHILOSOPHY.md.
23
13
  'devto',
24
14
  'github',
25
15
  'npm',
@@ -114,4 +104,3 @@ exports.utmTaxonomy = (0, eslint_devkit_1.createRule)({
114
104
  };
115
105
  },
116
106
  });
117
- //# sourceMappingURL=utm-taxonomy.js.map
@@ -1,9 +1,4 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.noDeprecatedApi = void 0;
9
4
  const eslint_devkit_1 = require("@interlace/eslint-devkit");
@@ -21,7 +16,6 @@ exports.noDeprecatedApi = (0, eslint_devkit_2.createRule)({
21
16
  fixable: 'code',
22
17
  hasSuggestions: true,
23
18
  messages: {
24
- // 🎯 Token optimization: 44% reduction (48→27 tokens) - removes verbose labels
25
19
  deprecatedAPI: (0, eslint_devkit_1.formatLLMMessage)({
26
20
  icon: eslint_devkit_1.MessageIcons.DEPRECATION,
27
21
  issueName: 'Deprecated API',
@@ -76,16 +70,10 @@ exports.noDeprecatedApi = (0, eslint_devkit_2.createRule)({
76
70
  ],
77
71
  create(context) {
78
72
  const options = context.options[0] || {};
79
- // `options` is always an object here (the `|| {}` above guarantees it).
80
73
  const { apis = [], warnDaysBeforeRemoval = 90 } = options;
81
- // Early return if no deprecated APIs configured
82
74
  if (!apis || apis.length === 0) {
83
75
  return {};
84
76
  }
85
- /**
86
- * Calculate days until removal
87
- */
88
- // oxlint-disable-next-line consistent-function-scoping
89
77
  const calculateDaysRemaining = (removalDate) => {
90
78
  if (!removalDate)
91
79
  return null;
@@ -94,14 +82,11 @@ exports.noDeprecatedApi = (0, eslint_devkit_2.createRule)({
94
82
  const diff = removal.getTime() - now.getTime();
95
83
  return Math.ceil(diff / (1000 * 60 * 60 * 24));
96
84
  };
97
- /**
98
- * Determine urgency level
99
- */
100
85
  const getUrgencyLevel = (daysRemaining) => {
101
86
  if (daysRemaining === null)
102
87
  return 'low';
103
88
  if (daysRemaining < 0)
104
- return 'critical'; // Already past removal date!
89
+ return 'critical';
105
90
  if (daysRemaining < 30)
106
91
  return 'critical';
107
92
  if (daysRemaining < warnDaysBeforeRemoval)
@@ -109,7 +94,6 @@ exports.noDeprecatedApi = (0, eslint_devkit_2.createRule)({
109
94
  return 'medium';
110
95
  };
111
96
  return {
112
- // Check member expressions (e.g., obj.deprecatedMethod())
113
97
  MemberExpression(node) {
114
98
  if (node.property.type !== 'Identifier')
115
99
  return;
@@ -141,7 +125,6 @@ exports.noDeprecatedApi = (0, eslint_devkit_2.createRule)({
141
125
  ],
142
126
  });
143
127
  },
144
- // Check call expressions (e.g., deprecatedFunction())
145
128
  CallExpression(node) {
146
129
  if (node.callee.type !== 'Identifier')
147
130
  return;
@@ -176,4 +159,3 @@ exports.noDeprecatedApi = (0, eslint_devkit_2.createRule)({
176
159
  };
177
160
  },
178
161
  });
179
- //# sourceMappingURL=no-deprecated-api.js.map
package/CHANGELOG.md DELETED
@@ -1,164 +0,0 @@
1
- ## 4.2.2
2
-
3
- ### Patch Changes
4
-
5
- - [#269](https://github.com/ofri-peretz/eslint/pull/269) [`7028fe2`](https://github.com/ofri-peretz/eslint/commit/7028fe2668a42266d831014184dcef70e73101ad) Thanks [@ofri-peretz](https://github.com/ofri-peretz)! - docs: dual-logo README header (Interlace mark + ESLint mark side by side) and closing Interlace footer — refreshes the README rendered on npmjs.com. No runtime changes.
6
-
7
- - Updated dependencies [[`7028fe2`](https://github.com/ofri-peretz/eslint/commit/7028fe2668a42266d831014184dcef70e73101ad)]:
8
- - @interlace/eslint-devkit@1.4.2
9
-
10
- ## 4.2.1
11
-
12
- ### Patch Changes
13
-
14
- - [#252](https://github.com/ofri-peretz/eslint/pull/252) [`d67e395`](https://github.com/ofri-peretz/eslint/commit/d67e3953c2748ad36e6aebe0f24b1d04e518b4d0) Thanks [@ofri-peretz](https://github.com/ofri-peretz)! - Fix Codecov badge showing "unknown" — switch from flag to component URL format
15
-
16
- ## 4.2.0
17
-
18
- ### Minor Changes
19
-
20
- - [#241](https://github.com/ofri-peretz/eslint/pull/241) [`69fe7ac`](https://github.com/ofri-peretz/eslint/commit/69fe7ac35589a8be64328fd5aa4fdedb60203a28) Thanks [@ofri-peretz](https://github.com/ofri-peretz)! - `conventions/utm-taxonomy`: align the taxonomy with the live attribution
21
- system — add `devto` to `utm_source` and `article` to `utm_medium`, and drop
22
- the never-shipped `dev_to` spelling.
23
-
24
- The blog's `/go/` redirect handler routes by
25
- `article_platforms.platform === utm_source`, and those platform rows are
26
- stored as `'devto'` — the un-underscored form is load-bearing and cannot
27
- change. Every hand-written article link and the Dev.to publisher transform
28
- already emit `utm_source=devto&utm_medium=article`; the taxonomy's `dev_to`
29
- was the outlier. No real link ever carried `dev_to`, so removing it turns
30
- the rule into a typo guard instead of a cohort-splitter. Runtime consumers
31
- (visitor-profile inference) still accept `dev_to` on inbound URLs for
32
- historical links.
33
-
34
- ## 4.1.0
35
-
36
- ### Minor Changes
37
-
38
- - [#129](https://github.com/ofri-peretz/eslint/pull/129) [`90b970c`](https://github.com/ofri-peretz/eslint/commit/90b970c547f867ee79ec393ecb4da3f3e22f11f0) Thanks [@ofri-peretz](https://github.com/ofri-peretz)! - Three new rules at `error` severity enforcing the Observability cluster of the design philosophies (ANALYTICS_PHILOSOPHY.md + UTM_PHILOSOPHY.md):
39
- - **`utm-taxonomy`** — `utm_source` and `utm_medium` query-param values in any URL string literal must match the fixed taxonomy. Free-text values (`Blog`, `blog_v2`, `BLOG`) destroy joinability in PostHog and are forbidden.
40
- - **`no-raw-cross-property-href`** — Hand-written `<a href="https://*.interlace.tools/…">` and `<a href="https://ofriperetz.dev/…">` JSX literals are flagged. The blessed escape hatch is the per-property `buildUtmHref()` helper from `lib/utm.ts`.
41
- - **`analytics-event-naming`** — Vendor-neutral. Matches `<obj>.capture()` (PostHog), `<obj>.track()` (Segment / Mixpanel / Amplitude), and bare `track()` (our primitive). Event names must follow `category:object_action` (lowercase snake_case) with action from a fixed verb list; `$`-prefixed reserved events are exempt; template-literal event names are forbidden.
42
-
43
- Also fixes `compareVersions` in `expiring-todo-comments` to normalise wildcards (`24.x` in `engines.node`) — without this, `parseSemver` returned `null` and the comparator falsely matched every `>= engine TODO` as expired.
44
-
45
- - [#148](https://github.com/ofri-peretz/eslint/pull/148) [`82718c2`](https://github.com/ofri-peretz/eslint/commit/82718c282895710d42c36d4679fb24d47f1c35c7) Thanks [@ofri-peretz](https://github.com/ofri-peretz)! - feat(no-magic-numbers): add conventions/no-magic-numbers — closes prob_magic_numbers ILB-Arena-Quality FN
46
-
47
- Flags numeric literals that lack a named constant, catching the magic number
48
- code smell that makes intent unclear.
49
-
50
- **Built-in allowlist:** `-1, 0, 1, 2` are always allowed as universally idiomatic.
51
-
52
- **Context-aware skips (by default):**
53
- - `const` / `export const` declarations — the literal IS the named constant
54
- - Array index access: `items[3]` (`ignoreArrayIndexes: true`)
55
- - Default parameter values: `function f(n = 1000) {}`
56
- - TypeScript enum initializers: `enum Status { Active = 1 }`
57
- - Numeric object property keys: `{ 404: 'Not Found' }`
58
-
59
- **Options:** `ignore`, `ignoreArrayIndexes`, `ignoreDefaultValues`, `ignoreEnums`, `ignoreBitwiseExpressions`.
60
-
61
- Added at `warn` severity in `conventions/recommended`.
62
-
63
- ### Patch Changes
64
-
65
- - [#154](https://github.com/ofri-peretz/eslint/pull/154) [`62e67a1`](https://github.com/ofri-peretz/eslint/commit/62e67a154a858b284899e17cfe39606e6bc08427) Thanks [@ofri-peretz](https://github.com/ofri-peretz)! - `no-magic-numbers`: add extract-const suggestion fixer. IDEs now offer a one-click "Extract to named constant" action that inserts a `const MAGIC_<value>` declaration before the containing statement and replaces the literal.
66
-
67
- - Updated dependencies [[`736a5fe`](https://github.com/ofri-peretz/eslint/commit/736a5fed47e673f6157ea900b29fe2a54e4bc7df)]:
68
- - @interlace/eslint-devkit@1.4.1
69
-
70
- ## 4.0.7 (2026-02-09)
71
-
72
- This was a version bump only for eslint-plugin-conventions to align it with other projects, there were no code changes.
73
-
74
- ## [4.0.5] - 2026-02-08
75
-
76
- This was a version bump only for eslint-plugin-conventions to align it with other projects, there were no code changes.
77
-
78
- ## [4.0.4] - 2026-02-08
79
-
80
- ### Bug Fixes
81
-
82
- - align codecov component IDs with full package names ([2831b968](https://github.com/ofri-peretz/eslint/commit/2831b968))
83
-
84
- ### Documentation
85
-
86
- - fix changelog header format across all packages ([c3a15082](https://github.com/ofri-peretz/eslint/commit/c3a15082))
87
-
88
- ### ❤️ Thank You
89
-
90
- - Ofri Peretz
91
-
92
- ## [4.0.3] - 2026-02-06
93
-
94
- ### Bug Fixes
95
-
96
- - align codecov component names and update docs components ([0a59a86c](https://github.com/ofri-peretz/eslint/commit/0a59a86c))
97
-
98
- ### ❤️ Thank You
99
-
100
- - Ofri Peretz
101
-
102
- ## [4.0.2] - 2026-02-02
103
-
104
- This was a version bump only for eslint-plugin-conventions to align it with other projects, there were no code changes.
105
-
106
- # Changelog
107
-
108
- All notable changes to `eslint-plugin-conventions` will be documented in this file.
109
-
110
- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
111
- and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
112
-
113
- ### Documentation
114
-
115
- - 📘 Launched new documentation site: [eslint.interlace.tools](https://eslint.interlace.tools/)
116
- - 📝 Achieved 100% documentation parity (both .md and .mdx files)
117
-
118
- ## [4.0.1] - 2026-02-02
119
-
120
- This was a version bump only for eslint-plugin-conventions to align it with other projects, there were no code changes.
121
-
122
- ## [4.0.0] - 2026-02-02
123
-
124
- This was a version bump only for eslint-plugin-conventions to align it with other projects, there were no code changes.
125
-
126
- ## [3.0.0] - 2026-02-02
127
-
128
- This was a version bump only for eslint-plugin-conventions to align it with other projects, there were no code changes.
129
-
130
- ## [1.0.0] - 2026-01-26
131
-
132
- ### Added
133
-
134
- - Initial stable release with 9 convention rules
135
- - LLM-optimized error messages for AI-assisted development
136
- - 100% test coverage across all rules
137
- - ESLint 9 flat config support
138
- - TypeScript type definitions for all rule options
139
-
140
- ### Rules
141
-
142
- | Rule | Description | 💼 | ⚠️ |
143
- | :----------------------------------- | :---------------------------------------------------- | :-: | :-: |
144
- | `no-commented-code` | Disallow commented-out code blocks | 💼 | ⚠️ |
145
- | `expiring-todo-comments` | Enforce expiration dates on TODO comments | 💼 | ⚠️ |
146
- | `prefer-code-point` | Prefer `codePointAt` over `charCodeAt` for Unicode | | |
147
- | `prefer-dom-node-text-content` | Prefer `textContent` over `innerText` for performance | | |
148
- | `no-console-spaces` | Disallow leading/trailing spaces in console calls | | |
149
- | `no-deprecated-api` | Disallow usage of deprecated Node.js APIs | 💼 | ⚠️ |
150
- | `prefer-dependency-version-strategy` | Enforce consistent version strategies | | |
151
- | `filename-case` | Enforce consistent file naming conventions | | |
152
- | `consistent-existence-index-check` | Enforce consistent array index existence checks | | |
153
-
154
- ### Presets
155
-
156
- - `recommended` - Balanced conventions for most teams
157
-
158
- ### Known Limitations
159
-
160
- Documented in `docs/KNOWN-LIMITATIONS.md`:
161
-
162
- - `filename-case`: Framework-required names (e.g., `webpack.config.js`) require manual exclude lists
163
- - `no-deprecated-api`: May not detect usage through wrapper libraries
164
- - `expiring-todo-comments`: Requires consistent date formats (ISO 8601 recommended)
package/src/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AAIH,oBAAoB;AACpB,6EAAwE;AACxE,uFAAkF;AAClF,6EAAwE;AACxE,mGAA4F;AAC5F,6EAAwE;AACxE,6EAAwE;AACxE,+GAAyG;AACzG,qEAAiE;AACjE,2GAAqG;AACrG,iFAA2E;AAC3E,iFAA4E;AAC5E,mEAA+D;AAC/D,+FAAwF;AACxF,uFAAkF;AAClF,2EAAsE;AAEzD,QAAA,KAAK,GAAG;IACnB,mBAAmB,EAAE,mCAAe;IACpC,wBAAwB,EAAE,6CAAoB;IAC9C,mBAAmB,EAAE,mCAAe;IACpC,8BAA8B,EAAE,uDAAwB;IACxD,mBAAmB,EAAE,mCAAe;IACpC,mBAAmB,EAAE,mCAAe;IACpC,oCAAoC,EAAE,oEAA+B;IACrE,eAAe,EAAE,4BAAY;IAC7B,kCAAkC,EAAE,gEAA6B;IACjE,qBAAqB,EAAE,sCAAgB;IACvC,qBAAqB,EAAE,uCAAiB;IACxC,cAAc,EAAE,0BAAW;IAC3B,4BAA4B,EAAE,mDAAsB;IACpD,wBAAwB,EAAE,6CAAoB;IAC9C,kBAAkB,EAAE,iCAAc;CACuC,CAAC;AAE/D,QAAA,MAAM,GAAG;IACpB,IAAI,EAAE;QACJ,IAAI,EAAE,2BAA2B;QACjC,OAAO,EAAE,OAAO;KACjB;IACD,KAAK,EAAL,aAAK;CAC+B,CAAC;AAE1B,QAAA,OAAO,GAAG;IACrB,WAAW,EAAE;QACX,OAAO,EAAE;YACP,WAAW,EAAE,cAAM;SACpB;QACD,KAAK,EAAE;YACL,+BAA+B,EAAE,MAAM;YACvC,oCAAoC,EAAE,MAAM;YAC5C,+BAA+B,EAAE,MAAM;YACvC,0BAA0B,EAAE,OAAO;YACnC,wCAAwC,EAAE,OAAO;YACjD,oCAAoC,EAAE,OAAO;YAC7C,8BAA8B,EAAE,MAAM;SACvC;KACmC;CACc,CAAC;AAEvD,kBAAe,cAAM,CAAC"}
@@ -1 +0,0 @@
1
- export declare function eslintPluginConventions(): string;
@@ -1 +0,0 @@
1
- {"version":3,"file":"eslint-plugin-conventions.js","sourceRoot":"","sources":["../../../src/lib/eslint-plugin-conventions.ts"],"names":[],"mappings":";;AAAA,0DAEC;AAFD,SAAgB,uBAAuB;IACrC,OAAO,2BAA2B,CAAC;AACrC,CAAC"}
package/src/oxlint.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"oxlint.js","sourceRoot":"","sources":["../../src/oxlint.ts"],"names":[],"mappings":";AAAA;;;;GAIG;AAEH;;;;;;;;;;;;GAYG;AAEH,mCAAiC;AAEjC,iBAAS,cAAM,CAAC"}
@@ -1,41 +0,0 @@
1
- /**
2
- * Copyright (c) 2025 Ofri Peretz
3
- * Licensed under the MIT License. Use of this source code is governed by the
4
- * MIT license that can be found in the LICENSE file.
5
- */
6
- /**
7
- * ESLint Rule: analytics-event-naming
8
- *
9
- * Vendor-neutral. Validates the first argument of analytics event-capture
10
- * calls against the naming grammar codified in ANALYTICS_PHILOSOPHY.md
11
- * principle 4:
12
- *
13
- * `category:object_action`
14
- *
15
- * - `category` and `object` are lowercase snake_case identifiers.
16
- * - `action` is one of a fixed verb list (present tense): click, submit,
17
- * view, add, remove, start, end, generate, send, cancel, fail, create,
18
- * delete, update, invite.
19
- * - Vendor reserved events starting with `$` (e.g. PostHog `$pageview`,
20
- * `$set`) are exempt.
21
- * - Template-literal event names are forbidden — they make the catalog
22
- * unbounded (principle 3).
23
- *
24
- * Matched call shapes (covers PostHog, Segment, Mixpanel, Amplitude
25
- * conventions plus our own primitives):
26
- *
27
- * - `<obj>.capture(name, props)` — PostHog
28
- * - `<obj>.track(name, props)` — Segment / Mixpanel / Amplitude
29
- * - `track(name, props)` — bare, our primitive
30
- *
31
- * Not matched:
32
- *
33
- * - `<obj>.identify(distinctId, props)` — first arg is a person id
34
- * - `<obj>.page(...)` / `pageview(...)` — event name is implicit
35
- */
36
- import type { TSESLint } from '@interlace/eslint-devkit';
37
- type MessageIds = 'invalidEventName' | 'interpolatedEventName';
38
- export declare const analyticsEventNaming: TSESLint.RuleModule<MessageIds, [], unknown, TSESLint.RuleListener> & {
39
- name: string;
40
- };
41
- export {};
@@ -1 +0,0 @@
1
- {"version":3,"file":"analytics-event-naming.js","sourceRoot":"","sources":["../../../../src/rules/conventions/analytics-event-naming.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AAiCH,4DAAsD;AACtD,4DAA0E;AAK1E,wEAAwE;AACxE,iBAAiB;AACjB,MAAM,YAAY,GAAG;IACnB,OAAO;IACP,QAAQ;IACR,MAAM;IACN,KAAK;IACL,QAAQ;IACR,OAAO;IACP,KAAK;IACL,UAAU;IACV,MAAM;IACN,QAAQ;IACR,MAAM;IACN,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,QAAQ;CACT,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAEZ,MAAM,aAAa,GAAG,IAAI,MAAM,CAC9B,qCAAqC,YAAY,iBAAiB,CACnE,CAAC;AAEW,QAAA,oBAAoB,GAAG,IAAA,0BAAU,EAA0B;IACtE,IAAI,EAAE,wBAAwB;IAC9B,IAAI,EAAE;QACJ,IAAI,EAAE,SAAS;QACf,IAAI,EAAE;YACJ,GAAG,EAAE,yHAAyH;YAC9H,WAAW,EACT,sJAAsJ;SACzJ;QACD,OAAO,EAAE,SAAS;QAClB,cAAc,EAAE,KAAK;QACrB,QAAQ,EAAE;YACR,gBAAgB,EAAE,IAAA,gCAAgB,EAAC;gBACjC,IAAI,EAAE,4BAAY,CAAC,OAAO;gBAC1B,SAAS,EAAE,8BAA8B;gBACzC,WAAW,EACT,qHAAqH;gBACvH,QAAQ,EAAE,MAAM;gBAChB,GAAG,EAAE,iOAAiO;gBACtO,iBAAiB,EACf,yEAAyE;aAC5E,CAAC;YACF,qBAAqB,EAAE,IAAA,gCAAgB,EAAC;gBACtC,IAAI,EAAE,4BAAY,CAAC,OAAO;gBAC1B,SAAS,EAAE,mCAAmC;gBAC9C,WAAW,EACT,kIAAkI;gBACpI,QAAQ,EAAE,MAAM;gBAChB,GAAG,EAAE,0FAA0F;gBAC/F,iBAAiB,EACf,yEAAyE;aAC5E,CAAC;SACH;QACD,MAAM,EAAE,EAAE;KACX;IACD,cAAc,EAAE,EAAE;IAElB,MAAM,CAAC,OAAsD;QAC3D,SAAS,aAAa,CAAC,IAA6B;YAClD,+BAA+B;YAC/B,sDAAsD;YACtD,IACE,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,kBAAkB;gBACvC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,YAAY;gBAC1C,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,SAAS;oBACtC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,OAAO,CAAC,EACxC,CAAC;gBACD,OAAO,IAAI,CAAC;YACd,CAAC;YACD,kCAAkC;YAClC,IACE,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,YAAY;gBACjC,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,OAAO,EAC5B,CAAC;gBACD,OAAO,IAAI,CAAC;YACd,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,OAAO;YACL,cAAc,CAAC,IAA6B;gBAC1C,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC;oBAAE,OAAO;gBAChE,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;gBAChC,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;oBAChE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;wBACrC,OAAO,CAAC,MAAM,CAAC;4BACb,IAAI,EAAE,KAAK;4BACX,SAAS,EAAE,kBAAkB;4BAC7B,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE;yBAC5B,CAAC,CAAC;oBACL,CAAC;oBACD,OAAO;gBACT,CAAC;gBACD,IAAI,KAAK,CAAC,IAAI,KAAK,iBAAiB,IAAI,KAAK,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACrE,OAAO,CAAC,MAAM,CAAC;wBACb,IAAI,EAAE,KAAK;wBACX,SAAS,EAAE,uBAAuB;qBACnC,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
@@ -1,19 +0,0 @@
1
- /**
2
- * Copyright (c) 2025 Ofri Peretz
3
- * Licensed under the MIT License. Use of this source code is governed by the
4
- * MIT license that can be found in the LICENSE file.
5
- */
6
- /**
7
- * ESLint Rule: consistent-existence-index-check
8
- * Enforce consistent style for checking object property existence
9
- */
10
- import type { TSESLint } from '@interlace/eslint-devkit';
11
- export interface Options {
12
- /** Preferred method for checking property existence */
13
- preferred?: 'in' | 'hasOwnProperty' | 'Object.hasOwn';
14
- }
15
- type RuleOptions = [Options?];
16
- export declare const consistentExistenceIndexCheck: TSESLint.RuleModule<"consistentExistenceCheck", RuleOptions, unknown, TSESLint.RuleListener> & {
17
- name: string;
18
- };
19
- export {};
@@ -1 +0,0 @@
1
- {"version":3,"file":"consistent-existence-index-check.js","sourceRoot":"","sources":["../../../../src/rules/conventions/consistent-existence-index-check.ts"],"names":[],"mappings":"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"}