eslint-plugin-conventions 4.0.7 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +149 -0
- package/LICENSE +23 -0
- package/README.md +51 -21
- package/package.json +20 -3
- package/src/index.d.ts +49 -0
- package/src/index.js +15 -1
- package/src/index.js.map +1 -1
- package/src/lib/eslint-plugin-conventions.js.map +1 -1
- package/src/oxlint.d.ts +20 -0
- package/src/oxlint.js +22 -0
- package/src/oxlint.js.map +1 -0
- package/src/rules/conventions/analytics-event-naming.d.ts +41 -0
- package/src/rules/conventions/analytics-event-naming.js +104 -0
- package/src/rules/conventions/analytics-event-naming.js.map +1 -0
- package/src/rules/conventions/consistent-existence-index-check.js +5 -1
- package/src/rules/conventions/consistent-existence-index-check.js.map +1 -1
- package/src/rules/conventions/expiring-todo-comments.d.ts +41 -0
- package/src/rules/conventions/expiring-todo-comments.js +174 -165
- package/src/rules/conventions/expiring-todo-comments.js.map +1 -1
- package/src/rules/conventions/filename-case.js +13 -4
- package/src/rules/conventions/filename-case.js.map +1 -1
- package/src/rules/conventions/no-commented-code.js +3 -2
- package/src/rules/conventions/no-commented-code.js.map +1 -1
- package/src/rules/conventions/no-console-spaces.js +5 -0
- package/src/rules/conventions/no-console-spaces.js.map +1 -1
- package/src/rules/conventions/no-json-schema-tags.js +2 -1
- package/src/rules/conventions/no-json-schema-tags.js.map +1 -1
- package/src/rules/conventions/no-magic-numbers.d.ts +41 -0
- package/src/rules/conventions/no-magic-numbers.js +205 -0
- package/src/rules/conventions/no-magic-numbers.js.map +1 -0
- package/src/rules/conventions/no-raw-cross-property-href.d.ts +24 -0
- package/src/rules/conventions/no-raw-cross-property-href.js +86 -0
- package/src/rules/conventions/no-raw-cross-property-href.js.map +1 -0
- package/src/rules/conventions/prefer-code-point.js +4 -1
- package/src/rules/conventions/prefer-code-point.js.map +1 -1
- package/src/rules/conventions/prefer-dependency-version-strategy.d.ts +5 -5
- package/src/rules/conventions/prefer-dependency-version-strategy.js +1 -0
- package/src/rules/conventions/prefer-dependency-version-strategy.js.map +1 -1
- package/src/rules/conventions/prefer-dom-node-text-content.js +7 -7
- package/src/rules/conventions/prefer-dom-node-text-content.js.map +1 -1
- package/src/rules/conventions/require-data-testid.d.ts +56 -0
- package/src/rules/conventions/require-data-testid.js +209 -0
- package/src/rules/conventions/require-data-testid.js.map +1 -0
- package/src/rules/conventions/utm-taxonomy.d.ts +25 -0
- package/src/rules/conventions/utm-taxonomy.js +117 -0
- package/src/rules/conventions/utm-taxonomy.js.map +1 -0
- package/src/rules/deprecation/no-deprecated-api.js +6 -1
- package/src/rules/deprecation/no-deprecated-api.js.map +1 -1
|
@@ -13,6 +13,7 @@ exports.preferCodePoint = (0, eslint_devkit_1.createRule)({
|
|
|
13
13
|
meta: {
|
|
14
14
|
type: 'suggestion',
|
|
15
15
|
docs: {
|
|
16
|
+
url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-conventions/docs/rules/prefer-code-point.md',
|
|
16
17
|
description: 'Prefer codePointAt over charCodeAt for proper Unicode character handling',
|
|
17
18
|
},
|
|
18
19
|
messages: {
|
|
@@ -35,12 +36,13 @@ exports.preferCodePoint = (0, eslint_devkit_1.createRule)({
|
|
|
35
36
|
},
|
|
36
37
|
defaultOptions: [],
|
|
37
38
|
create(context) {
|
|
38
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, consistent-function-scoping
|
|
39
40
|
function isInAllowedContext(node) {
|
|
40
41
|
// For simplicity, we'll skip the allow option for now
|
|
41
42
|
// This would require more complex logic to check comments/code context
|
|
42
43
|
return false;
|
|
43
44
|
}
|
|
45
|
+
// oxlint-disable-next-line consistent-function-scoping
|
|
44
46
|
function shouldIgnoreCall(node) {
|
|
45
47
|
// The unicorn rule flags ALL charCodeAt calls, but allows some contexts
|
|
46
48
|
if (node.callee.type === 'MemberExpression') {
|
|
@@ -57,6 +59,7 @@ exports.preferCodePoint = (0, eslint_devkit_1.createRule)({
|
|
|
57
59
|
}
|
|
58
60
|
return false;
|
|
59
61
|
}
|
|
62
|
+
// oxlint-disable-next-line consistent-function-scoping
|
|
60
63
|
function isCharCodeAtCall(node) {
|
|
61
64
|
// Check if this is a call to charCodeAt method
|
|
62
65
|
if (node.callee.type === 'MemberExpression') {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prefer-code-point.js","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"prefer-code-point.js","sourceRoot":"","sources":["../../../../src/rules/conventions/prefer-code-point.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AAOH,4DAAsD;AACtD,4DAA0E;AAM7D,QAAA,eAAe,GAAG,IAAA,0BAAU,EAA0B;IACjE,IAAI,EAAE,mBAAmB;IACzB,IAAI,EAAE;QACJ,IAAI,EAAE,YAAY;QAClB,IAAI,EAAE;YACJ,GAAG,EAAE,oHAAoH;YACzH,WAAW,EACT,0EAA0E;SAC7E;QACD,QAAQ,EAAE;YACR,eAAe,EAAE,IAAA,gCAAgB,EAAC;gBAChC,IAAI,EAAE,4BAAY,CAAC,OAAO;gBAC1B,SAAS,EAAE,oBAAoB;gBAC/B,WAAW,EAAE,0DAA0D;gBACvE,QAAQ,EAAE,QAAQ;gBAClB,GAAG,EAAE,yCAAyC;gBAC9C,iBAAiB,EACf,iGAAiG;aACpG,CAAC;SACH;QACD,MAAM,EAAE;YACN;gBACE,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE,EAAE;gBACd,oBAAoB,EAAE,KAAK;aAC5B;SACF;KACF;IACD,cAAc,EAAE,EAAE;IAElB,MAAM,CAAC,OAAsD;QAC3D,0FAA0F;QAC1F,SAAS,kBAAkB,CAAC,IAA6B;YACvD,sDAAsD;YACtD,uEAAuE;YACvE,OAAO,KAAK,CAAC;QACf,CAAC;QAED,uDAAuD;QACvD,SAAS,gBAAgB,CAAC,IAA6B;YACrD,wEAAwE;YAExE,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;gBAC5C,0BAA0B;gBAC1B,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;oBACzB,OAAO,IAAI,CAAC;gBACd,CAAC;gBAED,gFAAgF;gBAChF,4EAA4E;gBAC5E,IACE,IAAI,CAAC,MAAM,CAAC,QAAQ;oBACpB,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,YAAY,EAC1C,CAAC;oBACD,OAAO,IAAI,CAAC;gBACd,CAAC;YACH,CAAC;YAED,OAAO,KAAK,CAAC;QACf,CAAC;QAED,uDAAuD;QACvD,SAAS,gBAAgB,CAAC,IAA6B;YACrD,+CAA+C;YAC/C,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;gBAC5C,yCAAyC;gBACzC,IACE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,YAAY;oBAC1C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,YAAY,EAC1C,CAAC;oBACD,OAAO,IAAI,CAAC;gBACd,CAAC;gBAED,8CAA8C;gBAC9C,IACE,IAAI,CAAC,MAAM,CAAC,QAAQ;oBACpB,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAK,SAAS;oBACvC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,KAAK,YAAY,EAC3C,CAAC;oBACD,OAAO,IAAI,CAAC;gBACd,CAAC;YACH,CAAC;YAED,OAAO,KAAK,CAAC;QACf,CAAC;QAED,OAAO;YACL,cAAc,CAAC,IAA6B;gBAC1C,IACE,gBAAgB,CAAC,IAAI,CAAC;oBACtB,CAAC,kBAAkB,CAAC,IAAI,CAAC;oBACzB,CAAC,gBAAgB,CAAC,IAAI,CAAC,EACvB,CAAC;oBACD,OAAO,CAAC,MAAM,CAAC;wBACb,IAAI;wBACJ,SAAS,EAAE,iBAAiB;wBAC5B,IAAI,EAAE;4BACJ,OAAO,EAAE,cAAc;4BACvB,GAAG,EAAE,eAAe;yBACrB;qBACF,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
/**
|
|
7
7
|
* prefer-dependency-version-strategy
|
|
8
8
|
*
|
|
9
|
-
* Enforces a consistent version strategy (caret, tilde, exact, etc.)
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
9
|
+
* Enforces a consistent version-specifier strategy (caret, tilde, exact, etc.)
|
|
10
|
+
* for package.json dependencies. Pairs with a lockfile-alignment check
|
|
11
|
+
* (e.g. `npm ci`, or this monorepo's `scripts/check-version-alignment.ts`)
|
|
12
|
+
* so both format consistency and lockfile parity are enforced; this rule
|
|
13
|
+
* handles the "format" half at edit/PR time inside ESLint.
|
|
14
14
|
*/
|
|
15
15
|
import type { TSESLint } from '@interlace/eslint-devkit';
|
|
16
16
|
type VersionStrategy = 'caret' | 'tilde' | 'exact' | 'range' | 'any';
|
|
@@ -13,6 +13,7 @@ exports.preferDependencyVersionStrategy = (0, eslint_devkit_2.createRule)({
|
|
|
13
13
|
meta: {
|
|
14
14
|
type: 'suggestion',
|
|
15
15
|
docs: {
|
|
16
|
+
url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-conventions/docs/rules/prefer-dependency-version-strategy.md',
|
|
16
17
|
description: 'Enforce consistent version strategy (caret, tilde, exact, etc.) for package.json dependencies',
|
|
17
18
|
},
|
|
18
19
|
fixable: 'code',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prefer-dependency-version-strategy.js","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"prefer-dependency-version-strategy.js","sourceRoot":"","sources":["../../../../src/rules/conventions/prefer-dependency-version-strategy.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,6 +13,7 @@ exports.preferDomNodeTextContent = (0, eslint_devkit_1.createRule)({
|
|
|
13
13
|
meta: {
|
|
14
14
|
type: 'suggestion',
|
|
15
15
|
docs: {
|
|
16
|
+
url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-conventions/docs/rules/prefer-dom-node-text-content.md',
|
|
16
17
|
description: 'Prefer textContent over innerText for better performance and reliability',
|
|
17
18
|
},
|
|
18
19
|
hasSuggestions: true,
|
|
@@ -36,6 +37,7 @@ exports.preferDomNodeTextContent = (0, eslint_devkit_1.createRule)({
|
|
|
36
37
|
},
|
|
37
38
|
defaultOptions: [],
|
|
38
39
|
create(context) {
|
|
40
|
+
// oxlint-disable-next-line consistent-function-scoping
|
|
39
41
|
function isInAllowedContext() {
|
|
40
42
|
// For simplicity, we'll skip the allow option for now
|
|
41
43
|
return false;
|
|
@@ -84,15 +86,13 @@ exports.preferDomNodeTextContent = (0, eslint_devkit_1.createRule)({
|
|
|
84
86
|
return true;
|
|
85
87
|
}
|
|
86
88
|
}
|
|
87
|
-
//
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
obj.property.name === 'element') {
|
|
92
|
-
return true;
|
|
93
|
-
}
|
|
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).
|
|
94
93
|
return false;
|
|
95
94
|
}
|
|
95
|
+
// oxlint-disable-next-line consistent-function-scoping
|
|
96
96
|
function isInnerTextAccess(node) {
|
|
97
97
|
// Check if this is accessing .innerText property
|
|
98
98
|
if (node.property.type === 'Identifier' &&
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prefer-dom-node-text-content.js","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"prefer-dom-node-text-content.js","sourceRoot":"","sources":["../../../../src/rules/conventions/prefer-dom-node-text-content.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
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: require-data-testid
|
|
8
|
+
*
|
|
9
|
+
* Enforces a stable `data-testid` attribute on elements that are testing
|
|
10
|
+
* targets. Pairs with `apps/docs/A11Y.md` (Layer 3 of the a11y self-test
|
|
11
|
+
* model): brittle class-name selectors break on every styling refactor;
|
|
12
|
+
* `data-testid` gives every component a stable identity invisible at
|
|
13
|
+
* runtime and untouchable by Tailwind churn.
|
|
14
|
+
*
|
|
15
|
+
* By default flags:
|
|
16
|
+
* - native interactive elements: <button>, <input>, <select>,
|
|
17
|
+
* <textarea>, <a> with onClick or href
|
|
18
|
+
* - custom React components (PascalCase identifier)
|
|
19
|
+
*
|
|
20
|
+
* Options:
|
|
21
|
+
* - `requireOn`: extra element/component names that must have the attribute
|
|
22
|
+
* - `ignore`: names to skip even when default-flagged
|
|
23
|
+
* - `componentPattern`: regex string for "this is a custom component"
|
|
24
|
+
* detection (default: ^[A-Z])
|
|
25
|
+
*/
|
|
26
|
+
import type { TSESLint } from '@interlace/eslint-devkit';
|
|
27
|
+
type MessageIds = 'missingDataTestId' | 'dynamicDataTestId';
|
|
28
|
+
export interface Options {
|
|
29
|
+
/** Additional element / component names to require `data-testid` on. */
|
|
30
|
+
requireOn?: string[];
|
|
31
|
+
/** Names to skip (even when otherwise default-flagged). */
|
|
32
|
+
ignore?: string[];
|
|
33
|
+
/**
|
|
34
|
+
* Regex string. If a JSX element name matches, it's treated as a "custom
|
|
35
|
+
* component" and required to carry `data-testid`. Default: `^[A-Z]`.
|
|
36
|
+
*/
|
|
37
|
+
componentPattern?: string;
|
|
38
|
+
/**
|
|
39
|
+
* If true, `data-testid` values must be string-literal expressions or
|
|
40
|
+
* template literals containing only string + identifier expressions
|
|
41
|
+
* (so they're stable across renders). Default: true.
|
|
42
|
+
*/
|
|
43
|
+
enforceStableValues?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* If true, only flag custom components (matching `componentPattern`)
|
|
46
|
+
* when they accept an event handler prop (onClick / onChange / etc.).
|
|
47
|
+
* Native interactive elements (`button`, `a`, `input`, …) are always
|
|
48
|
+
* flagged regardless. Default: true.
|
|
49
|
+
*/
|
|
50
|
+
componentRequiresHandler?: boolean;
|
|
51
|
+
}
|
|
52
|
+
type RuleOptions = [Options?];
|
|
53
|
+
export declare const requireDataTestId: TSESLint.RuleModule<MessageIds, RuleOptions, unknown, TSESLint.RuleListener> & {
|
|
54
|
+
name: string;
|
|
55
|
+
};
|
|
56
|
+
export {};
|
|
@@ -0,0 +1,209 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.requireDataTestId = void 0;
|
|
9
|
+
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
|
+
const ALWAYS_INTERACTIVE = new Set([
|
|
15
|
+
'button',
|
|
16
|
+
'input',
|
|
17
|
+
'select',
|
|
18
|
+
'textarea',
|
|
19
|
+
'form',
|
|
20
|
+
]);
|
|
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
|
+
function isInteractiveAnchor(node) {
|
|
26
|
+
return node.attributes.some((attr) => {
|
|
27
|
+
if (attr.type !== 'JSXAttribute')
|
|
28
|
+
return false;
|
|
29
|
+
const name = attr.name.type === 'JSXIdentifier' ? attr.name.name : undefined;
|
|
30
|
+
return name === 'href' || name === 'onClick';
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
function hasEventHandler(node) {
|
|
34
|
+
return node.attributes.some((attr) => {
|
|
35
|
+
if (attr.type !== 'JSXAttribute')
|
|
36
|
+
return false;
|
|
37
|
+
if (attr.name.type !== 'JSXIdentifier')
|
|
38
|
+
return false;
|
|
39
|
+
// React `on*` handler convention: onClick, onChange, onSubmit, etc.
|
|
40
|
+
return /^on[A-Z]/.test(attr.name.name);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
function getJsxName(name) {
|
|
44
|
+
if (name.type === 'JSXIdentifier')
|
|
45
|
+
return name.name;
|
|
46
|
+
if (name.type === 'JSXMemberExpression') {
|
|
47
|
+
// Conservative: use the right-most segment. e.g. `<Card.Header />` → "Header"
|
|
48
|
+
return name.property.type === 'JSXIdentifier'
|
|
49
|
+
? name.property.name
|
|
50
|
+
: undefined;
|
|
51
|
+
}
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
function hasDataTestId(node) {
|
|
55
|
+
for (const attr of node.attributes) {
|
|
56
|
+
if (attr.type !== 'JSXAttribute')
|
|
57
|
+
continue;
|
|
58
|
+
if (attr.name.type === 'JSXIdentifier' && attr.name.name === 'data-testid')
|
|
59
|
+
return attr;
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
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
|
+
return node.attributes.some((a) => a.type === 'JSXSpreadAttribute');
|
|
68
|
+
}
|
|
69
|
+
function isStableValue(attr) {
|
|
70
|
+
if (!attr.value)
|
|
71
|
+
return false; // <... data-testid /> — boolean shorthand
|
|
72
|
+
if (attr.value.type === 'Literal')
|
|
73
|
+
return typeof attr.value.value === 'string';
|
|
74
|
+
if (attr.value.type === 'JSXExpressionContainer') {
|
|
75
|
+
const expr = attr.value.expression;
|
|
76
|
+
if (expr.type === 'Literal')
|
|
77
|
+
return typeof expr.value === 'string';
|
|
78
|
+
if (expr.type === 'TemplateLiteral') {
|
|
79
|
+
// Template literal is stable if all expressions are member/identifier
|
|
80
|
+
// (no function calls / random values).
|
|
81
|
+
return expr.expressions.every((e) => ['Identifier', 'MemberExpression', 'Literal'].includes(e.type));
|
|
82
|
+
}
|
|
83
|
+
// Identifier reference (likely a constant) — accept.
|
|
84
|
+
if (expr.type === 'Identifier')
|
|
85
|
+
return true;
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
exports.requireDataTestId = (0, eslint_devkit_1.createRule)({
|
|
91
|
+
name: 'require-data-testid',
|
|
92
|
+
meta: {
|
|
93
|
+
type: 'problem',
|
|
94
|
+
docs: {
|
|
95
|
+
url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-conventions/docs/rules/require-data-testid.md',
|
|
96
|
+
description: 'Require stable `data-testid` attributes on interactive elements and custom components for testability.',
|
|
97
|
+
},
|
|
98
|
+
fixable: undefined,
|
|
99
|
+
hasSuggestions: false,
|
|
100
|
+
messages: {
|
|
101
|
+
missingDataTestId: (0, eslint_devkit_1.formatLLMMessage)({
|
|
102
|
+
icon: eslint_devkit_1.MessageIcons.WARNING,
|
|
103
|
+
issueName: 'Missing data-testid',
|
|
104
|
+
description: 'Interactive elements and custom components must expose a stable `data-testid` for E2E testability. Class-name selectors break on every Tailwind refactor; `data-testid` survives.',
|
|
105
|
+
severity: 'MEDIUM',
|
|
106
|
+
fix: 'Add `data-testid="<lower-kebab-case-name>"` to the element.',
|
|
107
|
+
documentationLink: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-conventions/docs/rules/require-data-testid.md',
|
|
108
|
+
}),
|
|
109
|
+
dynamicDataTestId: (0, eslint_devkit_1.formatLLMMessage)({
|
|
110
|
+
icon: eslint_devkit_1.MessageIcons.WARNING,
|
|
111
|
+
issueName: 'Unstable data-testid value',
|
|
112
|
+
description: '`data-testid` should be a string literal or a template literal of stable identifiers — values that change between renders make tests flaky.',
|
|
113
|
+
severity: 'LOW',
|
|
114
|
+
// oxlint-disable-next-line eslint/no-template-curly-in-string
|
|
115
|
+
fix: 'Use a string literal or template like `pagination-page-${pageNumber}` instead of dynamic / computed values.',
|
|
116
|
+
documentationLink: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-conventions/docs/rules/require-data-testid.md',
|
|
117
|
+
}),
|
|
118
|
+
},
|
|
119
|
+
schema: [
|
|
120
|
+
{
|
|
121
|
+
type: 'object',
|
|
122
|
+
additionalProperties: false,
|
|
123
|
+
properties: {
|
|
124
|
+
requireOn: {
|
|
125
|
+
type: 'array',
|
|
126
|
+
items: { type: 'string' },
|
|
127
|
+
description: 'Extra element / component names to flag.',
|
|
128
|
+
},
|
|
129
|
+
ignore: {
|
|
130
|
+
type: 'array',
|
|
131
|
+
items: { type: 'string' },
|
|
132
|
+
description: 'Element / component names to skip.',
|
|
133
|
+
},
|
|
134
|
+
componentPattern: {
|
|
135
|
+
type: 'string',
|
|
136
|
+
description: 'Regex; matched names are treated as custom components. Empty disables component checks.',
|
|
137
|
+
},
|
|
138
|
+
componentRequiresHandler: {
|
|
139
|
+
type: 'boolean',
|
|
140
|
+
description: 'When `componentPattern` matches, only flag if the component has an `on*` event handler (default true). Set false to flag every matching component.',
|
|
141
|
+
},
|
|
142
|
+
enforceStableValues: {
|
|
143
|
+
type: 'boolean',
|
|
144
|
+
description: 'Reject computed `data-testid` values (default: true).',
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
},
|
|
150
|
+
defaultOptions: [
|
|
151
|
+
{
|
|
152
|
+
requireOn: [],
|
|
153
|
+
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
|
+
componentPattern: '^[A-Z]',
|
|
158
|
+
componentRequiresHandler: true,
|
|
159
|
+
enforceStableValues: true,
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
create(context, [opts]) {
|
|
163
|
+
const options = opts ?? {};
|
|
164
|
+
const requireOn = new Set(options.requireOn ?? []);
|
|
165
|
+
const ignore = new Set(options.ignore ?? []);
|
|
166
|
+
const componentPatternStr = options.componentPattern ?? '^[A-Z]';
|
|
167
|
+
const componentPattern = componentPatternStr
|
|
168
|
+
? new RegExp(componentPatternStr)
|
|
169
|
+
: null;
|
|
170
|
+
const componentRequiresHandler = options.componentRequiresHandler ?? true;
|
|
171
|
+
const enforceStableValues = options.enforceStableValues ?? true;
|
|
172
|
+
return {
|
|
173
|
+
JSXOpeningElement(node) {
|
|
174
|
+
const name = getJsxName(node.name);
|
|
175
|
+
if (!name)
|
|
176
|
+
return;
|
|
177
|
+
if (ignore.has(name))
|
|
178
|
+
return;
|
|
179
|
+
// Decide whether this element is in-scope.
|
|
180
|
+
let inScope = false;
|
|
181
|
+
if (ALWAYS_INTERACTIVE.has(name))
|
|
182
|
+
inScope = true;
|
|
183
|
+
else if (name === 'a' && isInteractiveAnchor(node))
|
|
184
|
+
inScope = true;
|
|
185
|
+
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
|
+
inScope = componentRequiresHandler ? hasEventHandler(node) : true;
|
|
189
|
+
}
|
|
190
|
+
else if (requireOn.has(name))
|
|
191
|
+
inScope = true;
|
|
192
|
+
if (!inScope)
|
|
193
|
+
return;
|
|
194
|
+
// Skip if a spread is forwarding all props — the parent owns testid.
|
|
195
|
+
if (hasSpreadingAllProps(node))
|
|
196
|
+
return;
|
|
197
|
+
const existing = hasDataTestId(node);
|
|
198
|
+
if (!existing) {
|
|
199
|
+
context.report({ node, messageId: 'missingDataTestId' });
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (enforceStableValues && !isStableValue(existing)) {
|
|
203
|
+
context.report({ node: existing, messageId: 'dynamicDataTestId' });
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
//# sourceMappingURL=require-data-testid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"require-data-testid.js","sourceRoot":"","sources":["../../../../src/rules/conventions/require-data-testid.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
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: utm-taxonomy
|
|
8
|
+
*
|
|
9
|
+
* Validates `utm_source` and `utm_medium` values in URL string literals
|
|
10
|
+
* against the fixed taxonomy codified in UTM_PHILOSOPHY.md. Free-text values
|
|
11
|
+
* (`Blog`, `BLOG`, `blog_v2`) destroy joinability in PostHog — they don't
|
|
12
|
+
* compose into a single cohort, they split into N rows that nothing can
|
|
13
|
+
* re-merge automatically.
|
|
14
|
+
*
|
|
15
|
+
* The rule scans string Literal nodes anywhere in source for `utm_source=`
|
|
16
|
+
* or `utm_medium=` query params and reports any value outside the fixed
|
|
17
|
+
* list. The fixed list lives in this rule and in UTM_PHILOSOPHY.md (taxonomy
|
|
18
|
+
* table) — both must be edited together.
|
|
19
|
+
*/
|
|
20
|
+
import type { TSESLint } from '@interlace/eslint-devkit';
|
|
21
|
+
type MessageIds = 'invalidUtmSource' | 'invalidUtmMedium';
|
|
22
|
+
export declare const utmTaxonomy: TSESLint.RuleModule<MessageIds, [], unknown, TSESLint.RuleListener> & {
|
|
23
|
+
name: string;
|
|
24
|
+
};
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,117 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.utmTaxonomy = void 0;
|
|
9
|
+
const eslint_devkit_1 = require("@interlace/eslint-devkit");
|
|
10
|
+
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
|
+
const VALID_UTM_SOURCES = new Set([
|
|
14
|
+
'ofriperetz_dev',
|
|
15
|
+
'interlace',
|
|
16
|
+
'eslint_docs',
|
|
17
|
+
'serverless_docs',
|
|
18
|
+
'ds',
|
|
19
|
+
'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
|
+
'devto',
|
|
24
|
+
'github',
|
|
25
|
+
'npm',
|
|
26
|
+
'x',
|
|
27
|
+
'linkedin',
|
|
28
|
+
'email',
|
|
29
|
+
]);
|
|
30
|
+
const VALID_UTM_MEDIUMS = new Set([
|
|
31
|
+
'article',
|
|
32
|
+
'blog',
|
|
33
|
+
'docs',
|
|
34
|
+
'landing',
|
|
35
|
+
'social',
|
|
36
|
+
'email',
|
|
37
|
+
'referral',
|
|
38
|
+
'cli',
|
|
39
|
+
]);
|
|
40
|
+
const UTM_PARAM_RE = /[?&](utm_source|utm_medium)=([^&#]+)/g;
|
|
41
|
+
exports.utmTaxonomy = (0, eslint_devkit_1.createRule)({
|
|
42
|
+
name: 'utm-taxonomy',
|
|
43
|
+
meta: {
|
|
44
|
+
type: 'problem',
|
|
45
|
+
docs: {
|
|
46
|
+
url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-conventions/docs/rules/utm-taxonomy.md',
|
|
47
|
+
description: 'Validate utm_source and utm_medium values against the fixed taxonomy in UTM_PHILOSOPHY.md',
|
|
48
|
+
},
|
|
49
|
+
fixable: undefined,
|
|
50
|
+
hasSuggestions: false,
|
|
51
|
+
messages: {
|
|
52
|
+
invalidUtmSource: (0, eslint_devkit_2.formatLLMMessage)({
|
|
53
|
+
icon: eslint_devkit_2.MessageIcons.WARNING,
|
|
54
|
+
issueName: 'Invalid utm_source',
|
|
55
|
+
description: 'utm_source="{{value}}" is not in the fixed taxonomy. Allowed: {{allowed}}',
|
|
56
|
+
severity: 'HIGH',
|
|
57
|
+
fix: 'Use one of the allowed values, or extend the taxonomy in UTM_PHILOSOPHY.md and this rule together.',
|
|
58
|
+
documentationLink: 'https://github.com/ofri-peretz/eslint/blob/main/UTM_PHILOSOPHY.md',
|
|
59
|
+
}),
|
|
60
|
+
invalidUtmMedium: (0, eslint_devkit_2.formatLLMMessage)({
|
|
61
|
+
icon: eslint_devkit_2.MessageIcons.WARNING,
|
|
62
|
+
issueName: 'Invalid utm_medium',
|
|
63
|
+
description: 'utm_medium="{{value}}" is not in the fixed taxonomy. Allowed: {{allowed}}',
|
|
64
|
+
severity: 'HIGH',
|
|
65
|
+
fix: 'Use one of the allowed values, or extend the taxonomy in UTM_PHILOSOPHY.md and this rule together.',
|
|
66
|
+
documentationLink: 'https://github.com/ofri-peretz/eslint/blob/main/UTM_PHILOSOPHY.md',
|
|
67
|
+
}),
|
|
68
|
+
},
|
|
69
|
+
schema: [],
|
|
70
|
+
},
|
|
71
|
+
defaultOptions: [],
|
|
72
|
+
create(context) {
|
|
73
|
+
function checkString(node, value) {
|
|
74
|
+
UTM_PARAM_RE.lastIndex = 0;
|
|
75
|
+
let match;
|
|
76
|
+
while ((match = UTM_PARAM_RE.exec(value)) !== null) {
|
|
77
|
+
const [, key, raw] = match;
|
|
78
|
+
const decoded = decodeURIComponent(raw);
|
|
79
|
+
if (key === 'utm_source' && !VALID_UTM_SOURCES.has(decoded)) {
|
|
80
|
+
context.report({
|
|
81
|
+
node,
|
|
82
|
+
messageId: 'invalidUtmSource',
|
|
83
|
+
data: {
|
|
84
|
+
value: decoded,
|
|
85
|
+
allowed: [...VALID_UTM_SOURCES].join(', '),
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
else if (key === 'utm_medium' && !VALID_UTM_MEDIUMS.has(decoded)) {
|
|
90
|
+
context.report({
|
|
91
|
+
node,
|
|
92
|
+
messageId: 'invalidUtmMedium',
|
|
93
|
+
data: {
|
|
94
|
+
value: decoded,
|
|
95
|
+
allowed: [...VALID_UTM_MEDIUMS].join(', '),
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
Literal(node) {
|
|
103
|
+
if (typeof node.value === 'string' && node.value.includes('utm_')) {
|
|
104
|
+
checkString(node, node.value);
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
TemplateLiteral(node) {
|
|
108
|
+
for (const quasi of node.quasis) {
|
|
109
|
+
if (quasi.value.cooked && quasi.value.cooked.includes('utm_')) {
|
|
110
|
+
checkString(quasi, quasi.value.cooked);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
//# sourceMappingURL=utm-taxonomy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utm-taxonomy.js","sourceRoot":"","sources":["../../../../src/rules/conventions/utm-taxonomy.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,7 +13,10 @@ exports.noDeprecatedApi = (0, eslint_devkit_2.createRule)({
|
|
|
13
13
|
meta: {
|
|
14
14
|
type: 'problem',
|
|
15
15
|
docs: {
|
|
16
|
+
url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-conventions/docs/rules/no-deprecated-api.md',
|
|
16
17
|
description: 'Prevent usage of deprecated APIs with migration context',
|
|
18
|
+
cwe: 'CWE-1078',
|
|
19
|
+
cvss: 7.5,
|
|
17
20
|
},
|
|
18
21
|
fixable: 'code',
|
|
19
22
|
hasSuggestions: true,
|
|
@@ -73,7 +76,8 @@ exports.noDeprecatedApi = (0, eslint_devkit_2.createRule)({
|
|
|
73
76
|
],
|
|
74
77
|
create(context) {
|
|
75
78
|
const options = context.options[0] || {};
|
|
76
|
-
|
|
79
|
+
// `options` is always an object here (the `|| {}` above guarantees it).
|
|
80
|
+
const { apis = [], warnDaysBeforeRemoval = 90 } = options;
|
|
77
81
|
// Early return if no deprecated APIs configured
|
|
78
82
|
if (!apis || apis.length === 0) {
|
|
79
83
|
return {};
|
|
@@ -81,6 +85,7 @@ exports.noDeprecatedApi = (0, eslint_devkit_2.createRule)({
|
|
|
81
85
|
/**
|
|
82
86
|
* Calculate days until removal
|
|
83
87
|
*/
|
|
88
|
+
// oxlint-disable-next-line consistent-function-scoping
|
|
84
89
|
const calculateDaysRemaining = (removalDate) => {
|
|
85
90
|
if (!removalDate)
|
|
86
91
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-deprecated-api.js","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"no-deprecated-api.js","sourceRoot":"","sources":["../../../../src/rules/deprecation/no-deprecated-api.ts"],"names":[],"mappings":"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"}
|