Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 28 additions & 10 deletions apps/desktop/electron-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,24 @@ const config: Configuration = {
asar: true,
asarUnpack: [
"**/node_modules/better-sqlite3/**/*",
// better-sqlite3 uses `bindings` to locate native modules - must be unpacked together
"**/node_modules/bindings/**/*",
"**/node_modules/file-uri-to-path/**/*",
"**/node_modules/node-pty/**/*",
// Sound files must be unpacked so external audio players (afplay, paplay, etc.) can access them
"**/resources/sounds/**/*",
],

// Extra resources placed outside asar archive (accessible via process.resourcesPath)
extraResources: [
// Database migrations - must be outside asar for drizzle-orm to read
{
from: "dist/resources/migrations",
to: "resources/migrations",
filter: ["**/*"],
},
],

files: [
"dist/**/*",
"package.json",
Expand All @@ -47,20 +60,27 @@ const config: Configuration = {
to: "resources",
filter: ["**/*"],
},
// Database migrations from local-db package (copied to dist/resources/migrations by vite)
{
from: "dist/resources/migrations",
to: "resources/migrations",
filter: ["**/*"],
},
// Native modules that can't be bundled by Vite.
// bun creates symlinks for direct deps in workspace node_modules.
// The copy:native-modules script replaces symlinks with real files
// before building (required for Bun 1.3+ isolated installs).
{
from: "node_modules/better-sqlite3",
to: "node_modules/better-sqlite3",
filter: ["**/*"],
},
// better-sqlite3 uses `bindings` package to locate its native .node file
{
from: "node_modules/bindings",
to: "node_modules/bindings",
filter: ["**/*"],
},
// `bindings` requires `file-uri-to-path` for file:// URL handling
{
from: "node_modules/file-uri-to-path",
to: "node_modules/file-uri-to-path",
filter: ["**/*"],
},
{
from: "node_modules/node-pty",
to: "node_modules/node-pty",
Expand All @@ -69,10 +89,8 @@ const config: Configuration = {
"!**/.DS_Store",
],

// Skip npm rebuild - dependencies already built in monorepo
npmRebuild: false,
buildDependenciesFromSource: false,
nodeGypRebuild: false,
// Rebuild native modules for Electron's Node.js version
npmRebuild: true,

// macOS
mac: {
Expand Down
3 changes: 3 additions & 0 deletions apps/desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"@xterm/addon-webgl": "^0.18.0",
"@xterm/xterm": "^5.5.0",
"better-sqlite3": "12.5.0",
"bindings": "^1.5.0",
"clsx": "^2.1.1",
"culori": "^4.0.2",
"date-fns": "^4.1.0",
Expand All @@ -71,6 +72,7 @@
"execa": "^9.6.0",
"express": "^5.1.0",
"fast-glob": "^3.3.3",
"file-uri-to-path": "^1.0.0",
"framer-motion": "^12.23.24",
"http-proxy": "^1.18.1",
"jose": "^6.1.3",
Expand All @@ -79,6 +81,7 @@
"lowdb": "^7.0.1",
"monaco-editor": "^0.55.1",
"nanoid": "^5.1.6",
"node-addon-api": "^7.1.0",
"node-pty": "1.1.0-beta30",
"os-locale": "^6.0.2",
"posthog-js": "^1.306.1",
Expand Down
70 changes: 48 additions & 22 deletions apps/desktop/scripts/copy-native-modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,42 +16,68 @@
import { cpSync, existsSync, lstatSync, realpathSync, rmSync } from "node:fs";
import { dirname, join } from "node:path";

// Native modules that must exist for the app to work
const NATIVE_MODULES = ["better-sqlite3", "node-pty"] as const;

function prepareNativeModules() {
console.log("Preparing native modules for electron-builder...");
// Dependencies of native modules that need to be copied (may be hoisted or symlinked)
const NATIVE_MODULE_DEPS = ["bindings", "file-uri-to-path"] as const;

const nodeModulesDir = join(dirname(import.meta.dirname), "node_modules");

for (const moduleName of NATIVE_MODULES) {
const modulePath = join(nodeModulesDir, moduleName);
function copyModuleIfSymlink(
nodeModulesDir: string,
moduleName: string,
required: boolean,
): boolean {
const modulePath = join(nodeModulesDir, moduleName);

if (!existsSync(modulePath)) {
if (!existsSync(modulePath)) {
if (required) {
console.error(` [ERROR] ${moduleName} not found at ${modulePath}`);
process.exit(1);
}
console.log(` ${moduleName}: not found (skipping)`);
return false;
}

const stats = lstatSync(modulePath);
const stats = lstatSync(modulePath);

if (stats.isSymbolicLink()) {
// Resolve symlink to get real path
const realPath = realpathSync(modulePath);
console.log(` ${moduleName}: symlink -> replacing with real files`);
console.log(` Real path: ${realPath}`);
if (stats.isSymbolicLink()) {
// Resolve symlink to get real path
const realPath = realpathSync(modulePath);
console.log(` ${moduleName}: symlink -> replacing with real files`);
console.log(` Real path: ${realPath}`);

// Remove the symlink
rmSync(modulePath);
// Remove the symlink
rmSync(modulePath);

// Copy the actual files
cpSync(realPath, modulePath, { recursive: true });
// Copy the actual files
cpSync(realPath, modulePath, { recursive: true });

console.log(` Copied to: ${modulePath}`);
} else {
console.log(` ${moduleName}: already real directory (not a symlink)`);
}
console.log(` Copied to: ${modulePath}`);
} else {
console.log(` ${moduleName}: already real directory (not a symlink)`);
}

return true;
}

function prepareNativeModules() {
console.log("Preparing native modules for electron-builder...");

// bun creates symlinks for direct dependencies in the workspace's node_modules
const nodeModulesDir = join(dirname(import.meta.dirname), "node_modules");

// Copy required native modules
for (const moduleName of NATIVE_MODULES) {
copyModuleIfSymlink(nodeModulesDir, moduleName, true);
}

// Copy native module dependencies (not required but needed if present)
console.log("\nPreparing native module dependencies...");
for (const moduleName of NATIVE_MODULE_DEPS) {
copyModuleIfSymlink(nodeModulesDir, moduleName, false);
}

console.log("Done!");
console.log("\nDone!");
}

prepareNativeModules();
2 changes: 2 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading