* Check a single file for forbidden patterns * @param {string} filePath * @returns {Violation[]}
(filePath)
| 138 | * @returns {Violation[]} |
| 139 | */ |
| 140 | function checkFile(filePath) { |
| 141 | /** @type {Violation[]} */ |
| 142 | const violations = []; |
| 143 | |
| 144 | let content; |
| 145 | try { |
| 146 | content = readFileSync(filePath, "utf-8"); |
| 147 | } catch { |
| 148 | return violations; |
| 149 | } |
| 150 | |
| 151 | const lines = content.split("\n"); |
| 152 | let inBlockComment = false; |
| 153 | |
| 154 | for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) { |
| 155 | const line = lines[lineIdx]; |
| 156 | if (line === undefined) continue; |
| 157 | |
| 158 | // Strip comments from line before checking |
| 159 | const result = stripComments(line, inBlockComment); |
| 160 | const codeLine = result.cleanedLine; |
| 161 | inBlockComment = result.inBlockComment; |
| 162 | |
| 163 | // Skip if line is empty after stripping comments |
| 164 | if (codeLine.trim() === "") continue; |
| 165 | |
| 166 | for (const { pattern, name } of FORBIDDEN_PATTERNS) { |
| 167 | // Reset the regex lastIndex for global patterns |
| 168 | pattern.lastIndex = 0; |
| 169 | |
| 170 | for (;;) { |
| 171 | const match = pattern.exec(codeLine); |
| 172 | if (match === null) break; |
| 173 | violations.push({ |
| 174 | file: filePath, |
| 175 | line: lineIdx + 1, |
| 176 | col: match.index + 1, |
| 177 | pattern: name, |
| 178 | match: match[0], |
| 179 | }); |
| 180 | } |
| 181 | } |
| 182 | } |
| 183 | |
| 184 | return violations; |
| 185 | } |
| 186 | |
| 187 | /** |
| 188 | * Main entry point |
no test coverage detected