Skip to content

Commit

Permalink
Make the converter using new manager compatible with the behavior of v3
Browse files Browse the repository at this point in the history
  • Loading branch information
yhatt committed Sep 25, 2024
1 parent b3bf0b0 commit d2c6441
Show file tree
Hide file tree
Showing 12 changed files with 118 additions and 48 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
"@tsconfig/node20": "^20.1.4",
"@tsconfig/recommended": "^1.0.7",
"@types/cheerio": "^0.22.35",
"@types/debug": "^4.1.12",
"@types/dom-view-transitions": "^1.0.5",
"@types/express": "^4.17.21",
"@types/jest": "^29.5.13",
Expand Down
26 changes: 14 additions & 12 deletions src/browser/finder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ export const findBrowser = async (
finders: readonly FinderName[] = defaultFinders,
opts: BrowserFinderOptions = {}
) => {
let found = false

const debug = (...args: Parameters<typeof debugBrowserFinder>) => {
if (!found) return debugBrowserFinder(...args)
}

const finderCount = finders.length
const normalizedOpts = {
preferredPath: await (async () => {
Expand All @@ -41,13 +47,10 @@ export const findBrowser = async (
}

if (finderCount === 0) {
debugBrowserFinder('No browser finder specified.')
debug('No browser finder specified.')

if (normalizedOpts.preferredPath) {
debugBrowserFinder(
'Use preferred path as Chrome: %s',
normalizedOpts.preferredPath
)
debug('Use preferred path as Chrome: %s', normalizedOpts.preferredPath)

return await chrome(normalizedOpts)
}
Expand All @@ -58,10 +61,7 @@ export const findBrowser = async (
)
}

debugBrowserFinder(
`Start finding browser from ${finders.join(', ')} (%o)`,
normalizedOpts
)
debug(`Start finding browser from ${finders.join(', ')} (%o)`, normalizedOpts)

return new Promise<BrowserFinderResult>((res, rej) => {
const results = Array<BrowserFinderResult>(finderCount)
Expand All @@ -72,12 +72,12 @@ export const findBrowser = async (

finder(normalizedOpts)
.then((ret) => {
debugBrowserFinder(`Found ${finderName}: %o`, ret)
debug(`Found ${finderName}: %o`, ret)
results[index] = ret
resolved[index] = true
})
.catch((e) => {
debugBrowserFinder(`Finder ${finderName} was failed: %o`, e)
debug(`Finder ${finderName} was failed: %o`, e)
resolved[index] = false
})
.finally(() => {
Expand All @@ -100,7 +100,9 @@ export const findBrowser = async (
})
})
}).then((result) => {
debugBrowserFinder('Use browser: %o', result)
debug('Use browser: %o', result)
found = true

return result
})
}
13 changes: 10 additions & 3 deletions src/browser/finders/chrome.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ import { error, CLIErrorCode } from '../../error'
import { ChromeBrowser } from '../browsers/chrome'
import { ChromeCdpBrowser } from '../browsers/chrome-cdp'
import type { BrowserFinder, BrowserFinderResult } from '../finder'
import { findExecutableBinary, getPlatform, isExecutable } from './utils'
import {
findExecutableBinary,
getPlatform,
isExecutable,
normalizeDarwinAppPath,
} from './utils'

const chrome = (path: string): BrowserFinderResult => ({
path,
Expand All @@ -18,8 +23,10 @@ const chrome = (path: string): BrowserFinderResult => ({
export const chromeFinder: BrowserFinder = async ({ preferredPath } = {}) => {
if (preferredPath) return chrome(preferredPath)

if (process.env.CHROME_PATH && (await isExecutable(process.env.CHROME_PATH)))
return chrome(process.env.CHROME_PATH)
if (process.env.CHROME_PATH) {
const path = await normalizeDarwinAppPath(process.env.CHROME_PATH)
if (path && (await isExecutable(path))) return chrome(path)
}

const platform = await getPlatform()
const installation = await (async () => {
Expand Down
10 changes: 5 additions & 5 deletions src/browser/finders/firefox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
findExecutable,
findExecutableBinary,
isExecutable,
normalizeDarwinAppPath,
} from './utils'

const firefox = (path: string): BrowserFinderResult => ({
Expand All @@ -22,11 +23,10 @@ const winFirefoxDefault = ['Mozilla Firefox', 'firefox.exe'] // Firefox stable,
export const firefoxFinder: BrowserFinder = async ({ preferredPath } = {}) => {
if (preferredPath) return firefox(preferredPath)

if (
process.env.FIREFOX_PATH &&
(await isExecutable(process.env.FIREFOX_PATH))
)
return firefox(process.env.FIREFOX_PATH)
if (process.env.FIREFOX_PATH) {
const nPath = await normalizeDarwinAppPath(process.env.FIREFOX_PATH)
if (nPath && (await isExecutable(nPath))) return firefox(nPath)
}

const platform = await getPlatform()
const installation = await (async () => {
Expand Down
26 changes: 20 additions & 6 deletions src/browser/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ export class BrowserManager implements AsyncDisposable {
this._preferredProtocol = config.protocol
}
if (config.timeout !== undefined) this._timeout = config.timeout

debugBrowser('Browser manager configured: %o', config)
}

async findBrowser() {
Expand All @@ -65,10 +67,23 @@ export class BrowserManager implements AsyncDisposable {
async browserForConversion(): Promise<Browser> {
if (!this._conversionBrowser) {
const { acceptedBrowsers, path } = await this.findBrowser()
const browser = acceptedBrowsers.find(
(browser) => browser.protocol === this._preferredProtocol
)

const browser =
acceptedBrowsers.find(
({ protocol }) => protocol === this._preferredProtocol
) ||
(() => {
if (acceptedBrowsers.length > 0) {
debugBrowser(
'The available browsers do not support the preferred protocol "%s". Using the first available browser.',
this._preferredProtocol
)
}
return acceptedBrowsers[0]
})()

if (!browser) error('No browser found for conversion')
debugBrowser('Use browser class for conversion: %o', browser)

// @ts-expect-error ts2511: TS cannot create an instance of an abstract class
this._conversionBrowser = new browser({ path, timeout: this._timeout })
Expand All @@ -80,9 +95,11 @@ export class BrowserManager implements AsyncDisposable {
async browserForPreview(): Promise<ChromeCdpBrowser> {
if (!this._previewBrowser) {
const { acceptedBrowsers, path } = await this.findBrowser()

if (!acceptedBrowsers.some((browser) => browser === ChromeCdpBrowser)) {
error('No browser found for preview')
}
debugBrowser('Use browser class for preview: %o', ChromeCdpBrowser)

this._previewBrowser = new ChromeCdpBrowser({
path,
Expand All @@ -103,6 +120,3 @@ export class BrowserManager implements AsyncDisposable {
await this.dispose()
}
}

export const browserManager = new BrowserManager()
export default browserManager
22 changes: 13 additions & 9 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import path from 'node:path'
import chalk from 'chalk'
import { cosmiconfig, cosmiconfigSync } from 'cosmiconfig'
import { osLocale } from 'os-locale'
import { BrowserManager } from './browser/manager'
import { info, warn, error as cliError } from './cli'
import { ConverterOption, ConvertType } from './converter'
import { ResolvableEngine, ResolvedEngine } from './engine'
Expand Down Expand Up @@ -254,22 +255,25 @@ export class MarpCLIConfig {
return scale
})()

const puppeteerTimeout = (() => {
if (process.env['PUPPETEER_TIMEOUT']) {
const envTimeout = Number.parseInt(process.env['PUPPETEER_TIMEOUT'], 10)
if (!Number.isNaN(envTimeout)) return envTimeout
}
return undefined
})()
const browserManager = new BrowserManager({
protocol: 'cdp',
timeout: (() => {
if (process.env.PUPPETEER_TIMEOUT) {
const envTimeout = Number.parseInt(process.env.PUPPETEER_TIMEOUT, 10)
if (!Number.isNaN(envTimeout)) return envTimeout
}
return undefined
})(),
})

return {
browserManager,
imageScale,
inputDir,
output,
pdfNotes,
pdfOutlines,
preview,
puppeteerTimeout,
server,
template,
templateOption,
Expand Down Expand Up @@ -411,4 +415,4 @@ export class MarpCLIConfig {
}
}

export default MarpCLIConfig.fromArguments
export const { fromArguments } = MarpCLIConfig
13 changes: 9 additions & 4 deletions src/converter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type {
Viewport,
} from 'puppeteer-core'
import type { Browser } from './browser/browser'
import { browserManager } from './browser/manager'
import type { BrowserManager } from './browser/manager'
import { silence, warn } from './cli'
import { Engine, ResolvedEngine } from './engine'
import { generateOverrideGlobalDirectivesPlugin } from './engine/directive-plugin'
Expand Down Expand Up @@ -64,6 +64,7 @@ export const mimeTypes = {
export interface ConverterOption {
allowLocalFiles: boolean
baseUrl?: string
browserManager: BrowserManager
engine: Engine
globalDirectives: { theme?: string } & Partial<TemplateMeta>
html?: MarpOptions['html']
Expand Down Expand Up @@ -130,6 +131,10 @@ export class Converter {
this.options = opts
}

get browser(): Promise<Browser> {
return this.options.browserManager.browserForConversion()
}

get template(): Template {
const template = templates[this.options.template]
if (!template) error(`Template "${this.options.template}" is not found.`)
Expand All @@ -153,7 +158,7 @@ export class Converter {
if (this.options.baseUrl) return this.options.baseUrl

if (isFile(f) && type !== ConvertType.html) {
const browser = await browserManager.browserForConversion()
const browser = await this.browser

return (await browser.browserInWSLHost())
? `file:${await resolveWSLPathToHost(f.absolutePath)}`
Expand Down Expand Up @@ -304,7 +309,7 @@ export class Converter {
const ret = file.convert(this.options.output, { extension: 'pdf' })

// Generate PDF
const browser = await browserManager.browserForConversion()
const browser = await this.browser

if (browser.kind === 'firefox' && !this._firefoxPDFConversionWarning) {
this._firefoxPDFConversionWarning = true
Expand Down Expand Up @@ -634,7 +639,7 @@ export class Converter {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
using _tmpFile = tmpFile ?? { [Symbol.dispose]: () => void 0 }

const browser = await browserManager.browserForConversion()
const browser = await this.browser

if (tmpFile) {
if (await browser.browserInWSLHost()) {
Expand Down
26 changes: 22 additions & 4 deletions src/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import importFrom from 'import-from'
import { resolve as importMetaResolve } from 'import-meta-resolve'
import { pkgUp } from 'pkg-up'
import { error, isError } from './error'
import { debugEngine } from './utils/debug'

type FunctionalEngine<T extends typeof Marpit = typeof Marpit> = (
constructorOptions: ConstructorParameters<T>[0] & { readonly marp: Marp }
Expand Down Expand Up @@ -137,21 +138,25 @@ export class ResolvedEngine<T extends Engine = Engine> {
moduleId: string,
from?: string
): Promise<T | null> {
let normalizedModuleId = moduleId

const basePath = path.join(from || process.cwd(), '_.js')
const dirPath = path.dirname(basePath)
const moduleFilePath = path.resolve(dirPath, moduleId)
const moduleFilePath = path.resolve(dirPath, normalizedModuleId)

try {
const stat = await fs.promises.stat(moduleFilePath)

if (stat.isFile()) moduleId = url.pathToFileURL(moduleFilePath).toString()
if (stat.isFile()) {
normalizedModuleId = url.pathToFileURL(moduleFilePath).toString()
}
} catch {
// No ops
}

try {
const resolved = importMetaResolve(
moduleId,
normalizedModuleId,
url.pathToFileURL(basePath).toString()
)

Expand All @@ -169,7 +174,14 @@ export class ResolvedEngine<T extends Engine = Engine> {

return await import(resolved)
/* c8 ignore stop */
} catch {
} catch (e) {
debugEngine(
'Failed to import %s. (Normalized module id: %s)',
moduleId + (from ? ` from ${from}` : ''),
normalizedModuleId
)
debugEngine('%O', e)

return null
}
}
Expand All @@ -187,6 +199,12 @@ export class ResolvedEngine<T extends Engine = Engine> {

/* c8 ignore start */
} catch (e) {
debugEngine(
'Failed to require %s.',
moduleId + (from ? ` from ${from}` : '')
)
debugEngine('%O', e)

if (isError(e) && e.code === 'ERR_REQUIRE_ESM') {
// Show reason why `require()` failed in the current context
if ('pkg' in process) {
Expand Down
7 changes: 6 additions & 1 deletion src/error.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { debug } from './utils/debug'

export class CLIError extends Error {
readonly errorCode: number
readonly message: string
Expand Down Expand Up @@ -28,7 +30,10 @@ export function error(
msg: string,
errorCode: number = CLIErrorCode.GENERAL_ERROR
): never {
throw new CLIError(msg, errorCode)
const cliError = new CLIError(msg, errorCode)
debug('%O', cliError)

throw cliError
}

export const isError = (e: unknown): e is NodeJS.ErrnoException =>
Expand Down
Loading

0 comments on commit d2c6441

Please sign in to comment.