* 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)
| 242 | * Returns '' when there's nothing usable. |
| 243 | */ |
| 244 | function readGitignorePatterns(giPath: string): string { |
| 245 | let buf: Buffer; |
| 246 | try { |
| 247 | buf = fs.readFileSync(giPath); |
| 248 | } catch { |
| 249 | return ''; // unreadable (permissions / race) — treat as absent |
| 250 | } |
| 251 | // A NUL byte never appears in real gitignore text, and a fatal UTF-8 decode |
| 252 | // catches the rest. Such a file isn't ignore patterns at all. |
| 253 | if (buf.includes(0) || !isValidUtf8(buf)) { |
| 254 | logWarn( |
| 255 | 'Ignoring a .gitignore that is not valid UTF-8 text — it may have been encrypted ' + |
| 256 | 'in place by endpoint-security software. Indexing continues without it.', |
| 257 | { file: giPath }, |
| 258 | ); |
| 259 | return ''; |
| 260 | } |
| 261 | const content = buf.toString('utf-8'); |
| 262 | // Fast path: one `.ignores()` call forces the library to compile EVERY rule, |
| 263 | // so if it doesn't throw, the whole file is safe to use verbatim. |
| 264 | try { |
| 265 | ignore().add(content).ignores('.codegraph-probe'); |
| 266 | return content; |
| 267 | } catch { |
| 268 | // Fall through: a line is uncompilable — keep the good ones, drop the bad. |
| 269 | } |
| 270 | const kept: string[] = []; |
| 271 | let dropped = 0; |
| 272 | for (const line of content.split(/\r?\n/)) { |
| 273 | try { |
| 274 | ignore().add(line).ignores('.codegraph-probe'); |
| 275 | kept.push(line); |
| 276 | } catch { |
| 277 | dropped++; |
| 278 | } |
| 279 | } |
| 280 | if (dropped > 0) { |
| 281 | logWarn( |
| 282 | `Skipped ${dropped} unparseable pattern(s) in a .gitignore; the rest are applied.`, |
| 283 | { file: giPath }, |
| 284 | ); |
| 285 | } |
| 286 | return kept.join('\n'); |
| 287 | } |
| 288 | |
| 289 | /** |
| 290 | * An `ignore` matcher seeded with the built-in defaults, merged with the project's |
no test coverage detected