fix(cli): install better-sqlite3 without build tools on Node 22+
The runtime hook pinned better-sqlite3 12.6.2, whose prebuilds stop at Node ABI 141 — on Node 26 the install fell back to a node-gyp source build and failed on machines without build tools, silently degrading to the sql.js fallback. Node >= 22 now installs 13.0.3, which is N-API and ships per-platform prebuilds inside the package. Two things were needed to make that actually work: - npm injects an implicit `node-gyp rebuild` for any package shipping a binding.gyp, so the install still demanded build tools; `--ignore-scripts` skips it and uses the bundled prebuild as-is. - the binary check only looked at build/Release, which 13.x no longer creates, so every start re-ran npm install; it now also accepts prebuilds/<platform>-<arch>.node. Node < 22 stays on 12.6.2 (13.x requires Node >= 22), and an existing working install is left untouched either way.
This commit is contained in:
@@ -6,7 +6,13 @@ const fs = require("fs");
|
||||
const os = require("os");
|
||||
const path = require("path");
|
||||
|
||||
const BETTER_SQLITE3_VERSION = "12.6.2";
|
||||
// Gate the pinned version by Node major, mirroring src/lib/db/driver.js gating
|
||||
// style: 13.x is N-API and ships per-platform prebuilds inside the package, so
|
||||
// it needs no ABI-specific download. It requires Node >= 22; older runtimes stay
|
||||
// on 12.6.2, which fetches an ABI-specific binary via prebuild-install.
|
||||
const [NODE_MAJOR] = process.versions.node.split(".").map(Number);
|
||||
const USE_NAPI_BUILD = NODE_MAJOR >= 22;
|
||||
const BETTER_SQLITE3_VERSION = USE_NAPI_BUILD ? "13.0.3" : "12.6.2";
|
||||
const SQL_JS_VERSION = "1.14.1";
|
||||
|
||||
function getDataDir() {
|
||||
@@ -45,9 +51,23 @@ function hasModule(name) {
|
||||
return fs.existsSync(path.join(getRuntimeNodeModules(), name, "package.json"));
|
||||
}
|
||||
|
||||
function isGlibcRuntime() {
|
||||
try { return Boolean(process.report?.getReport()?.header?.glibcVersionRuntime); } catch { return true; }
|
||||
}
|
||||
|
||||
// 12.x compiles/downloads into build/Release; 13.x ships prebuilds/<platform>-<arch>.node.
|
||||
function getBetterSqliteBinary() {
|
||||
const root = path.join(getRuntimeNodeModules(), "better-sqlite3");
|
||||
const platform = process.platform === "linux" && !isGlibcRuntime() ? "linuxmusl" : process.platform;
|
||||
return [
|
||||
path.join(root, "build", "Release", "better_sqlite3.node"),
|
||||
path.join(root, "prebuilds", `${platform}-${process.arch}.node`),
|
||||
].find((file) => fs.existsSync(file));
|
||||
}
|
||||
|
||||
function isBetterSqliteBinaryValid() {
|
||||
const binary = path.join(getRuntimeNodeModules(), "better-sqlite3", "build", "Release", "better_sqlite3.node");
|
||||
if (!fs.existsSync(binary)) return false;
|
||||
const binary = getBetterSqliteBinary();
|
||||
if (!binary) return false;
|
||||
try {
|
||||
const fd = fs.openSync(binary, "r");
|
||||
const buf = Buffer.alloc(4);
|
||||
@@ -91,6 +111,7 @@ function runNpmInstall({ cwd, pkgs, extraArgs = [], timeout = 180000 }) {
|
||||
function npmInstall(pkgs, opts = {}) {
|
||||
const cwd = ensureRuntimeDir();
|
||||
const extra = opts.optional ? ["--no-save"] : [];
|
||||
if (opts.ignoreScripts) extra.push("--ignore-scripts");
|
||||
if (!opts.silent) console.log("⏳ Installing SQLite engine (first run)...");
|
||||
const res = runNpmInstall({ cwd, pkgs, extraArgs: extra, timeout: opts.timeout || 180000 });
|
||||
if (!res.ok && !opts.silent) {
|
||||
@@ -129,7 +150,10 @@ function ensureSqliteRuntime({ silent = false } = {}) {
|
||||
return { betterSqlite: true, sqlJs: sqlJsOk };
|
||||
}
|
||||
|
||||
const ok = npmInstall([`better-sqlite3@${BETTER_SQLITE3_VERSION}`], { optional: true, silent });
|
||||
// npm injects an implicit `node-gyp rebuild` for any package carrying a
|
||||
// binding.gyp, which would demand build tools even though 13.x already bundles
|
||||
// the binary — skip scripts so the bundled prebuild is used as-is.
|
||||
const ok = npmInstall([`better-sqlite3@${BETTER_SQLITE3_VERSION}`], { optional: true, silent, ignoreScripts: USE_NAPI_BUILD });
|
||||
return {
|
||||
betterSqlite: ok && hasModule("better-sqlite3") && isBetterSqliteBinaryValid(),
|
||||
sqlJs: sqlJsOk,
|
||||
|
||||
Reference in New Issue
Block a user