* Read a `.gitignore` and return patterns safe to hand to the `ignore` matcher — * never throwing, even when the file isn't real gitignore text. Two failure * modes, both seen in the wild (issue #682): * * - The file isn't valid UTF-8 — e.g. transparently encrypted in place by * corporate D
(giPath: string)
| 227 | * Returns '' when there's nothing usable. |
| 228 | */ |
| 229 | function readGitignorePatterns(giPath: string): string { |
| 230 | let buf: Buffer; |
| 231 | try { |
| 232 | buf = fs.readFileSync(giPath); |
| 233 | } catch { |
| 234 | return ''; // unreadable (permissions / race) — treat as absent |
| 235 | } |
| 236 | // A NUL byte never appears in real gitignore text, and a fatal UTF-8 decode |
| 237 | // catches the rest. Such a file isn't ignore patterns at all. |
| 238 | if (buf.includes(0) || !isValidUtf8(buf)) { |
| 239 | logWarn( |
| 240 | 'Ignoring a .gitignore that is not valid UTF-8 text — it may have been encrypted ' + |
| 241 | 'in place by endpoint-security software. Indexing continues without it.', |
| 242 | { file: giPath }, |
| 243 | ); |
| 244 | return ''; |
| 245 | } |
| 246 | const content = buf.toString('utf-8'); |
| 247 | // Fast path: one `.ignores()` call forces the library to compile EVERY rule, |
| 248 | // so if it doesn't throw, the whole file is safe to use verbatim. |
| 249 | try { |
| 250 | ignore().add(content).ignores('.codegraph-probe'); |
| 251 | return content; |
| 252 | } catch { |
| 253 | // Fall through: a line is uncompilable — keep the good ones, drop the bad. |
| 254 | } |
| 255 | const kept: string[] = []; |
| 256 | let dropped = 0; |
| 257 | for (const line of content.split(/\r?\n/)) { |
| 258 | try { |
| 259 | ignore().add(line).ignores('.codegraph-probe'); |
| 260 | kept.push(line); |
| 261 | } catch { |
| 262 | dropped++; |
| 263 | } |
| 264 | } |
| 265 | if (dropped > 0) { |
| 266 | logWarn( |
| 267 | `Skipped ${dropped} unparseable pattern(s) in a .gitignore; the rest are applied.`, |
| 268 | { file: giPath }, |
| 269 | ); |
| 270 | } |
| 271 | return kept.join('\n'); |
| 272 | } |
| 273 | |
| 274 | /** |
| 275 | * An `ignore` matcher seeded with the built-in defaults, merged with the project's |
no test coverage detected