| 210 | return lensSecrets(file, content); |
| 211 | case 'custom': |
| 212 | return lensCustom(file, content, config); |
| 213 | default: |
| 214 | return []; |
| 215 | } |
| 216 | } |
| 217 | |
| 218 | // ── Performance Lens ──────────────────────────────────────────────────────── |
| 219 | |
| 220 | function lensPerformance(file, content, builtInRules) { |
| 221 | const violations = []; |
| 222 | |
| 223 | if (builtInRules.includes('no-confirm-alert') && /\.(ts|tsx|js|jsx)$/.test(file)) { |
| 224 | if (/\bconfirm\s*\(/.test(content)) { |
| 225 | violations.push({ file, lens: 'performance', rule: 'no-confirm-alert', message: 'Uses confirm() — use an in-app modal' }); |
| 226 | } |
| 227 | if (/\balert\s*\((?!.*eslint)/.test(content)) { |
| 228 | violations.push({ file, lens: 'performance', rule: 'no-confirm-alert', message: 'Uses alert() — use an in-app notification' }); |
| 229 | } |
| 230 | } |
| 231 | |
| 232 | if (builtInRules.includes('no-transition-all')) { |
| 233 | if (/transition-all/.test(content)) { |
| 234 | violations.push({ file, lens: 'performance', rule: 'no-transition-all', message: 'Uses transition-all — name specific properties (e.g., transition-[opacity,transform])' }); |
| 235 | } |
| 236 | } |
| 237 | |
| 238 | if (builtInRules.includes('no-magic-intervals') && /\.(ts|tsx|js|jsx)$/.test(file)) { |
| 239 | if (/setInterval\s*\([^,]+,\s*\d+\s*\)/.test(content)) { |
| 240 | violations.push({ file, lens: 'performance', rule: 'no-magic-intervals', message: 'Hardcoded setInterval — use a named constant for the interval' }); |