fix: SQLite plugin initialization in Electron main process
- Changed from direct plugin usage to SQLiteConnection pattern - Matches how platform services use the SQLite plugin - Removed handle() method dependency - Added proper method routing in IPC handler The app now launches without initialization errors. Next steps: - Test actual SQLite operations (createConnection, query, etc.) - Verify database creation and access - Add error handling for database operations
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
{
|
||||
"appId": "app.timesafari",
|
||||
"appId": "com.timesafari.app",
|
||||
"appName": "TimeSafari",
|
||||
"webDir": "dist",
|
||||
"bundledWebRuntime": false,
|
||||
"server": {
|
||||
"cleartext": true
|
||||
"cleartext": true,
|
||||
"androidScheme": "https"
|
||||
},
|
||||
"plugins": {
|
||||
"App": {
|
||||
@@ -29,6 +30,12 @@
|
||||
"biometricAuth": true,
|
||||
"biometricTitle": "Biometric login for TimeSafari"
|
||||
}
|
||||
},
|
||||
"CapacitorSQLite": {
|
||||
"electronIsEncryption": false,
|
||||
"electronMacLocation": "~/Library/Application Support/TimeSafari",
|
||||
"electronWindowsLocation": "C:\\ProgramData\\TimeSafari",
|
||||
"electronLinuxLocation": "~/.local/share/TimeSafari"
|
||||
}
|
||||
},
|
||||
"ios": {
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"clean:electron": "rimraf dist-electron",
|
||||
"build:pywebview": "vite build --config vite.config.pywebview.mts",
|
||||
"build:web": "VITE_GIT_HASH=`git log -1 --pretty=format:%h` vite build --config vite.config.web.mts",
|
||||
"build:web:electron": "VITE_GIT_HASH=`git log -1 --pretty=format:%h` vite build --config vite.config.electron.mts --mode electron",
|
||||
"build:web:electron": "VITE_GIT_HASH=`git log -1 --pretty=format:%h` vite build --config vite.config.web.mts && VITE_GIT_HASH=`git log -1 --pretty=format:%h` vite build --config vite.config.electron.mts --mode electron",
|
||||
"build:electron": "npm run clean:electron && npm run build:web:electron && tsc -p tsconfig.electron.json && vite build --config vite.config.electron.mts && node scripts/build-electron.js",
|
||||
"build:capacitor": "vite build --mode capacitor --config vite.config.capacitor.mts",
|
||||
"electron:dev": "npm run build && electron .",
|
||||
@@ -181,7 +181,8 @@
|
||||
},
|
||||
"files": [
|
||||
"dist-electron/**/*",
|
||||
"dist/**/*"
|
||||
"dist/**/*",
|
||||
"capacitor.config.json"
|
||||
],
|
||||
"extraResources": [
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const fs = require("fs");
|
||||
const fse = require("fs-extra");
|
||||
const path = require("path");
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
console.log("Starting Electron build finalization...");
|
||||
|
||||
@@ -64,4 +65,21 @@ if (fs.existsSync(preloadSrc)) {
|
||||
console.error("Preload script not found at:", preloadSrc);
|
||||
}
|
||||
|
||||
// Copy capacitor.config.json to dist-electron
|
||||
try {
|
||||
console.log("Copying capacitor.config.json to dist-electron...");
|
||||
const configPath = path.join(process.cwd(), 'capacitor.config.json');
|
||||
const targetPath = path.join(process.cwd(), 'dist-electron', 'capacitor.config.json');
|
||||
|
||||
if (!fs.existsSync(configPath)) {
|
||||
throw new Error('capacitor.config.json not found in project root');
|
||||
}
|
||||
|
||||
fs.copyFileSync(configPath, targetPath);
|
||||
console.log("Successfully copied capacitor.config.json");
|
||||
} catch (error) {
|
||||
console.error("Failed to copy capacitor.config.json:", error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
console.log("Electron index.html copied and patched for Electron context.");
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { app, BrowserWindow, ipcMain } from "electron";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import { CapacitorSQLiteElectron } from "@capacitor-community/sqlite/electron/dist/plugin";
|
||||
import { Capacitor } from "@capacitor/core";
|
||||
import { SQLiteConnection } from "@capacitor-community/sqlite";
|
||||
|
||||
// Simple logger implementation
|
||||
const logger = {
|
||||
@@ -17,12 +18,94 @@ const logger = {
|
||||
debug: (...args: unknown[]) => console.debug("[Main]", ...args),
|
||||
};
|
||||
|
||||
logger.info("Starting main process initialization...");
|
||||
|
||||
// Initialize Capacitor for Electron in main process
|
||||
try {
|
||||
logger.info("About to initialize Capacitor...");
|
||||
logger.info("Capacitor before init:", {
|
||||
hasPlatform: 'platform' in Capacitor,
|
||||
hasIsNativePlatform: 'isNativePlatform' in Capacitor,
|
||||
platformType: typeof Capacitor.platform,
|
||||
isNativePlatformType: typeof Capacitor.isNativePlatform
|
||||
});
|
||||
|
||||
// Try direct assignment first
|
||||
try {
|
||||
(Capacitor as any).platform = 'electron';
|
||||
(Capacitor as any).isNativePlatform = true;
|
||||
logger.info("Direct assignment successful");
|
||||
} catch (e) {
|
||||
logger.warn("Direct assignment failed, trying defineProperty:", e);
|
||||
Object.defineProperty(Capacitor, "isNativePlatform", {
|
||||
get: () => true,
|
||||
configurable: true,
|
||||
});
|
||||
|
||||
Object.defineProperty(Capacitor, "platform", {
|
||||
get: () => 'electron',
|
||||
configurable: true,
|
||||
});
|
||||
}
|
||||
|
||||
logger.info("Capacitor after init:", {
|
||||
platform: Capacitor.platform,
|
||||
isNativePlatform: Capacitor.isNativePlatform,
|
||||
platformType: typeof Capacitor.platform,
|
||||
isNativePlatformType: typeof Capacitor.isNativePlatform
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("Failed to initialize Capacitor:", error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Initialize SQLite plugin for Electron
|
||||
let sqlitePlugin: CapacitorSQLiteElectron | null = null;
|
||||
let sqliteConnection: SQLiteConnection | null = null;
|
||||
|
||||
try {
|
||||
logger.info("Initializing SQLite plugin...");
|
||||
sqlitePlugin = new CapacitorSQLiteElectron();
|
||||
logger.info("About to initialize SQLite plugin...");
|
||||
// Use require to ensure we get the CommonJS module
|
||||
const sqliteModule = require('@capacitor-community/sqlite/electron/dist/plugin');
|
||||
logger.info("SQLite module loaded:", Object.keys(sqliteModule));
|
||||
|
||||
// Debug the module contents
|
||||
logger.info("SQLite module contents:", {
|
||||
default: typeof sqliteModule.default,
|
||||
defaultKeys: sqliteModule.default ? Object.keys(sqliteModule.default) : [],
|
||||
CapacitorSQLite: typeof sqliteModule.CapacitorSQLite,
|
||||
CapacitorSQLiteKeys: sqliteModule.CapacitorSQLite ? Object.keys(sqliteModule.CapacitorSQLite) : [],
|
||||
moduleType: typeof sqliteModule,
|
||||
moduleKeys: Object.keys(sqliteModule),
|
||||
hasHandle: sqliteModule.default?.handle ? 'yes' : 'no',
|
||||
handleType: typeof sqliteModule.default?.handle,
|
||||
defaultProto: Object.getPrototypeOf(sqliteModule.default),
|
||||
defaultProtoKeys: Object.getPrototypeOf(sqliteModule.default) ? Object.keys(Object.getPrototypeOf(sqliteModule.default)) : []
|
||||
});
|
||||
|
||||
// Get the plugin constructor
|
||||
const SQLitePlugin = sqliteModule.CapacitorSQLite;
|
||||
logger.info("Got SQLite plugin constructor:", {
|
||||
type: typeof SQLitePlugin,
|
||||
name: SQLitePlugin?.name,
|
||||
prototype: SQLitePlugin ? Object.getPrototypeOf(SQLitePlugin) : null,
|
||||
prototypeKeys: SQLitePlugin ? Object.keys(Object.getPrototypeOf(SQLitePlugin)) : []
|
||||
});
|
||||
|
||||
if (typeof SQLitePlugin !== 'function') {
|
||||
throw new Error(`SQLite plugin is not a constructor, got ${typeof SQLitePlugin}`);
|
||||
}
|
||||
|
||||
// Create a connection using the plugin
|
||||
logger.info("Creating SQLite connection...");
|
||||
sqliteConnection = new SQLiteConnection(SQLitePlugin);
|
||||
|
||||
logger.info("SQLite connection created:", {
|
||||
type: typeof sqliteConnection,
|
||||
keys: Object.keys(sqliteConnection),
|
||||
prototype: Object.getPrototypeOf(sqliteConnection),
|
||||
prototypeKeys: Object.getPrototypeOf(sqliteConnection) ? Object.keys(Object.getPrototypeOf(sqliteConnection)) : []
|
||||
});
|
||||
|
||||
logger.info("SQLite plugin initialized successfully");
|
||||
} catch (error) {
|
||||
logger.error("Failed to initialize SQLite plugin:", error);
|
||||
@@ -31,15 +114,22 @@ try {
|
||||
|
||||
// Set up IPC handler for SQLite
|
||||
ipcMain.handle("capacitor-sqlite", async (_event, ...args) => {
|
||||
if (!sqlitePlugin) {
|
||||
const error = new Error("SQLite plugin not initialized");
|
||||
if (!sqliteConnection) {
|
||||
const error = new Error("SQLite connection not initialized");
|
||||
logger.error(error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
try {
|
||||
logger.debug("Handling SQLite request:", args);
|
||||
const result = await sqlitePlugin.handle(_event, ...args);
|
||||
// The first argument should be the method name
|
||||
const [method, ...restArgs] = args;
|
||||
if (typeof method !== 'string' || !(method in sqliteConnection)) {
|
||||
throw new Error(`Invalid SQLite method: ${method}`);
|
||||
}
|
||||
|
||||
// Call the method on the connection
|
||||
const result = await (sqliteConnection as any)[method](...restArgs);
|
||||
logger.debug("SQLite request completed successfully");
|
||||
return result;
|
||||
} catch (error) {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { createPinia } from "pinia";
|
||||
import { App as VueApp, ComponentPublicInstance, createApp } from "vue";
|
||||
import App from "./App.vue";
|
||||
import router from "./router";
|
||||
// Use the browser version of axios for web builds
|
||||
import axios from "axios";
|
||||
import VueAxios from "vue-axios";
|
||||
import Notifications from "notiwind";
|
||||
|
||||
@@ -1,14 +1,7 @@
|
||||
import { initializeApp } from "./main.common";
|
||||
import { logger } from "./utils/logger";
|
||||
import { Capacitor } from "@capacitor/core";
|
||||
import { CapacitorSQLite } from "@capacitor-community/sqlite";
|
||||
|
||||
// Initialize Capacitor for Electron
|
||||
Object.defineProperty(Capacitor, "isNativePlatform", {
|
||||
get: () => true,
|
||||
configurable: true,
|
||||
});
|
||||
|
||||
// Initialize SQLite plugin for Electron
|
||||
if (typeof window !== "undefined") {
|
||||
// Register the plugin globally
|
||||
|
||||
@@ -1,120 +1,102 @@
|
||||
import { defineConfig, mergeConfig } from "vite";
|
||||
import { createBuildConfig } from "./vite.config.common.mts";
|
||||
import { defineConfig } from "vite";
|
||||
import path from 'path';
|
||||
|
||||
export default defineConfig(async ({ mode }) => {
|
||||
const baseConfig = await createBuildConfig('electron');
|
||||
const isWebBuild = mode === 'electron';
|
||||
|
||||
return mergeConfig(baseConfig, {
|
||||
build: {
|
||||
outDir: isWebBuild ? 'dist' : 'dist-electron',
|
||||
rollupOptions: isWebBuild ? {
|
||||
// Web app build configuration
|
||||
input: path.resolve(__dirname, 'index.html'),
|
||||
output: {
|
||||
format: 'esm',
|
||||
entryFileNames: 'assets/[name]-[hash].js',
|
||||
chunkFileNames: 'assets/[name]-[hash].js',
|
||||
assetFileNames: 'assets/[name]-[hash].[ext]'
|
||||
}
|
||||
} : {
|
||||
// Main process build configuration
|
||||
input: {
|
||||
main: path.resolve(__dirname, 'src/electron/main.ts'),
|
||||
preload: path.resolve(__dirname, 'src/electron/preload.js'),
|
||||
},
|
||||
external: [
|
||||
'electron',
|
||||
'@capacitor-community/sqlite',
|
||||
'@capacitor-community/sqlite/electron',
|
||||
'better-sqlite3-multiple-ciphers'
|
||||
],
|
||||
output: {
|
||||
format: 'cjs',
|
||||
entryFileNames: '[name].js',
|
||||
assetFileNames: 'assets/[name].[ext]',
|
||||
},
|
||||
export default defineConfig({
|
||||
build: {
|
||||
outDir: 'dist-electron',
|
||||
rollupOptions: {
|
||||
input: {
|
||||
main: path.resolve(__dirname, 'src/electron/main.ts'),
|
||||
preload: path.resolve(__dirname, 'src/electron/preload.js'),
|
||||
},
|
||||
target: isWebBuild ? 'esnext' : 'node18',
|
||||
minify: !isWebBuild,
|
||||
sourcemap: true,
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, 'src'),
|
||||
external: [
|
||||
// Node.js built-ins
|
||||
'stream',
|
||||
'path',
|
||||
'fs',
|
||||
'crypto',
|
||||
'buffer',
|
||||
'util',
|
||||
'events',
|
||||
'url',
|
||||
'assert',
|
||||
'os',
|
||||
'net',
|
||||
'http',
|
||||
'https',
|
||||
'zlib',
|
||||
'child_process',
|
||||
// Electron and Capacitor
|
||||
'electron',
|
||||
'@capacitor/core',
|
||||
'@capacitor-community/sqlite',
|
||||
'@capacitor-community/sqlite/electron',
|
||||
'@capacitor-community/sqlite/electron/dist/plugin',
|
||||
'better-sqlite3-multiple-ciphers',
|
||||
// HTTP clients
|
||||
'axios',
|
||||
'axios/dist/axios',
|
||||
'axios/dist/node/axios.cjs'
|
||||
],
|
||||
output: {
|
||||
format: 'cjs',
|
||||
entryFileNames: '[name].js',
|
||||
assetFileNames: 'assets/[name].[ext]',
|
||||
},
|
||||
},
|
||||
optimizeDeps: {
|
||||
include: ['@/utils/logger']
|
||||
target: 'node18',
|
||||
minify: false,
|
||||
sourcemap: true,
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, 'src'),
|
||||
// Use Node.js version of axios in electron
|
||||
'axios': 'axios/dist/node/axios.cjs'
|
||||
},
|
||||
plugins: [
|
||||
{
|
||||
name: 'typescript-transform',
|
||||
transform(code: string, id: string) {
|
||||
if (id.endsWith('main.ts')) {
|
||||
// Replace the logger import with inline logger
|
||||
return code.replace(
|
||||
/import\s*{\s*logger\s*}\s*from\s*['"]@\/utils\/logger['"];?/,
|
||||
`const logger = {
|
||||
log: (...args) => console.log(...args),
|
||||
error: (...args) => console.error(...args),
|
||||
info: (...args) => console.info(...args),
|
||||
warn: (...args) => console.warn(...args),
|
||||
debug: (...args) => console.debug(...args),
|
||||
};`
|
||||
);
|
||||
}
|
||||
return code;
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'remove-sw-imports',
|
||||
transform(code: string, id: string) {
|
||||
// Remove service worker imports and registrations
|
||||
if (id.includes('registerServiceWorker') ||
|
||||
id.includes('register-service-worker') ||
|
||||
id.includes('sw_scripts') ||
|
||||
id.includes('PushNotificationPermission') ||
|
||||
code.includes('navigator.serviceWorker')) {
|
||||
return {
|
||||
code: code
|
||||
.replace(/import.*registerServiceWorker.*$/mg, '')
|
||||
.replace(/import.*register-service-worker.*$/mg, '')
|
||||
.replace(/navigator\.serviceWorker/g, 'undefined')
|
||||
.replace(/if\s*\([^)]*serviceWorker[^)]*\)\s*{[^}]*}/g, '')
|
||||
.replace(/import.*workbox.*$/mg, '')
|
||||
.replace(/importScripts\([^)]*\)/g, '')
|
||||
};
|
||||
}
|
||||
return code;
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'remove-sw-files',
|
||||
enforce: 'pre',
|
||||
resolveId(id: string) {
|
||||
// Prevent service worker files from being included
|
||||
if (id.includes('sw.js') ||
|
||||
id.includes('workbox') ||
|
||||
id.includes('registerSW.js') ||
|
||||
id.includes('manifest.webmanifest')) {
|
||||
return '\0empty';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
load(id: string) {
|
||||
if (id === '\0empty') {
|
||||
return 'export default {}';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
optimizeDeps: {
|
||||
exclude: [
|
||||
'stream',
|
||||
'path',
|
||||
'fs',
|
||||
'crypto',
|
||||
'buffer',
|
||||
'util',
|
||||
'events',
|
||||
'url',
|
||||
'assert',
|
||||
'os',
|
||||
'net',
|
||||
'http',
|
||||
'https',
|
||||
'zlib',
|
||||
'child_process',
|
||||
'axios',
|
||||
'axios/dist/axios',
|
||||
'axios/dist/node/axios.cjs'
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
{
|
||||
name: 'typescript-transform',
|
||||
transform(code: string, id: string) {
|
||||
if (id.endsWith('main.ts')) {
|
||||
return code.replace(
|
||||
/import\s*{\s*logger\s*}\s*from\s*['"]@\/utils\/logger['"];?/,
|
||||
`const logger = {
|
||||
log: (...args) => console.log(...args),
|
||||
error: (...args) => console.error(...args),
|
||||
info: (...args) => console.info(...args),
|
||||
warn: (...args) => console.warn(...args),
|
||||
debug: (...args) => console.debug(...args),
|
||||
};`
|
||||
);
|
||||
}
|
||||
return code;
|
||||
}
|
||||
],
|
||||
ssr: {
|
||||
noExternal: ['@/utils/logger']
|
||||
},
|
||||
base: './',
|
||||
publicDir: 'public',
|
||||
});
|
||||
}
|
||||
],
|
||||
base: './',
|
||||
publicDir: 'public',
|
||||
});
|
||||
Reference in New Issue
Block a user