@kernlang/review 3.1.8 → 3.2.3
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/dist/cache.js +4 -0
- package/dist/cache.js.map +1 -1
- package/dist/file-context.d.ts +6 -0
- package/dist/file-context.js +6 -1
- package/dist/file-context.js.map +1 -1
- package/dist/rules/a11y.d.ts +10 -0
- package/dist/rules/a11y.js +294 -0
- package/dist/rules/a11y.js.map +1 -0
- package/dist/rules/async.d.ts +8 -0
- package/dist/rules/async.js +154 -0
- package/dist/rules/async.js.map +1 -0
- package/dist/rules/base.js +29 -0
- package/dist/rules/base.js.map +1 -1
- package/dist/rules/index.d.ts +12 -0
- package/dist/rules/index.js +283 -4
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/ink.js +41 -0
- package/dist/rules/ink.js.map +1 -1
- package/dist/rules/kern-source.js +94 -14
- package/dist/rules/kern-source.js.map +1 -1
- package/dist/rules/nextjs-app-router.d.ts +11 -0
- package/dist/rules/nextjs-app-router.js +277 -0
- package/dist/rules/nextjs-app-router.js.map +1 -0
- package/dist/rules/nextjs.js +77 -1
- package/dist/rules/nextjs.js.map +1 -1
- package/dist/rules/perf.d.ts +11 -0
- package/dist/rules/perf.js +131 -0
- package/dist/rules/perf.js.map +1 -0
- package/dist/rules/react-composition.d.ts +12 -0
- package/dist/rules/react-composition.js +360 -0
- package/dist/rules/react-composition.js.map +1 -0
- package/dist/rules/react-hooks.d.ts +11 -0
- package/dist/rules/react-hooks.js +380 -0
- package/dist/rules/react-hooks.js.map +1 -0
- package/dist/rules/security-v5.d.ts +11 -0
- package/dist/rules/security-v5.js +200 -0
- package/dist/rules/security-v5.js.map +1 -0
- package/dist/rules/utils.d.ts +16 -0
- package/dist/rules/utils.js +46 -0
- package/dist/rules/utils.js.map +1 -1
- package/dist/taint-ast.js +32 -6
- package/dist/taint-ast.js.map +1 -1
- package/dist/taint-findings.js +3 -0
- package/dist/taint-findings.js.map +1 -1
- package/dist/taint-types.d.ts +2 -2
- package/dist/taint-types.js +38 -4
- package/dist/taint-types.js.map +1 -1
- package/dist/types.d.ts +20 -0
- package/dist/types.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React composition rules — catch prop-drilling and "parent rerenders child
|
|
3
|
+
* that doesn't depend on parent state" antipatterns.
|
|
4
|
+
*
|
|
5
|
+
* These rules push toward the `children` prop pattern, which preserves
|
|
6
|
+
* element identity across parent renders and lets React skip reconciliation
|
|
7
|
+
* of unchanged subtrees.
|
|
8
|
+
*/
|
|
9
|
+
import { Node, SyntaxKind } from 'ts-morph';
|
|
10
|
+
import { finding, nodeSpan } from './utils.js';
|
|
11
|
+
/** Is this node a React component function? (Capitalized name + returns JSX) */
|
|
12
|
+
function isComponentFunction(node) {
|
|
13
|
+
let name = '';
|
|
14
|
+
if (Node.isFunctionDeclaration(node)) {
|
|
15
|
+
name = node.getName() ?? '';
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
// Arrow/function expression — look at the parent variable declaration
|
|
19
|
+
const parent = node.getParent();
|
|
20
|
+
if (parent && Node.isVariableDeclaration(parent)) {
|
|
21
|
+
const n = parent.getNameNode();
|
|
22
|
+
if (Node.isIdentifier(n))
|
|
23
|
+
name = n.getText();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (!name || !/^[A-Z]/.test(name))
|
|
27
|
+
return { name, isComponent: false };
|
|
28
|
+
// Must contain JSX somewhere in the body
|
|
29
|
+
const body = node.getBody();
|
|
30
|
+
if (!body)
|
|
31
|
+
return { name, isComponent: false };
|
|
32
|
+
const hasJsx = body.getDescendantsOfKind(SyntaxKind.JsxOpeningElement).length > 0 ||
|
|
33
|
+
body.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement).length > 0 ||
|
|
34
|
+
body.getDescendantsOfKind(SyntaxKind.JsxFragment).length > 0;
|
|
35
|
+
return { name, isComponent: hasJsx };
|
|
36
|
+
}
|
|
37
|
+
/** Extract destructured prop names from the first parameter of a component function. */
|
|
38
|
+
function getDestructuredPropNames(fn) {
|
|
39
|
+
const params = fn.getParameters();
|
|
40
|
+
if (params.length === 0)
|
|
41
|
+
return undefined;
|
|
42
|
+
const nameNode = params[0].getNameNode();
|
|
43
|
+
if (!Node.isObjectBindingPattern(nameNode))
|
|
44
|
+
return undefined;
|
|
45
|
+
const names = [];
|
|
46
|
+
for (const el of nameNode.getElements()) {
|
|
47
|
+
// Use the property name if aliased, otherwise the binding name
|
|
48
|
+
const propName = el.getPropertyNameNode()?.getText() ?? el.getNameNode().getText();
|
|
49
|
+
names.push(propName);
|
|
50
|
+
}
|
|
51
|
+
return names;
|
|
52
|
+
}
|
|
53
|
+
function iterComponentFunctions(ctx) {
|
|
54
|
+
const results = [];
|
|
55
|
+
for (const fn of ctx.sourceFile.getFunctions()) {
|
|
56
|
+
const info = isComponentFunction(fn);
|
|
57
|
+
if (info.isComponent)
|
|
58
|
+
results.push(fn);
|
|
59
|
+
}
|
|
60
|
+
for (const stmt of ctx.sourceFile.getVariableStatements()) {
|
|
61
|
+
for (const decl of stmt.getDeclarations()) {
|
|
62
|
+
const init = decl.getInitializer();
|
|
63
|
+
if (!init)
|
|
64
|
+
continue;
|
|
65
|
+
if (Node.isArrowFunction(init) || Node.isFunctionExpression(init)) {
|
|
66
|
+
const info = isComponentFunction(init);
|
|
67
|
+
if (info.isComponent)
|
|
68
|
+
results.push(init);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return results;
|
|
73
|
+
}
|
|
74
|
+
// ── Rule: children-not-used ──────────────────────────────────────────────
|
|
75
|
+
// Component accepts `children` in its destructured props but never renders it.
|
|
76
|
+
function childrenNotUsed(ctx) {
|
|
77
|
+
const findings = [];
|
|
78
|
+
for (const fn of iterComponentFunctions(ctx)) {
|
|
79
|
+
const propNames = getDestructuredPropNames(fn);
|
|
80
|
+
if (!propNames || !propNames.includes('children'))
|
|
81
|
+
continue;
|
|
82
|
+
const body = fn.getBody();
|
|
83
|
+
if (!body)
|
|
84
|
+
continue;
|
|
85
|
+
// Look for any identifier reference to `children` in the body
|
|
86
|
+
let rendered = false;
|
|
87
|
+
for (const id of body.getDescendantsOfKind(SyntaxKind.Identifier)) {
|
|
88
|
+
if (id.getText() !== 'children')
|
|
89
|
+
continue;
|
|
90
|
+
// Skip the declaration in the parameter binding — we want usage, not the binding itself
|
|
91
|
+
const parent = id.getParent();
|
|
92
|
+
if (parent && Node.isBindingElement(parent))
|
|
93
|
+
continue;
|
|
94
|
+
rendered = true;
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
if (!rendered) {
|
|
98
|
+
const { name } = isComponentFunction(fn);
|
|
99
|
+
// Autofix: remove the `children` entry from the destructured props
|
|
100
|
+
// pattern. Only applies when the binding pattern is simple (no renames,
|
|
101
|
+
// defaults, or rest — those are fine, we just leave them alone here).
|
|
102
|
+
let autofixAction;
|
|
103
|
+
const firstParam = fn.getParameters()[0];
|
|
104
|
+
if (firstParam) {
|
|
105
|
+
const nameNode = firstParam.getNameNode();
|
|
106
|
+
if (Node.isObjectBindingPattern(nameNode)) {
|
|
107
|
+
const elements = nameNode.getElements();
|
|
108
|
+
const remaining = elements.filter((el) => {
|
|
109
|
+
const propName = el.getPropertyNameNode()?.getText() ?? el.getNameNode().getText();
|
|
110
|
+
return propName !== 'children';
|
|
111
|
+
});
|
|
112
|
+
// Reconstruct a clean `{ a, b, c }` pattern using each element's
|
|
113
|
+
// original text. Preserves renames, defaults, and rest operators.
|
|
114
|
+
const rebuilt = `{ ${remaining.map((el) => el.getText()).join(', ')} }`;
|
|
115
|
+
autofixAction = {
|
|
116
|
+
type: 'replace',
|
|
117
|
+
span: nodeSpan(nameNode, ctx.filePath),
|
|
118
|
+
replacement: rebuilt,
|
|
119
|
+
description: `Remove unused 'children' from the props destructuring`,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
findings.push(finding('children-not-used', 'warning', 'pattern', `'${name}' destructures 'children' from props but never renders it — dead API or forgotten {children}`, ctx.filePath, fn.getStartLineNumber(), 1, {
|
|
124
|
+
suggestion: `Render {children} in the JSX output, or remove 'children' from the props destructuring if the component should not accept children`,
|
|
125
|
+
...(autofixAction ? { autofix: autofixAction } : {}),
|
|
126
|
+
}));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return findings;
|
|
130
|
+
}
|
|
131
|
+
// ── Rule: prop-drill-passthrough ─────────────────────────────────────────
|
|
132
|
+
// Component receives >= 3 props, body is a single JSX element, and >= 2 of
|
|
133
|
+
// those props are passed unchanged to that element without being read anywhere
|
|
134
|
+
// else. Suggest `children` or context.
|
|
135
|
+
function getSingleReturnedJsx(fn) {
|
|
136
|
+
const body = fn.getBody();
|
|
137
|
+
if (!body)
|
|
138
|
+
return undefined;
|
|
139
|
+
// Case 1: arrow function with implicit return — body IS the JSX
|
|
140
|
+
if (Node.isJsxElement(body))
|
|
141
|
+
return body.getOpeningElement();
|
|
142
|
+
if (Node.isJsxSelfClosingElement(body))
|
|
143
|
+
return body;
|
|
144
|
+
if (Node.isJsxFragment(body))
|
|
145
|
+
return undefined; // fragments have multiple children
|
|
146
|
+
// Case 2: block body — look for a single return statement at the top level
|
|
147
|
+
if (Node.isBlock(body)) {
|
|
148
|
+
const statements = body.getStatements();
|
|
149
|
+
// Allow preamble (const x = ..., hook calls) but require the LAST statement to be a return with a single JSX root
|
|
150
|
+
const ret = statements.find((s) => Node.isReturnStatement(s));
|
|
151
|
+
if (!ret || !Node.isReturnStatement(ret))
|
|
152
|
+
return undefined;
|
|
153
|
+
const expr = ret.getExpression();
|
|
154
|
+
if (!expr)
|
|
155
|
+
return undefined;
|
|
156
|
+
// Walk through parentheses
|
|
157
|
+
let unwrapped = expr;
|
|
158
|
+
while (Node.isParenthesizedExpression(unwrapped)) {
|
|
159
|
+
unwrapped = unwrapped.getExpression();
|
|
160
|
+
}
|
|
161
|
+
if (Node.isJsxElement(unwrapped))
|
|
162
|
+
return unwrapped.getOpeningElement();
|
|
163
|
+
if (Node.isJsxSelfClosingElement(unwrapped))
|
|
164
|
+
return unwrapped;
|
|
165
|
+
}
|
|
166
|
+
return undefined;
|
|
167
|
+
}
|
|
168
|
+
function propDrillPassthrough(ctx) {
|
|
169
|
+
const findings = [];
|
|
170
|
+
for (const fn of iterComponentFunctions(ctx)) {
|
|
171
|
+
const propNames = getDestructuredPropNames(fn);
|
|
172
|
+
if (!propNames || propNames.length < 3)
|
|
173
|
+
continue;
|
|
174
|
+
const root = getSingleReturnedJsx(fn);
|
|
175
|
+
if (!root)
|
|
176
|
+
continue;
|
|
177
|
+
// The root element must be a custom component (capitalized tag) to be a meaningful passthrough.
|
|
178
|
+
const tag = root.getTagNameNode().getText();
|
|
179
|
+
if (!/^[A-Z]/.test(tag))
|
|
180
|
+
continue;
|
|
181
|
+
// Collect prop names passed as shorthand attributes to the root child.
|
|
182
|
+
// Only count attributes where the passed identifier is actually one of
|
|
183
|
+
// THIS component's destructured props — local variables with matching
|
|
184
|
+
// names (e.g. `title={title}` where `title` is a local const) are not
|
|
185
|
+
// drilling.
|
|
186
|
+
const propSet = new Set(propNames);
|
|
187
|
+
const passedToChild = new Set();
|
|
188
|
+
const passedWithShorthand = new Set();
|
|
189
|
+
for (const attr of root.getAttributes()) {
|
|
190
|
+
if (!Node.isJsxAttribute(attr))
|
|
191
|
+
continue;
|
|
192
|
+
const attrName = attr.getNameNode().getText();
|
|
193
|
+
const init = attr.getInitializer();
|
|
194
|
+
if (!init)
|
|
195
|
+
continue;
|
|
196
|
+
if (!Node.isJsxExpression(init))
|
|
197
|
+
continue;
|
|
198
|
+
const expr = init.getExpression();
|
|
199
|
+
if (!expr)
|
|
200
|
+
continue;
|
|
201
|
+
if (Node.isIdentifier(expr) && expr.getText() === attrName && propSet.has(attrName)) {
|
|
202
|
+
// Classic shorthand of a destructured prop: <Child user={user} theme={theme} />
|
|
203
|
+
passedToChild.add(attrName);
|
|
204
|
+
passedWithShorthand.add(attrName);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
// Count props read OUTSIDE of passing to the root child
|
|
208
|
+
const body = fn.getBody();
|
|
209
|
+
if (!body)
|
|
210
|
+
continue;
|
|
211
|
+
const bodyText = body.getText();
|
|
212
|
+
// A prop is "consumed" if it appears somewhere other than as the RHS of an attribute
|
|
213
|
+
// of the single root child. We approximate: count total identifier refs in the body,
|
|
214
|
+
// and subtract the passthrough refs.
|
|
215
|
+
const consumedProps = new Set();
|
|
216
|
+
for (const propName of propNames) {
|
|
217
|
+
if (propName === 'children')
|
|
218
|
+
continue;
|
|
219
|
+
// Skip counting refs that are literally the attribute value of the root child.
|
|
220
|
+
// For a shorthand pass like `<Child user={user} />`, the identifier `user`
|
|
221
|
+
// appears TWICE in the body text: once as the attribute name and once as
|
|
222
|
+
// the value expression. Subtract 2 per shorthand to compensate.
|
|
223
|
+
const escaped = propName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
224
|
+
const totalRefs = (bodyText.match(new RegExp(`\\b${escaped}\\b`, 'g')) ?? []).length;
|
|
225
|
+
const passthroughRefs = passedWithShorthand.has(propName) ? 2 : 0;
|
|
226
|
+
if (totalRefs - passthroughRefs > 0) {
|
|
227
|
+
consumedProps.add(propName);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
const passthroughCount = [...passedToChild].filter((p) => !consumedProps.has(p)).length;
|
|
231
|
+
if (passthroughCount >= 2 && propNames.length >= 3) {
|
|
232
|
+
const info = isComponentFunction(fn);
|
|
233
|
+
findings.push(finding('prop-drill-passthrough', 'warning', 'pattern', `'${info.name}' passes ${passthroughCount} of ${propNames.length} props through to <${tag}> without reading them — consider 'children' prop or React context`, ctx.filePath, fn.getStartLineNumber(), 1, {
|
|
234
|
+
suggestion: `Accept <${tag} .../> as the 'children' prop, or move the shared data into a React context. Passing props through an intermediate component forces it to re-render whenever any of them change.`,
|
|
235
|
+
}));
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return findings;
|
|
239
|
+
}
|
|
240
|
+
// ── Rule: parent-rerender-via-state ──────────────────────────────────────
|
|
241
|
+
// Component holds useState AND renders a child component that receives NEITHER
|
|
242
|
+
// the state variables NOR the setters. That child will re-render on every
|
|
243
|
+
// state change for no reason — lifting it to `children` preserves its element
|
|
244
|
+
// identity and avoids the re-render.
|
|
245
|
+
/**
|
|
246
|
+
* Get the DIRECT-child JSX elements of the top-level return. Skips nested
|
|
247
|
+
* descendants, elements inside callbacks (map renderers), and elements deep
|
|
248
|
+
* in conditional branches. This is the key guard against false positives:
|
|
249
|
+
* we only care about JSX that the parent component's own render produces
|
|
250
|
+
* positionally — those are the elements that could be lifted to `children`.
|
|
251
|
+
*/
|
|
252
|
+
function getDirectChildrenOfReturn(root) {
|
|
253
|
+
// If the root is already a self-closing element, there are no direct JSX children.
|
|
254
|
+
if (Node.isJsxSelfClosingElement(root))
|
|
255
|
+
return [root];
|
|
256
|
+
// Root is a JsxOpeningElement — walk its parent JsxElement children once.
|
|
257
|
+
const parent = root.getParent();
|
|
258
|
+
if (!parent || !Node.isJsxElement(parent))
|
|
259
|
+
return [root];
|
|
260
|
+
const result = [root];
|
|
261
|
+
for (const child of parent.getJsxChildren()) {
|
|
262
|
+
if (Node.isJsxElement(child)) {
|
|
263
|
+
result.push(child.getOpeningElement());
|
|
264
|
+
}
|
|
265
|
+
else if (Node.isJsxSelfClosingElement(child)) {
|
|
266
|
+
result.push(child);
|
|
267
|
+
}
|
|
268
|
+
// Skip JsxExpression / JsxText / JsxFragment content — too dynamic to reason about
|
|
269
|
+
}
|
|
270
|
+
return result;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Does this expression text mention any of the state variables? Wraps each
|
|
274
|
+
* variable in \b boundaries and tests the combined text. Handles callbacks
|
|
275
|
+
* too (e.g. onClick={() => setCount(c => c + 1)} — we treat ANY reference
|
|
276
|
+
* to setCount as a legitimate state dependency).
|
|
277
|
+
*/
|
|
278
|
+
function mentionsStateVars(text, stateVars) {
|
|
279
|
+
for (const v of stateVars) {
|
|
280
|
+
if (new RegExp(`\\b${v}\\b`).test(text))
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
function parentRerenderViaState(ctx) {
|
|
286
|
+
const findings = [];
|
|
287
|
+
for (const fn of iterComponentFunctions(ctx)) {
|
|
288
|
+
const body = fn.getBody();
|
|
289
|
+
if (!body)
|
|
290
|
+
continue;
|
|
291
|
+
// Collect state variable names AND setter names from useState/useReducer.
|
|
292
|
+
// Both the value and the setter count as "state refs" — a child that
|
|
293
|
+
// receives `setCount` is wiring to state and should NOT be flagged.
|
|
294
|
+
const stateVars = new Set();
|
|
295
|
+
for (const decl of body.getDescendantsOfKind(SyntaxKind.VariableDeclaration)) {
|
|
296
|
+
const init = decl.getInitializer();
|
|
297
|
+
if (!init || !Node.isCallExpression(init))
|
|
298
|
+
continue;
|
|
299
|
+
const calleeText = init.getExpression().getText();
|
|
300
|
+
const calleeName = calleeText.includes('.') ? calleeText.split('.').pop() : calleeText;
|
|
301
|
+
if (calleeName !== 'useState' && calleeName !== 'useReducer')
|
|
302
|
+
continue;
|
|
303
|
+
const nameNode = decl.getNameNode();
|
|
304
|
+
if (!Node.isArrayBindingPattern(nameNode))
|
|
305
|
+
continue;
|
|
306
|
+
for (const el of nameNode.getElements()) {
|
|
307
|
+
if (Node.isBindingElement(el)) {
|
|
308
|
+
stateVars.add(el.getNameNode().getText());
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (stateVars.size === 0)
|
|
313
|
+
continue;
|
|
314
|
+
// Already composing with children? Skip — the user is on the correct path.
|
|
315
|
+
const propNames = getDestructuredPropNames(fn);
|
|
316
|
+
const alreadyComposesChildren = propNames?.includes('children') ?? false;
|
|
317
|
+
if (alreadyComposesChildren)
|
|
318
|
+
continue;
|
|
319
|
+
// Require a clean single-root returned JSX tree. Fragments, conditional
|
|
320
|
+
// returns, and dynamic structures are too ambiguous to reason about
|
|
321
|
+
// without a real dataflow pass — skip them.
|
|
322
|
+
const root = getSingleReturnedJsx(fn);
|
|
323
|
+
if (!root)
|
|
324
|
+
continue;
|
|
325
|
+
// Only look at the DIRECT children of the returned root. Nested helper
|
|
326
|
+
// JSX inside map callbacks, conditional branches, or deep descendants
|
|
327
|
+
// are not flaggable — they may close over state transitively.
|
|
328
|
+
const candidates = getDirectChildrenOfReturn(root);
|
|
329
|
+
for (const el of candidates) {
|
|
330
|
+
const tag = el.getTagNameNode().getText();
|
|
331
|
+
if (!/^[A-Z]/.test(tag))
|
|
332
|
+
continue; // HTML element — not a rerender target we care about
|
|
333
|
+
// Does this child receive any state var (or setter) via attributes?
|
|
334
|
+
// Scan the entire attribute bag's text in one pass so callback props
|
|
335
|
+
// like onClick={() => setCount(c => c + 1)} count as state-dependent.
|
|
336
|
+
const attrsText = el
|
|
337
|
+
.getAttributes()
|
|
338
|
+
.map((a) => (Node.isJsxAttribute(a) ? a.getText() : ''))
|
|
339
|
+
.join(' ');
|
|
340
|
+
if (mentionsStateVars(attrsText, stateVars))
|
|
341
|
+
continue;
|
|
342
|
+
// Is this element inside a JsxExpression that references state (a
|
|
343
|
+
// conditional render like `{count > 0 && <Child />}` or a map based
|
|
344
|
+
// on state)? Walk up the JSX container chain.
|
|
345
|
+
const containingExpr = el.getFirstAncestorByKind(SyntaxKind.JsxExpression);
|
|
346
|
+
if (containingExpr && mentionsStateVars(containingExpr.getText(), stateVars))
|
|
347
|
+
continue;
|
|
348
|
+
// Flag: this direct child never sees state and re-renders unnecessarily.
|
|
349
|
+
const info = isComponentFunction(fn);
|
|
350
|
+
findings.push(finding('parent-rerender-via-state', 'info', 'pattern', `<${tag}> is rendered by '${info.name}' but does not receive any of its state variables (${[...stateVars].slice(0, 3).join(', ')}${stateVars.size > 3 ? '…' : ''}) — it re-renders on every state change. Consider lifting it to the 'children' prop so React can reuse the element.`, ctx.filePath, el.getStartLineNumber(), 1, {
|
|
351
|
+
suggestion: `Accept <${tag}> as the 'children' prop of '${info.name}' and render it with {children}. The caller composes: <${info.name}><${tag} /></${info.name}>. React will reuse the child element across re-renders.`,
|
|
352
|
+
}));
|
|
353
|
+
break; // one finding per component is enough — avoid noise
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return findings;
|
|
357
|
+
}
|
|
358
|
+
// ── Exported composition rules ───────────────────────────────────────────
|
|
359
|
+
export const reactCompositionRules = [childrenNotUsed, propDrillPassthrough, parentRerenderViaState];
|
|
360
|
+
//# sourceMappingURL=react-composition.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react-composition.js","sourceRoot":"","sources":["../../src/rules/react-composition.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React hooks correctness — Wave 2 net-new rules.
|
|
3
|
+
*
|
|
4
|
+
* Intentionally conservative: each rule only fires when the pattern is
|
|
5
|
+
* unambiguous. eslint-plugin-react-hooks is the authority for exhaustive
|
|
6
|
+
* correctness; this layer catches the common footguns with high precision.
|
|
7
|
+
*/
|
|
8
|
+
import type { ReviewFinding, RuleContext } from '../types.js';
|
|
9
|
+
declare function exhaustiveDeps(ctx: RuleContext): ReviewFinding[];
|
|
10
|
+
export declare const reactHooksRules: (typeof exhaustiveDeps)[];
|
|
11
|
+
export {};
|