@@ -14,6 +14,15 @@ import {
1414} from "@puppeteer/browsers" ;
1515import { writeJSONToStdout } from "./stdin" ;
1616
17+ /**
18+ * Progress callback type for install operations
19+ */
20+ export type InstallProgressCallback = ( progress : {
21+ status : string ;
22+ message : string ;
23+ progress ?: number ;
24+ } ) => void ;
25+
1726/**
1827 * Gets the executable path for the installed Chrome browser.
1928 * @param cacheDir - The cache directory where browsers are stored
@@ -31,7 +40,6 @@ const getInstalledBrowserPath = (cacheDir: string): string | null => {
3140 const browserPlatform =
3241 platformMap [ currentPlatform ] || BrowserPlatform . LINUX ;
3342
34- // Try to get the latest installed browser
3543 const cache = new Cache ( cacheDir ) ;
3644 const installedBrowsers = cache . getInstalledBrowsers ( ) ;
3745 const chromeBrowser = installedBrowsers . find (
@@ -57,14 +65,9 @@ const getInstalledBrowserPath = (cacheDir: string): string | null => {
5765 * Sets up Puppeteer cache directory and ensures browser is installed.
5866 * This is necessary when running in a bundled binary where the default
5967 * cache path might not be accessible.
60- * Uses platform-specific default cache directories:
61- * - Windows: %LOCALAPPDATA%\puppeteer
62- * - macOS: ~/Library/Caches/puppeteer
63- * - Linux: ~/.cache/puppeteer
6468 * @returns The executable path of the installed browser, or null if not found
6569 */
6670export const setupPuppeteer = async ( ) : Promise < string | null > => {
67- // Use platform-specific default cache directory
6871 const cacheDir = path . join ( os . homedir ( ) , ".cache" , "puppeteer" ) ;
6972
7073 // Ensure the cache directory exists
@@ -138,3 +141,149 @@ export const setupPuppeteer = async (): Promise<string | null> => {
138141
139142 return executablePath ;
140143} ;
144+
145+ /**
146+ * Checks if Puppeteer browser is installed
147+ * @param cacheDir - The cache directory where browsers are stored
148+ * @returns Object with isInstalled boolean and executablePath if installed
149+ */
150+ export const checkPuppeteerInstalled = (
151+ cacheDir : string ,
152+ ) : { isInstalled : boolean ; executablePath : string | null } => {
153+ try {
154+ const executablePath = getInstalledBrowserPath ( cacheDir ) ;
155+ if ( executablePath && existsSync ( executablePath ) ) {
156+ return { isInstalled : true , executablePath } ;
157+ }
158+
159+ try {
160+ const puppeteerPath = puppeteer . executablePath ( ) ;
161+ if ( puppeteerPath && existsSync ( puppeteerPath ) ) {
162+ return { isInstalled : true , executablePath : puppeteerPath } ;
163+ }
164+ } catch ( /* eslint-disable-line */ error ) {
165+ // Browser is not installed
166+ }
167+
168+ return { isInstalled : false , executablePath : null } ;
169+ } catch ( /* eslint-disable-line */ error ) {
170+ return { isInstalled : false , executablePath : null } ;
171+ }
172+ } ;
173+
174+ /**
175+ * Installs Puppeteer browser with progress updates
176+ * @param cacheDir - The cache directory where browsers are stored
177+ * @param progressCallback - Callback function to report progress
178+ * @returns The executable path of the installed browser, or null if installation failed
179+ */
180+ export const installPuppeteer = async (
181+ cacheDir : string ,
182+ progressCallback ?: InstallProgressCallback ,
183+ ) : Promise < string | null > => {
184+ try {
185+ await mkdir ( cacheDir , { recursive : true } ) ;
186+
187+ const platformMap : Record < string , BrowserPlatform > = {
188+ win32 : BrowserPlatform . WIN32 ,
189+ darwin : BrowserPlatform . MAC ,
190+ linux : BrowserPlatform . LINUX ,
191+ } ;
192+
193+ const currentPlatform = process . platform ;
194+ const browserPlatform =
195+ platformMap [ currentPlatform ] || BrowserPlatform . LINUX ;
196+
197+ if ( progressCallback ) {
198+ progressCallback ( {
199+ status : "resolving" ,
200+ message : "Resolving requirements ..." ,
201+ } ) ;
202+ }
203+
204+ const buildId = await resolveBuildId (
205+ Browser . CHROME ,
206+ browserPlatform ,
207+ BrowserTag . LATEST ,
208+ ) ;
209+
210+ if ( progressCallback ) {
211+ progressCallback ( {
212+ status : "installing" ,
213+ message : `Installing requirements ...` ,
214+ progress : 0 ,
215+ } ) ;
216+ }
217+
218+ const installStartTime = Date . now ( ) ;
219+ let periodicUpdateInterval : NodeJS . Timeout | null = null ;
220+ let isInstalling = true ;
221+
222+ if ( progressCallback ) {
223+ periodicUpdateInterval = setInterval ( ( ) => {
224+ if ( ! isInstalling || ! progressCallback ) {
225+ return ;
226+ }
227+
228+ const elapsedSeconds = Math . floor (
229+ ( Date . now ( ) - installStartTime ) / 1000 ,
230+ ) ;
231+
232+ progressCallback ( {
233+ status : "installing" ,
234+ message : `Still installing ... (${ elapsedSeconds } s elapsed)` ,
235+ progress : undefined ,
236+ } ) ;
237+ } , 5000 ) ;
238+ }
239+
240+ try {
241+ await install ( {
242+ browser : Browser . CHROME ,
243+ buildId : buildId ,
244+ cacheDir : cacheDir ,
245+ } ) ;
246+
247+ isInstalling = false ;
248+ if ( periodicUpdateInterval ) {
249+ clearInterval ( periodicUpdateInterval ) ;
250+ periodicUpdateInterval = null ;
251+ }
252+ } catch ( installErr ) {
253+ isInstalling = false ;
254+ if ( periodicUpdateInterval ) {
255+ clearInterval ( periodicUpdateInterval ) ;
256+ periodicUpdateInterval = null ;
257+ }
258+ throw installErr ;
259+ }
260+
261+ if ( progressCallback ) {
262+ progressCallback ( {
263+ status : "completed" ,
264+ message : "Requirements installed successfully." ,
265+ progress : 100 ,
266+ } ) ;
267+ }
268+
269+ const executablePath = computeExecutablePath ( {
270+ cacheDir : cacheDir ,
271+ browser : Browser . CHROME ,
272+ platform : browserPlatform ,
273+ buildId : buildId ,
274+ } ) ;
275+
276+ return executablePath ;
277+ } catch ( installError ) {
278+ if ( progressCallback ) {
279+ progressCallback ( {
280+ status : "error" ,
281+ message :
282+ installError instanceof Error
283+ ? installError . message
284+ : String ( installError ) ,
285+ } ) ;
286+ }
287+ return null ;
288+ }
289+ } ;
0 commit comments