* The static directory prefix of each `include` pattern — the literal leading * path up to the first glob segment — trailing-slashed, used to (a) walk only * the opted-in subtrees in `collectIncludedFiles` and (b) let `ScopeIgnore` keep * the watcher descending toward them. `Tools/` stays `Tools/
(patterns: string[])
| 366 | * nested under a broader root are collapsed so each subtree is walked once. |
| 367 | */ |
| 368 | function includeStaticRoots(patterns: string[]): string[] { |
| 369 | const roots = new Set<string>(); |
| 370 | for (const pattern of patterns) { |
| 371 | let p = pattern.replace(/^\/+/, ''); |
| 372 | const trailingSlash = p.endsWith('/'); |
| 373 | if (trailingSlash) p = p.slice(0, -1); |
| 374 | const segs = p.split('/').filter(Boolean); |
| 375 | const lead: string[] = []; |
| 376 | for (const s of segs) { |
| 377 | if (GLOB_META.test(s)) break; |
| 378 | lead.push(s); |
| 379 | } |
| 380 | const hadWildcard = lead.length < segs.length; |
| 381 | // A wholly-literal pattern with no trailing slash names a file (or a dir we |
| 382 | // can't tell apart) — drop its last segment so we walk the containing dir |
| 383 | // and let the matcher pick the file. A trailing slash or a glob means the |
| 384 | // remaining `lead` is already the directory to walk. |
| 385 | if (!hadWildcard && !trailingSlash && lead.length > 0) lead.pop(); |
| 386 | if (lead.length === 0) { |
| 387 | roots.clear(); |
| 388 | roots.add(''); |
| 389 | return ['']; // a top-level glob forces a whole-tree walk; nothing narrower matters |
| 390 | } |
| 391 | roots.add(lead.join('/') + '/'); |
| 392 | } |
| 393 | // Collapse roots nested under a broader one (e.g. drop `a/b/` if `a/` is present). |
| 394 | const all = [...roots]; |
| 395 | return all.filter((r) => !all.some((other) => other !== r && r.startsWith(other))); |
| 396 | } |
| 397 | |
| 398 | /** |
| 399 | * Actively discover the source files an `include` whitelist forces in. `git |
no test coverage detected