forked from trent_larson/crowd-funder-for-time-pwa
WIP (fix): improve electron build configuration and service worker handling
- Properly disable service workers in electron builds - Add CSP headers for electron security - Fix path resolution in electron context - Improve preload script error handling and IPC setup - Update build scripts for better electron/capacitor compatibility - Fix router path handling in electron context - Remove electron-builder dependency - Streamline build process and output structure This change improves the stability and security of electron builds while maintaining PWA functionality in web builds. Service workers are now properly disabled in electron context, and path resolution issues are fixed.
This commit is contained in:
11
BUILDING.md
11
BUILDING.md
@@ -46,21 +46,16 @@ To build the desktop application:
|
|||||||
|
|
||||||
1. Run the Electron build:
|
1. Run the Electron build:
|
||||||
```bash
|
```bash
|
||||||
npm run build -- --mode electron
|
npm run build:electron
|
||||||
```
|
```
|
||||||
|
|
||||||
2. The built files will be in `dist-electron`.
|
2. The built files will be in `dist-electron`.
|
||||||
|
|
||||||
3. To create installable packages:
|
3. To run the desktop app:
|
||||||
```bash
|
```bash
|
||||||
npm run electron:build
|
electron dist-electron
|
||||||
```
|
```
|
||||||
|
|
||||||
This will create platform-specific installers in `dist-electron-build`:
|
|
||||||
- Windows: `.exe` installer
|
|
||||||
- macOS: `.dmg` file
|
|
||||||
- Linux: `.AppImage` file
|
|
||||||
|
|
||||||
## Mobile Builds (Capacitor)
|
## Mobile Builds (Capacitor)
|
||||||
|
|
||||||
### iOS Build
|
### iOS Build
|
||||||
|
|||||||
2591
package-lock.json
generated
2591
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -14,13 +14,12 @@
|
|||||||
"prebuild": "eslint --ext .js,.ts,.vue --ignore-path .gitignore src && node sw_combine.js",
|
"prebuild": "eslint --ext .js,.ts,.vue --ignore-path .gitignore src && node sw_combine.js",
|
||||||
"test-local": "npx playwright test -c playwright.config-local.ts --trace on",
|
"test-local": "npx playwright test -c playwright.config-local.ts --trace on",
|
||||||
"test-all": "npm run build && npx playwright test -c playwright.config-local.ts --trace on",
|
"test-all": "npm run build && npx playwright test -c playwright.config-local.ts --trace on",
|
||||||
"clean:electron": "rimraf dist-electron dist-electron-build",
|
"clean:electron": "rimraf dist-electron",
|
||||||
"build:electron": "npm run clean:electron && vite build --mode electron && node scripts/build-electron.js",
|
"build:electron": "npm run clean:electron && vite build --mode electron && node scripts/build-electron.js",
|
||||||
"build:capacitor": "vite build --mode capacitor",
|
"build:capacitor": "vite build --mode capacitor",
|
||||||
"build:web": "vite build",
|
"build:web": "vite build",
|
||||||
"electron:build:linux": "npm run build:electron && electron-builder --linux",
|
"electron:dev": "npm run build:electron && electron dist-electron --inspect",
|
||||||
"electron:build:mac": "npm run build:electron && npx electron-builder --mac",
|
"electron:start": "electron dist-electron"
|
||||||
"electron:build:win": "npm run build:electron && npx electron-builder --win"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@capacitor/android": "^6.2.0",
|
"@capacitor/android": "^6.2.0",
|
||||||
@@ -107,7 +106,6 @@
|
|||||||
"@vue/eslint-config-typescript": "^11.0.3",
|
"@vue/eslint-config-typescript": "^11.0.3",
|
||||||
"autoprefixer": "^10.4.19",
|
"autoprefixer": "^10.4.19",
|
||||||
"electron": "^33.2.1",
|
"electron": "^33.2.1",
|
||||||
"electron-builder": "^25.1.8",
|
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-prettier": "^5.2.1",
|
"eslint-plugin-prettier": "^5.2.1",
|
||||||
|
|||||||
@@ -1,61 +1,48 @@
|
|||||||
const fs = require('fs-extra');
|
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
try {
|
try {
|
||||||
console.log('Starting electron build process...');
|
console.log('Starting electron build process...');
|
||||||
|
|
||||||
// Clean directories
|
// Create dist directory if it doesn't exist
|
||||||
const distElectronDir = path.resolve(__dirname, '../dist-electron');
|
const distElectronDir = path.resolve(__dirname, '../dist-electron');
|
||||||
const buildDir = path.resolve(__dirname, '../dist-electron-build');
|
await fs.ensureDir(distElectronDir);
|
||||||
await fs.emptyDir(distElectronDir);
|
|
||||||
await fs.emptyDir(buildDir);
|
|
||||||
console.log('Cleaned directories');
|
|
||||||
|
|
||||||
// First build the web app if it doesn't exist
|
// Copy web files
|
||||||
const webDist = path.resolve(__dirname, '../dist');
|
|
||||||
if (!await fs.pathExists(webDist)) {
|
|
||||||
console.log('Web dist not found, building web app first...');
|
|
||||||
throw new Error('Please run \'npm run build\' first to build the web app');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy web files to www directory
|
|
||||||
const wwwDir = path.join(distElectronDir, 'www');
|
const wwwDir = path.join(distElectronDir, 'www');
|
||||||
await fs.copy(webDist, wwwDir);
|
await fs.ensureDir(wwwDir);
|
||||||
|
await fs.copy('dist', wwwDir);
|
||||||
|
|
||||||
// Fix paths in index.html
|
// Copy and fix index.html
|
||||||
const indexPath = path.join(wwwDir, 'index.html');
|
const indexPath = path.join(wwwDir, 'index.html');
|
||||||
let indexContent = await fs.readFile(indexPath, 'utf8');
|
let indexContent = await fs.readFile(indexPath, 'utf8');
|
||||||
|
|
||||||
|
// Fix paths in index.html
|
||||||
indexContent = indexContent
|
indexContent = indexContent
|
||||||
// Fix absolute paths to be relative
|
|
||||||
.replace(/src="\//g, 'src="\./')
|
.replace(/src="\//g, 'src="\./')
|
||||||
.replace(/href="\//g, 'href="\./')
|
.replace(/href="\//g, 'href="\./')
|
||||||
// Fix relative asset paths
|
|
||||||
.replace(/src="\.\.\/assets\//g, 'src="./www/assets/')
|
.replace(/src="\.\.\/assets\//g, 'src="./www/assets/')
|
||||||
.replace(/href="\.\.\/assets\//g, 'href="./www/assets/')
|
.replace(/href="\.\.\/assets\//g, 'href="./www/assets/');
|
||||||
// Fix modulepreload paths specifically
|
|
||||||
.replace(/<link [^>]*rel="modulepreload"[^>]*href="(?!\.?\/www\/)(\/\.\/)?assets\//g, '<link rel="modulepreload" as="script" crossorigin="" href="./www/assets/')
|
|
||||||
.replace(/<link [^>]*rel="modulepreload"[^>]*href="(?!\.?\/www\/)(\/)?assets\//g, '<link rel="modulepreload" as="script" crossorigin="" href="./www/assets/')
|
|
||||||
// Fix stylesheet paths
|
|
||||||
.replace(/<link [^>]*rel="stylesheet"[^>]*href="(?!\.?\/www\/)(\/\.\/)?assets\//g, '<link rel="stylesheet" crossorigin="" href="./www/assets/')
|
|
||||||
.replace(/<link [^>]*rel="stylesheet"[^>]*href="(?!\.?\/www\/)(\/)?assets\//g, '<link rel="stylesheet" crossorigin="" href="./www/assets/')
|
|
||||||
// Fix any remaining asset paths that don't already have www
|
|
||||||
.replace(/(['"]\/?)((?!www\/)(assets\/))/, '"./www/assets/');
|
|
||||||
await fs.writeFile(indexPath, indexContent);
|
await fs.writeFile(indexPath, indexContent);
|
||||||
|
|
||||||
console.log('Copied and fixed web files in:', wwwDir);
|
console.log('Copied and fixed web files in:', wwwDir);
|
||||||
|
|
||||||
// Copy main process files
|
// Copy main process files
|
||||||
|
console.log('Copying main process files...');
|
||||||
const mainProcessFiles = [
|
const mainProcessFiles = [
|
||||||
'src/electron/main.js',
|
['src/electron/main.js', 'main.js'],
|
||||||
|
['src/electron/preload.js', 'preload.js']
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const file of mainProcessFiles) {
|
for (const [src, dest] of mainProcessFiles) {
|
||||||
const destPath = path.join(distElectronDir, path.basename(file));
|
const destPath = path.join(distElectronDir, dest);
|
||||||
await fs.copy(file, destPath);
|
console.log(`Copying ${src} to ${destPath}`);
|
||||||
|
await fs.copy(src, destPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the production package.json
|
// Create package.json for production
|
||||||
const devPackageJson = require('../package.json');
|
const devPackageJson = require('../package.json');
|
||||||
const prodPackageJson = {
|
const prodPackageJson = {
|
||||||
name: devPackageJson.name,
|
name: devPackageJson.name,
|
||||||
@@ -72,26 +59,26 @@ async function main() {
|
|||||||
{ spaces: 2 }
|
{ spaces: 2 }
|
||||||
);
|
);
|
||||||
|
|
||||||
// Verify the structure
|
// Verify the build
|
||||||
console.log('\nVerifying build structure:');
|
console.log('\nVerifying build structure:');
|
||||||
const printDir = async (dir, prefix = '') => {
|
const files = await fs.readdir(distElectronDir);
|
||||||
const items = await fs.readdir(dir);
|
console.log('Files in dist-electron:', files);
|
||||||
for (const item of items) {
|
|
||||||
const fullPath = path.join(dir, item);
|
|
||||||
const stat = await fs.stat(fullPath);
|
|
||||||
console.log(`${prefix}${item}${stat.isDirectory() ? '/' : ''}`);
|
|
||||||
if (stat.isDirectory()) {
|
|
||||||
await printDir(fullPath, `${prefix} `);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
await printDir(distElectronDir);
|
|
||||||
|
|
||||||
console.log('\nBuild completed successfully!');
|
if (!files.includes('main.js')) {
|
||||||
|
throw new Error('main.js not found in build directory');
|
||||||
|
}
|
||||||
|
if (!files.includes('preload.js')) {
|
||||||
|
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!');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Build failed:', error);
|
console.error('Build failed:', error);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
main().catch(console.error);
|
main();
|
||||||
@@ -2,99 +2,103 @@ const { app, BrowserWindow } = require("electron");
|
|||||||
const path = require("path");
|
const path = require("path");
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
|
|
||||||
|
// Check if running in dev mode
|
||||||
|
const isDev = process.argv.includes('--inspect');
|
||||||
|
|
||||||
function createWindow() {
|
function createWindow() {
|
||||||
|
// Add before createWindow function
|
||||||
|
const preloadPath = path.join(__dirname, 'preload.js');
|
||||||
|
console.log('Checking preload path:', preloadPath);
|
||||||
|
console.log('Preload exists:', fs.existsSync(preloadPath));
|
||||||
|
|
||||||
// Create the browser window.
|
// Create the browser window.
|
||||||
const mainWindow = new BrowserWindow({
|
const mainWindow = new BrowserWindow({
|
||||||
width: 1200,
|
width: 1200,
|
||||||
height: 800,
|
height: 800,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
nodeIntegration: true,
|
nodeIntegration: false,
|
||||||
contextIsolation: false,
|
contextIsolation: true,
|
||||||
webSecurity: true,
|
webSecurity: true,
|
||||||
allowRunningInsecureContent: false,
|
allowRunningInsecureContent: false,
|
||||||
|
preload: path.join(__dirname, 'preload.js')
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Disable service worker in Electron
|
// Debug info
|
||||||
mainWindow.webContents.session.setPermissionRequestHandler(
|
|
||||||
(webContents, permission, callback) => {
|
|
||||||
if (permission === "serviceWorker") {
|
|
||||||
return callback(false);
|
|
||||||
}
|
|
||||||
callback(true);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// Get the correct app path for packaged and development environments
|
|
||||||
const appPath = app.isPackaged ? process.resourcesPath : app.getAppPath();
|
|
||||||
|
|
||||||
// Add debug logging for paths
|
|
||||||
console.log("Debug Info:");
|
console.log("Debug Info:");
|
||||||
|
console.log("Running in dev mode:", isDev);
|
||||||
console.log("App is packaged:", app.isPackaged);
|
console.log("App is packaged:", app.isPackaged);
|
||||||
console.log("Process resource path:", process.resourcesPath);
|
console.log("Process resource path:", process.resourcesPath);
|
||||||
console.log("App path:", appPath);
|
console.log("App path:", app.getAppPath());
|
||||||
console.log("__dirname:", __dirname);
|
console.log("__dirname:", __dirname);
|
||||||
console.log("process.cwd():", process.cwd());
|
console.log("process.cwd():", process.cwd());
|
||||||
console.log("www path:", path.join(process.resourcesPath, "www"));
|
|
||||||
console.log(
|
|
||||||
"www assets path:",
|
|
||||||
path.join(process.resourcesPath, "www", "assets"),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Try both possible www locations
|
const indexPath = path.join(__dirname, 'www', 'index.html');
|
||||||
const possiblePaths = [
|
console.log("www path:", path.join(__dirname, 'www'));
|
||||||
path.join(appPath, "www", "index.html"),
|
console.log("www assets path:", path.join(__dirname, 'www', 'assets'));
|
||||||
path.join(appPath, "..", "www", "index.html"),
|
|
||||||
path.join(process.resourcesPath, "www", "index.html"),
|
|
||||||
];
|
|
||||||
|
|
||||||
let indexPath;
|
if (!fs.existsSync(indexPath)) {
|
||||||
for (const testPath of possiblePaths) {
|
console.error(`Index file not found at: ${indexPath}`);
|
||||||
console.log("Testing path:", testPath);
|
throw new Error('Index file not found');
|
||||||
if (fs.existsSync(testPath)) {
|
|
||||||
indexPath = testPath;
|
|
||||||
console.log("Found valid path:", indexPath);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set CSP headers
|
||||||
|
mainWindow.webContents.session.webRequest.onHeadersReceived((details, callback) => {
|
||||||
|
callback({
|
||||||
|
responseHeaders: {
|
||||||
|
...details.responseHeaders,
|
||||||
|
'Content-Security-Policy': [
|
||||||
|
"default-src 'self';" +
|
||||||
|
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;" +
|
||||||
|
"font-src 'self' https://fonts.gstatic.com;" +
|
||||||
|
"script-src 'self' 'unsafe-eval' 'unsafe-inline';" +
|
||||||
|
"img-src 'self' data: https:;"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Load the index.html
|
// Load the index.html
|
||||||
mainWindow
|
mainWindow
|
||||||
.loadFile(indexPath)
|
.loadFile(indexPath)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
console.log("Successfully loaded index.html");
|
console.log("Successfully loaded index.html");
|
||||||
// Always open DevTools in packaged app for debugging
|
if (isDev) {
|
||||||
mainWindow.webContents.openDevTools();
|
mainWindow.webContents.openDevTools();
|
||||||
|
console.log("DevTools opened - running in dev mode");
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.error("Failed to load index.html:", err);
|
console.error("Failed to load index.html:", err);
|
||||||
console.error("Attempted path:", indexPath);
|
console.error("Attempted path:", indexPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Listen for page errors
|
|
||||||
mainWindow.webContents.on(
|
|
||||||
"did-fail-load",
|
|
||||||
(event, errorCode, errorDescription) => {
|
|
||||||
console.error("Page failed to load:", errorCode, errorDescription);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// Listen for console messages from the renderer
|
// Listen for console messages from the renderer
|
||||||
mainWindow.webContents.on("console-message", (_event, level, message) => {
|
mainWindow.webContents.on("console-message", (_event, level, message) => {
|
||||||
console.log("Renderer Console:", message);
|
console.log("Renderer Console:", message);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Add right after creating the BrowserWindow
|
||||||
|
mainWindow.webContents.on('did-fail-load', (event, errorCode, errorDescription) => {
|
||||||
|
console.error('Page failed to load:', errorCode, errorDescription);
|
||||||
|
});
|
||||||
|
|
||||||
|
mainWindow.webContents.on('preload-error', (event, preloadPath, error) => {
|
||||||
|
console.error('Preload script error:', preloadPath, error);
|
||||||
|
});
|
||||||
|
|
||||||
|
mainWindow.webContents.on('console-message', (event, level, message, line, sourceId) => {
|
||||||
|
console.log('Renderer Console:', message);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Enable remote debugging when in dev mode
|
||||||
|
if (isDev) {
|
||||||
|
mainWindow.webContents.openDevTools();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle app ready
|
// Handle app ready
|
||||||
app.whenReady().then(() => {
|
app.whenReady().then(createWindow);
|
||||||
createWindow();
|
|
||||||
|
|
||||||
app.on("activate", () => {
|
|
||||||
if (BrowserWindow.getAllWindows().length === 0) {
|
|
||||||
createWindow();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle all windows closed
|
// Handle all windows closed
|
||||||
app.on("window-all-closed", () => {
|
app.on("window-all-closed", () => {
|
||||||
@@ -103,6 +107,12 @@ app.on("window-all-closed", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.on("activate", () => {
|
||||||
|
if (BrowserWindow.getAllWindows().length === 0) {
|
||||||
|
createWindow();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Handle any errors
|
// Handle any errors
|
||||||
process.on("uncaughtException", (error) => {
|
process.on("uncaughtException", (error) => {
|
||||||
console.error("Uncaught Exception:", error);
|
console.error("Uncaught Exception:", error);
|
||||||
|
|||||||
@@ -1,5 +1,55 @@
|
|||||||
const { contextBridge } = require("electron");
|
const { contextBridge, ipcRenderer } = require('electron');
|
||||||
|
|
||||||
contextBridge.exposeInMainWorld("api", {
|
// Use a more direct path resolution approach
|
||||||
logMessage: (message) => console.log(`[Electron]: ${message}`),
|
const getPath = (pathType) => {
|
||||||
});
|
switch(pathType) {
|
||||||
|
case 'userData':
|
||||||
|
return process.env.APPDATA || (
|
||||||
|
process.platform === 'darwin'
|
||||||
|
? `${process.env.HOME}/Library/Application Support`
|
||||||
|
: `${process.env.HOME}/.local/share`
|
||||||
|
);
|
||||||
|
case 'home':
|
||||||
|
return process.env.HOME;
|
||||||
|
case 'appPath':
|
||||||
|
return process.resourcesPath;
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('Preload script starting...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
contextBridge.exposeInMainWorld('electronAPI', {
|
||||||
|
// Path utilities
|
||||||
|
getPath,
|
||||||
|
|
||||||
|
// IPC functions
|
||||||
|
send: (channel, data) => {
|
||||||
|
const validChannels = ['toMain'];
|
||||||
|
if (validChannels.includes(channel)) {
|
||||||
|
ipcRenderer.send(channel, data);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
receive: (channel, func) => {
|
||||||
|
const validChannels = ['fromMain'];
|
||||||
|
if (validChannels.includes(channel)) {
|
||||||
|
ipcRenderer.on(channel, (event, ...args) => func(...args));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Environment info
|
||||||
|
env: {
|
||||||
|
isElectron: true,
|
||||||
|
isDev: process.env.NODE_ENV === 'development'
|
||||||
|
},
|
||||||
|
// Path utilities
|
||||||
|
getBasePath: () => {
|
||||||
|
return process.env.NODE_ENV === 'development' ? '/' : './';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Preload script completed successfully');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in preload script:', error);
|
||||||
|
}
|
||||||
@@ -2,14 +2,11 @@
|
|||||||
|
|
||||||
import { register } from "register-service-worker";
|
import { register } from "register-service-worker";
|
||||||
|
|
||||||
// NODE_ENV is "production" by default with "vite build". See https://vitejs.dev/guide/env-and-mode
|
// Only register service worker if explicitly enabled and in production
|
||||||
if (import.meta.env.NODE_ENV === "production") {
|
if (process.env.VITE_PWA_ENABLED === 'true' && process.env.NODE_ENV === "production") {
|
||||||
register("/sw_scripts-combined.js", {
|
register(`${process.env.BASE_URL}sw.js`, {
|
||||||
ready() {
|
ready() {
|
||||||
console.log(
|
console.log("Service worker is active.");
|
||||||
"App is being served from cache by a service worker.\n" +
|
|
||||||
"For more details, visit https://goo.gl/AFskqB",
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
registered() {
|
registered() {
|
||||||
console.log("Service worker has been registered.");
|
console.log("Service worker has been registered.");
|
||||||
@@ -24,12 +21,12 @@ if (import.meta.env.NODE_ENV === "production") {
|
|||||||
console.log("New content is available; please refresh.");
|
console.log("New content is available; please refresh.");
|
||||||
},
|
},
|
||||||
offline() {
|
offline() {
|
||||||
console.log(
|
console.log("No internet connection found. App is running in offline mode.");
|
||||||
"No internet connection found. App is running in offline mode.",
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
error(error) {
|
error(error) {
|
||||||
console.error("Error during service worker registration:", error);
|
console.error("Error during service worker registration:", error);
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
console.log("Service worker registration skipped - not enabled or not in production");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -284,9 +284,9 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const isElectron = window.location.protocol === "file:"; // Check if running in Electron
|
const isElectron = window.location.protocol === "file:";
|
||||||
const initialPath = isElectron
|
const initialPath = isElectron
|
||||||
? window.location.pathname.replace("/dist-electron/index.html", "/")
|
? window.location.pathname.split('/dist-electron/www/')[1] || '/'
|
||||||
: window.location.pathname;
|
: window.location.pathname;
|
||||||
|
|
||||||
const history = isElectron
|
const history = isElectron
|
||||||
|
|||||||
@@ -1,16 +1,14 @@
|
|||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
import { promises as fs } from "fs";
|
import { promises as fs } from "fs";
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
export async function loadAppConfig() {
|
export async function loadAppConfig() {
|
||||||
const packageJson = await loadPackageJson();
|
const packageJson = await loadPackageJson();
|
||||||
const appName = process.env.TIME_SAFARI_APP_TITLE || packageJson.name;
|
const appName = process.env.TIME_SAFARI_APP_TITLE || packageJson.name;
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
pwaConfig: {
|
pwaConfig: {
|
||||||
registerType: "autoUpdate",
|
|
||||||
strategies: "injectManifest",
|
|
||||||
srcDir: ".",
|
|
||||||
filename: "sw_scripts-combined.js",
|
|
||||||
manifest: {
|
manifest: {
|
||||||
name: appName,
|
name: appName,
|
||||||
short_name: appName,
|
short_name: appName,
|
||||||
@@ -36,34 +34,21 @@ export async function loadAppConfig() {
|
|||||||
sizes: "512x512",
|
sizes: "512x512",
|
||||||
type: "image/png",
|
type: "image/png",
|
||||||
purpose: "maskable",
|
purpose: "maskable",
|
||||||
},
|
}
|
||||||
],
|
]
|
||||||
share_target: {
|
}
|
||||||
action: "/share-target",
|
|
||||||
method: "POST",
|
|
||||||
enctype: "multipart/form-data",
|
|
||||||
params: {
|
|
||||||
files: [
|
|
||||||
{
|
|
||||||
name: "photo",
|
|
||||||
accept: ["image/*"],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
aliasConfig: {
|
aliasConfig: {
|
||||||
"@": path.resolve(__dirname, "./src"),
|
"@": path.resolve(path.dirname(__dirname), "src"),
|
||||||
buffer: path.resolve(__dirname, "node_modules", "buffer"),
|
buffer: path.resolve(path.dirname(__dirname), "node_modules", "buffer"),
|
||||||
"dexie-export-import/dist/import":
|
"dexie-export-import/dist/import": "dexie-export-import/dist/import/index.js",
|
||||||
"dexie-export-import/dist/import/index.js",
|
}
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadPackageJson() {
|
async function loadPackageJson() {
|
||||||
const packageJsonPath = path.resolve(__dirname, "package.json");
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
const packageJsonPath = path.resolve(path.dirname(__dirname), "package.json");
|
||||||
const packageJsonData = await fs.readFile(packageJsonPath, "utf-8");
|
const packageJsonData = await fs.readFile(packageJsonPath, "utf-8");
|
||||||
return JSON.parse(packageJsonData);
|
return JSON.parse(packageJsonData);
|
||||||
}
|
}
|
||||||
|
|||||||
114
vite.config.mjs
114
vite.config.mjs
@@ -4,10 +4,15 @@ import vue from "@vitejs/plugin-vue";
|
|||||||
import dotenv from "dotenv";
|
import dotenv from "dotenv";
|
||||||
import { loadAppConfig } from "./vite.config.utils";
|
import { loadAppConfig } from "./vite.config.utils";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
// Load environment variables from .env file
|
// Load environment variables from .env file
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
|
// Get dirname in ESM context
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
// Load application configuration
|
// Load application configuration
|
||||||
const appConfig = loadAppConfig();
|
const appConfig = loadAppConfig();
|
||||||
|
|
||||||
@@ -15,48 +20,101 @@ export default defineConfig(({ mode }) => {
|
|||||||
const isElectron = mode === "electron";
|
const isElectron = mode === "electron";
|
||||||
const isCapacitor = mode === "capacitor";
|
const isCapacitor = mode === "capacitor";
|
||||||
|
|
||||||
// Set output directory based on build mode
|
// Completely disable PWA features for electron builds
|
||||||
const outDir = isElectron
|
if (isElectron) {
|
||||||
? "dist-electron/www"
|
process.env.VITE_PWA_ENABLED = 'false';
|
||||||
: isCapacitor
|
}
|
||||||
? "dist-capacitor"
|
|
||||||
: "dist";
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
base: isElectron ? "./" : "/",
|
base: isElectron ? "./" : "/",
|
||||||
server: {
|
server: {
|
||||||
port: process.env.VITE_PORT || 8080,
|
port: process.env.VITE_PORT || 8080,
|
||||||
|
fs: {
|
||||||
|
strict: false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
outDir,
|
outDir: isElectron ? "dist-electron" : "dist",
|
||||||
|
assetsDir: 'assets',
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
...(isElectron && {
|
external: ['electron', 'path'],
|
||||||
input: {
|
input: {
|
||||||
index: path.resolve(__dirname, 'index.html')
|
main: path.resolve(__dirname, 'index.html')
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
dir: outDir,
|
manualChunks(id) {
|
||||||
format: 'cjs',
|
if (isElectron && (
|
||||||
entryFileNames: 'assets/[name].[hash].js',
|
id.includes('registerServiceWorker') ||
|
||||||
chunkFileNames: 'assets/[name].[hash].js',
|
id.includes('register-service-worker') ||
|
||||||
assetFileNames: 'assets/[name].[hash][extname]'
|
id.includes('workbox') ||
|
||||||
|
id.includes('sw_scripts') ||
|
||||||
|
id.includes('PushNotificationPermission')
|
||||||
|
)) {
|
||||||
|
return null; // Exclude these modules completely
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
},
|
||||||
|
chunkSizeWarningLimit: 1000
|
||||||
|
},
|
||||||
|
define: {
|
||||||
|
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
|
||||||
|
'process.env.VITE_PWA_ENABLED': JSON.stringify(!isElectron),
|
||||||
|
__dirname: isElectron ? JSON.stringify(process.cwd()) : '""',
|
||||||
|
'navigator.serviceWorker': isElectron ? 'undefined' : 'navigator.serviceWorker'
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
vue(),
|
vue(),
|
||||||
...(isElectron
|
{
|
||||||
? []
|
name: 'remove-sw-imports',
|
||||||
: [
|
transform(code, id) {
|
||||||
VitePWA({
|
if (isElectron) {
|
||||||
...appConfig.pwaConfig,
|
if (
|
||||||
disable: isElectron
|
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, '')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
...(!isElectron && !isCapacitor ? [
|
||||||
|
VitePWA({
|
||||||
|
disable: true,
|
||||||
|
registerType: 'autoUpdate',
|
||||||
|
injectRegister: null,
|
||||||
|
workbox: {
|
||||||
|
cleanupOutdatedCaches: true,
|
||||||
|
skipWaiting: true,
|
||||||
|
clientsClaim: true,
|
||||||
|
sourcemap: true
|
||||||
|
},
|
||||||
|
manifest: appConfig.pwaConfig?.manifest,
|
||||||
|
devOptions: {
|
||||||
|
enabled: false
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
] : []),
|
||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: appConfig.aliasConfig,
|
alias: appConfig.aliasConfig
|
||||||
},
|
},
|
||||||
|
optimizeDeps: {
|
||||||
|
exclude: isElectron ? [
|
||||||
|
'register-service-worker',
|
||||||
|
'workbox-window',
|
||||||
|
'web-push',
|
||||||
|
'serviceworker-webpack-plugin'
|
||||||
|
] : []
|
||||||
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user