import fs from 'fs' ; import os from 'os' ; import path from 'path' ; import puppeteer from 'puppeteer-core' ; import { warn } from '../cli' ; import { CLIErrorCode , error , isError } from '../error' ; import { isDocker } from '../utils/docker' ; import { findChromeInstallation } from './chrome-finder' ; import { findEdgeInstallation } from './edge-finder' ; import { isWSL , resolveWindowsEnv } from './wsl' ; let executablePath = false ; let wslTmp ; const isShebang = ( path ) => { let fd = null ; try { fd = fs . openSync ( path , 'r' ) ; const shebangBuffer = Buffer . alloc ( 2 ) ; fs . readSync ( fd , shebangBuffer , 0 , 2 , 0 ) ; if ( shebangBuffer [ 0 ] === 0x23 && shebangBuffer [ 1 ] === 0x21 ) return true ; } catch ( e ) { } finally { if ( fd !== null ) fs . closeSync ( fd ) ; } return false ; } ; const isSnapBrowser = ( executablePath ) => { if ( process . platform === 'linux' && executablePath ) { if ( executablePath . startsWith ( '/snap/' ) ) return true ; if ( isShebang ( executablePath ) ) { const scriptContent = fs . readFileSync ( executablePath , 'utf8' ) ; if ( scriptContent . includes ( '/snap/' ) ) return true ; } } return false ; } ; export const generatePuppeteerDataDirPath = async ( name , { wslHost } = { } ) : Promise < string > => { const dataDir = await ( async ( ) => { if ( isWSL ( ) && wslHost ) { if ( wslTmp === undefined ) wslTmp = await resolveWindowsEnv ( 'TMP' ) ; if ( wslTmp !== undefined ) return path . win32 . resolve ( wslTmp , name ) ; } return path . resolve ( os . tmpdir ( ) , name ) ; } ) ( ) ; try { await fs . promises . mkdir ( dataDir , { recursive : true } ) ; } catch ( e ) { if ( isError ( e ) && e . code !== 'EEXIST' ) throw e ; } return dataDir ; } ; export const generatePuppeteerLaunchArgs = ( ) => { const args = new Set < string > ( [ '--export-tagged-pdf' ] ) ; if ( isDocker ( ) || isWSL ( ) ) args . add ( '--no-sandbox' ) ; if ( isDocker ( ) || process . env . CI ) args . add ( '--disable-features=VizDisplayCompositor' ) ; if ( ! process . env . CI ) args . add ( '--enable-blink-features=DocumentTransition' ) ; if ( executablePath === false ) { let findChromeError ; try { executablePath = findChromeInstallation ( ) ; } catch ( e ) { if ( isError ( e ) ) findChromeError = e ; } if ( ! executablePath ) { executablePath = findEdgeInstallation ( ) ; if ( ! executablePath ) { if ( findChromeError ) warn ( findChromeError . message ) ; error ( 'You have to install Google Chrome, Chromium, or Microsoft Edge to convert slide deck with current options.' , CLIErrorCode . NOT_FOUND_CHROMIUM ) ; } } } return { executablePath , args : [ ... args ] , pipe : ! ( isWSL ( ) || isSnapBrowser ( executablePath ) ) , ignoreDefaultArgs : process . env . CHROME_ENABLE_EXTENSIONS ? [ '--disable-extensions' ] : undefined , } ; } ; export const launchPuppeteer = async ( ... [ options ] ) => { try { return await puppeteer . launch ( options ) ; } catch ( e ) { if ( isError ( e ) ) { if ( options ?. pipe && e . message . includes ( 'Target.setDiscoverTargets' ) ) { return await puppeteer . launch ( { ... options , pipe : false } ) ; } if ( options ?. executablePath && isSnapBrowser ( options . executablePath ) && /^need to run as root or suid$/im . test ( e . message ) ) { error ( 'Marp CLI has detected trying to spawn Chromium browser installed by snap, from the confined environment like another snap app. At least either of Chrome/Chromium or the shell environment must be non snap app.' , CLIErrorCode . CANNOT_SPAWN_SNAP_CHROMIUM ) ; } } throw e ; } } ; export const resetExecutablePath = ( ) => { executablePath = false ; } ;