This commit is contained in:
124
scripts/build-sidecar.mjs
Normal file
124
scripts/build-sidecar.mjs
Normal file
@@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Build the Node.js sidecar for Tauri production.
|
||||
*
|
||||
* Bundles packages/server/src/local/service.ts into a single JS file
|
||||
* at app/src-tauri/resources/service.js using esbuild JS API.
|
||||
*
|
||||
* Usage:
|
||||
* node scripts/build-sidecar.mjs
|
||||
*/
|
||||
|
||||
import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const root = path.resolve(__dirname, '..');
|
||||
const resourcesDir = path.join(root, 'app', 'src-tauri', 'resources');
|
||||
const outFile = path.join(resourcesDir, 'service.js');
|
||||
const entryPoint = path.join(root, 'packages', 'server', 'src', 'local', 'service.ts');
|
||||
// Metafile goes to a temp path (NOT resources/) so it's neither bundled into
|
||||
// the app nor left as an untracked repo artifact. stage-sidecar-deps.mjs reads
|
||||
// it back from the same well-known path. Keep the two in sync.
|
||||
const metaFile = path.join(os.tmpdir(), 'waggle-sidecar-meta.json');
|
||||
|
||||
// Ensure resources directory exists
|
||||
fs.mkdirSync(resourcesDir, { recursive: true });
|
||||
|
||||
console.log('[build-sidecar] Bundling server into', outFile);
|
||||
|
||||
// Native modules that can't be bundled — must be installed alongside the sidecar
|
||||
const EXTERNAL = [
|
||||
'better-sqlite3',
|
||||
'@vscode/sqlite3',
|
||||
'bullmq',
|
||||
'ioredis',
|
||||
'pg',
|
||||
'postgres',
|
||||
'drizzle-orm',
|
||||
'drizzle-orm/*',
|
||||
'mammoth',
|
||||
'pdf-parse',
|
||||
'exceljs',
|
||||
'archiver',
|
||||
'@fastify/static',
|
||||
'@huggingface/transformers',
|
||||
'onnxruntime-node',
|
||||
'onnxruntime-common',
|
||||
'onnxruntime-web',
|
||||
'sharp',
|
||||
'playwright-core',
|
||||
'playwright-core/*',
|
||||
'@playwright/*',
|
||||
'chromium-bidi',
|
||||
'chromium-bidi/*',
|
||||
];
|
||||
|
||||
try {
|
||||
// Dynamic import esbuild (available via vite dependency)
|
||||
const esbuild = await import('esbuild');
|
||||
|
||||
const result = await esbuild.build({
|
||||
entryPoints: [entryPoint],
|
||||
bundle: true,
|
||||
platform: 'node',
|
||||
target: 'node20',
|
||||
format: 'esm',
|
||||
outfile: outFile,
|
||||
external: EXTERNAL,
|
||||
// P4/D12: @waggle/shared and @waggle/hive-mind-core export ONLY their
|
||||
// gitignored dist/ (unlike core/agent/server, which export src/*.ts).
|
||||
// Without these aliases the bundle silently embeds whatever dist/ was
|
||||
// last compiled — the same stale-server-in-the-binary class D12 exists
|
||||
// to kill — and a clean checkout can't build at all without
|
||||
// build:packages. Alias to source so the bundle ALWAYS compiles from
|
||||
// src, like the vitest aliases do. (No subpath imports of either
|
||||
// package exist — verified before aliasing the bare names.)
|
||||
alias: {
|
||||
'@waggle/shared': path.join(root, 'packages', 'shared', 'src', 'index.ts'),
|
||||
'@waggle/hive-mind-core': path.join(root, 'packages', 'hive-mind-core', 'src', 'index.ts'),
|
||||
},
|
||||
sourcemap: true,
|
||||
minify: true,
|
||||
// metafile lets stage-sidecar-deps.mjs enumerate exactly which of the
|
||||
// EXTERNAL packages the bundle actually `require()`s at runtime, so it
|
||||
// stages precisely that set (and its transitive prod deps) into
|
||||
// resources/node_modules/ — no more, no less. Written next to the bundle.
|
||||
metafile: true,
|
||||
banner: {
|
||||
js: '// Waggle Sidecar — bundled server for Tauri desktop\n'
|
||||
+ '// Generated by scripts/build-sidecar.mjs\n'
|
||||
+ 'import { createRequire } from "node:module";\n'
|
||||
+ 'const require = createRequire(import.meta.url);\n',
|
||||
},
|
||||
logLevel: 'warning',
|
||||
});
|
||||
|
||||
if (result.errors.length > 0) {
|
||||
console.error('[build-sidecar] Build errors:', result.errors);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (result.warnings.length > 0) {
|
||||
console.warn(`[build-sidecar] ${result.warnings.length} warnings (non-blocking)`);
|
||||
}
|
||||
|
||||
fs.writeFileSync(metaFile, JSON.stringify(result.metafile));
|
||||
console.log('[build-sidecar] Wrote esbuild metafile', metaFile);
|
||||
|
||||
const stat = fs.statSync(outFile);
|
||||
const sizeMB = (stat.size / 1024 / 1024).toFixed(1);
|
||||
console.log(`[build-sidecar] Done. Output: ${sizeMB} MB`);
|
||||
|
||||
// Copy marketplace seed database if it exists
|
||||
const marketplaceDb = path.join(root, 'packages', 'marketplace', 'seed', 'marketplace.db');
|
||||
if (fs.existsSync(marketplaceDb)) {
|
||||
fs.copyFileSync(marketplaceDb, path.join(resourcesDir, 'marketplace.db'));
|
||||
console.log('[build-sidecar] Copied marketplace.db seed');
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[build-sidecar] Build failed:', err.message);
|
||||
process.exit(1);
|
||||
}
|
||||
Reference in New Issue
Block a user