| 1818 | // with its file-order sequence so flushOrdered commits results in order. The |
| 1819 | // backpressure below bounds how far parsing runs ahead of the in-order commit. |
| 1820 | const feed = async (filePath: string, content: string, stats: fs.Stats): Promise<void> => { |
| 1821 | const seq = nextSeq++; |
| 1822 | const p = (async () => { |
| 1823 | try { |
| 1824 | const result = await parseFile(filePath, content); |
| 1825 | completed.set(seq, { ok: true, filePath, content, stats, result }); |
| 1826 | } catch (parseErr) { |
| 1827 | completed.set(seq, { ok: false, filePath, err: parseErr }); |
| 1828 | } |
| 1829 | flushOrdered(); |
| 1830 | })(); |
| 1831 | const tracked = p.finally(() => { inFlight.delete(tracked); }); |
| 1832 | inFlight.add(tracked); |
| 1833 | // Backpressure on the dispatched-but-not-yet-committed count (in-flight + |
| 1834 | // buffered), not just in-flight: a slow file sitting at the commit cursor |
| 1835 | // lets later parses finish and buffer, which would otherwise grow without |
| 1836 | // bound. Wait for parses to settle (each may advance the cursor) until the |
| 1837 | // window has room. When nothing is in flight but the window is still full, |
| 1838 | // the async commit chain is what's behind — await it so the cursor |
| 1839 | // advances (buffered items hold whole file contents, so this bound is |
| 1840 | // load-bearing for memory). |
| 1841 | while (nextSeq - nextToStore >= windowSize) { |
| 1842 | if (inFlight.size > 0) await Promise.race(inFlight); |
| 1843 | else await flushOrdered(); |
| 1844 | } |
| 1845 | }; |
| 1846 | |
| 1847 | const tParseLoop = Date.now(); |
| 1848 | for (let i = 0; i < files.length; i += FILE_IO_BATCH_SIZE) { |