201 lines
5.8 KiB
JavaScript
201 lines
5.8 KiB
JavaScript
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
|
import { copyFileSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
import path from 'node:path'
|
|
import { spawnSync } from 'node:child_process'
|
|
import { createRequire } from 'node:module'
|
|
import { rebuildBetterSqlite3ForElectron } from './rebuild-better-sqlite3-electron.mjs'
|
|
|
|
const require = createRequire(import.meta.url)
|
|
const root = process.cwd()
|
|
const cacheRoot = path.join(root, 'node_modules', '.native-cache', 'better-sqlite3')
|
|
const markerPath = path.join(cacheRoot, 'current.json')
|
|
const binaryRelativePath = path.join('build', 'Release', 'better_sqlite3.node')
|
|
|
|
const resolveFromRoot = (id) => require.resolve(id, { paths: [root] })
|
|
|
|
const readPackage = (id) => require(resolveFromRoot(`${id}/package.json`))
|
|
|
|
const getBetterSqliteDir = () => path.dirname(resolveFromRoot('better-sqlite3/package.json'))
|
|
|
|
const getActiveBinaryPath = () => path.join(getBetterSqliteDir(), binaryRelativePath)
|
|
|
|
const getElectronAbi = () => {
|
|
const electronVersion = readPackage('electron').version
|
|
const betterSqliteDir = getBetterSqliteDir()
|
|
const prebuildInstallPackagePath = require.resolve('prebuild-install/package.json', {
|
|
paths: [betterSqliteDir]
|
|
})
|
|
const prebuildInstallDir = path.dirname(prebuildInstallPackagePath)
|
|
const { getAbi } = require(require.resolve('node-abi', { paths: [prebuildInstallDir] }))
|
|
return {
|
|
electronVersion,
|
|
abi: getAbi(electronVersion, 'electron')
|
|
}
|
|
}
|
|
|
|
const targetInfo = (target) => {
|
|
const platform = process.platform
|
|
const arch = process.arch
|
|
if (target === 'node') {
|
|
return {
|
|
target,
|
|
runtime: 'node',
|
|
runtimeVersion: process.versions.node,
|
|
abi: process.versions.modules,
|
|
platform,
|
|
arch,
|
|
cacheDir: path.join(cacheRoot, `node-v${process.versions.modules}-${platform}-${arch}`)
|
|
}
|
|
}
|
|
if (target === 'electron') {
|
|
const { electronVersion, abi } = getElectronAbi()
|
|
return {
|
|
target,
|
|
runtime: 'electron',
|
|
runtimeVersion: electronVersion,
|
|
abi,
|
|
platform,
|
|
arch,
|
|
cacheDir: path.join(cacheRoot, `electron-v${abi}-${platform}-${arch}`)
|
|
}
|
|
}
|
|
throw new Error(`unknown native target: ${target}`)
|
|
}
|
|
|
|
const cacheBinaryPath = (info) => path.join(info.cacheDir, 'better_sqlite3.node')
|
|
|
|
const ensureBinaryExists = (binaryPath, label) => {
|
|
if (!existsSync(binaryPath)) {
|
|
throw new Error(`${label} does not exist: ${binaryPath}`)
|
|
}
|
|
}
|
|
|
|
const run = (command, args, options = {}) => {
|
|
const result = spawnSync(command, args, {
|
|
cwd: options.cwd || root,
|
|
stdio: 'inherit',
|
|
env: options.env || process.env,
|
|
shell: process.platform === 'win32'
|
|
})
|
|
if (result.status !== 0) {
|
|
process.exit(result.status || 1)
|
|
}
|
|
}
|
|
|
|
const writeMarker = (info) => {
|
|
mkdirSync(cacheRoot, { recursive: true })
|
|
writeFileSync(
|
|
markerPath,
|
|
`${JSON.stringify(
|
|
{
|
|
...info,
|
|
cacheDir: path.relative(root, info.cacheDir),
|
|
activeBinary: path.relative(root, getActiveBinaryPath()),
|
|
switchedAt: new Date().toISOString()
|
|
},
|
|
null,
|
|
2
|
|
)}\n`
|
|
)
|
|
}
|
|
|
|
const copyCacheToActive = (info) => {
|
|
const source = cacheBinaryPath(info)
|
|
const target = getActiveBinaryPath()
|
|
ensureBinaryExists(source, `${info.runtime} cached better-sqlite3 binary`)
|
|
mkdirSync(path.dirname(target), { recursive: true })
|
|
copyFileSync(source, target)
|
|
writeMarker(info)
|
|
console.log(
|
|
`[native] active better-sqlite3 => ${info.runtime} abi ${info.abi} (${info.platform}-${info.arch})`
|
|
)
|
|
}
|
|
|
|
const cacheActiveBinary = (info) => {
|
|
const active = getActiveBinaryPath()
|
|
ensureBinaryExists(active, 'active better-sqlite3 binary')
|
|
mkdirSync(info.cacheDir, { recursive: true })
|
|
copyFileSync(active, cacheBinaryPath(info))
|
|
}
|
|
|
|
const ensureNodeCache = (info) => {
|
|
if (existsSync(cacheBinaryPath(info))) return
|
|
console.log(`[native] preparing better-sqlite3 for node ${process.versions.node}`)
|
|
run(process.platform === 'win32' ? 'npm.cmd' : 'npm', ['run', 'install'], {
|
|
cwd: getBetterSqliteDir()
|
|
})
|
|
cacheActiveBinary(info)
|
|
}
|
|
|
|
const ensureElectronCache = async (info) => {
|
|
if (existsSync(cacheBinaryPath(info))) return
|
|
console.log(`[native] preparing better-sqlite3 for electron ${info.runtimeVersion}`)
|
|
await rebuildBetterSqlite3ForElectron({
|
|
electronVersion: info.runtimeVersion,
|
|
platform: info.platform,
|
|
arch: info.arch
|
|
})
|
|
cacheActiveBinary(info)
|
|
}
|
|
|
|
const switchTarget = async (target) => {
|
|
const info = targetInfo(target)
|
|
if (target === 'node') {
|
|
ensureNodeCache(info)
|
|
} else {
|
|
await ensureElectronCache(info)
|
|
}
|
|
copyCacheToActive(info)
|
|
}
|
|
|
|
const status = () => {
|
|
const active = getActiveBinaryPath()
|
|
console.log(`[native] active binary: ${path.relative(root, active)}`)
|
|
if (existsSync(markerPath)) {
|
|
console.log(readFileSync(markerPath, 'utf8').trim())
|
|
} else {
|
|
console.log('[native] no current marker')
|
|
}
|
|
for (const target of ['node', 'electron']) {
|
|
const info = targetInfo(target)
|
|
console.log(
|
|
`[native] cache ${target}: ${existsSync(cacheBinaryPath(info)) ? 'ready' : 'missing'} ${path.relative(
|
|
root,
|
|
cacheBinaryPath(info)
|
|
)}`
|
|
)
|
|
}
|
|
}
|
|
|
|
const clean = () => {
|
|
rmSync(cacheRoot, { recursive: true, force: true })
|
|
console.log('[native] cache removed')
|
|
}
|
|
|
|
const main = async () => {
|
|
const command = process.argv[2] || 'status'
|
|
if (command === 'node' || command === 'electron') {
|
|
await switchTarget(command)
|
|
return
|
|
}
|
|
if (command === 'prepare') {
|
|
await switchTarget('node')
|
|
await switchTarget('electron')
|
|
return
|
|
}
|
|
if (command === 'status') {
|
|
status()
|
|
return
|
|
}
|
|
if (command === 'clean') {
|
|
clean()
|
|
return
|
|
}
|
|
throw new Error(`unknown command: ${command}`)
|
|
}
|
|
|
|
main().catch((error) => {
|
|
console.error(error instanceof Error ? error.message : String(error))
|
|
process.exit(1)
|
|
})
|