@kernlang/review 2.0.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/LICENSE +661 -0
- package/dist/differ.d.ts +11 -0
- package/dist/differ.js +132 -0
- package/dist/differ.js.map +1 -0
- package/dist/external-tools.d.ts +32 -0
- package/dist/external-tools.js +173 -0
- package/dist/external-tools.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +98 -0
- package/dist/index.js.map +1 -0
- package/dist/inferrer.d.ts +15 -0
- package/dist/inferrer.js +502 -0
- package/dist/inferrer.js.map +1 -0
- package/dist/llm-review.d.ts +24 -0
- package/dist/llm-review.js +197 -0
- package/dist/llm-review.js.map +1 -0
- package/dist/quality-rules.d.ts +12 -0
- package/dist/quality-rules.js +28 -0
- package/dist/quality-rules.js.map +1 -0
- package/dist/reporter.d.ts +15 -0
- package/dist/reporter.js +217 -0
- package/dist/reporter.js.map +1 -0
- package/dist/rules/base.d.ts +10 -0
- package/dist/rules/base.js +556 -0
- package/dist/rules/base.js.map +1 -0
- package/dist/rules/express.d.ts +9 -0
- package/dist/rules/express.js +107 -0
- package/dist/rules/express.js.map +1 -0
- package/dist/rules/index.d.ts +16 -0
- package/dist/rules/index.js +38 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/nextjs.d.ts +9 -0
- package/dist/rules/nextjs.js +128 -0
- package/dist/rules/nextjs.js.map +1 -0
- package/dist/rules/react.d.ts +9 -0
- package/dist/rules/react.js +252 -0
- package/dist/rules/react.js.map +1 -0
- package/dist/rules/vue.d.ts +9 -0
- package/dist/rules/vue.js +198 -0
- package/dist/rules/vue.js.map +1 -0
- package/dist/template-detector.d.ts +12 -0
- package/dist/template-detector.js +225 -0
- package/dist/template-detector.js.map +1 -0
- package/dist/types.d.ts +152 -0
- package/dist/types.js +17 -0
- package/dist/types.js.map +1 -0
- package/package.json +23 -0
|
@@ -0,0 +1,556 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base review rules — always active, universal TS/KERN rules.
|
|
3
|
+
*
|
|
4
|
+
* These rules leverage the KERN IR for structural analysis.
|
|
5
|
+
* AST-level rules that duplicate ESLint are excluded.
|
|
6
|
+
*/
|
|
7
|
+
import { SyntaxKind } from 'ts-morph';
|
|
8
|
+
import { countTokens } from '@kernlang/core';
|
|
9
|
+
import { createFingerprint } from '../types.js';
|
|
10
|
+
// ── Helpers ──────────────────────────────────────────────────────────────
|
|
11
|
+
function span(file, line, col = 1, endLine, endCol) {
|
|
12
|
+
return { file, startLine: line, startCol: col, endLine: endLine ?? line, endCol: endCol ?? col };
|
|
13
|
+
}
|
|
14
|
+
function finding(ruleId, severity, category, message, file, line, col = 1, extra) {
|
|
15
|
+
return {
|
|
16
|
+
source: 'kern',
|
|
17
|
+
ruleId,
|
|
18
|
+
severity,
|
|
19
|
+
category,
|
|
20
|
+
message,
|
|
21
|
+
primarySpan: span(file, line, col),
|
|
22
|
+
fingerprint: createFingerprint(ruleId, line, col),
|
|
23
|
+
...extra,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
// ── Rule 1: floating-promise ─────────────────────────────────────────────
|
|
27
|
+
// fn body with call returning Promise but no await/void/return
|
|
28
|
+
function floatingPromise(ctx) {
|
|
29
|
+
const findings = [];
|
|
30
|
+
// Collect known async function names from inferred results
|
|
31
|
+
const asyncFns = new Set();
|
|
32
|
+
for (const r of ctx.inferred) {
|
|
33
|
+
if (r.node.type === 'fn' && r.node.props?.async === 'true') {
|
|
34
|
+
asyncFns.add(r.node.props.name);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// Walk expression statements — standalone calls whose return value is discarded.
|
|
38
|
+
// This is AST-based: it won't match .then() in strings, comments, or regex patterns.
|
|
39
|
+
for (const exprStmt of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.ExpressionStatement)) {
|
|
40
|
+
const expr = exprStmt.getExpression();
|
|
41
|
+
// Skip void/await wrappers (intentional discard)
|
|
42
|
+
if (expr.getKind() === SyntaxKind.VoidExpression)
|
|
43
|
+
continue;
|
|
44
|
+
if (expr.getKind() === SyntaxKind.AwaitExpression)
|
|
45
|
+
continue;
|
|
46
|
+
if (expr.getKind() !== SyntaxKind.CallExpression)
|
|
47
|
+
continue;
|
|
48
|
+
const callExpr = expr;
|
|
49
|
+
const calleeExpr = callExpr.getExpression();
|
|
50
|
+
// Case 1: .then() chain — somePromise.then(...)
|
|
51
|
+
if (calleeExpr.getKind() === SyntaxKind.PropertyAccessExpression) {
|
|
52
|
+
const propAccess = calleeExpr;
|
|
53
|
+
if (propAccess.getName() === 'then') {
|
|
54
|
+
const objText = propAccess.getExpression().getText();
|
|
55
|
+
findings.push(finding('floating-promise', 'error', 'bug', `Promise chain '${objText}.then(...)' is not awaited, returned, or voided`, ctx.filePath, exprStmt.getStartLineNumber()));
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Case 2: Direct call to known async function — asyncFn()
|
|
60
|
+
if (calleeExpr.getKind() === SyntaxKind.Identifier) {
|
|
61
|
+
const fnName = calleeExpr.getText();
|
|
62
|
+
if (asyncFns.has(fnName)) {
|
|
63
|
+
const matchingNode = ctx.inferred.find(r => r.node.type === 'fn' && r.node.props?.name === fnName);
|
|
64
|
+
findings.push(finding('floating-promise', 'error', 'bug', `Async function '${fnName}()' called without await — floating promise`, ctx.filePath, exprStmt.getStartLineNumber(), 1, matchingNode ? { nodeIds: [matchingNode.nodeId] } : undefined));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return findings;
|
|
69
|
+
}
|
|
70
|
+
// ── Rule 2: state-mutation ───────────────────────────────────────────────
|
|
71
|
+
// Direct mutation of state variables (push, splice, delete, assignment to .prop)
|
|
72
|
+
function stateMutation(ctx) {
|
|
73
|
+
const findings = [];
|
|
74
|
+
const mutationMethods = new Set(['push', 'splice', 'pop', 'shift', 'unshift', 'sort', 'reverse', 'fill', 'copyWithin']);
|
|
75
|
+
const stateNames = new Set(['state', 'store', 'data']);
|
|
76
|
+
// AST-based: find mutation method calls on state-like objects
|
|
77
|
+
for (const call of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {
|
|
78
|
+
const callee = call.getExpression();
|
|
79
|
+
if (callee.getKind() !== SyntaxKind.PropertyAccessExpression)
|
|
80
|
+
continue;
|
|
81
|
+
const propAccess = callee;
|
|
82
|
+
const methodName = propAccess.getName();
|
|
83
|
+
if (!mutationMethods.has(methodName))
|
|
84
|
+
continue;
|
|
85
|
+
// Check if the root object is state-like
|
|
86
|
+
const objText = propAccess.getExpression().getText();
|
|
87
|
+
const rootObj = objText.split('.')[0];
|
|
88
|
+
if (!stateNames.has(rootObj))
|
|
89
|
+
continue;
|
|
90
|
+
// Skip if inside zustand set() or immer produce()
|
|
91
|
+
if (isInsideCall(call, 'set') || isInsideCall(call, 'produce'))
|
|
92
|
+
continue;
|
|
93
|
+
findings.push(finding('state-mutation', 'error', 'bug', `Direct state mutation via .${methodName}() on '${objText}' — use immutable update`, ctx.filePath, call.getStartLineNumber()));
|
|
94
|
+
}
|
|
95
|
+
// AST-based: find delete expressions on state-like objects
|
|
96
|
+
for (const del of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.DeleteExpression)) {
|
|
97
|
+
const operand = del.getExpression().getText();
|
|
98
|
+
const rootObj = operand.split('.')[0];
|
|
99
|
+
if (!stateNames.has(rootObj))
|
|
100
|
+
continue;
|
|
101
|
+
if (isInsideCall(del, 'set') || isInsideCall(del, 'produce'))
|
|
102
|
+
continue;
|
|
103
|
+
findings.push(finding('state-mutation', 'error', 'bug', `Direct state mutation via delete on '${operand}' — use immutable update`, ctx.filePath, del.getStartLineNumber()));
|
|
104
|
+
}
|
|
105
|
+
return findings;
|
|
106
|
+
}
|
|
107
|
+
/** Check if a node is inside a specific function call (e.g., set(), produce()) */
|
|
108
|
+
function isInsideCall(node, callName) {
|
|
109
|
+
let parent = node.getParent();
|
|
110
|
+
while (parent) {
|
|
111
|
+
if (parent.getKind() === SyntaxKind.CallExpression) {
|
|
112
|
+
const callExpr = parent;
|
|
113
|
+
const callee = callExpr.getExpression();
|
|
114
|
+
const text = callee.getText();
|
|
115
|
+
if (text === callName || text.endsWith(`.${callName}`))
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
parent = parent.getParent();
|
|
119
|
+
}
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
// ── Rule 3: empty-catch ──────────────────────────────────────────────────
|
|
123
|
+
// Catch block that swallows exceptions silently
|
|
124
|
+
function emptyCatch(ctx) {
|
|
125
|
+
const findings = [];
|
|
126
|
+
for (const stmt of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.CatchClause)) {
|
|
127
|
+
const block = stmt.getBlock();
|
|
128
|
+
const stmts = block.getStatements();
|
|
129
|
+
if (stmts.length === 0) {
|
|
130
|
+
const line = stmt.getStartLineNumber();
|
|
131
|
+
findings.push(finding('empty-catch', 'warning', 'bug', 'Empty catch block swallows exception — at minimum log or rethrow', ctx.filePath, line));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return findings;
|
|
135
|
+
}
|
|
136
|
+
// ── Rule 4: machine-gap ─────────────────────────────────────────────────
|
|
137
|
+
// Unreachable states + missing transitions in machine nodes
|
|
138
|
+
function machineGap(ctx) {
|
|
139
|
+
const findings = [];
|
|
140
|
+
const machines = ctx.inferred.filter(r => r.node.type === 'machine');
|
|
141
|
+
for (const m of machines) {
|
|
142
|
+
const states = (m.node.children || []).filter(c => c.type === 'state');
|
|
143
|
+
const transitions = (m.node.children || []).filter(c => c.type === 'transition');
|
|
144
|
+
const stateNames = new Set(states.map(s => (s.props?.name || s.props?.value)));
|
|
145
|
+
if (stateNames.size === 0)
|
|
146
|
+
continue;
|
|
147
|
+
// Check: states that can never be reached (no transition leads to them)
|
|
148
|
+
const initialState = states.find(s => s.props?.initial === 'true');
|
|
149
|
+
const reachable = new Set();
|
|
150
|
+
if (initialState)
|
|
151
|
+
reachable.add((initialState.props?.name || initialState.props?.value));
|
|
152
|
+
for (const t of transitions) {
|
|
153
|
+
const to = t.props?.to;
|
|
154
|
+
if (to)
|
|
155
|
+
reachable.add(to);
|
|
156
|
+
}
|
|
157
|
+
for (const name of stateNames) {
|
|
158
|
+
if (!reachable.has(name)) {
|
|
159
|
+
findings.push(finding('machine-gap', 'warning', 'structure', `State '${name}' in machine '${m.node.props?.name}' has no transition leading to it — unreachable`, ctx.filePath, m.startLine, 1, { nodeIds: [m.nodeId] }));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// Check: terminal states (no transitions FROM them) that aren't clearly final
|
|
163
|
+
const hasTransitionFrom = new Set();
|
|
164
|
+
for (const t of transitions) {
|
|
165
|
+
const from = t.props?.from;
|
|
166
|
+
if (from) {
|
|
167
|
+
for (const f of from.split('|'))
|
|
168
|
+
hasTransitionFrom.add(f.trim());
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
const terminalStates = [...stateNames].filter(s => !hasTransitionFrom.has(s));
|
|
172
|
+
const clearlyFinal = ['completed', 'done', 'failed', 'cancelled', 'error', 'success', 'closed', 'terminated'];
|
|
173
|
+
for (const ts of terminalStates) {
|
|
174
|
+
if (!clearlyFinal.some(f => ts.toLowerCase().includes(f))) {
|
|
175
|
+
findings.push(finding('machine-gap', 'warning', 'structure', `State '${ts}' in machine '${m.node.props?.name}' has no outgoing transitions — dead end?`, ctx.filePath, m.startLine, 1, { nodeIds: [m.nodeId] }));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// Check: transition references non-existent state
|
|
179
|
+
for (const t of transitions) {
|
|
180
|
+
const from = t.props?.from;
|
|
181
|
+
const to = t.props?.to;
|
|
182
|
+
if (from) {
|
|
183
|
+
for (const f of from.split('|')) {
|
|
184
|
+
if (f.trim() && !stateNames.has(f.trim())) {
|
|
185
|
+
findings.push(finding('machine-gap', 'error', 'bug', `Transition '${t.props?.name}' references unknown state '${f.trim()}'`, ctx.filePath, m.startLine, 1, { nodeIds: [m.nodeId] }));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (to && !stateNames.has(to)) {
|
|
190
|
+
findings.push(finding('machine-gap', 'error', 'bug', `Transition '${t.props?.name}' targets unknown state '${to}'`, ctx.filePath, m.startLine, 1, { nodeIds: [m.nodeId] }));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return findings;
|
|
195
|
+
}
|
|
196
|
+
// ── Rule 5: config-default-mismatch ──────────────────────────────────────
|
|
197
|
+
// Config interface fields vs DEFAULT_X const keys
|
|
198
|
+
function configDefaultMismatch(ctx) {
|
|
199
|
+
const findings = [];
|
|
200
|
+
const configs = ctx.inferred.filter(r => r.node.type === 'config');
|
|
201
|
+
for (const cfg of configs) {
|
|
202
|
+
const fields = (cfg.node.children || [])
|
|
203
|
+
.filter(c => c.type === 'field')
|
|
204
|
+
.map(c => c.props?.name)
|
|
205
|
+
.filter(Boolean);
|
|
206
|
+
if (fields.length === 0)
|
|
207
|
+
continue;
|
|
208
|
+
// Find the DEFAULT_X constant via AST VariableDeclaration
|
|
209
|
+
const configName = cfg.node.props?.name;
|
|
210
|
+
const upperName = configName.replace(/([a-z])([A-Z])/g, '$1_$2').toUpperCase();
|
|
211
|
+
let defaultKeys = null;
|
|
212
|
+
for (const stmt of ctx.sourceFile.getVariableStatements()) {
|
|
213
|
+
for (const decl of stmt.getDeclarations()) {
|
|
214
|
+
const declName = decl.getName();
|
|
215
|
+
if (declName !== `DEFAULT_${upperName}` && declName !== `default${configName}`)
|
|
216
|
+
continue;
|
|
217
|
+
const init = decl.getInitializer();
|
|
218
|
+
if (!init || init.getKind() !== SyntaxKind.ObjectLiteralExpression)
|
|
219
|
+
continue;
|
|
220
|
+
const objLiteral = init;
|
|
221
|
+
defaultKeys = new Set();
|
|
222
|
+
for (const prop of objLiteral.getProperties()) {
|
|
223
|
+
if (prop.getKind() === SyntaxKind.PropertyAssignment) {
|
|
224
|
+
const pa = prop;
|
|
225
|
+
defaultKeys.add(pa.getName());
|
|
226
|
+
}
|
|
227
|
+
else if (prop.getKind() === SyntaxKind.ShorthandPropertyAssignment) {
|
|
228
|
+
const spa = prop;
|
|
229
|
+
defaultKeys.add(spa.getName());
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
if (defaultKeys)
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
if (!defaultKeys)
|
|
238
|
+
continue;
|
|
239
|
+
// Fields in interface but not in defaults
|
|
240
|
+
for (const field of fields) {
|
|
241
|
+
if (!defaultKeys.has(field)) {
|
|
242
|
+
findings.push(finding('config-default-mismatch', 'warning', 'pattern', `Config field '${field}' in ${configName} has no default value`, ctx.filePath, cfg.startLine, 1, { nodeIds: [cfg.nodeId] }));
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
// Keys in defaults but not in interface
|
|
246
|
+
for (const key of defaultKeys) {
|
|
247
|
+
if (!fields.includes(key)) {
|
|
248
|
+
findings.push(finding('config-default-mismatch', 'warning', 'pattern', `Default key '${key}' not defined in ${configName} interface`, ctx.filePath, cfg.startLine, 1, { nodeIds: [cfg.nodeId] }));
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return findings;
|
|
253
|
+
}
|
|
254
|
+
// ── Rule 6: event-map-mismatch ───────────────────────────────────────────
|
|
255
|
+
// EventType values vs EventMap keys
|
|
256
|
+
function eventMapMismatch(ctx) {
|
|
257
|
+
const findings = [];
|
|
258
|
+
const events = ctx.inferred.filter(r => r.node.type === 'event');
|
|
259
|
+
for (const evt of events) {
|
|
260
|
+
const eventValues = (evt.node.children || [])
|
|
261
|
+
.filter(c => c.type === 'type')
|
|
262
|
+
.map(c => (c.props?.value || c.props?.name))
|
|
263
|
+
.filter(Boolean);
|
|
264
|
+
if (eventValues.length === 0)
|
|
265
|
+
continue;
|
|
266
|
+
// Look for matching EventMap interface
|
|
267
|
+
const baseName = evt.node.props?.name;
|
|
268
|
+
const eventMap = ctx.inferred.find(r => r.node.type === 'interface' &&
|
|
269
|
+
r.node.props?.name === `${baseName}Map`);
|
|
270
|
+
if (!eventMap)
|
|
271
|
+
continue;
|
|
272
|
+
const mapKeys = (eventMap.node.children || [])
|
|
273
|
+
.filter(c => c.type === 'field')
|
|
274
|
+
.map(c => c.props?.name)
|
|
275
|
+
.filter(Boolean);
|
|
276
|
+
// Events not in map
|
|
277
|
+
for (const val of eventValues) {
|
|
278
|
+
if (!mapKeys.includes(val)) {
|
|
279
|
+
findings.push(finding('event-map-mismatch', 'warning', 'pattern', `Event type '${val}' has no handler in ${baseName}Map`, ctx.filePath, evt.startLine, 1, { nodeIds: [evt.nodeId, eventMap.nodeId] }));
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
// Map keys not in events
|
|
283
|
+
for (const key of mapKeys) {
|
|
284
|
+
if (!eventValues.includes(key)) {
|
|
285
|
+
findings.push(finding('event-map-mismatch', 'warning', 'pattern', `Handler '${key}' in ${baseName}Map has no matching event type`, ctx.filePath, evt.startLine, 1, { nodeIds: [evt.nodeId, eventMap.nodeId] }));
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return findings;
|
|
290
|
+
}
|
|
291
|
+
// ── Rule 7: non-exhaustive-switch ────────────────────────────────────────
|
|
292
|
+
// Switch over union/machine state that misses cases
|
|
293
|
+
function nonExhaustiveSwitch(ctx) {
|
|
294
|
+
const findings = [];
|
|
295
|
+
const fullText = ctx.sourceFile.getFullText();
|
|
296
|
+
// Collect known union types from inferred results
|
|
297
|
+
const unionTypes = new Map();
|
|
298
|
+
for (const r of ctx.inferred) {
|
|
299
|
+
if (r.node.type === 'type' && r.node.props?.values) {
|
|
300
|
+
const name = r.node.props.name;
|
|
301
|
+
const values = r.node.props.values.split('|');
|
|
302
|
+
unionTypes.set(name, values);
|
|
303
|
+
}
|
|
304
|
+
if (r.node.type === 'machine') {
|
|
305
|
+
const name = r.node.props?.name + 'State';
|
|
306
|
+
const states = (r.node.children || [])
|
|
307
|
+
.filter(c => c.type === 'state')
|
|
308
|
+
.map(c => (c.props?.name || c.props?.value));
|
|
309
|
+
if (states.length > 0)
|
|
310
|
+
unionTypes.set(name, states);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if (unionTypes.size === 0)
|
|
314
|
+
return findings;
|
|
315
|
+
// Find switch statements
|
|
316
|
+
for (const switchStmt of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.SwitchStatement)) {
|
|
317
|
+
const expr = switchStmt.getExpression().getText();
|
|
318
|
+
const line = switchStmt.getStartLineNumber();
|
|
319
|
+
// Try to match switch expression to a known type
|
|
320
|
+
// e.g., switch (state) where state: PlanState
|
|
321
|
+
const cases = switchStmt.getCaseBlock().getClauses();
|
|
322
|
+
const caseValues = new Set();
|
|
323
|
+
let hasDefault = false;
|
|
324
|
+
for (const clause of cases) {
|
|
325
|
+
if (clause.getKind() === SyntaxKind.DefaultClause) {
|
|
326
|
+
hasDefault = true;
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
// CaseClause has getExpression(), DefaultClause does not
|
|
330
|
+
if (clause.getKind() === SyntaxKind.CaseClause) {
|
|
331
|
+
const caseClause = clause;
|
|
332
|
+
const caseExpr = caseClause.getExpression()?.getText() || '';
|
|
333
|
+
const val = caseExpr.replace(/['"]/g, '');
|
|
334
|
+
if (val)
|
|
335
|
+
caseValues.add(val);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
if (hasDefault)
|
|
339
|
+
continue; // default clause covers missing cases
|
|
340
|
+
// Check each union type to see if this switch might be over it
|
|
341
|
+
for (const [typeName, values] of unionTypes) {
|
|
342
|
+
// Heuristic: if >50% of values match cases, it's likely a switch over this type
|
|
343
|
+
const matchCount = values.filter(v => caseValues.has(v)).length;
|
|
344
|
+
if (matchCount < values.length * 0.5 || matchCount < 2)
|
|
345
|
+
continue;
|
|
346
|
+
const missing = values.filter(v => !caseValues.has(v));
|
|
347
|
+
if (missing.length > 0) {
|
|
348
|
+
findings.push(finding('non-exhaustive-switch', 'warning', 'pattern', `Switch on ${expr} appears to cover ${typeName} but misses: ${missing.map(m => `'${m}'`).join(', ')}`, ctx.filePath, line));
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return findings;
|
|
353
|
+
}
|
|
354
|
+
// ── Rule 8: complexity-spike ─────────────────────────────────────────────
|
|
355
|
+
// Handler block exceeds 200 tokens
|
|
356
|
+
function complexitySpike(ctx) {
|
|
357
|
+
const findings = [];
|
|
358
|
+
for (const r of ctx.inferred) {
|
|
359
|
+
if (r.node.type !== 'fn')
|
|
360
|
+
continue;
|
|
361
|
+
const handler = (r.node.children || []).find(c => c.type === 'handler');
|
|
362
|
+
if (!handler?.props?.code)
|
|
363
|
+
continue;
|
|
364
|
+
const code = handler.props.code;
|
|
365
|
+
const tokens = countTokens(code);
|
|
366
|
+
if (tokens > 200) {
|
|
367
|
+
findings.push(finding('complexity-spike', 'info', 'structure', `Function '${r.node.props?.name}' has ${tokens} tokens in handler body — consider splitting`, ctx.filePath, r.startLine, 1, { nodeIds: [r.nodeId] }));
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
return findings;
|
|
371
|
+
}
|
|
372
|
+
// ── Rule 9: template-available ───────────────────────────────────────────
|
|
373
|
+
// Library pattern detected, matching template exists
|
|
374
|
+
function templateAvailable(ctx) {
|
|
375
|
+
const findings = [];
|
|
376
|
+
if (!ctx.config?.registeredTemplates?.length)
|
|
377
|
+
return findings;
|
|
378
|
+
const registered = new Set(ctx.config.registeredTemplates);
|
|
379
|
+
for (const t of ctx.templateMatches) {
|
|
380
|
+
if (!registered.has(t.templateName))
|
|
381
|
+
continue;
|
|
382
|
+
if (t.suggestedKern) {
|
|
383
|
+
findings.push(finding('template-available', 'info', 'pattern', `${t.libraryName} pattern should use KERN template '${t.templateName}'`, ctx.filePath, t.startLine, 1, { suggestion: t.suggestedKern }));
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
return findings;
|
|
387
|
+
}
|
|
388
|
+
// ── Rule 10: handler-extraction ──────────────────────────────────────────
|
|
389
|
+
// Large handler that should be extracted to a fn node
|
|
390
|
+
function handlerExtraction(ctx) {
|
|
391
|
+
const findings = [];
|
|
392
|
+
for (const r of ctx.inferred) {
|
|
393
|
+
if (r.node.type !== 'fn')
|
|
394
|
+
continue;
|
|
395
|
+
const handler = (r.node.children || []).find(c => c.type === 'handler');
|
|
396
|
+
if (!handler?.props?.code)
|
|
397
|
+
continue;
|
|
398
|
+
const code = handler.props.code;
|
|
399
|
+
const tokens = countTokens(code);
|
|
400
|
+
if (tokens > 300) {
|
|
401
|
+
findings.push(finding('handler-extraction', 'info', 'structure', `Handler in '${r.node.props?.name}' (${tokens} tokens) is a candidate for extraction to separate fn node`, ctx.filePath, r.startLine, 1, { nodeIds: [r.nodeId] }));
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return findings;
|
|
405
|
+
}
|
|
406
|
+
// ── Rule: memory-leak (from v1) ──────────────────────────────────────────
|
|
407
|
+
// useEffect/watch/onMounted that creates subscriptions without cleanup
|
|
408
|
+
const EFFECT_CALLEE_NAMES = new Set(['useEffect', 'watch', 'onMounted', 'watchEffect']);
|
|
409
|
+
const SUBSCRIPTION_METHODS = new Set(['addEventListener', 'subscribe', 'on']);
|
|
410
|
+
const SUBSCRIPTION_FUNCTIONS = new Set(['setInterval', 'setTimeout']);
|
|
411
|
+
const SUBSCRIPTION_CONSTRUCTORS = new Set([
|
|
412
|
+
'WebSocket', 'EventSource', 'MutationObserver', 'IntersectionObserver', 'ResizeObserver',
|
|
413
|
+
]);
|
|
414
|
+
function memoryLeak(ctx) {
|
|
415
|
+
const findings = [];
|
|
416
|
+
for (const call of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {
|
|
417
|
+
const callee = call.getExpression();
|
|
418
|
+
if (callee.getKind() !== SyntaxKind.Identifier)
|
|
419
|
+
continue;
|
|
420
|
+
if (!EFFECT_CALLEE_NAMES.has(callee.getText()))
|
|
421
|
+
continue;
|
|
422
|
+
// Get the callback (first argument)
|
|
423
|
+
const args = call.getArguments();
|
|
424
|
+
if (args.length === 0)
|
|
425
|
+
continue;
|
|
426
|
+
const callback = args[0];
|
|
427
|
+
if (callback.getKind() !== SyntaxKind.ArrowFunction &&
|
|
428
|
+
callback.getKind() !== SyntaxKind.FunctionExpression)
|
|
429
|
+
continue;
|
|
430
|
+
// Walk callback descendants for subscription patterns
|
|
431
|
+
let subscriptionLabel = null;
|
|
432
|
+
for (const desc of callback.getDescendantsOfKind(SyntaxKind.CallExpression)) {
|
|
433
|
+
const descCallee = desc.getExpression();
|
|
434
|
+
// PropertyAccess calls: obj.addEventListener(), obs.subscribe(), emitter.on()
|
|
435
|
+
if (descCallee.getKind() === SyntaxKind.PropertyAccessExpression) {
|
|
436
|
+
const pa = descCallee;
|
|
437
|
+
if (SUBSCRIPTION_METHODS.has(pa.getName())) {
|
|
438
|
+
subscriptionLabel = pa.getName();
|
|
439
|
+
break;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
// Direct calls: setInterval(), setTimeout()
|
|
443
|
+
if (descCallee.getKind() === SyntaxKind.Identifier) {
|
|
444
|
+
if (SUBSCRIPTION_FUNCTIONS.has(descCallee.getText())) {
|
|
445
|
+
subscriptionLabel = descCallee.getText();
|
|
446
|
+
break;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
// Also check for new WebSocket/EventSource/Observer constructors
|
|
451
|
+
if (!subscriptionLabel) {
|
|
452
|
+
for (const newExpr of callback.getDescendantsOfKind(SyntaxKind.NewExpression)) {
|
|
453
|
+
const ctorName = newExpr.getExpression().getText();
|
|
454
|
+
if (SUBSCRIPTION_CONSTRUCTORS.has(ctorName)) {
|
|
455
|
+
subscriptionLabel = `new ${ctorName}`;
|
|
456
|
+
break;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
if (!subscriptionLabel)
|
|
461
|
+
continue;
|
|
462
|
+
// Check if callback has a cleanup return statement
|
|
463
|
+
const callbackBody = callback.getKind() === SyntaxKind.ArrowFunction
|
|
464
|
+
? callback.getBody()
|
|
465
|
+
: callback.getBody();
|
|
466
|
+
if (!callbackBody)
|
|
467
|
+
continue;
|
|
468
|
+
let hasCleanupReturn = false;
|
|
469
|
+
// For block bodies, check top-level return statements
|
|
470
|
+
if (callbackBody.getKind() === SyntaxKind.Block) {
|
|
471
|
+
const block = callbackBody;
|
|
472
|
+
for (const stmt of block.getStatements()) {
|
|
473
|
+
if (stmt.getKind() !== SyntaxKind.ReturnStatement)
|
|
474
|
+
continue;
|
|
475
|
+
const retStmt = stmt;
|
|
476
|
+
const retExpr = retStmt.getExpression();
|
|
477
|
+
if (!retExpr)
|
|
478
|
+
continue;
|
|
479
|
+
// return () => { ... } — ArrowFunction
|
|
480
|
+
if (retExpr.getKind() === SyntaxKind.ArrowFunction) {
|
|
481
|
+
hasCleanupReturn = true;
|
|
482
|
+
break;
|
|
483
|
+
}
|
|
484
|
+
// return function() { ... } — FunctionExpression
|
|
485
|
+
if (retExpr.getKind() === SyntaxKind.FunctionExpression) {
|
|
486
|
+
hasCleanupReturn = true;
|
|
487
|
+
break;
|
|
488
|
+
}
|
|
489
|
+
// return cleanup — Identifier (variable reference)
|
|
490
|
+
if (retExpr.getKind() === SyntaxKind.Identifier) {
|
|
491
|
+
hasCleanupReturn = true;
|
|
492
|
+
break;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
if (hasCleanupReturn)
|
|
497
|
+
continue;
|
|
498
|
+
findings.push(finding('memory-leak', 'error', 'bug', `Effect creates ${subscriptionLabel}() but has no cleanup — memory leak`, ctx.filePath, call.getStartLineNumber(), 1, { suggestion: 'Add cleanup: return () => { /* remove listener/clear interval */ }' }));
|
|
499
|
+
}
|
|
500
|
+
return findings;
|
|
501
|
+
}
|
|
502
|
+
// ── Rule: unhandled-async (from v1) ──────────────────────────────────────
|
|
503
|
+
// Async functions with await but no try/catch
|
|
504
|
+
function unhandledAsync(ctx) {
|
|
505
|
+
const findings = [];
|
|
506
|
+
for (const fn of ctx.sourceFile.getFunctions()) {
|
|
507
|
+
if (!fn.isAsync())
|
|
508
|
+
continue;
|
|
509
|
+
const body = fn.getBody()?.getText() || '';
|
|
510
|
+
const name = fn.getName() || 'anonymous';
|
|
511
|
+
const line = fn.getStartLineNumber();
|
|
512
|
+
const hasTryCatch = body.includes('try') && body.includes('catch');
|
|
513
|
+
const hasDotCatch = body.includes('.catch(') || body.includes('.catch (');
|
|
514
|
+
const hasThrow = body.includes('throw ');
|
|
515
|
+
if (!hasTryCatch && !hasDotCatch && !hasThrow) {
|
|
516
|
+
const hasAwait = body.includes('await ');
|
|
517
|
+
if (hasAwait) {
|
|
518
|
+
findings.push(finding('unhandled-async', 'warning', 'bug', `Async function '${name}' has await but no try/catch — unhandled rejection risk`, ctx.filePath, line, 1, { suggestion: 'Wrap await calls in try/catch or add .catch() handler' }));
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
// Arrow function expressions with async
|
|
523
|
+
for (const stmt of ctx.sourceFile.getVariableStatements()) {
|
|
524
|
+
for (const decl of stmt.getDeclarations()) {
|
|
525
|
+
const init = decl.getInitializer();
|
|
526
|
+
if (!init)
|
|
527
|
+
continue;
|
|
528
|
+
const text = init.getText();
|
|
529
|
+
if (!text.startsWith('async'))
|
|
530
|
+
continue;
|
|
531
|
+
const hasAwait = text.includes('await ');
|
|
532
|
+
const hasTryCatch = text.includes('try') && text.includes('catch');
|
|
533
|
+
const hasDotCatch = text.includes('.catch(');
|
|
534
|
+
if (hasAwait && !hasTryCatch && !hasDotCatch) {
|
|
535
|
+
findings.push(finding('unhandled-async', 'warning', 'bug', `Async '${decl.getName()}' has await but no error handling`, ctx.filePath, stmt.getStartLineNumber(), 1, { suggestion: 'Wrap await calls in try/catch' }));
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
return findings;
|
|
540
|
+
}
|
|
541
|
+
// ── Exported Base Rules ──────────────────────────────────────────────────
|
|
542
|
+
export const baseRules = [
|
|
543
|
+
floatingPromise,
|
|
544
|
+
stateMutation,
|
|
545
|
+
emptyCatch,
|
|
546
|
+
machineGap,
|
|
547
|
+
configDefaultMismatch,
|
|
548
|
+
eventMapMismatch,
|
|
549
|
+
nonExhaustiveSwitch,
|
|
550
|
+
complexitySpike,
|
|
551
|
+
templateAvailable,
|
|
552
|
+
handlerExtraction,
|
|
553
|
+
memoryLeak,
|
|
554
|
+
unhandledAsync,
|
|
555
|
+
];
|
|
556
|
+
//# sourceMappingURL=base.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.js","sourceRoot":"","sources":["../../src/rules/base.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Express review rules — active when target = express.
|
|
3
|
+
*
|
|
4
|
+
* Catches common Express security and performance issues.
|
|
5
|
+
*/
|
|
6
|
+
import type { ReviewFinding, RuleContext } from '../types.js';
|
|
7
|
+
declare function unvalidatedInput(ctx: RuleContext): ReviewFinding[];
|
|
8
|
+
export declare const expressRules: (typeof unvalidatedInput)[];
|
|
9
|
+
export {};
|