@kernlang/review 3.1.5 → 3.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/LICENSE +17 -0
  2. package/dist/cache.d.ts +1 -1
  3. package/dist/cache.js +5 -3
  4. package/dist/cache.js.map +1 -1
  5. package/dist/call-graph.d.ts +63 -0
  6. package/dist/call-graph.js +380 -0
  7. package/dist/call-graph.js.map +1 -0
  8. package/dist/concept-rules/boundary-mutation.d.ts +1 -1
  9. package/dist/concept-rules/boundary-mutation.js.map +1 -1
  10. package/dist/concept-rules/ignored-error.d.ts +1 -1
  11. package/dist/concept-rules/ignored-error.js.map +1 -1
  12. package/dist/concept-rules/illegal-dependency.d.ts +1 -1
  13. package/dist/concept-rules/illegal-dependency.js.map +1 -1
  14. package/dist/concept-rules/index.js +1 -6
  15. package/dist/concept-rules/index.js.map +1 -1
  16. package/dist/concept-rules/unguarded-effect.d.ts +1 -1
  17. package/dist/concept-rules/unguarded-effect.js.map +1 -1
  18. package/dist/concept-rules/unrecovered-effect.d.ts +1 -1
  19. package/dist/concept-rules/unrecovered-effect.js +2 -1
  20. package/dist/concept-rules/unrecovered-effect.js.map +1 -1
  21. package/dist/confidence.js +12 -8
  22. package/dist/confidence.js.map +1 -1
  23. package/dist/differ.js +3 -7
  24. package/dist/differ.js.map +1 -1
  25. package/dist/external-tools.js +5 -6
  26. package/dist/external-tools.js.map +1 -1
  27. package/dist/file-context.d.ts +21 -0
  28. package/dist/file-context.js +234 -0
  29. package/dist/file-context.js.map +1 -0
  30. package/dist/file-role.js +14 -7
  31. package/dist/file-role.js.map +1 -1
  32. package/dist/graph.d.ts +1 -1
  33. package/dist/graph.js +24 -16
  34. package/dist/graph.js.map +1 -1
  35. package/dist/index.d.ts +44 -35
  36. package/dist/index.js +221 -68
  37. package/dist/index.js.map +1 -1
  38. package/dist/inferrer.d.ts +8 -2
  39. package/dist/inferrer.js +80 -47
  40. package/dist/inferrer.js.map +1 -1
  41. package/dist/kern-lint.d.ts +3 -4
  42. package/dist/kern-lint.js +7 -5
  43. package/dist/kern-lint.js.map +1 -1
  44. package/dist/llm-bridge.d.ts +23 -7
  45. package/dist/llm-bridge.js +267 -31
  46. package/dist/llm-bridge.js.map +1 -1
  47. package/dist/llm-review.d.ts +16 -2
  48. package/dist/llm-review.js +240 -35
  49. package/dist/llm-review.js.map +1 -1
  50. package/dist/mappers/ts-concepts.d.ts +1 -1
  51. package/dist/mappers/ts-concepts.js +303 -32
  52. package/dist/mappers/ts-concepts.js.map +1 -1
  53. package/dist/norm-miner.d.ts +31 -0
  54. package/dist/norm-miner.js +119 -0
  55. package/dist/norm-miner.js.map +1 -0
  56. package/dist/obligations.d.ts +63 -0
  57. package/dist/obligations.js +158 -0
  58. package/dist/obligations.js.map +1 -0
  59. package/dist/quality-rules.d.ts +3 -3
  60. package/dist/quality-rules.js +4 -2
  61. package/dist/quality-rules.js.map +1 -1
  62. package/dist/reporter.d.ts +7 -2
  63. package/dist/reporter.js +82 -51
  64. package/dist/reporter.js.map +1 -1
  65. package/dist/rule-eval.d.ts +1 -2
  66. package/dist/rule-eval.js +5 -9
  67. package/dist/rule-eval.js.map +1 -1
  68. package/dist/rule-loader.js +16 -14
  69. package/dist/rule-loader.js.map +1 -1
  70. package/dist/rules/base.js +153 -69
  71. package/dist/rules/base.js.map +1 -1
  72. package/dist/rules/cli.d.ts +7 -0
  73. package/dist/rules/cli.js +99 -0
  74. package/dist/rules/cli.js.map +1 -0
  75. package/dist/rules/confidence.d.ts +1 -1
  76. package/dist/rules/confidence.js +5 -5
  77. package/dist/rules/confidence.js.map +1 -1
  78. package/dist/rules/dead-code.d.ts +10 -0
  79. package/dist/rules/dead-code.js +75 -0
  80. package/dist/rules/dead-code.js.map +1 -0
  81. package/dist/rules/dead-logic.js +35 -31
  82. package/dist/rules/dead-logic.js.map +1 -1
  83. package/dist/rules/express.d.ts +2 -1
  84. package/dist/rules/express.js +380 -126
  85. package/dist/rules/express.js.map +1 -1
  86. package/dist/rules/fastapi.d.ts +10 -0
  87. package/dist/rules/fastapi.js +183 -0
  88. package/dist/rules/fastapi.js.map +1 -0
  89. package/dist/rules/ground-layer.js +3 -3
  90. package/dist/rules/ground-layer.js.map +1 -1
  91. package/dist/rules/index.d.ts +5 -1
  92. package/dist/rules/index.js +602 -84
  93. package/dist/rules/index.js.map +1 -1
  94. package/dist/rules/ink.d.ts +8 -0
  95. package/dist/rules/ink.js +88 -0
  96. package/dist/rules/ink.js.map +1 -0
  97. package/dist/rules/kern-source.js +202 -63
  98. package/dist/rules/kern-source.js.map +1 -1
  99. package/dist/rules/nextjs.js +88 -33
  100. package/dist/rules/nextjs.js.map +1 -1
  101. package/dist/rules/null-safety.js +52 -26
  102. package/dist/rules/null-safety.js.map +1 -1
  103. package/dist/rules/nuxt.js +24 -29
  104. package/dist/rules/nuxt.js.map +1 -1
  105. package/dist/rules/react.js +355 -69
  106. package/dist/rules/react.js.map +1 -1
  107. package/dist/rules/security-v2.js +71 -57
  108. package/dist/rules/security-v2.js.map +1 -1
  109. package/dist/rules/security-v3.js.map +1 -1
  110. package/dist/rules/security-v4.js +54 -27
  111. package/dist/rules/security-v4.js.map +1 -1
  112. package/dist/rules/security.js +35 -5
  113. package/dist/rules/security.js.map +1 -1
  114. package/dist/rules/terminal.d.ts +8 -0
  115. package/dist/rules/terminal.js +139 -0
  116. package/dist/rules/terminal.js.map +1 -0
  117. package/dist/rules/vue.js +162 -107
  118. package/dist/rules/vue.js.map +1 -1
  119. package/dist/semantic-diff.d.ts +52 -0
  120. package/dist/semantic-diff.js +342 -0
  121. package/dist/semantic-diff.js.map +1 -0
  122. package/dist/spec-checker.js +11 -10
  123. package/dist/spec-checker.js.map +1 -1
  124. package/dist/suppression/apply-suppression.d.ts +2 -3
  125. package/dist/suppression/apply-suppression.js +3 -3
  126. package/dist/suppression/apply-suppression.js.map +1 -1
  127. package/dist/suppression/index.d.ts +2 -2
  128. package/dist/suppression/index.js +1 -1
  129. package/dist/suppression/index.js.map +1 -1
  130. package/dist/suppression/parse-directives.d.ts +1 -1
  131. package/dist/suppression/parse-directives.js +9 -4
  132. package/dist/suppression/parse-directives.js.map +1 -1
  133. package/dist/taint-ast.d.ts +20 -0
  134. package/dist/taint-ast.js +427 -0
  135. package/dist/taint-ast.js.map +1 -0
  136. package/dist/taint-crossfile.d.ts +28 -0
  137. package/dist/taint-crossfile.js +174 -0
  138. package/dist/taint-crossfile.js.map +1 -0
  139. package/dist/taint-findings.d.ts +17 -0
  140. package/dist/taint-findings.js +131 -0
  141. package/dist/taint-findings.js.map +1 -0
  142. package/dist/taint-regex.d.ts +61 -0
  143. package/dist/taint-regex.js +379 -0
  144. package/dist/taint-regex.js.map +1 -0
  145. package/dist/taint-types.d.ts +128 -0
  146. package/dist/taint-types.js +174 -0
  147. package/dist/taint-types.js.map +1 -0
  148. package/dist/taint.d.ts +13 -107
  149. package/dist/taint.js +16 -1067
  150. package/dist/taint.js.map +1 -1
  151. package/dist/template-detector.d.ts +2 -2
  152. package/dist/template-detector.js +11 -16
  153. package/dist/template-detector.js.map +1 -1
  154. package/dist/types.d.ts +35 -0
  155. package/dist/types.js.map +1 -1
  156. package/package.json +3 -3
