diff --git a/packages/main/index.ts b/packages/main/index.ts index beb45e5..394f3cc 100644 --- a/packages/main/index.ts +++ b/packages/main/index.ts @@ -1,10 +1,12 @@ -import os from 'os' -import path from 'path' -import { app, BrowserWindow } from 'electron' +import { app, BrowserWindow, shell } from 'electron' +import { release } from 'os' +import { join } from 'path' -// https://stackoverflow.com/questions/42524606/how-to-get-windows-version-using-node-js -const isWin7 = os.release().startsWith('6.1') -if (isWin7) app.disableHardwareAcceleration() +// Disable GPU Acceleration for Windows 7 +if (release().startsWith('6.1')) app.disableHardwareAcceleration() + +// Set application name for Windows 10+ notifications +if (process.platform === 'win32') app.setAppUserModelId(app.getName()) if (!app.requestSingleInstanceLock()) { app.quit() @@ -15,34 +17,44 @@ let win: BrowserWindow | null = null async function createWindow() { win = new BrowserWindow({ + title: 'Main window', webPreferences: { - preload: path.join(__dirname, '../preload/index.cjs'), + preload: join(__dirname, '../preload/index.cjs') }, }) if (app.isPackaged) { - win.loadFile(path.join(__dirname, '../renderer/index.html')) + win.loadFile(join(__dirname, '../renderer/index.html')) } else { - const pkg = await import('../../package.json') - const url = `http://${pkg.env.HOST || '127.0.0.1'}:${pkg.env.PORT}` + // 🚧 Use ['ENV_NAME'] avoid vite:define plugin + const url = `http://${process.env['VITE_DEV_SERVER_HOST']}:${process.env['VITE_DEV_SERVER_PORT']}` win.loadURL(url) win.webContents.openDevTools() } + + // Test active push message to Renderer-process + win.webContents.on('did-finish-load', () => { + win?.webContents.send('main-process-message', (new Date).toLocaleString()) + }) + + // Make all links open with the browser, not with the application + win.webContents.setWindowOpenHandler(({ url }) => { + if (url.startsWith('https:')) shell.openExternal(url) + return { action: 'deny' } + }) } app.whenReady().then(createWindow) app.on('window-all-closed', () => { win = null - if (process.platform !== 'darwin') { - app.quit() - } + if (process.platform !== 'darwin') app.quit() }) app.on('second-instance', () => { if (win) { - // someone tried to run a second instance, we should focus our window. + // Focus on the main window if the user tried to open another if (win.isMinimized()) win.restore() win.focus() } @@ -56,15 +68,3 @@ app.on('activate', () => { createWindow() } }) - -// @TODO -// auto update -/* if (app.isPackaged) { - app.whenReady() - .then(() => import('electron-updater')) - .then(({ autoUpdater }) => autoUpdater.checkForUpdatesAndNotify()) - .catch((e) => - // maybe you need to record some log files. - console.error('Failed check update:', e) - ) -} */ diff --git a/packages/main/vite.config.ts b/packages/main/vite.config.ts new file mode 100644 index 0000000..c415c0c --- /dev/null +++ b/packages/main/vite.config.ts @@ -0,0 +1,24 @@ +import { builtinModules } from 'module' +import { defineConfig } from 'vite' +import pkg from '../../package.json' + +export default defineConfig({ + root: __dirname, + build: { + outDir: '../../dist/main', + lib: { + entry: 'index.ts', + formats: ['cjs'], + fileName: () => '[name].cjs', + }, + minify: process.env./* from mode option */NODE_ENV === 'production', + emptyOutDir: true, + rollupOptions: { + external: [ + 'electron', + ...builtinModules, + ...Object.keys(pkg.dependencies || {}), + ], + }, + }, +}) diff --git a/packages/preload/index.ts b/packages/preload/index.ts index cb2242e..c3ef1ba 100644 --- a/packages/preload/index.ts +++ b/packages/preload/index.ts @@ -1,22 +1,22 @@ import fs from 'fs' -import { contextBridge, ipcRenderer, IpcRenderer } from 'electron' +import { contextBridge, ipcRenderer } from 'electron' import { domReady } from './utils' import { useLoading } from './loading' -const isDev = process.env.NODE_ENV === 'development' -const { removeLoading, appendLoading } = useLoading() +const { appendLoading, removeLoading } = useLoading() + +;(async () => { + await domReady() -domReady().then(() => { appendLoading() -}) +})() - -// --------- Expose some API to Renderer process. --------- +// --------- Expose some API to the Renderer process. --------- contextBridge.exposeInMainWorld('fs', fs) contextBridge.exposeInMainWorld('removeLoading', removeLoading) contextBridge.exposeInMainWorld('ipcRenderer', withPrototype(ipcRenderer)) -// `exposeInMainWorld` can not detect `prototype` attribute and methods, manually patch it. +// `exposeInMainWorld` can't detect attributes and methods of `prototype`, manually patching it. function withPrototype(obj: Record) { const protos = Object.getPrototypeOf(obj) @@ -24,7 +24,7 @@ function withPrototype(obj: Record) { if (Object.prototype.hasOwnProperty.call(obj, key)) continue if (typeof value === 'function') { - // Some native API not work in Renderer-process, like `NodeJS.EventEmitter['on']`. Wrap a function patch it. + // Some native APIs, like `NodeJS.EventEmitter['on']`, don't work in the Renderer process. Wrapping them into a function. obj[key] = function (...args: any) { return value.call(obj, ...args) } diff --git a/packages/preload/vite.config.ts b/packages/preload/vite.config.ts new file mode 100644 index 0000000..3f3ddce --- /dev/null +++ b/packages/preload/vite.config.ts @@ -0,0 +1,24 @@ +import { builtinModules } from 'module' +import { defineConfig } from 'vite' +import pkg from '../../package.json' + +export default defineConfig({ + root: __dirname, + build: { + outDir: '../../dist/preload', + lib: { + entry: 'index.ts', + formats: ['cjs'], + fileName: () => '[name].cjs', + }, + minify: process.env./* from mode option */NODE_ENV === 'production', + emptyOutDir: true, + rollupOptions: { + external: [ + 'electron', + ...builtinModules, + ...Object.keys(pkg.dependencies || {}), + ], + }, + }, +}) diff --git a/packages/renderer/src/App.vue b/packages/renderer/src/App.vue index 807ac14..c145f27 100644 --- a/packages/renderer/src/App.vue +++ b/packages/renderer/src/App.vue @@ -14,6 +14,7 @@ import HelloWorld from './components/HelloWorld.vue'
+ asjdfljasldfjasldfjlsdjflskjdf----------- Place static files into the src/renderer/public folder
diff --git a/packages/renderer/src/main.ts b/packages/renderer/src/main.ts index 85cdeb3..a6c38af 100644 --- a/packages/renderer/src/main.ts +++ b/packages/renderer/src/main.ts @@ -5,5 +5,10 @@ createApp(App) .mount('#app') .$nextTick(window.removeLoading) -console.log('fs', window.fs) -console.log('ipcRenderer', window.ipcRenderer) +// console.log('fs', window.fs) +// console.log('ipcRenderer', window.ipcRenderer) + +// Usage of ipcRenderer.on +window.ipcRenderer.on('main-process-message', (_event, ...args) => { + console.log('[Receive Main-process message]:', ...args) +}) diff --git a/packages/renderer/vite.config.ts b/packages/renderer/vite.config.ts index 4b9dae9..691a095 100644 --- a/packages/renderer/vite.config.ts +++ b/packages/renderer/vite.config.ts @@ -1,4 +1,3 @@ -import { join } from 'path' import { builtinModules } from 'module' import { defineConfig, Plugin } from 'vite' import vue from '@vitejs/plugin-vue' @@ -13,11 +12,12 @@ export default defineConfig({ vue(), resolveElectron( /** - * you can custom other module in here - * 🚧 need to make sure custom-resolve-module in `dependencies`, that will ensure that the electron-builder can package them correctly + * Here you can specify other modules + * 🚧 You have to make sure that your module is in `dependencies` and not in the` devDependencies`, + * which will ensure that the electron-builder can package it correctly * @example * { - * 'electron-store': 'const Store = require("electron-store"); export defalut Store;', + * 'electron-store': 'const Store = require("electron-store"); export default Store;', * } */ ), @@ -28,17 +28,22 @@ export default defineConfig({ outDir: '../../dist/renderer', }, server: { - host: pkg.env.HOST, port: pkg.env.PORT, }, }) -// ------- For use Electron, NodeJs in Renderer-process ------- -// https://github.com/caoxiemeihao/electron-vue-vite/issues/52 -export function resolveElectron(resolves: Parameters[0] = {}): Plugin { - const builtins = builtinModules.filter(t => !t.startsWith('_')) +/** + * For usage of Electron and NodeJS APIs in the Renderer process + * @see https://github.com/caoxiemeihao/electron-vue-vite/issues/52 + */ +export function resolveElectron( + resolves: Parameters[0] = {} +): Plugin { + const builtins = builtinModules.filter((t) => !t.startsWith('_')) - // https://github.com/caoxiemeihao/vite-plugins/tree/main/packages/resolve#readme + /** + * @see https://github.com/caoxiemeihao/vite-plugins/tree/main/packages/resolve#readme + */ return resolve({ electron: electronExport(), ...builtinModulesExport(builtins), @@ -48,7 +53,7 @@ export function resolveElectron(resolves: Parameters[0] = {}): P function electronExport() { return ` /** - * All exports module see https://www.electronjs.org -> API -> Renderer Process Modules + * For all exported modules see https://www.electronjs.org/docs/latest/api/clipboard -> Renderer Process Modules */ const electron = require("electron"); const { @@ -62,7 +67,7 @@ export function resolveElectron(resolves: Parameters[0] = {}): P desktopCapturer, deprecate, } = electron; - + export { electron as default, clipboard, @@ -75,24 +80,29 @@ export function resolveElectron(resolves: Parameters[0] = {}): P desktopCapturer, deprecate, } - ` +` } function builtinModulesExport(modules: string[]) { - return modules.map((moduleId) => { - const nodeModule = require(moduleId) - const requireModule = `const M = require("${moduleId}");` - const exportDefault = `export default M;` - const exportMembers = Object.keys(nodeModule).map(attr => `export const ${attr} = M.${attr}`).join(';\n') + ';' - const nodeModuleCode = ` + return modules + .map((moduleId) => { + const nodeModule = require(moduleId) + const requireModule = `const M = require("${moduleId}");` + const exportDefault = `export default M;` + const exportMembers = + Object.keys(nodeModule) + .map((attr) => `export const ${attr} = M.${attr}`) + .join(';\n') + ';' + const nodeModuleCode = ` ${requireModule} ${exportDefault} ${exportMembers} - ` +` - return { [moduleId]: nodeModuleCode } - }).reduce((memo, item) => Object.assign(memo, item), {}) + return { [moduleId]: nodeModuleCode } + }) + .reduce((memo, item) => Object.assign(memo, item), {}) } } diff --git a/scripts/build.mjs b/scripts/build.mjs index 2e538fb..cb967ca 100644 --- a/scripts/build.mjs +++ b/scripts/build.mjs @@ -1,25 +1,5 @@ -process.env.NODE_ENV = 'production' - -import { dirname, join } from 'path' -import { fileURLToPath } from 'url' import { build } from 'vite' -const __dirname = dirname(fileURLToPath(import.meta.url)) - -await build({ - configFile: 'scripts/vite.config.mjs', - root: join(__dirname, '../packages/main'), - build: { - outDir: '../../dist/main', - }, -}) - -await build({ - configFile: 'scripts/vite.config.mjs', - root: join(__dirname, '../packages/preload'), - build: { - outDir: '../../dist/preload', - }, -}) - +await build({ configFile: 'packages/main/vite.config.ts' }) +await build({ configFile: 'packages/preload/vite.config.ts' }) await build({ configFile: 'packages/renderer/vite.config.ts' }) diff --git a/scripts/vite.config.mjs b/scripts/vite.config.mjs deleted file mode 100644 index 2e38c2e..0000000 --- a/scripts/vite.config.mjs +++ /dev/null @@ -1,32 +0,0 @@ -import { builtinModules, createRequire } from 'module' -import { defineConfig } from 'vite' - -const require = createRequire(import.meta.url) -const pkg = require('../package.json') - -export default defineConfig({ - mode: process.env.NODE_ENV, - // root: [path], - build: { - // outDir: [path], - lib: { - entry: 'index.ts', - formats: ['cjs'], - fileName: () => '[name].cjs', - }, - minify: process.env.NODE_ENV === 'production', - emptyOutDir: true, - rollupOptions: { - external: [ - 'electron', - ...builtinModules, - ...Object.keys(pkg.dependencies || {}), - ], - }, - }, -}) - -/** - * 2202-02-05 - * @todo process.env.NODE_ENV always return true, need to submit PR to vite and improvement vite:define plugin. - */ diff --git a/scripts/watch.mjs b/scripts/watch.mjs index fd7f1cc..d578f53 100644 --- a/scripts/watch.mjs +++ b/scripts/watch.mjs @@ -1,42 +1,34 @@ -process.env.NODE_ENV = 'development' - -import { fileURLToPath } from 'url' -import { join, dirname } from 'path' -import { createRequire } from 'module' import { spawn } from 'child_process' import { createServer, build } from 'vite' import electron from 'electron' -const __dirname = dirname(fileURLToPath(import.meta.url)) -const require = createRequire(import.meta.url) -const pkg = require('../package.json') - /** - * @type {() => Promise} + * @type {(server: import('vite').ViteDevServer) => Promise} */ -function watchMain() { +function watchMain(server) { /** * @type {import('child_process').ChildProcessWithoutNullStreams | null} */ let electronProcess = null + const address = server.httpServer.address() + const env = Object.assign(process.env, { + VITE_DEV_SERVER_HOST: address.address, + VITE_DEV_SERVER_PORT: address.port, + }) return build({ - configFile: 'scripts/vite.config.mjs', - root: join(__dirname, '../packages/main'), - build: { - outDir: '../../dist/main', - watch: true, - }, + configFile: 'packages/main/vite.config.ts', + mode: 'development', plugins: [{ name: 'electron-main-watcher', writeBundle() { electronProcess && electronProcess.kill() - electronProcess = spawn(electron, ['.'], { - stdio: 'inherit', - env: Object.assign(process.env, pkg.env), - }) + electronProcess = spawn(electron, ['.'], { stdio: 'inherit', env }) }, }], + build: { + watch: true, + }, }) } @@ -45,18 +37,17 @@ function watchMain() { */ function watchPreload(server) { return build({ - configFile: 'scripts/vite.config.mjs', - root: join(__dirname, '../packages/preload'), - build: { - outDir: '../../dist/preload', - watch: true, - }, + configFile: 'packages/preload/vite.config.ts', + mode: 'development', plugins: [{ name: 'electron-preload-watcher', writeBundle() { server.ws.send({ type: 'full-reload' }) }, }], + build: { + watch: true, + }, }) } @@ -65,4 +56,4 @@ const server = await createServer({ configFile: 'packages/renderer/vite.config.t await server.listen() await watchPreload(server) -await watchMain() +await watchMain(server)