| 176 | } |
| 177 | |
| 178 | function findFromPuppeteerCache(): BrowserResult | undefined { |
| 179 | if (!existsSync(PUPPETEER_CACHE_DIR)) return undefined; |
| 180 | let versions: string[]; |
| 181 | try { |
| 182 | // Numeric semver-style sort, newest first. Lexicographic `.sort().reverse()` |
| 183 | // (the previous implementation, still in engine `resolveHeadlessShellPath`) |
| 184 | // mis-orders `linux-99...` ahead of `linux-148...` because character `'9'` |
| 185 | // outranks `'1'`. See `parseVersionSegments` above. |
| 186 | versions = [...readdirSync(PUPPETEER_CACHE_DIR)].sort(compareVersionDirsDescending); |
| 187 | } catch { |
| 188 | return undefined; |
| 189 | } |
| 190 | for (const version of versions) { |
| 191 | // Same shape as `resolveHeadlessShellPath` in engine/browserManager.ts — |
| 192 | // keep them aligned. If puppeteer ever changes the on-disk layout the two |
| 193 | // need to move together. |
| 194 | const candidates = [ |
| 195 | join(PUPPETEER_CACHE_DIR, version, "chrome-headless-shell-linux64", "chrome-headless-shell"), |
| 196 | join( |
| 197 | PUPPETEER_CACHE_DIR, |
| 198 | version, |
| 199 | "chrome-headless-shell-mac-arm64", |
| 200 | "chrome-headless-shell", |
| 201 | ), |
| 202 | join(PUPPETEER_CACHE_DIR, version, "chrome-headless-shell-mac-x64", "chrome-headless-shell"), |
| 203 | join( |
| 204 | PUPPETEER_CACHE_DIR, |
| 205 | version, |
| 206 | "chrome-headless-shell-win64", |
| 207 | "chrome-headless-shell.exe", |
| 208 | ), |
| 209 | ]; |
| 210 | for (const binary of candidates) { |
| 211 | if (existsSync(binary)) { |
| 212 | return { executablePath: binary, source: "cache" }; |
| 213 | } |
| 214 | } |
| 215 | } |
| 216 | return undefined; |
| 217 | } |
| 218 | |
| 219 | /** |
| 220 | * True iff the binary at `executablePath` is `chrome-headless-shell` (i.e. the |