forked from jsnbuchanan/crowd-funder-for-time-pwa
refactor(electron): improve build process and configuration
- Enhance electron build configuration with proper asset handling - Add comprehensive logging and error tracking - Implement CSP headers for security - Fix module exports for logger compatibility - Update TypeScript and Vite configs for better build support - Improve development workflow with better dev tools integration
This commit is contained in:
13
package.json
13
package.json
@@ -22,11 +22,11 @@
|
|||||||
"check:ios-device": "xcrun xctrace list devices 2>&1 | grep -w 'Booted' || (echo 'No iOS simulator running' && exit 1)",
|
"check:ios-device": "xcrun xctrace list devices 2>&1 | grep -w 'Booted' || (echo 'No iOS simulator running' && exit 1)",
|
||||||
"clean:electron": "rimraf dist-electron",
|
"clean:electron": "rimraf dist-electron",
|
||||||
"build:pywebview": "vite build --config vite.config.pywebview.mts",
|
"build:pywebview": "vite build --config vite.config.pywebview.mts",
|
||||||
"build:electron": "npm run clean:electron && vite build --config vite.config.electron.mts && node scripts/build-electron.js",
|
"build:electron": "npm run clean: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",
|
"build:capacitor": "vite build --mode capacitor --config vite.config.capacitor.mts",
|
||||||
"build:web": "vite build --config vite.config.web.mts",
|
"build:web": "vite build --config vite.config.web.mts",
|
||||||
"electron:dev": "npm run build && electron dist-electron",
|
"electron:dev": "npm run build && electron .",
|
||||||
"electron:start": "electron dist-electron",
|
"electron:start": "electron .",
|
||||||
"clean:android": "adb uninstall app.timesafari.app || true",
|
"clean:android": "adb uninstall app.timesafari.app || true",
|
||||||
"build:android": "npm run clean:android && rm -rf dist && npm run build:web && npm run build:capacitor && cd android && ./gradlew clean && ./gradlew assembleDebug && cd .. && npx cap sync android && npx capacitor-assets generate --android && npx cap open android",
|
"build:android": "npm run clean:android && rm -rf dist && npm run build:web && npm run build:capacitor && cd android && ./gradlew clean && ./gradlew assembleDebug && cd .. && npx cap sync android && npx capacitor-assets generate --android && npx cap open android",
|
||||||
"electron:build-linux": "npm run build:electron && electron-builder --linux AppImage",
|
"electron:build-linux": "npm run build:electron && electron-builder --linux AppImage",
|
||||||
@@ -169,13 +169,12 @@
|
|||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist-electron/**/*",
|
"dist-electron/**/*",
|
||||||
"src/electron/**/*",
|
"dist/**/*"
|
||||||
"main.js"
|
|
||||||
],
|
],
|
||||||
"extraResources": [
|
"extraResources": [
|
||||||
{
|
{
|
||||||
"from": "dist-electron",
|
"from": "dist",
|
||||||
"to": "."
|
"to": "www"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"linux": {
|
"linux": {
|
||||||
|
|||||||
@@ -1,98 +1,243 @@
|
|||||||
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const fs = require('fs-extra');
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
try {
|
|
||||||
console.log('Starting electron build process...');
|
console.log('Starting electron build process...');
|
||||||
|
|
||||||
// Create dist directory if it doesn't exist
|
|
||||||
const distElectronDir = path.resolve(__dirname, '../dist-electron');
|
|
||||||
await fs.ensureDir(distElectronDir);
|
|
||||||
|
|
||||||
// Copy web files
|
// Copy web files
|
||||||
const wwwDir = path.join(distElectronDir, 'www');
|
const webDistPath = path.join(__dirname, '..', 'dist');
|
||||||
await fs.ensureDir(wwwDir);
|
const electronDistPath = path.join(__dirname, '..', 'dist-electron');
|
||||||
await fs.copy('dist', wwwDir);
|
const wwwPath = path.join(electronDistPath, 'www');
|
||||||
|
|
||||||
// Copy and fix index.html
|
// Create www directory if it doesn't exist
|
||||||
const indexPath = path.join(wwwDir, 'index.html');
|
if (!fs.existsSync(wwwPath)) {
|
||||||
let indexContent = await fs.readFile(indexPath, 'utf8');
|
fs.mkdirSync(wwwPath, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
// More comprehensive path fixing
|
// Copy web files to www directory
|
||||||
|
fs.cpSync(webDistPath, wwwPath, { recursive: true });
|
||||||
|
|
||||||
|
// Fix asset paths in index.html
|
||||||
|
const indexPath = path.join(wwwPath, 'index.html');
|
||||||
|
let indexContent = fs.readFileSync(indexPath, 'utf8');
|
||||||
|
|
||||||
|
// Fix asset paths
|
||||||
indexContent = indexContent
|
indexContent = indexContent
|
||||||
// Fix absolute paths to be relative
|
.replace(/\/assets\//g, './assets/')
|
||||||
.replace(/src="\//g, 'src="\./')
|
.replace(/href="\//g, 'href="./')
|
||||||
.replace(/href="\//g, 'href="\./')
|
.replace(/src="\//g, 'src="./');
|
||||||
// Fix modulepreload paths
|
|
||||||
.replace(/<link [^>]*rel="modulepreload"[^>]*href="\/assets\//g, '<link rel="modulepreload" as="script" crossorigin="" href="./assets/')
|
|
||||||
.replace(/<link [^>]*rel="modulepreload"[^>]*href="\.\/assets\//g, '<link rel="modulepreload" as="script" crossorigin="" href="./assets/')
|
|
||||||
// Fix stylesheet paths
|
|
||||||
.replace(/<link [^>]*rel="stylesheet"[^>]*href="\/assets\//g, '<link rel="stylesheet" crossorigin="" href="./assets/')
|
|
||||||
.replace(/<link [^>]*rel="stylesheet"[^>]*href="\.\/assets\//g, '<link rel="stylesheet" crossorigin="" href="./assets/')
|
|
||||||
// Fix script paths
|
|
||||||
.replace(/src="\/assets\//g, 'src="./assets/')
|
|
||||||
.replace(/src="\.\/assets\//g, 'src="./assets/')
|
|
||||||
// Fix any remaining asset paths
|
|
||||||
.replace(/(['"]\/?)(assets\/)/g, '"./assets/');
|
|
||||||
|
|
||||||
// Debug output
|
fs.writeFileSync(indexPath, indexContent);
|
||||||
|
|
||||||
|
// Check for remaining /assets/ paths
|
||||||
console.log('After path fixing, checking for remaining /assets/ paths:', indexContent.includes('/assets/'));
|
console.log('After path fixing, checking for remaining /assets/ paths:', indexContent.includes('/assets/'));
|
||||||
console.log('Sample of fixed content:', indexContent.slice(0, 500));
|
console.log('Sample of fixed content:', indexContent.substring(0, 500));
|
||||||
|
|
||||||
await fs.writeFile(indexPath, indexContent);
|
console.log('Copied and fixed web files in:', wwwPath);
|
||||||
|
|
||||||
console.log('Copied and fixed web files in:', wwwDir);
|
|
||||||
|
|
||||||
// Copy main process files
|
// Copy main process files
|
||||||
console.log('Copying main process files...');
|
console.log('Copying main process files...');
|
||||||
const mainProcessFiles = [
|
|
||||||
['src/electron/main.js', 'main.js'],
|
|
||||||
['src/electron/preload.js', 'preload.js']
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const [src, dest] of mainProcessFiles) {
|
// Create the main process file with inlined logger
|
||||||
const destPath = path.join(distElectronDir, dest);
|
const mainContent = `const { app, BrowserWindow } = require("electron");
|
||||||
console.log(`Copying ${src} to ${destPath}`);
|
const path = require("path");
|
||||||
await fs.copy(src, destPath);
|
const fs = require("fs");
|
||||||
|
|
||||||
|
// Inline logger implementation
|
||||||
|
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),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if running in dev mode
|
||||||
|
const isDev = process.argv.includes("--inspect");
|
||||||
|
|
||||||
|
function createWindow() {
|
||||||
|
// Add before createWindow function
|
||||||
|
const preloadPath = path.join(__dirname, "preload.js");
|
||||||
|
logger.log("Checking preload path:", preloadPath);
|
||||||
|
logger.log("Preload exists:", fs.existsSync(preloadPath));
|
||||||
|
|
||||||
|
// Create the browser window.
|
||||||
|
const mainWindow = new BrowserWindow({
|
||||||
|
width: 1200,
|
||||||
|
height: 800,
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: false,
|
||||||
|
contextIsolation: true,
|
||||||
|
webSecurity: true,
|
||||||
|
allowRunningInsecureContent: false,
|
||||||
|
preload: path.join(__dirname, "preload.js"),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Always open DevTools for now
|
||||||
|
mainWindow.webContents.openDevTools();
|
||||||
|
|
||||||
|
// Intercept requests to fix asset paths
|
||||||
|
mainWindow.webContents.session.webRequest.onBeforeRequest(
|
||||||
|
{
|
||||||
|
urls: [
|
||||||
|
"file://*/*/assets/*",
|
||||||
|
"file://*/assets/*",
|
||||||
|
"file:///assets/*", // Catch absolute paths
|
||||||
|
"<all_urls>", // Catch all URLs as a fallback
|
||||||
|
],
|
||||||
|
},
|
||||||
|
(details, callback) => {
|
||||||
|
let url = details.url;
|
||||||
|
|
||||||
|
// Handle paths that don't start with file://
|
||||||
|
if (!url.startsWith("file://") && url.includes("/assets/")) {
|
||||||
|
url = \`file://\${path.join(__dirname, "www", url)}\`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create package.json for production
|
// Handle absolute paths starting with /assets/
|
||||||
const devPackageJson = require('../package.json');
|
if (url.includes("/assets/") && !url.includes("/www/assets/")) {
|
||||||
const prodPackageJson = {
|
const baseDir = url.includes("dist-electron")
|
||||||
name: devPackageJson.name,
|
? url.substring(
|
||||||
version: devPackageJson.version,
|
0,
|
||||||
description: devPackageJson.description,
|
url.indexOf("/dist-electron") + "/dist-electron".length,
|
||||||
author: devPackageJson.author,
|
)
|
||||||
main: 'main.js',
|
: \`file://\${__dirname}\`;
|
||||||
private: true,
|
const assetPath = url.split("/assets/")[1];
|
||||||
};
|
const newUrl = \`\${baseDir}/www/assets/\${assetPath}\`;
|
||||||
|
callback({ redirectURL: newUrl });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await fs.writeJson(
|
callback({}); // No redirect for other URLs
|
||||||
path.join(distElectronDir, 'package.json'),
|
},
|
||||||
prodPackageJson,
|
|
||||||
{ spaces: 2 }
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Verify the build
|
if (isDev) {
|
||||||
console.log('\nVerifying build structure:');
|
// Debug info
|
||||||
const files = await fs.readdir(distElectronDir);
|
logger.log("Debug Info:");
|
||||||
console.log('Files in dist-electron:', files);
|
logger.log("Running in dev mode:", isDev);
|
||||||
|
logger.log("App is packaged:", app.isPackaged);
|
||||||
if (!files.includes('main.js')) {
|
logger.log("Process resource path:", process.resourcesPath);
|
||||||
throw new Error('main.js not found in build directory');
|
logger.log("App path:", app.getAppPath());
|
||||||
}
|
logger.log("__dirname:", __dirname);
|
||||||
if (!files.includes('preload.js')) {
|
logger.log("process.cwd():", process.cwd());
|
||||||
throw new Error('preload.js not found in build directory');
|
|
||||||
}
|
|
||||||
if (!files.includes('package.json')) {
|
|
||||||
throw new Error('package.json not found in build directory');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Build completed successfully!');
|
const indexPath = path.join(__dirname, "www", "index.html");
|
||||||
} catch (error) {
|
|
||||||
console.error('Build failed:', error);
|
if (isDev) {
|
||||||
process.exit(1);
|
logger.log("Loading index from:", indexPath);
|
||||||
|
logger.log("www path:", path.join(__dirname, "www"));
|
||||||
|
logger.log("www assets path:", path.join(__dirname, "www", "assets"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fs.existsSync(indexPath)) {
|
||||||
|
logger.error(\`Index file not found at: \${indexPath}\`);
|
||||||
|
throw new Error("Index file not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add CSP headers to allow API connections, Google Fonts, and zxing-wasm
|
||||||
|
mainWindow.webContents.session.webRequest.onHeadersReceived(
|
||||||
|
(details, callback) => {
|
||||||
|
callback({
|
||||||
|
responseHeaders: {
|
||||||
|
...details.responseHeaders,
|
||||||
|
"Content-Security-Policy": [
|
||||||
|
"default-src 'self';" +
|
||||||
|
"connect-src 'self' https://api.endorser.ch https://*.timesafari.app https://*.jsdelivr.net;" +
|
||||||
|
"img-src 'self' data: https: blob:;" +
|
||||||
|
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://*.jsdelivr.net;" +
|
||||||
|
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;" +
|
||||||
|
"font-src 'self' data: https://fonts.gstatic.com;" +
|
||||||
|
"style-src-elem 'self' 'unsafe-inline' https://fonts.googleapis.com;" +
|
||||||
|
"worker-src 'self' blob:;",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Load the index.html
|
||||||
|
mainWindow
|
||||||
|
.loadFile(indexPath)
|
||||||
|
.then(() => {
|
||||||
|
logger.log("Successfully loaded index.html");
|
||||||
|
if (isDev) {
|
||||||
|
mainWindow.webContents.openDevTools();
|
||||||
|
logger.log("DevTools opened - running in dev mode");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
logger.error("Failed to load index.html:", err);
|
||||||
|
logger.error("Attempted path:", indexPath);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Listen for console messages from the renderer
|
||||||
|
mainWindow.webContents.on("console-message", (_event, _level, message) => {
|
||||||
|
logger.log("Renderer Console:", message);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add right after creating the BrowserWindow
|
||||||
|
mainWindow.webContents.on(
|
||||||
|
"did-fail-load",
|
||||||
|
(_event, errorCode, errorDescription) => {
|
||||||
|
logger.error("Page failed to load:", errorCode, errorDescription);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
mainWindow.webContents.on("preload-error", (_event, preloadPath, error) => {
|
||||||
|
logger.error("Preload script error:", preloadPath, error);
|
||||||
|
});
|
||||||
|
|
||||||
|
mainWindow.webContents.on(
|
||||||
|
"console-message",
|
||||||
|
(_event, _level, message, line, sourceId) => {
|
||||||
|
logger.log("Renderer Console:", line, sourceId, message);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Enable remote debugging when in dev mode
|
||||||
|
if (isDev) {
|
||||||
|
mainWindow.webContents.openDevTools();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
main();
|
// Handle app ready
|
||||||
|
app.whenReady().then(createWindow);
|
||||||
|
|
||||||
|
// Handle all windows closed
|
||||||
|
app.on("window-all-closed", () => {
|
||||||
|
if (process.platform !== "darwin") {
|
||||||
|
app.quit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.on("activate", () => {
|
||||||
|
if (BrowserWindow.getAllWindows().length === 0) {
|
||||||
|
createWindow();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle any errors
|
||||||
|
process.on("uncaughtException", (error) => {
|
||||||
|
logger.error("Uncaught Exception:", error);
|
||||||
|
});
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Write the main process file
|
||||||
|
const mainDest = path.join(electronDistPath, 'main.js');
|
||||||
|
fs.writeFileSync(mainDest, mainContent);
|
||||||
|
|
||||||
|
// Copy preload script if it exists
|
||||||
|
const preloadSrc = path.join(__dirname, '..', 'src', 'electron', 'preload.js');
|
||||||
|
const preloadDest = path.join(electronDistPath, 'preload.js');
|
||||||
|
if (fs.existsSync(preloadSrc)) {
|
||||||
|
console.log(`Copying ${preloadSrc} to ${preloadDest}`);
|
||||||
|
fs.copyFileSync(preloadSrc, preloadDest);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify build structure
|
||||||
|
console.log('\nVerifying build structure:');
|
||||||
|
console.log('Files in dist-electron:', fs.readdirSync(electronDistPath));
|
||||||
|
|
||||||
|
console.log('Build completed successfully!');
|
||||||
183
src/electron/main.ts
Normal file
183
src/electron/main.ts
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
import { app, BrowserWindow } from "electron";
|
||||||
|
import path from "path";
|
||||||
|
import fs from "fs";
|
||||||
|
|
||||||
|
// Simple logger implementation
|
||||||
|
const logger = {
|
||||||
|
log: (...args: unknown[]) => console.log(...args),
|
||||||
|
error: (...args: unknown[]) => console.error(...args),
|
||||||
|
info: (...args: unknown[]) => console.info(...args),
|
||||||
|
warn: (...args: unknown[]) => console.warn(...args),
|
||||||
|
debug: (...args: unknown[]) => console.debug(...args),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if running in dev mode
|
||||||
|
const isDev = process.argv.includes("--inspect");
|
||||||
|
|
||||||
|
function createWindow(): void {
|
||||||
|
// Add before createWindow function
|
||||||
|
const preloadPath = path.join(__dirname, "preload.js");
|
||||||
|
logger.log("Checking preload path:", preloadPath);
|
||||||
|
logger.log("Preload exists:", fs.existsSync(preloadPath));
|
||||||
|
|
||||||
|
// Create the browser window.
|
||||||
|
const mainWindow = new BrowserWindow({
|
||||||
|
width: 1200,
|
||||||
|
height: 800,
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: false,
|
||||||
|
contextIsolation: true,
|
||||||
|
webSecurity: true,
|
||||||
|
allowRunningInsecureContent: false,
|
||||||
|
preload: path.join(__dirname, "preload.js"),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Always open DevTools for now
|
||||||
|
mainWindow.webContents.openDevTools();
|
||||||
|
|
||||||
|
// Intercept requests to fix asset paths
|
||||||
|
mainWindow.webContents.session.webRequest.onBeforeRequest(
|
||||||
|
{
|
||||||
|
urls: [
|
||||||
|
"file://*/*/assets/*",
|
||||||
|
"file://*/assets/*",
|
||||||
|
"file:///assets/*", // Catch absolute paths
|
||||||
|
"<all_urls>", // Catch all URLs as a fallback
|
||||||
|
],
|
||||||
|
},
|
||||||
|
(details, callback) => {
|
||||||
|
let url = details.url;
|
||||||
|
|
||||||
|
// Handle paths that don't start with file://
|
||||||
|
if (!url.startsWith("file://") && url.includes("/assets/")) {
|
||||||
|
url = `file://${path.join(__dirname, "www", url)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle absolute paths starting with /assets/
|
||||||
|
if (url.includes("/assets/") && !url.includes("/www/assets/")) {
|
||||||
|
const baseDir = url.includes("dist-electron")
|
||||||
|
? url.substring(
|
||||||
|
0,
|
||||||
|
url.indexOf("/dist-electron") + "/dist-electron".length,
|
||||||
|
)
|
||||||
|
: `file://${__dirname}`;
|
||||||
|
const assetPath = url.split("/assets/")[1];
|
||||||
|
const newUrl = `${baseDir}/www/assets/${assetPath}`;
|
||||||
|
callback({ redirectURL: newUrl });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback({}); // No redirect for other URLs
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isDev) {
|
||||||
|
// Debug info
|
||||||
|
logger.log("Debug Info:");
|
||||||
|
logger.log("Running in dev mode:", isDev);
|
||||||
|
logger.log("App is packaged:", app.isPackaged);
|
||||||
|
logger.log("Process resource path:", process.resourcesPath);
|
||||||
|
logger.log("App path:", app.getAppPath());
|
||||||
|
logger.log("__dirname:", __dirname);
|
||||||
|
logger.log("process.cwd():", process.cwd());
|
||||||
|
}
|
||||||
|
|
||||||
|
const indexPath = path.join(__dirname, "www", "index.html");
|
||||||
|
|
||||||
|
if (isDev) {
|
||||||
|
logger.log("Loading index from:", indexPath);
|
||||||
|
logger.log("www path:", path.join(__dirname, "www"));
|
||||||
|
logger.log("www assets path:", path.join(__dirname, "www", "assets"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fs.existsSync(indexPath)) {
|
||||||
|
logger.error(`Index file not found at: ${indexPath}`);
|
||||||
|
throw new Error("Index file not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add CSP headers to allow API connections
|
||||||
|
mainWindow.webContents.session.webRequest.onHeadersReceived(
|
||||||
|
(details, callback) => {
|
||||||
|
callback({
|
||||||
|
responseHeaders: {
|
||||||
|
...details.responseHeaders,
|
||||||
|
"Content-Security-Policy": [
|
||||||
|
"default-src 'self';" +
|
||||||
|
"connect-src 'self' https://api.endorser.ch https://*.timesafari.app;" +
|
||||||
|
"img-src 'self' data: https: blob:;" +
|
||||||
|
"script-src 'self' 'unsafe-inline' 'unsafe-eval';" +
|
||||||
|
"style-src 'self' 'unsafe-inline';" +
|
||||||
|
"font-src 'self' data:;",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Load the index.html
|
||||||
|
mainWindow
|
||||||
|
.loadFile(indexPath)
|
||||||
|
.then(() => {
|
||||||
|
logger.log("Successfully loaded index.html");
|
||||||
|
if (isDev) {
|
||||||
|
mainWindow.webContents.openDevTools();
|
||||||
|
logger.log("DevTools opened - running in dev mode");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
logger.error("Failed to load index.html:", err);
|
||||||
|
logger.error("Attempted path:", indexPath);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Listen for console messages from the renderer
|
||||||
|
mainWindow.webContents.on("console-message", (_event, _level, message) => {
|
||||||
|
logger.log("Renderer Console:", message);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add right after creating the BrowserWindow
|
||||||
|
mainWindow.webContents.on(
|
||||||
|
"did-fail-load",
|
||||||
|
(_event, errorCode, errorDescription) => {
|
||||||
|
logger.error("Page failed to load:", errorCode, errorDescription);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
mainWindow.webContents.on("preload-error", (_event, preloadPath, error) => {
|
||||||
|
logger.error("Preload script error:", preloadPath, error);
|
||||||
|
});
|
||||||
|
|
||||||
|
mainWindow.webContents.on(
|
||||||
|
"console-message",
|
||||||
|
(_event, _level, message, line, sourceId) => {
|
||||||
|
logger.log("Renderer Console:", line, sourceId, message);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Enable remote debugging when in dev mode
|
||||||
|
if (isDev) {
|
||||||
|
mainWindow.webContents.openDevTools();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle app ready
|
||||||
|
app.whenReady().then(createWindow);
|
||||||
|
|
||||||
|
// Handle all windows closed
|
||||||
|
app.on("window-all-closed", () => {
|
||||||
|
if (process.platform !== "darwin") {
|
||||||
|
app.quit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.on("activate", () => {
|
||||||
|
if (BrowserWindow.getAllWindows().length === 0) {
|
||||||
|
createWindow();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle any errors
|
||||||
|
process.on("uncaughtException", (error) => {
|
||||||
|
logger.error("Uncaught Exception:", error);
|
||||||
|
});
|
||||||
|
|
||||||
@@ -3,7 +3,7 @@ import { logToDb } from "../db";
|
|||||||
function safeStringify(obj: unknown) {
|
function safeStringify(obj: unknown) {
|
||||||
const seen = new WeakSet();
|
const seen = new WeakSet();
|
||||||
|
|
||||||
return JSON.stringify(obj, (key, value) => {
|
return JSON.stringify(obj, (_key, value) => {
|
||||||
if (typeof value === "object" && value !== null) {
|
if (typeof value === "object" && value !== null) {
|
||||||
if (seen.has(value)) {
|
if (seen.has(value)) {
|
||||||
return "[Circular]";
|
return "[Circular]";
|
||||||
@@ -69,3 +69,11 @@ export const logger = {
|
|||||||
logToDb(message + argsString);
|
logToDb(message + argsString);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add CommonJS export for Electron
|
||||||
|
if (typeof module !== "undefined" && module.exports) {
|
||||||
|
module.exports = { logger };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add default export for ESM
|
||||||
|
export default { logger };
|
||||||
|
|||||||
26
tsconfig.electron.json
Normal file
26
tsconfig.electron.json
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"target": "ES2020",
|
||||||
|
"outDir": "dist-electron",
|
||||||
|
"rootDir": "src",
|
||||||
|
"sourceMap": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowJs": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"types": ["vite/client"],
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/electron/**/*.ts",
|
||||||
|
"src/utils/**/*.ts",
|
||||||
|
"src/constants/**/*.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -4,7 +4,9 @@
|
|||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
"allowSyntheticDefaultImports": true
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"noEmit": true
|
||||||
},
|
},
|
||||||
"include": ["vite.config.*"]
|
"include": ["vite.config.*"]
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,58 @@
|
|||||||
import { defineConfig, mergeConfig } from "vite";
|
import { defineConfig, mergeConfig } from "vite";
|
||||||
import { createBuildConfig } from "./vite.config.common.mts";
|
import { createBuildConfig } from "./vite.config.common.mts";
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
export default defineConfig(async () => {
|
export default defineConfig(async () => {
|
||||||
const baseConfig = await createBuildConfig('electron');
|
const baseConfig = await createBuildConfig('electron');
|
||||||
|
|
||||||
return mergeConfig(baseConfig, {
|
return mergeConfig(baseConfig, {
|
||||||
plugins: [{
|
build: {
|
||||||
|
outDir: 'dist-electron',
|
||||||
|
rollupOptions: {
|
||||||
|
input: {
|
||||||
|
main: path.resolve(__dirname, 'src/electron/main.ts'),
|
||||||
|
preload: path.resolve(__dirname, 'src/electron/preload.js'),
|
||||||
|
},
|
||||||
|
external: ['electron'],
|
||||||
|
output: {
|
||||||
|
format: 'cjs',
|
||||||
|
entryFileNames: '[name].js',
|
||||||
|
assetFileNames: 'assets/[name].[ext]',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
target: 'node18',
|
||||||
|
minify: false,
|
||||||
|
sourcemap: true,
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': path.resolve(__dirname, 'src'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
optimizeDeps: {
|
||||||
|
include: ['@/utils/logger']
|
||||||
|
},
|
||||||
|
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',
|
name: 'remove-sw-imports',
|
||||||
transform(code: string, id: string) {
|
transform(code: string, id: string) {
|
||||||
if (
|
if (
|
||||||
@@ -24,6 +71,12 @@ export default defineConfig(async () => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}]
|
}
|
||||||
|
],
|
||||||
|
ssr: {
|
||||||
|
noExternal: ['@/utils/logger']
|
||||||
|
},
|
||||||
|
base: './',
|
||||||
|
publicDir: 'public',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
Reference in New Issue
Block a user