101 lines
3.8 KiB
TypeScript
101 lines
3.8 KiB
TypeScript
/**
|
|
* Locating the dev Electron binary for the e2e fixtures.
|
|
*
|
|
* Kept in its own module so the resolution rules can be unit-tested without
|
|
* importing the Playwright runner (fixtures.ts pulls in `_electron`, the mock
|
|
* server and the error-banner guard).
|
|
*
|
|
* Three rules the previous single-path probe got wrong:
|
|
*
|
|
* 1. The binary is not always under the REPO ROOT. This is an npm workspaces
|
|
* repo, and npm only hoists a dependency to the root when nothing conflicts
|
|
* — otherwise `electron` installs into `apps/desktop/node_modules`. Both
|
|
* layouts are normal, so both have to be searched, nearest package first.
|
|
* 2. The binary is `electron.exe` on Windows. A bare `electron` never exists
|
|
* there, so the probe could only ever miss.
|
|
* 3. `which` is not a command on Windows. The PATH fallback spawned it
|
|
* unconditionally, so on Windows the fallback failed for the wrong reason
|
|
* and the error message blamed a missing `npm install`.
|
|
*/
|
|
|
|
import { spawnSync } from 'node:child_process'
|
|
import * as fs from 'node:fs'
|
|
import { createRequire } from 'node:module'
|
|
import * as path from 'node:path'
|
|
|
|
/** The dist file name: `electron.exe` on Windows, `electron` elsewhere. */
|
|
export function electronBinaryName(platform: NodeJS.Platform = process.platform): string {
|
|
return platform === 'win32' ? 'electron.exe' : 'electron'
|
|
}
|
|
|
|
/**
|
|
* Where an npm install can leave the binary, in probe order: nearest package
|
|
* first, so a workspace-local install wins over a stale hoisted one.
|
|
*/
|
|
export function electronDistCandidates(roots: string[], platform: NodeJS.Platform = process.platform): string[] {
|
|
return roots.map((root) => path.join(root, 'node_modules', 'electron', 'dist', electronBinaryName(platform)))
|
|
}
|
|
|
|
/** The PATH-lookup command for this platform. Windows has `where`, not `which`. */
|
|
export function pathLookupCommand(platform: NodeJS.Platform = process.platform): string {
|
|
return platform === 'win32' ? 'where' : 'which'
|
|
}
|
|
|
|
/**
|
|
* Ask the installed `electron` package where its own binary is.
|
|
*
|
|
* Its main export IS the absolute executable path, resolved from `path.txt`
|
|
* and honouring `ELECTRON_OVERRIDE_DIST_PATH`, so this covers layouts and
|
|
* overrides a hand-built path cannot know about. Returns null when the package
|
|
* is not resolvable from `from`, or when it does not hand back a path (the
|
|
* export is the Electron API object, not a path, when required from inside
|
|
* Electron itself).
|
|
*/
|
|
export function electronPackagePath(from: string): null | string {
|
|
try {
|
|
const resolved = createRequire(path.join(from, 'package.json'))('electron') as unknown
|
|
|
|
return typeof resolved === 'string' && resolved ? resolved : null
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Resolve the Electron binary, or throw with the layouts that were searched.
|
|
*
|
|
* `roots` are searched in order; pass the desktop package before the repo root.
|
|
*/
|
|
export function resolveElectronBinary(roots: string[]): string {
|
|
for (const root of roots) {
|
|
const declared = electronPackagePath(root)
|
|
|
|
if (declared && fs.existsSync(declared)) {
|
|
return declared
|
|
}
|
|
}
|
|
|
|
for (const candidate of electronDistCandidates(roots)) {
|
|
if (fs.existsSync(candidate)) {
|
|
return candidate
|
|
}
|
|
}
|
|
|
|
// Nix devshells put `electron` on PATH with no node_modules copy at all.
|
|
const lookup = spawnSync(pathLookupCommand(), ['electron'], { encoding: 'utf8' })
|
|
|
|
if (lookup.status === 0 && lookup.stdout.trim()) {
|
|
// `where` reports every match, one per line; take the first.
|
|
const first = lookup.stdout.trim().split(/\r?\n/)[0].trim()
|
|
|
|
if (first) {
|
|
return first
|
|
}
|
|
}
|
|
|
|
throw new Error(
|
|
`Electron binary not found. Searched ${electronDistCandidates(roots).join(', ')} and PATH. ` +
|
|
'Run "npm install" from the repo root to install devDependencies.',
|
|
)
|
|
}
|