package/dist/rules/vue.js CHANGED
@@ -3,38 +3,18 @@
3
3
  *
4
4
  * Catches Vue 3 Composition API pitfalls.
5
5
  */
6
- import { SyntaxKind } from 'ts-morph';
7
- import { createFingerprint } from '../types.js';
8
- function span(file, line, col = 1) {
9
- return { file, startLine: line, startCol: col, endLine: line, endCol: col };
10
- }
11
- function finding(ruleId, severity, category, message, file, line, extra) {
12
- return {
13
- source: 'kern',
14
- ruleId,
15
- severity,
16
- category,
17
- message,
18
- primarySpan: span(file, line),
19
- fingerprint: createFingerprint(ruleId, line, 1),
20
- ...extra,
21
- };
22
- }
6
+ import { Node, SyntaxKind } from 'ts-morph';
7
+ import { finding } from './utils.js';
23
8
  // ── Rule 17: missing-ref-value ───────────────────────────────────────────
24
9
  // Using ref() result without .value in script setup
25
10
  function missingRefValue(ctx) {
26
11
  const findings = [];
27
- // AST-based: find ref() declarations via variable statements
28
- const refVarNames = new Map(); // name → declaration line
12
+ const refVarNames = new Map();
29
13
  for (const stmt of ctx.sourceFile.getVariableStatements()) {
30
14
  for (const decl of stmt.getDeclarations()) {
31
15
  const init = decl.getInitializer();
32
- if (!init)
33
- continue;
34
- // Match ref() or ref<T>() calls
35
- if (init.getKind() === SyntaxKind.CallExpression) {
36
- const call = init;
37
- const calleeName = call.getExpression().getText();
16
+ if (init && Node.isCallExpression(init)) {
17
+ const calleeName = init.getExpression().getText();
38
18
  if (calleeName === 'ref') {
39
19
  refVarNames.set(decl.getName(), stmt.getStartLineNumber());
40
20
  }
@@ -43,134 +23,111 @@ function missingRefValue(ctx) {
43
23
  }
44
24
  if (refVarNames.size === 0)
45
25
  return findings;
46
- // Walk all identifiers and check if ref vars are used without .value
47
26
  for (const ident of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.Identifier)) {
48
27
  const name = ident.getText();
49
28
  if (!refVarNames.has(name))
50
29
  continue;
51
- // Skip the declaration itself
52
30
  if (ident.getStartLineNumber() === refVarNames.get(name))
53
31
  continue;
54
32
  const parent = ident.getParent();
55
33
  if (!parent)
56
34
  continue;
57
- // If parent is PropertyAccessExpression and ident is the object, check if accessing .value
58
- if (parent.getKind() === SyntaxKind.PropertyAccessExpression) {
59
- const propAccess = parent;
60
- if (propAccess.getExpression() === ident) {
61
- if (propAccess.getName() === 'value')
62
- continue; // correct: ref.value
63
- // Accessing some other property on ref without .value — still a bug
64
- }
35
+ if (Node.isPropertyAccessExpression(parent)) {
36
+ if (parent.getExpression() === ident && parent.getName() === 'value')
37
+ continue;
65
38
  }
66
- // Skip if ref is passed as a function argument (intentional: watch(myRef), toRef(myRef))
67
- if (parent.getKind() === SyntaxKind.CallExpression)
39
+ if (Node.isCallExpression(parent))
68
40
  continue;
69
- // Also skip if it's an argument in a call's argument list
70
41
  const grandparent = parent.getParent();
71
- if (grandparent?.getKind() === SyntaxKind.CallExpression)
72
- continue;
73
- // Skip type contexts
74
- if (parent.getKind() === SyntaxKind.TypeReference)
75
- continue;
76
- if (parent.getKind() === SyntaxKind.TypeQuery)
42
+ if (grandparent && Node.isCallExpression(grandparent))
77
43
  continue;
78
- // Skip shorthand property assignments: { count } in object literals
79
- if (parent.getKind() === SyntaxKind.ShorthandPropertyAssignment)
44
+ if (Node.isTypeReference(parent) || Node.isTypeQuery(parent))
80
45
  continue;
81
- // Skip imports and variable declarations
82
- if (parent.getKind() === SyntaxKind.ImportSpecifier)
46
+ if (Node.isShorthandPropertyAssignment(parent))
83
47
  continue;
84
- if (parent.getKind() === SyntaxKind.VariableDeclaration)
48
+ if (Node.isImportSpecifier(parent) || Node.isVariableDeclaration(parent))
85
49
  continue;
86
- // Likely a bug: ref used in expression context without .value
87
- if (parent.getKind() === SyntaxKind.BinaryExpression ||
88
- parent.getKind() === SyntaxKind.ConditionalExpression ||
89
- parent.getKind() === SyntaxKind.TemplateSpan ||
90
- parent.getKind() === SyntaxKind.ReturnStatement ||
91
- parent.getKind() === SyntaxKind.ElementAccessExpression) {
92
- findings.push(finding('missing-ref-value', 'warning', 'bug', `'${name}' is a ref — did you mean '${name}.value'?`, ctx.filePath, ident.getStartLineNumber(), { suggestion: `${name}.value` }));
93
- // One finding per ref variable to avoid noise
50
+ if (Node.isBinaryExpression(parent) ||
51
+ Node.isConditionalExpression(parent) ||
52
+ Node.isTemplateSpan(parent) ||
53
+ Node.isReturnStatement(parent) ||
54
+ Node.isElementAccessExpression(parent)) {
55
+ findings.push(finding('missing-ref-value', 'warning', 'bug', `'${name}' is a ref — did you mean '${name}.value'?`, ctx.filePath, ident.getStartLineNumber(), 1, { suggestion: `${name}.value` }));
94
56
  refVarNames.delete(name);
95
57
  }
96
58
  }
97
59
  return findings;
98
60
  }
99
61
  // ── Rule 18: missing-onUnmounted ─────────────────────────────────────────
100
- // watch/addEventListener without cleanup in onUnmounted
62
+ // watch/watchEffect/addEventListener without cleanup in onUnmounted
63
+ const WATCH_FUNCTIONS = new Set(['watch', 'watchEffect', 'watchSyncEffect', 'watchPostEffect']);
101
64
  function missingOnUnmounted(ctx) {
102
65
  const findings = [];
103
- // AST pre-check: find onUnmounted/onBeforeUnmount CallExpressions
104
66
  let hasLifecycleCleanup = false;
105
67
  for (const call of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {
106
- const callee = call.getExpression();
107
- if (callee.getKind() === SyntaxKind.Identifier) {
108
- const name = callee.getText();
109
- if (name === 'onUnmounted' || name === 'onBeforeUnmount') {
110
- hasLifecycleCleanup = true;
111
- break;
112
- }
68
+ const callee = call.getExpression().getText();
69
+ if (callee === 'onUnmounted' || callee === 'onBeforeUnmount') {
70
+ hasLifecycleCleanup = true;
71
+ break;
113
72
  }
114
73
  }
115
- // Check for watch() calls via AST
116
74
  for (const call of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {
117
- const callee = call.getExpression();
118
- if (callee.getKind() !== SyntaxKind.Identifier || callee.getText() !== 'watch')
75
+ const callee = call.getExpression().getText();
76
+ if (!WATCH_FUNCTIONS.has(callee))
119
77
  continue;
120
- // Check if parent is VariableDeclaration (stop handle assigned)
121
78
  const parent = call.getParent();
122
- const hasStopHandle = parent?.getKind() === SyntaxKind.VariableDeclaration;
79
+ const hasStopHandle = Node.isVariableDeclaration(parent);
123
80
  if (!hasStopHandle && !hasLifecycleCleanup) {
124
- findings.push(finding('missing-onUnmounted', 'error', 'bug', 'watch() without stop handle or onUnmounted cleanup — potential memory leak', ctx.filePath, call.getStartLineNumber(), { suggestion: 'Assign watch to a variable and call stop() in onUnmounted, or use watchEffect (auto-stops)' }));
81
+ findings.push(finding('missing-onUnmounted', 'error', 'bug', `${callee}() without stop handle or onUnmounted cleanup — potential memory leak`, ctx.filePath, call.getStartLineNumber(), 1, { suggestion: `Assign ${callee} to a variable and call stop() in onUnmounted` }));
125
82
  }
126
83
  }
127
- // Check for addEventListener via AST
128
84
  let hasRemoveListener = false;
129
85
  for (const call of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {
130
86
  const callee = call.getExpression();
131
- if (callee.getKind() === SyntaxKind.PropertyAccessExpression) {
132
- const pa = callee;
133
- if (pa.getName() === 'removeEventListener') {
134
- hasRemoveListener = true;
135
- break;
136
- }
87
+ if (Node.isPropertyAccessExpression(callee) && callee.getName() === 'removeEventListener') {
88
+ hasRemoveListener = true;
89
+ break;
137
90
  }
138
91
  }
139
92
  for (const call of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {
140
93
  const callee = call.getExpression();
141
- if (callee.getKind() !== SyntaxKind.PropertyAccessExpression)
142
- continue;
143
- const pa = callee;
144
- if (pa.getName() !== 'addEventListener')
94
+ if (!Node.isPropertyAccessExpression(callee) || callee.getName() !== 'addEventListener')
145
95
  continue;
146
96
  if (!hasRemoveListener && !hasLifecycleCleanup) {
147
- findings.push(finding('missing-onUnmounted', 'error', 'bug', 'addEventListener without removeEventListener in onUnmounted — memory leak', ctx.filePath, call.getStartLineNumber(), { suggestion: 'Clean up event listeners in onUnmounted()' }));
97
+ findings.push(finding('missing-onUnmounted', 'error', 'bug', 'addEventListener without removeEventListener in onUnmounted — memory leak', ctx.filePath, call.getStartLineNumber(), 1, { suggestion: 'Clean up event listeners in onUnmounted()' }));
148
98
  }
149
99
  }
150
100
  return findings;
151
101
  }
152
102
  // ── Rule 19: setup-side-effect ───────────────────────────────────────────
153
- // Async call in setup() without onMounted wrapper
103
+ // Top-level await in setup without onMounted wrapper (SSR safety)
154
104
  function setupSideEffect(ctx) {
155
105
  const findings = [];
156
106
  const fullText = ctx.sourceFile.getFullText();
157
- // Detect <script setup> or setup() function
158
- const isScriptSetup = fullText.includes('<script setup') || fullText.includes('defineComponent');
159
- if (!isScriptSetup)
107
+ const isVueFile = ctx.filePath.endsWith('.vue') || fullText.includes('defineComponent');
108
+ if (!isVueFile)
109
+ return findings;
110
+ const hasOnMounted = ctx.sourceFile
111
+ .getDescendantsOfKind(SyntaxKind.CallExpression)
112
+ .some((c) => Node.isIdentifier(c.getExpression()) && c.getExpression().getText() === 'onMounted');
113
+ if (hasOnMounted)
160
114
  return findings;
161
- // Check for top-level await without onMounted
162
- const hasOnMounted = fullText.includes('onMounted');
163
- const awaitRegex = /(?:^|\n)[ \t]{0,20}(?:(?:const|let|var) \w+ ?= ?)?await /g;
164
- let match;
165
- while ((match = awaitRegex.exec(fullText)) !== null) {
166
- const line = fullText.substring(0, match.index).split('\n').length;
167
- // Skip if inside a function body
168
- const lineText = fullText.split('\n')[line - 1] || '';
169
- if (lineText.trim().startsWith('//') || lineText.trim().startsWith('*'))
170
- continue;
171
- if (!hasOnMounted) {
172
- findings.push(finding('setup-side-effect', 'warning', 'pattern', 'Top-level await in setup — consider wrapping in onMounted() for SSR compatibility', ctx.filePath, line, { suggestion: 'onMounted(async () => { ... })' }));
115
+ for (const awaitExpr of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.AwaitExpression)) {
116
+ let isInsideFunction = false;
117
+ let cur = awaitExpr.getParent();
118
+ while (cur && cur !== ctx.sourceFile) {
119
+ if (Node.isArrowFunction(cur) || Node.isFunctionExpression(cur) || Node.isFunctionDeclaration(cur)) {
120
+ isInsideFunction = true;
121
+ break;
122
+ }
123
+ // MethodDeclaration with name 'setup' counts as top-level setup scope
124
+ if (Node.isMethodDeclaration(cur) && cur.getName() === 'setup')
125
+ break;
126
+ cur = cur.getParent();
173
127
  }
128
+ if (isInsideFunction)
129
+ continue;
130
+ findings.push(finding('setup-side-effect', 'warning', 'pattern', 'Top-level await in setup — consider wrapping in onMounted() for SSR compatibility', ctx.filePath, awaitExpr.getStartLineNumber(), 1, { suggestion: 'onMounted(async () => { ... })' }));
174
131
  }
175
132
  return findings;
176
133
  }
@@ -178,13 +135,109 @@ function setupSideEffect(ctx) {
178
135
  // Destructuring reactive() loses reactivity
179
136
  function reactiveDestructure(ctx) {
180
137
  const findings = [];
181
- const fullText = ctx.sourceFile.getFullText();
182
- // Find const { ... } = reactive(...)
183
- const destructRegex = /(?:const|let)\s*\{[^}]+\}\s*=\s*reactive\s*\(/g;
184
- let match;
185
- while ((match = destructRegex.exec(fullText)) !== null) {
186
- const line = fullText.substring(0, match.index).split('\n').length;
187
- findings.push(finding('reactive-destructure', 'warning', 'bug', 'Destructuring reactive() loses reactivity — use toRefs() or access properties directly', ctx.filePath, line, { suggestion: 'const state = reactive({...}); use state.prop, or const { prop } = toRefs(state)' }));
138
+ for (const decl of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.VariableDeclaration)) {
139
+ const nameNode = decl.getNameNode();
140
+ if (!Node.isObjectBindingPattern(nameNode))
141
+ continue;
142
+ const init = decl.getInitializer();
143
+ if (init && Node.isCallExpression(init)) {
144
+ const callee = init.getExpression().getText();
145
+ if (callee === 'reactive') {
146
+ findings.push(finding('reactive-destructure', 'warning', 'bug', 'Destructuring reactive() loses reactivity — use toRefs() or access properties directly', ctx.filePath, decl.getStartLineNumber(), 1, { suggestion: 'const state = reactive({...}); use state.prop, or const { prop } = toRefs(state)' }));
147
+ }
148
+ }
149
+ }
150
+ return findings;
151
+ }
152
+ // ── Rule: computed-side-effect ───────────────────────────────────────────
153
+ // Side effects inside computed() properties
154
+ function computedSideEffect(ctx) {
155
+ const findings = [];
156
+ for (const call of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {
157
+ if (call.getExpression().getText() !== 'computed')
158
+ continue;
159
+ const args = call.getArguments();
160
+ if (args.length === 0)
161
+ continue;
162
+ const getter = args[0];
163
+ if (!Node.isArrowFunction(getter) && !Node.isFunctionExpression(getter))
164
+ continue;
165
+ // Detect side-effect calls: fetch, axios.*, set* (state setters)
166
+ const sideEffectCalls = getter.getDescendantsOfKind(SyntaxKind.CallExpression).filter((c) => {
167
+ const name = c.getExpression().getText();
168
+ return name === 'fetch' || name.startsWith('axios.');
169
+ });
170
+ // Detect mutations: assignments (=, +=, -=, etc.) and ++/--
171
+ const mutations = getter.getDescendantsOfKind(SyntaxKind.BinaryExpression).filter((b) => {
172
+ const op = b.getOperatorToken().getKind();
173
+ return op === SyntaxKind.EqualsToken || op === SyntaxKind.PlusEqualsToken || op === SyntaxKind.MinusEqualsToken;
174
+ });
175
+ const prefixMutations = getter.getDescendantsOfKind(SyntaxKind.PrefixUnaryExpression).filter((p) => {
176
+ const op = p.getOperatorToken();
177
+ return op === SyntaxKind.PlusPlusToken || op === SyntaxKind.MinusMinusToken;
178
+ });
179
+ const postfixMutations = getter.getDescendantsOfKind(SyntaxKind.PostfixUnaryExpression).filter((p) => {
180
+ const op = p.getOperatorToken();
181
+ return op === SyntaxKind.PlusPlusToken || op === SyntaxKind.MinusMinusToken;
182
+ });
183
+ if (sideEffectCalls.length > 0 ||
184
+ mutations.length > 0 ||
185
+ prefixMutations.length > 0 ||
186
+ postfixMutations.length > 0) {
187
+ findings.push(finding('computed-side-effect', 'warning', 'bug', 'Side effect detected inside computed property — computed should be pure', ctx.filePath, call.getStartLineNumber(), 1, { suggestion: 'Move side effects to watch() or a method' }));
188
+ }
189
+ }
190
+ return findings;
191
+ }
192
+ // ── Rule: shallow-ref-mutation ───────────────────────────────────────────
193
+ // Deep mutation on shallowRef without triggerRef — change won't be reactive
194
+ // Source: vuejs.org/api/reactivity-advanced.html#shallowref
195
+ function shallowRefMutation(ctx) {
196
+ const findings = [];
197
+ // Collect shallowRef variable names
198
+ const shallowRefs = new Set();
199
+ for (const decl of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.VariableDeclaration)) {
200
+ const init = decl.getInitializer();
201
+ if (init && Node.isCallExpression(init) && init.getExpression().getText() === 'shallowRef') {
202
+ shallowRefs.add(decl.getName());
203
+ }
204
+ }
205
+ if (shallowRefs.size === 0)
206
+ return findings;
207
+ // Collect which shallowRefs have matching triggerRef(refName) calls
208
+ const triggeredRefs = new Set();
209
+ for (const call of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression)) {
210
+ if (call.getExpression().getText() !== 'triggerRef')
211
+ continue;
212
+ const args = call.getArguments();
213
+ if (args.length > 0 && Node.isIdentifier(args[0])) {
214
+ triggeredRefs.add(args[0].getText());
215
+ }
216
+ }
217
+ // Find deep property access: state.value.prop = ... (not state.value = ...)
218
+ for (const bin of ctx.sourceFile.getDescendantsOfKind(SyntaxKind.BinaryExpression)) {
219
+ const op = bin.getOperatorToken().getKind();
220
+ if (op !== SyntaxKind.EqualsToken && op !== SyntaxKind.PlusEqualsToken && op !== SyntaxKind.MinusEqualsToken)
221
+ continue;
222
+ const left = bin.getLeft();
223
+ if (!Node.isPropertyAccessExpression(left))
224
+ continue;
225
+ // Check for pattern: shallowRef.value.deepProp = ...
226
+ const obj = left.getExpression();
227
+ if (!Node.isPropertyAccessExpression(obj))
228
+ continue;
229
+ if (obj.getName() !== 'value')
230
+ continue;
231
+ const root = obj.getExpression();
232
+ if (!Node.isIdentifier(root) || !shallowRefs.has(root.getText()))
233
+ continue;
234
+ const refName = root.getText();
235
+ // Only suppress if triggerRef is called on THIS specific ref
236
+ if (!triggeredRefs.has(refName)) {
237
+ findings.push(finding('shallow-ref-mutation', 'warning', 'bug', `Deep mutation on shallowRef '${refName}' won't trigger reactivity — use triggerRef() or reassign .value`, ctx.filePath, bin.getStartLineNumber(), 1, {
238
+ suggestion: `Use ${refName}.value = { ...${refName}.value, ${left.getName()}: newVal } or call triggerRef(${refName})`,
239
+ }));
240
+ }
188
241
  }
189
242
  return findings;
190
243
  }
@@ -194,5 +247,7 @@ export const vueRules = [
194
247
  missingOnUnmounted,
195
248
  setupSideEffect,
196
249
  reactiveDestructure,
250
+ computedSideEffect,
251
+ shallowRefMutation,
197
252
  ];
198
253
  //# sourceMappingURL=vue.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"vue.js","sourceRoot":"","sources":["../../src/rules/vue.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"vue.js","sourceRoot":"","sources":["../../src/rules/vue.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Semantic Diff — detects behavior changes between old and new versions of code.
3
+ *
4
+ * Compares IR (InferResult[]) and concept maps (ConceptMap) between git versions
5
+ * to detect guard removals, error handling changes, new effects, param changes, etc.
6
+ *
7
+ * Used in `kern review --diff <base>` to augment line-level diffs with
8
+ * semantic understanding of WHAT changed.
9
+ */
10
+ import type { ConceptMap } from '@kernlang/core';
11
+ import type { InferResult } from './types.js';
12
+ export type SemanticChangeType = 'guard-removed' | 'guard-added' | 'error-handling-removed' | 'error-handling-added' | 'effect-added' | 'effect-removed' | 'return-type-changed' | 'new-code-path' | 'param-changed';
13
+ export interface SemanticChange {
14
+ type: SemanticChangeType;
15
+ severity: 'error' | 'warning' | 'info';
16
+ functionName: string;
17
+ filePath: string;
18
+ line: number;
19
+ description: string;
20
+ oldValue?: string;
21
+ newValue?: string;
22
+ }
23
+ /**
24
+ * Get the old version of a file from git.
25
+ * Returns null for new files (no old version exists).
26
+ */
27
+ export declare function getOldFileContent(filePath: string, baseRef: string): string | null;
28
+ /**
29
+ * Compute semantic differences between old and new versions of a file.
30
+ *
31
+ * Compares:
32
+ * - Functions (InferResult[]) for parameter and return type changes
33
+ * - Concepts (ConceptMap) for guard, error handling, and effect changes
34
+ * - New functions in the new version
35
+ */
36
+ export declare function computeSemanticDiff(oldInferred: InferResult[], newInferred: InferResult[], oldConcepts: ConceptMap | undefined, newConcepts: ConceptMap | undefined, filePath: string): SemanticChange[];
37
+ /**
38
+ * Compute semantic diff given old source string and new report data.
39
+ *
40
+ * This is the main entry point for the CLI — handles IR inference and
41
+ * concept extraction from both old and new source internally.
42
+ *
43
+ * @param oldSource - Source code from the base version
44
+ * @param newInferred - Inferred IR from the new version (from review pipeline)
45
+ * @param filePath - Relative or absolute file path
46
+ * @param newSource - New source code (used to extract concepts if provided)
47
+ */
48
+ export declare function computeSemanticDiffFromSource(oldSource: string, newInferred: InferResult[], filePath: string, newSource?: string): SemanticChange[];
49
+ /** Format semantic changes as a <kern-diff> section for LLM review. */
50
+ export declare function formatSemanticDiff(changes: SemanticChange[], filePath: string): string;
51
+ /** Convert semantic changes to ReviewFindings for inclusion in reports. */
52
+ export declare function semanticChangesToFindings(changes: SemanticChange[]): import('./types.js').ReviewFinding[];