@blumintinc/eslint-plugin-blumint 1.17.2 → 1.18.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 +1 -0
- package/lib/index.js +4 -1
- package/lib/rules/no-unnecessary-verb-suffix.js +115 -24
- package/package.json +1 -1
- package/release-manifest.json +28 -0
package/README.md
CHANGED
|
@@ -176,6 +176,7 @@ full closed loop is documented in agora's `.claude/skills/eslint-autonomy/SKILL.
|
|
|
176
176
|
| [no-firestore-object-arrays](docs/rules/no-firestore-object-arrays.md) | Disallow arrays of object types in Firestore models. Prefer Record maps keyed by id with an index field, or subcollections/arrays of IDs. | ✅ | | | | | |
|
|
177
177
|
| [no-handler-suffix](docs/rules/no-handler-suffix.md) | Disallow the generic "handler" suffix in callback names so names explain the action they perform | ✅ | | | | | |
|
|
178
178
|
| [no-hungarian](docs/rules/no-hungarian.md) | Disallow Hungarian notation in locally declared variables, types, and classes | ✅ | | | | | |
|
|
179
|
+
| [no-inline-component-prop](docs/rules/no-inline-component-prop.md) | Prevent inline function components defined in render from being passed to component-type props like CatalogWrapper to avoid remounts and UI flashes. | ✅ | | | | | |
|
|
179
180
|
| [no-jsx-in-hooks](docs/rules/no-jsx-in-hooks.md) | Prevent hooks from returning JSX | ✅ | | | | | |
|
|
180
181
|
| [no-jsx-whitespace-literal](docs/rules/no-jsx-whitespace-literal.md) | Disallow the use of {" "} elements in JSX code | ✅ | | | | | |
|
|
181
182
|
| [no-margin-properties](docs/rules/no-margin-properties.md) | Prevent margin properties (margin, marginLeft, marginRight, marginTop, marginBottom, mx, my, etc.) in MUI styling because margins fight container-controlled spacing, double gutters, and misaligned breakpoints; keep spacing centralized with padding, gap, or spacing props instead. | ✅ | | | | | |
|
package/lib/index.js
CHANGED
|
@@ -38,6 +38,7 @@ const no_hungarian_1 = require("./rules/no-hungarian");
|
|
|
38
38
|
const no_misused_switch_case_1 = require("./rules/no-misused-switch-case");
|
|
39
39
|
const no_unpinned_dependencies_1 = require("./rules/no-unpinned-dependencies");
|
|
40
40
|
const no_unused_props_1 = require("./rules/no-unused-props");
|
|
41
|
+
const no_inline_component_prop_1 = require("./rules/no-inline-component-prop");
|
|
41
42
|
const no_useless_fragment_1 = require("./rules/no-useless-fragment");
|
|
42
43
|
const prefer_fragment_shorthand_1 = require("./rules/prefer-fragment-shorthand");
|
|
43
44
|
const prefer_getter_over_parameterless_method_1 = require("./rules/prefer-getter-over-parameterless-method");
|
|
@@ -217,7 +218,7 @@ function noFrontendImportsFromFunctionsPatterns(pattern) {
|
|
|
217
218
|
module.exports = {
|
|
218
219
|
meta: {
|
|
219
220
|
name: '@blumintinc/eslint-plugin-blumint',
|
|
220
|
-
version: '1.
|
|
221
|
+
version: '1.18.0',
|
|
221
222
|
},
|
|
222
223
|
parseOptions: {
|
|
223
224
|
ecmaVersion: 2020,
|
|
@@ -271,6 +272,7 @@ module.exports = {
|
|
|
271
272
|
'@blumintinc/blumint/no-misused-switch-case': 'error',
|
|
272
273
|
'@blumintinc/blumint/no-unpinned-dependencies': 'error',
|
|
273
274
|
'@blumintinc/blumint/no-unused-props': 'error',
|
|
275
|
+
'@blumintinc/blumint/no-inline-component-prop': 'error',
|
|
274
276
|
'@blumintinc/blumint/no-uuidv4-base62-as-key': 'error',
|
|
275
277
|
'@blumintinc/blumint/no-useless-fragment': 'error',
|
|
276
278
|
'@blumintinc/blumint/no-useless-usememo-primitives': 'error',
|
|
@@ -537,6 +539,7 @@ module.exports = {
|
|
|
537
539
|
'no-misused-switch-case': no_misused_switch_case_1.noMisusedSwitchCase,
|
|
538
540
|
'no-unpinned-dependencies': no_unpinned_dependencies_1.noUnpinnedDependencies,
|
|
539
541
|
'no-unused-props': no_unused_props_1.noUnusedProps,
|
|
542
|
+
'no-inline-component-prop': no_inline_component_prop_1.noInlineComponentProp,
|
|
540
543
|
'no-useless-fragment': no_useless_fragment_1.noUselessFragment,
|
|
541
544
|
'no-uuidv4-base62-as-key': no_uuidv4_base62_as_key_1.noUuidv4Base62AsKey,
|
|
542
545
|
'enforce-dynamic-file-naming': enforce_dynamic_file_naming_1.default,
|
|
@@ -131,6 +131,41 @@ function isPhrasalVerbEnding(name, suffix) {
|
|
|
131
131
|
const lastWord = beforeSuffix.match(/[A-Z]?[a-z]+$/)?.[0] ?? '';
|
|
132
132
|
return phrasalVerbStem(lastWord) !== null;
|
|
133
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* Returns true when `node` (or its relevant ancestor for the declaration kind)
|
|
136
|
+
* is directly wrapped in an export declaration. An exported symbol must not be
|
|
137
|
+
* auto-fixed because the fixer can only rename within the current file, leaving
|
|
138
|
+
* cross-file import references broken.
|
|
139
|
+
*
|
|
140
|
+
* Call this with:
|
|
141
|
+
* - the FunctionDeclaration node for `function foo() {}`
|
|
142
|
+
* - the ArrowFunctionExpression/FunctionExpression node for `const foo = () => {}`
|
|
143
|
+
* (its parent chain: arrow → VariableDeclarator → VariableDeclaration → export?)
|
|
144
|
+
*/
|
|
145
|
+
function isExported(node) {
|
|
146
|
+
const parent = node.parent;
|
|
147
|
+
if (!parent)
|
|
148
|
+
return false;
|
|
149
|
+
// FunctionDeclaration directly inside `export function foo() {}`
|
|
150
|
+
if (parent.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration ||
|
|
151
|
+
parent.type === utils_1.AST_NODE_TYPES.ExportDefaultDeclaration) {
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
// Arrow/FunctionExpression assigned to a VariableDeclarator:
|
|
155
|
+
// VariableDeclarator → VariableDeclaration → ExportNamedDeclaration
|
|
156
|
+
if (parent.type === utils_1.AST_NODE_TYPES.VariableDeclarator) {
|
|
157
|
+
const varDecl = parent.parent; // VariableDeclaration
|
|
158
|
+
if (!varDecl)
|
|
159
|
+
return false;
|
|
160
|
+
const varDeclParent = varDecl.parent; // possible ExportNamedDeclaration
|
|
161
|
+
if (varDeclParent &&
|
|
162
|
+
(varDeclParent.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration ||
|
|
163
|
+
varDeclParent.type === utils_1.AST_NODE_TYPES.ExportDefaultDeclaration)) {
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
134
169
|
exports.noUnnecessaryVerbSuffix = (0, createRule_1.createRule)({
|
|
135
170
|
name: 'no-unnecessary-verb-suffix',
|
|
136
171
|
meta: {
|
|
@@ -147,7 +182,27 @@ exports.noUnnecessaryVerbSuffix = (0, createRule_1.createRule)({
|
|
|
147
182
|
},
|
|
148
183
|
defaultOptions: [],
|
|
149
184
|
create(context) {
|
|
150
|
-
function checkFunctionName(node, name
|
|
185
|
+
function checkFunctionName(node, name,
|
|
186
|
+
/**
|
|
187
|
+
* The AST node that holds the identifier being renamed.
|
|
188
|
+
* For FunctionDeclaration this is `node.id`; for VariableDeclarator
|
|
189
|
+
* assigned arrows it is `declarator.id`; for Property/MethodDefinition
|
|
190
|
+
* it is the key node. Passing it explicitly avoids re-deriving it inside
|
|
191
|
+
* the fixer and allows the reference-rename loop to skip it cleanly.
|
|
192
|
+
*/
|
|
193
|
+
declarationIdNode,
|
|
194
|
+
/**
|
|
195
|
+
* The AST node whose declared variables the scope manager tracks.
|
|
196
|
+
* For FunctionDeclaration this is `node` itself; for a VariableDeclarator
|
|
197
|
+
* arrow it is the VariableDeclarator; for methods/properties it is null
|
|
198
|
+
* because member references are not in scope (resolved via `this.x`).
|
|
199
|
+
*/
|
|
200
|
+
scopeNode,
|
|
201
|
+
/**
|
|
202
|
+
* Whether the symbol is exported. When true, the fix is suppressed so we
|
|
203
|
+
* don't produce broken cross-file renames.
|
|
204
|
+
*/
|
|
205
|
+
exported) {
|
|
151
206
|
if (!name)
|
|
152
207
|
return;
|
|
153
208
|
for (const suffix of COMMON_PREPOSITION_SUFFIXES) {
|
|
@@ -179,25 +234,50 @@ exports.noUnnecessaryVerbSuffix = (0, createRule_1.createRule)({
|
|
|
179
234
|
suggestion,
|
|
180
235
|
},
|
|
181
236
|
fix(fixer) {
|
|
182
|
-
|
|
183
|
-
|
|
237
|
+
// An autofix here is only reference-safe when the fixer can
|
|
238
|
+
// rename EVERY use of the symbol, not just its declaration —
|
|
239
|
+
// otherwise call sites are orphaned and produce a ReferenceError
|
|
240
|
+
// (#1256). Two cases make that impossible, so the fix is
|
|
241
|
+
// suppressed (report only, no `--fix` change):
|
|
242
|
+
//
|
|
243
|
+
// 1. Exported symbols — a single-file fixer cannot reach
|
|
244
|
+
// cross-file import references.
|
|
245
|
+
// 2. Member-accessed symbols (class methods, object-literal
|
|
246
|
+
// properties, interface method signatures) — their call
|
|
247
|
+
// sites are member expressions (`this.x()`, `obj.x()`) that
|
|
248
|
+
// the scope manager does not track as variable references,
|
|
249
|
+
// so they cannot be found and renamed syntactically.
|
|
250
|
+
// `scopeNode` is null for exactly these declarations.
|
|
251
|
+
if (exported || !scopeNode || !declarationIdNode) {
|
|
252
|
+
return null;
|
|
184
253
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
254
|
+
// Scope-tracked symbols (FunctionDeclaration, VariableDeclarator
|
|
255
|
+
// arrows/functions, named FunctionExpression): rename the
|
|
256
|
+
// declaration identifier and every in-file reference together so
|
|
257
|
+
// no call site is left pointing at the old name.
|
|
258
|
+
const fixes = [
|
|
259
|
+
fixer.replaceText(declarationIdNode, suggestion),
|
|
260
|
+
];
|
|
261
|
+
// Note: context.getDeclaredVariables is the API available in the
|
|
262
|
+
// pinned @typescript-eslint version (the SourceCode-based
|
|
263
|
+
// replacement is not yet in these type definitions).
|
|
264
|
+
const declaredVars = context.getDeclaredVariables(scopeNode);
|
|
265
|
+
// getDeclaredVariables returns ALL variables the node declares
|
|
266
|
+
// (e.g. for a FunctionDeclaration it includes the function name
|
|
267
|
+
// variable AND its parameter variables). Filter to the one whose
|
|
268
|
+
// name matches the symbol being renamed so we only follow
|
|
269
|
+
// references to the name, not parameters.
|
|
270
|
+
for (const variable of declaredVars) {
|
|
271
|
+
if (variable.name !== name)
|
|
272
|
+
continue;
|
|
273
|
+
for (const ref of variable.references) {
|
|
274
|
+
// Skip the declaration identifier itself — already handled.
|
|
275
|
+
if (ref.identifier === declarationIdNode)
|
|
276
|
+
continue;
|
|
277
|
+
fixes.push(fixer.replaceText(ref.identifier, suggestion));
|
|
198
278
|
}
|
|
199
279
|
}
|
|
200
|
-
return
|
|
280
|
+
return fixes;
|
|
201
281
|
},
|
|
202
282
|
});
|
|
203
283
|
}
|
|
@@ -207,38 +287,49 @@ exports.noUnnecessaryVerbSuffix = (0, createRule_1.createRule)({
|
|
|
207
287
|
return {
|
|
208
288
|
FunctionDeclaration(node) {
|
|
209
289
|
if (node.id) {
|
|
210
|
-
checkFunctionName(node, node.id.name);
|
|
290
|
+
checkFunctionName(node, node.id.name, node.id, node, isExported(node));
|
|
211
291
|
}
|
|
212
292
|
},
|
|
213
293
|
VariableDeclarator(node) {
|
|
214
294
|
if (node.id.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
215
295
|
(node.init?.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
216
296
|
node.init?.type === utils_1.AST_NODE_TYPES.FunctionExpression)) {
|
|
217
|
-
checkFunctionName(node.init, node.id.name);
|
|
297
|
+
checkFunctionName(node.init, node.id.name, node.id, node, isExported(node.init));
|
|
218
298
|
}
|
|
219
299
|
},
|
|
220
300
|
MethodDefinition(node) {
|
|
221
301
|
if (node.key.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
222
|
-
|
|
302
|
+
// Class methods are called via member expressions (`this.method()`,
|
|
303
|
+
// `instance.method()`) that the scope manager does not track as
|
|
304
|
+
// references. A syntactic single-file fixer therefore cannot find and
|
|
305
|
+
// rename those call sites, so renaming the method would orphan them
|
|
306
|
+
// (#1256). Pass null for both the rename target and scopeNode so the
|
|
307
|
+
// violation is still reported but no unsafe fix is offered.
|
|
308
|
+
checkFunctionName(node.value, node.key.name, null, null, false);
|
|
223
309
|
}
|
|
224
310
|
},
|
|
225
311
|
TSMethodSignature(node) {
|
|
226
|
-
//
|
|
312
|
+
// Interface method signatures have their implementations and call sites
|
|
313
|
+
// elsewhere (member accesses on implementers), unreachable from this
|
|
314
|
+
// declaration. Report only — never offer a rename fix.
|
|
227
315
|
if (node.key.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
228
|
-
checkFunctionName(node, node.key.name);
|
|
316
|
+
checkFunctionName(node, node.key.name, null, null, false);
|
|
229
317
|
}
|
|
230
318
|
},
|
|
231
319
|
Property(node) {
|
|
232
320
|
if (node.key.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
233
321
|
(node.value.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
234
322
|
node.value.type === utils_1.AST_NODE_TYPES.FunctionExpression)) {
|
|
235
|
-
|
|
323
|
+
// Object-literal method properties are accessed via member expressions
|
|
324
|
+
// (`obj.method()`) the scope manager does not track. As with class
|
|
325
|
+
// methods, the fix is suppressed to avoid orphaning call sites.
|
|
326
|
+
checkFunctionName(node.value, node.key.name, null, null, false);
|
|
236
327
|
}
|
|
237
328
|
},
|
|
238
329
|
FunctionExpression(node) {
|
|
239
330
|
// Handle named function expressions
|
|
240
331
|
if (node.id) {
|
|
241
|
-
checkFunctionName(node, node.id.name);
|
|
332
|
+
checkFunctionName(node, node.id.name, node.id, node, isExported(node));
|
|
242
333
|
}
|
|
243
334
|
},
|
|
244
335
|
};
|
package/package.json
CHANGED
package/release-manifest.json
CHANGED
|
@@ -1,4 +1,32 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "1.18.0",
|
|
4
|
+
"date": "2026-07-02T16:19:34.385Z",
|
|
5
|
+
"rules": [
|
|
6
|
+
{
|
|
7
|
+
"name": "no-inline-component-prop",
|
|
8
|
+
"changeType": "feat",
|
|
9
|
+
"issues": [
|
|
10
|
+
833
|
|
11
|
+
],
|
|
12
|
+
"summary": "register the orphaned rule in the plugin (#1233)"
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"version": "1.17.3",
|
|
18
|
+
"date": "2026-07-02T16:07:25.816Z",
|
|
19
|
+
"rules": [
|
|
20
|
+
{
|
|
21
|
+
"name": "no-unnecessary-verb-suffix",
|
|
22
|
+
"changeType": "fix",
|
|
23
|
+
"issues": [
|
|
24
|
+
1256
|
|
25
|
+
],
|
|
26
|
+
"summary": "make autofix reference-safe (closes #1256)"
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
},
|
|
2
30
|
{
|
|
3
31
|
"version": "1.17.2",
|
|
4
32
|
"date": "2026-07-02T12:29:19.590Z",
|