Files
手瓜一十雪 2c565fbd8d Switch from @snowluma/sqlite to node:sqlite (sync)
Remove the in-tree @snowluma/sqlite wrapper and vendored better-sqlite3 prebuilts; migrate consumers to node:sqlite's synchronous API. Replaced imports and types (Database/Statement -> DatabaseSync/StatementSync), updated PRAGMA calls to use exec, and adjusted code in onebot (media/message/reaction stores) and protocol (identity service). Vite config no longer bundles sqlite native binaries and references to the sqlite workspace were removed. Also delete the packages/sqlite package and its native prebuild artifacts, and add a couple of unrelated AST improvements in proton: use a module-level shared wrapper cache and add an inheritance cache to avoid repeated scans. Minor import reordering/cleanup applied where needed.
2026-05-26 21:59:26 +08:00

174 lines
6.5 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import protobufVitePlugin from '@snowluma/proton/vite';
import fs from 'fs';
import { builtinModules } from 'module';
import path from 'path';
import { fileURLToPath } from 'url';
import { defineConfig, PluginOption, UserConfig } from 'vite';
import cp from 'vite-plugin-cp';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const repoRoot = path.resolve(__dirname, '..', '..');
const distDir = path.resolve(repoRoot, 'dist');
const runtimeDir = path.resolve(repoRoot, 'packages', 'runtime');
const nativeDir = path.resolve(runtimeDir, 'native');
// Single source of truth for the user-facing app version: monorepo root package.json.
const rootPkg = JSON.parse(
fs.readFileSync(path.resolve(repoRoot, 'package.json'), 'utf-8'),
) as { version: string };
// vite-plugin-cp consumes globs through globby; on Windows we must use POSIX-style separators.
const toPosix = (p: string) => p.replace(/\\/g, '/');
// `@snowluma/websocket` is bundled — it's an
// in-tree TS workspace wrapper that routes its native `.node`
// addon through `dist/native/`. Only Node builtins stay external.
const external: string[] = [];
const nodeModules = [...builtinModules, ...builtinModules.map((m) => `node:${m}`)].flat();
const runtimeSrc = toPosix(runtimeDir);
const nativeSrc = toPosix(nativeDir);
// Target selection: `SNOWLUMA_TARGET=<platform>-<arch>` overrides the host
// detection, enabling cross-target packaging on CI.
const targetTriple = process.env.SNOWLUMA_TARGET ?? `${process.platform}-${process.arch}`;
const [targetPlatform, targetArch] = targetTriple.split('-');
// Runtime scaffolding files copied into dist/. The NTQQ hook is Windows-only,
// so its launcher/shell script differ per target.
const runtimeDistFiles = ['package.json',
targetPlatform === 'win32' ? 'launcher.bat' : 'launcher.sh',
];
// Native binaries shipped for the selected target:
// * `snowluma-*.{dll,node,so}` – NTQQ hook (Windows + Linux).
// * `websocket-*.node` – RFC 6455 framing/mask addon (all platforms).
const nativeFiles = [
`websocket-${targetTriple}.node`,
...(targetPlatform === 'win32'
? [`snowluma-${targetTriple}.dll`, `snowluma-${targetTriple}.node`]
: []),
...(targetPlatform === 'linux'
? [`snowluma-${targetTriple}.node`, `snowluma-${targetTriple}.so`]
: []),
];
// FFmpeg native addon ported from NapCat. Uses NapCat's
// `<platform>.<arch>` naming so the prebuilt binaries can be vendored
// as-is without renaming. Lives under `native/ffmpeg/` to keep it
// separate from the SnowLuma flat-naming addons above.
const ffmpegAddonFile = `ffmpegAddon.${targetPlatform}.${targetArch}.node`;
const ffmpegSrcDir = path.resolve(nativeDir, 'ffmpeg');
// Fail fast if any expected native binary is missing from packages/runtime/native/.
// vite-plugin-cp only emits a warning on missing source files; we want a hard
// error so CI can't accidentally ship an incomplete archive.
const missingNatives = nativeFiles.filter(
(f) => !fs.existsSync(path.join(nativeDir, f)),
);
if (!fs.existsSync(path.join(ffmpegSrcDir, ffmpegAddonFile))) {
missingNatives.push(`ffmpeg/${ffmpegAddonFile}`);
}
if (missingNatives.length > 0) {
throw new Error(
`Missing native binaries for target ${targetTriple}:\n` +
missingNatives.map((f) => ` - ${path.join(nativeDir, f)}`).join('\n'),
);
}
const BaseConfigPlugin: PluginOption[] = [
// Proton substitutes every `protobuf_encode<T>` / `protobuf_decode<T>`
// call site with a monomorphized codec at build time. WITHOUT this
// plugin in the plugins array the runtime fallback in
// `@snowluma/proton/runtime.ts` throws on the first call (and the
// packaged Win/Linux release does exactly that). It MUST appear
// before `cp` so the transform runs on every source file Vite asks
// for, regardless of where `cp` later copies emitted assets.
protobufVitePlugin(),
cp({
targets: [
...runtimeDistFiles.map((file) => ({
src: `${runtimeSrc}/${file}`,
dest: distDir,
flatten: true,
})),
...nativeFiles.map((f) => ({
src: `${nativeSrc}/${f}`,
dest: path.join(distDir, 'native'),
flatten: true,
})),
{
src: `${toPosix(ffmpegSrcDir)}/${ffmpegAddonFile}`,
dest: path.join(distDir, 'native', 'ffmpeg'),
flatten: true,
},
]
})
];
const BaseConfig = (source_map: boolean = false) => defineConfig({
resolve: {
conditions: ['node', 'default'],
alias: {
'@': path.resolve(__dirname, 'src')
}
},
build: {
sourcemap: source_map,
target: 'esnext',
minify: false,
lib: {
entry: {
index: path.resolve(__dirname, 'src/index.ts')
},
formats: ['es'],
fileName: (_, entryName) => `${entryName}.mjs`
},
rollupOptions: {
external: [...nodeModules, ...external],
output: {
// better-sqlite3's JS wrapper is pure CJS — its `require('fs')` /
// `require('path')` / `require('bindings')` calls get inlined into
// the ESM bundle as `__require("fs")` shims by rolldown. Those
// shims gate on `typeof require !== "undefined"`, which is true
// in CJS but FALSE in our `.mjs` output, so they fall through to
// a runtime `throw Error("Calling \`require\` ... in an environment
// that doesn't expose the \`require\` function.")` on first call —
// crashing the launcher immediately.
//
// Defining a module-scope `require` via `createRequire(import.meta.url)`
// at the very top of the bundle satisfies the `typeof` check, and
// the shim then transparently delegates every inlined CJS require
// to the real CJS loader. Node 18+ ships `createRequire` in the
// `node:module` builtin, so this is supported on every target.
banner: [
"import { createRequire as __snowlumaCreateRequire } from 'node:module';",
"const require = __snowlumaCreateRequire(import.meta.url);",
].join('\n'),
},
},
// Emit to monorepo root dist/ so the existing release pipeline keeps working.
outDir: distDir,
// Required since outDir is outside the vite project root.
emptyOutDir: true
},
define: {
__BUILD_WEBUI__: process.env.BUILD_WEBUI === 'true',
__APP_VERSION__: JSON.stringify(rootPkg.version)
}
});
export default defineConfig(({ mode }): UserConfig => {
if (mode === 'development') {
return {
...BaseConfig(true),
plugins: [...BaseConfigPlugin]
};
}
return {
...BaseConfig(),
plugins: [...BaseConfigPlugin]
};
});