Merge pull request #34 from electron-vite/v2.0.0

V2.0.0
This commit is contained in:
草鞋没号 2022-06-07 19:08:56 +08:00 committed by GitHub
commit 99d473948b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 277 additions and 11549 deletions

12
.editorconfig Normal file
View File

@ -0,0 +1,12 @@
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false

97
.gitignore vendored
View File

@ -4,86 +4,25 @@ logs
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
# Runtime data node_modules
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
.debug.env
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# ----
dist dist
**/.tmp
release
.DS_Store
dist-ssr dist-ssr
*.local *.local
# Editor directories and files
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
release
.vscode/.debug.env
package-lock.json
pnpm-lock.yaml
yarn.lock

View File

@ -2,13 +2,19 @@ import fs from 'fs'
import path from 'path' import path from 'path'
import { fileURLToPath } from 'url' import { fileURLToPath } from 'url'
import { createRequire } from 'module' import { createRequire } from 'module'
import { spawn } from 'child_process'
const pkg = createRequire(import.meta.url)('../package.json')
const __dirname = path.dirname(fileURLToPath(import.meta.url)) const __dirname = path.dirname(fileURLToPath(import.meta.url))
const require = createRequire(import.meta.url)
const pkg = require('../package.json')
// write .debug.env // write .debug.env
const envContent = Object.entries(pkg.env).map(([key, val]) => `${key}=${val}`) const envContent = Object.entries(pkg.env).map(([key, val]) => `${key}=${val}`)
fs.writeFileSync(path.join(__dirname, '.debug.env'), envContent.join('\n')) fs.writeFileSync(path.join(__dirname, '.debug.env'), envContent.join('\n'))
// bootstrap // bootstrap
import('../scripts/watch.mjs?debug=vscode') spawn(
process.platform === 'win32' ? 'npm.cmd' : 'npm',
['run', 'dev'],
{ stdio: 'inherit' },
)

10
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,10 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"editorconfig.editorconfig",
"mrmlnc.vscode-json5",
"rbbit.typescript-hero",
"syler.sass-indented",
]
}

2
.vscode/launch.json vendored
View File

@ -30,7 +30,7 @@
}, },
"runtimeArgs": [ "runtimeArgs": [
"--remote-debugging-port=9229", "--remote-debugging-port=9229",
"${workspaceRoot}/dist/main/index.cjs" "${workspaceRoot}/dist/electron/main/index.js"
], ],
"envFile": "${workspaceFolder}/.vscode/.debug.env" "envFile": "${workspaceFolder}/.vscode/.debug.env"
}, },

View File

@ -1,4 +1,4 @@
# vite-react-electron # electron-vite-react
![GitHub stars](https://img.shields.io/github/stars/caoxiemeihao/vite-react-electron?color=fa6470&style=flat) ![GitHub stars](https://img.shields.io/github/stars/caoxiemeihao/vite-react-electron?color=fa6470&style=flat)
![GitHub issues](https://img.shields.io/github/issues/caoxiemeihao/vite-react-electron?color=d8b22d&style=flat) ![GitHub issues](https://img.shields.io/github/issues/caoxiemeihao/vite-react-electron?color=d8b22d&style=flat)
@ -21,98 +21,68 @@ You need a basic understanding of `Electron` and `Vite` to get started. But that
npm create electron-vite npm create electron-vite
``` ```
![electron-vite-react.gif](https://github.com/electron-vite/electron-vite-react/blob/main/packages/renderer/public/electron-vite-react.gif?raw=true) ![electron-vite-react.gif](https://github.com/electron-vite/electron-vite-react/blob/main/public/electron-vite-react.gif?raw=true)
## Debug ## Debug
![electron-vite-react-debug.gif](https://github.com/electron-vite/electron-vite-react/blob/main/packages/renderer/public/electron-vite-react-debug.gif?raw=true) ![electron-vite-react-debug.gif](https://github.com/electron-vite/electron-vite-react/blob/main/public/electron-vite-react-debug.gif?raw=true)
<!--
```sh
# clone the project
git clone https://github.com/caoxiemeihao/vite-react-electron.git
# open the project directory
cd vite-react-electron
# install dependencies
npm install
# start the application
npm run dev
# make a production build
npm run build
```
-->
## Directory structure ## Directory structure
Once `dev` or `build` npm-script is executed, the `dist` folder will be generated. It has the same structure as the `packages` folder, the purpose of this design is to ensure the correct path calculation. Once `dev` or `build` npm-script is executed, the `dist` folder will be generated. It has the same structure as the project, the purpose of this design is to ensure the correct path calculation.
```tree ```tree
├── build Resources for the production build ├── electron Electron-related code
| ├── main Main-process source code
| ├── preload Preload-script source code
| └── resources Resources for the production build
| ├── icon.icns Icon for the application on macOS | ├── icon.icns Icon for the application on macOS
| ├── icon.ico Icon for the application | ├── icon.ico Icon for the application
| ├── installerIcon.ico Icon for the application installer | ├── installerIcon.ico Icon for the application installer
| └── uninstallerIcon.ico Icon for the application uninstaller | └── uninstallerIcon.ico Icon for the application uninstaller
| |
├── dist Generated after build according to the "packages" directory
| ├── main
| ├── preload
| └── renderer
|
├── release Generated after production build, contains executables ├── release Generated after production build, contains executables
| └──{version} | └──{version}
| ├── win-unpacked Contains unpacked application executable | ├── {os}-unpacked Contains unpacked application executable
| └── Setup.exe Installer for the application | └── Setup.{ext} Installer for the application
| |
├── scripts ├── public Static assets
| ├── build.mjs Develop script -> npm run build └── src Renderer source code, your React application
| └── watch.mjs Develop script -> npm run dev
|
├── packages
| ├── main Main-process source code
| | └── vite.config.ts
| ├── preload Preload-script source code
| | └── vite.config.ts
| └── renderer Renderer-process source code
| └── vite.config.ts
``` ```
## Use Electron and NodeJS API ## Use Electron and NodeJS API
> 🚧 By default, Electron doesn't support the use of API related to Electron and NodeJS in the Renderer process, but someone might need to use it. If so, you can see the template 👉 **[electron-vite-boilerplate](https://github.com/caoxiemeihao/electron-vite-boilerplate)** > 🚧 By default, Electron doesn't support the use of API related to Electron and NodeJS in the Renderer process, but someone might need to use it. If so, you can see the template 👉 **[electron-vite-boilerplate](https://github.com/electron-vite/electron-vite-boilerplate)**
#### Invoke Electron and NodeJS API in `Preload-script` #### Invoke Electron and NodeJS API in `Preload-script`
- **packages/preload/index.ts** - **electron/preload/index.ts**
```typescript ```typescript
import fs from "fs" import fs from "fs";
import { contextBridge, ipcRenderer } from "electron" import { contextBridge, ipcRenderer } from "electron";
// --------- Expose some API to Renderer-process. --------- // --------- Expose some API to Renderer-process. ---------
contextBridge.exposeInMainWorld("fs", fs) contextBridge.exposeInMainWorld("fs", fs);
contextBridge.exposeInMainWorld("ipcRenderer", ipcRenderer) contextBridge.exposeInMainWorld("ipcRenderer", ipcRenderer);
``` ```
- **packages/renderer/src/global.d.ts** - **src/global.d.ts**
```typescript ```typescript
// Defined in the window // Defined in the window
interface Window { interface Window {
fs: typeof import("fs") fs: typeof import("fs");
ipcRenderer: import("electron").IpcRenderer ipcRenderer: import("electron").IpcRenderer;
} }
``` ```
- **packages/renderer/src/main.ts** - **src/main.ts**
```typescript ```typescript
// Use Electron and NodeJS API in the Renderer-process // Use Electron and NodeJS API in the Renderer-process
console.log("fs", window.fs) console.log("fs", window.fs);
console.log("ipcRenderer", window.ipcRenderer) console.log("ipcRenderer", window.ipcRenderer);
``` ```
## Use SerialPort, SQLite3, or other node-native addons in the Main-process ## Use SerialPort, SQLite3, or other node-native addons in the Main-process
@ -122,8 +92,6 @@ Once `dev` or `build` npm-script is executed, the `dist` folder will be generate
- Main-process, Preload-script are also built with Vite, and they're built as [build.lib](https://vitejs.dev/config/#build-lib). - Main-process, Preload-script are also built with Vite, and they're built as [build.lib](https://vitejs.dev/config/#build-lib).
So they just need to configure Rollup. So they just need to configure Rollup.
**Click to see more** 👉 [packages/main/vite.config.ts](https://github.com/caoxiemeihao/vite-react-electron/blob/main/packages/main/vite.config.ts)
```js ```js
export default { export default {
build: { build: {
@ -138,7 +106,7 @@ export default {
external: ["serialport", "sqlite3"], external: ["serialport", "sqlite3"],
}, },
}, },
} };
``` ```
## `dependencies` vs `devDependencies` ## `dependencies` vs `devDependencies`
@ -148,9 +116,3 @@ export default {
- Like [serialport](https://www.npmjs.com/package/serialport), [sqlite3](https://www.npmjs.com/package/sqlite3) they are node-native modules and should be placed in `dependencies`. In addition, Vite will not build them, but treat them as external modules. - Like [serialport](https://www.npmjs.com/package/serialport), [sqlite3](https://www.npmjs.com/package/sqlite3) they are node-native modules and should be placed in `dependencies`. In addition, Vite will not build them, but treat them as external modules.
- Dependencies like [Vue](https://www.npmjs.com/package/vue) and [React](https://www.npmjs.com/package/react), which are pure javascript modules that can be built with Vite, can be placed in `devDependencies`. This reduces the size of the application. - Dependencies like [Vue](https://www.npmjs.com/package/vue) and [React](https://www.npmjs.com/package/react), which are pure javascript modules that can be built with Vite, can be placed in `devDependencies`. This reduces the size of the application.
<!--
## Result
<img width="400px" src="https://raw.githubusercontent.com/caoxiemeihao/blog/main/vite-react-electron/react-win.png" />
-->

View File

@ -2,36 +2,36 @@
* @see https://www.electron.build/configuration/configuration * @see https://www.electron.build/configuration/configuration
*/ */
{ {
"appId": "YourAppID", appId: "YourAppID",
"productName": "YourAppName", productName: "YourAppName",
"copyright": "Copyright © 2022 ${author}", copyright: "Copyright © 2022 ${author}",
"asar": true, asar: true,
"directories": { directories: {
"output": "release/${version}", output: "release/${version}",
"buildResources": "resources" buildResources: "electron/resources",
}, },
"files": ["dist"], files: ["dist"],
"win": { win: {
"target": [ target: [
{ {
"target": "nsis", target: "nsis",
"arch": ["x64"] arch: ["x64"],
} },
], ],
"artifactName": "${productName}-${version}-Setup.${ext}" artifactName: "${productName}-${version}-Setup.${ext}",
}, },
"nsis": { nsis: {
"oneClick": false, oneClick: false,
"perMachine": false, perMachine: false,
"allowToChangeInstallationDirectory": true, allowToChangeInstallationDirectory: true,
"deleteAppDataOnUninstall": false deleteAppDataOnUninstall: false,
}, },
"mac": { mac: {
"target": ["dmg"], target: ["dmg"],
"artifactName": "${productName}-${version}-Installer.${ext}" artifactName: "${productName}-${version}-Installer.${ext}",
},
linux: {
target: ["AppImage"],
artifactName: "${productName}-${version}-Installer.${ext}",
}, },
"linux": {
"target": ["AppImage"],
"artifactName": "${productName}-${version}-Installer.${ext}"
}
} }

View File

@ -1,8 +1,6 @@
import { app, BrowserWindow, shell } from 'electron' import { app, BrowserWindow, shell } from 'electron'
import { release } from 'os' import { release } from 'os'
import { join } from 'path' import { join } from 'path'
import './samples/electron-store'
import './samples/npm-esm-packages'
// Disable GPU Acceleration for Windows 7 // Disable GPU Acceleration for Windows 7
if (release().startsWith('6.1')) app.disableHardwareAcceleration() if (release().startsWith('6.1')) app.disableHardwareAcceleration()
@ -14,23 +12,27 @@ if (!app.requestSingleInstanceLock()) {
app.quit() app.quit()
process.exit(0) process.exit(0)
} }
process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true'
let win: BrowserWindow | null = null let win: BrowserWindow | null = null
// Here you can add more preload scripts
const splash = join(__dirname, '../preload/splash.js')
// 🚧 Use ['ENV_NAME'] to avoid vite:define plugin
const url = `http://${process.env['VITE_DEV_SERVER_HOST']}:${process.env['VITE_DEV_SERVER_PORT']}`
async function createWindow() { async function createWindow() {
win = new BrowserWindow({ win = new BrowserWindow({
title: 'Main window', title: 'Main window',
webPreferences: { webPreferences: {
preload: join(__dirname, '../preload/index.cjs') preload: splash,
nodeIntegration: true,
contextIsolation: false,
}, },
}) })
if (app.isPackaged) { if (app.isPackaged) {
win.loadFile(join(__dirname, '../renderer/index.html')) win.loadFile(join(__dirname, '../../index.html'))
} else { } else {
// 🚧 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.loadURL(url)
// win.webContents.openDevTools() // win.webContents.openDevTools()
} }

View File

@ -1,10 +1,37 @@
function domReady(condition: DocumentReadyState[] = ['complete', 'interactive']) {
return new Promise(resolve => {
if (condition.includes(document.readyState)) {
resolve(true)
} else {
document.addEventListener('readystatechange', () => {
if (condition.includes(document.readyState)) {
resolve(true)
}
})
}
})
}
const safeDOM = {
append(parent: HTMLElement, child: HTMLElement) {
if (!Array.from(parent.children).find(e => e === child)) {
return parent.appendChild(child)
}
},
remove(parent: HTMLElement, child: HTMLElement) {
if (Array.from(parent.children).find(e => e === child)) {
return parent.removeChild(child)
}
},
}
/** /**
* https://tobiasahlin.com/spinkit * https://tobiasahlin.com/spinkit
* https://connoratherton.com/loaders * https://connoratherton.com/loaders
* https://projects.lukehaas.me/css-loaders * https://projects.lukehaas.me/css-loaders
* https://matejkustec.github.io/SpinThatShit * https://matejkustec.github.io/SpinThatShit
*/ */
export function useLoading() { function useLoading() {
const className = `loaders-css__square-spin` const className = `loaders-css__square-spin`
const styleContent = ` const styleContent = `
@keyframes square-spin { @keyframes square-spin {
@ -43,25 +70,18 @@ export function useLoading() {
return { return {
appendLoading() { appendLoading() {
safe.append(document.head, oStyle) safeDOM.append(document.head, oStyle)
safe.append(document.body, oDiv) safeDOM.append(document.body, oDiv)
}, },
removeLoading() { removeLoading() {
safe.remove(document.head, oStyle) safeDOM.remove(document.head, oStyle)
safe.remove(document.body, oDiv) safeDOM.remove(document.body, oDiv)
}, },
} }
} }
const safe = { // ----------------------------------------------------------------------
append(parent: HTMLElement, child: HTMLElement) {
if (!Array.from(parent.children).find(e => e === child)) { const { appendLoading, removeLoading } = useLoading()
return parent.appendChild(child) window.removeLoading = removeLoading
} domReady().then(appendLoading)
},
remove(parent: HTMLElement, child: HTMLElement) {
if (Array.from(parent.children).find(e => e === child)) {
return parent.removeChild(child)
}
},
}

View File

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

@ -4,10 +4,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/src/assets/favicon.svg" /> <link rel="icon" type="image/svg+xml" href="/src/assets/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
http-equiv="Content-Security-Policy"
content="script-src 'self' 'unsafe-inline';"
/>
<title>Vite App</title> <title>Vite App</title>
</head> </head>
<body> <body>

10854
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,15 +1,15 @@
{ {
"name": "vite-react-electron", "name": "electron-vite-react",
"productName": "Electron", "productName": "Electron",
"private": true, "private": true,
"version": "1.0.0", "version": "2.0.0",
"description": "Vite React Electron boilerplate.", "description": "Vite React Electron boilerplate.",
"author": "草鞋没号 <308487730@qq.com>", "author": "草鞋没号 <308487730@qq.com>",
"license": "MIT", "license": "MIT",
"main": "dist/main/index.cjs", "main": "dist/electron/main/index.js",
"scripts": { "scripts": {
"dev": "node scripts/watch.mjs", "dev": "vite",
"build": "tsc --noEmit -p packages/renderer/tsconfig.json && node scripts/build.mjs && electron-builder" "build": "tsc && vite build && electron-builder"
}, },
"engines": { "engines": {
"node": ">=14.17.0" "node": ">=14.17.0"
@ -18,20 +18,17 @@
"electron-store": "^8.0.1" "electron-store": "^8.0.1"
}, },
"devDependencies": { "devDependencies": {
"@types/react": "^18.0.9", "@types/react": "^18.0.12",
"@types/react-dom": "^18.0.4", "@types/react-dom": "^18.0.5",
"@vitejs/plugin-react": "^1.3.2", "@vitejs/plugin-react": "^1.3.2",
"electron": "^18.2.4", "electron": "^19.0.3",
"electron-builder": "^23.0.3", "electron-builder": "^23.0.3",
"execa": "^6.1.0",
"react": "^18.1.0", "react": "^18.1.0",
"react-dom": "^18.1.0", "react-dom": "^18.1.0",
"sass": "^1.51.0", "sass": "^1.52.2",
"typescript": "^4.6.4", "typescript": "^4.7.3",
"vite": "^2.9.9", "vite": "^2.9.10",
"vite-plugin-esmodule": "^1.2.5", "vite-plugin-electron": "^0.4.7"
"vite-plugin-optimizer": "^1.3.3",
"vite-plugin-resolve": "^2.1.1"
}, },
"env": { "env": {
"VITE_DEV_SERVER_HOST": "127.0.0.1", "VITE_DEV_SERVER_HOST": "127.0.0.1",

View File

@ -1,19 +0,0 @@
/**
* Example of 'electron-store' usage.
*/
import { ipcMain } from 'electron'
import Store from 'electron-store'
/**
* Expose 'electron-store' to Renderer-process through 'ipcMain.handle'
*/
const store = new Store()
ipcMain.handle(
'electron-store',
async (_event, methodSign: string, ...args: any[]) => {
if (typeof (store as any)[methodSign] === 'function') {
return (store as any)[methodSign](...args)
}
return (store as any)[methodSign]
}
)

View File

@ -1,7 +0,0 @@
import { execa } from 'execa'
(async () => {
const { stdout } = await execa('echo', ['unicorns'])
// console.log(stdout) // unicorns
})()

View File

@ -1,31 +0,0 @@
import { builtinModules } from 'module'
import { defineConfig } from 'vite'
import esmodule from 'vite-plugin-esmodule'
import pkg from '../../package.json'
export default defineConfig({
root: __dirname,
plugins: [
esmodule([
'execa',
]),
],
build: {
outDir: '../../dist/main',
emptyOutDir: true,
minify: process.env./* from mode option */NODE_ENV === 'production',
sourcemap: true,
lib: {
entry: 'index.ts',
formats: ['cjs'],
fileName: () => '[name].cjs',
},
rollupOptions: {
external: [
'electron',
...builtinModules,
...Object.keys(pkg.dependencies || {}),
],
},
},
})

View File

@ -1,36 +0,0 @@
import fs from 'fs'
import { contextBridge, ipcRenderer } from 'electron'
import { domReady } from './utils'
import { useLoading } from './loading'
const { appendLoading, removeLoading } = useLoading()
;(async () => {
await domReady()
appendLoading()
})()
// --------- Expose some API to the Renderer process. ---------
contextBridge.exposeInMainWorld('fs', fs)
contextBridge.exposeInMainWorld('removeLoading', removeLoading)
contextBridge.exposeInMainWorld('ipcRenderer', withPrototype(ipcRenderer))
// `exposeInMainWorld` can't detect attributes and methods of `prototype`, manually patching it.
function withPrototype(obj: Record<string, any>) {
const protos = Object.getPrototypeOf(obj)
for (const [key, value] of Object.entries(protos)) {
if (Object.prototype.hasOwnProperty.call(obj, key)) continue
if (typeof value === 'function') {
// 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)
}
} else {
obj[key] = value
}
}
return obj
}

View File

@ -1,16 +0,0 @@
/** Document ready */
export const domReady = (
condition: DocumentReadyState[] = ['complete', 'interactive']
) => {
return new Promise((resolve) => {
if (condition.includes(document.readyState)) {
resolve(true)
} else {
document.addEventListener('readystatechange', () => {
if (condition.includes(document.readyState)) {
resolve(true)
}
})
}
})
}

View File

@ -1,31 +0,0 @@
import { join } from 'path'
import { builtinModules } from 'module'
import { defineConfig } from 'vite'
import pkg from '../../package.json'
export default defineConfig({
root: __dirname,
build: {
outDir: '../../dist/preload',
emptyOutDir: true,
minify: process.env./* from mode option */NODE_ENV === 'production',
// https://github.com/caoxiemeihao/electron-vue-vite/issues/61
sourcemap: 'inline',
rollupOptions: {
input: {
// multiple entry
index: join(__dirname, 'index.ts'),
},
output: {
format: 'cjs',
entryFileNames: '[name].cjs',
manualChunks: {},
},
external: [
'electron',
...builtinModules,
...Object.keys(pkg.dependencies || {}),
],
},
},
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -1,16 +0,0 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App'
import './samples/electron-store'
import './samples/preload-module'
import './styles/index.css'
const root = createRoot(document.getElementById('root')!)
root.render(
<StrictMode>
<App />
</StrictMode>
)
window.removeLoading()

View File

@ -1,31 +0,0 @@
// Usage of 'electron-store'
const store = {
async get(key: string) {
const { invoke } = window.ipcRenderer
let value = await invoke('electron-store', 'get', key)
try {
value = JSON.parse(value)
} finally {
return value
}
},
async set(key: string, value: any) {
const { invoke } = window.ipcRenderer
let val = value
try {
if (value && typeof value === 'object') {
val = JSON.stringify(value)
}
} finally {
await invoke('electron-store', 'set', key, val)
}
},
};
(async () => {
await store.set('Date.now', Date.now())
console.log('electron-store ->', 'Date.now:', await store.get('Date.now'))
console.log('electron-store ->', 'path:', await window.ipcRenderer.invoke('electron-store', 'path'))
})();
export { }

View File

@ -1,9 +0,0 @@
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)
})
export default {}

View File

@ -1,21 +0,0 @@
{
"extends": "../../paths.json",
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["src"]
}

View File

@ -1,144 +0,0 @@
import { join } from 'path'
import { builtinModules } from 'module'
import { defineConfig, Plugin } from 'vite'
import react from '@vitejs/plugin-react'
import optimizer from 'vite-plugin-optimizer'
import resolve, { lib2esm } from 'vite-plugin-resolve'
import pkg from '../../package.json'
/**
* @see https://vitejs.dev/config/
*/
export default defineConfig({
mode: process.env.NODE_ENV,
root: __dirname,
plugins: [
react(),
electron(),
resolve({
/**
* Here you can resolve some CommonJs module.
* Or some Node.js native modules they may not be built correctly by vite.
* At the same time, these modules should be put in `dependencies`,
* because they will not be built by vite, but will be packaged into `app.asar` by electron-builder
*/
// ESM format code snippets
'electron-store': 'export default require("electron-store");',
/**
* Node.js native module
* Use lib2esm() to easy to convert ESM
* Equivalent to
*
* ```js
* sqlite3: () => `
* const _M_ = require('sqlite3');
* const _D_ = _M_.default || _M_;
* export { _D_ as default }
* `
* ```
*/
sqlite3: lib2esm('sqlite3', { format: 'cjs' }),
serialport: lib2esm(
// CJS lib name
'serialport',
// export memebers
[
'SerialPort',
'SerialPortMock',
],
{ format: 'cjs' },
),
}),
],
base: './',
build: {
outDir: '../../dist/renderer',
emptyOutDir: true,
sourcemap: true,
},
resolve: {
alias: {
'@': join(__dirname, 'src'),
},
},
server: {
host: pkg.env.VITE_DEV_SERVER_HOST,
port: pkg.env.VITE_DEV_SERVER_PORT,
},
})
/**
* For usage of Electron and NodeJS APIs in the Renderer process
* @see https://github.com/caoxiemeihao/electron-vue-vite/issues/52
*/
export function electron(
entries: Parameters<typeof optimizer>[0] = {}
): Plugin {
const builtins = builtinModules.filter((t) => !t.startsWith('_'))
/**
* @see https://github.com/caoxiemeihao/vite-plugins/tree/main/packages/resolve#readme
*/
return optimizer({
electron: electronExport(),
...builtinModulesExport(builtins),
...entries,
})
function electronExport() {
return `
/**
* For all exported modules see https://www.electronjs.org/docs/latest/api/clipboard -> Renderer Process Modules
*/
const electron = require("electron");
const {
clipboard,
nativeImage,
shell,
contextBridge,
crashReporter,
ipcRenderer,
webFrame,
desktopCapturer,
deprecate,
} = electron;
export {
electron as default,
clipboard,
nativeImage,
shell,
contextBridge,
crashReporter,
ipcRenderer,
webFrame,
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 = `
${requireModule}
${exportDefault}
${exportMembers}
`
return { [moduleId]: nodeModuleCode }
})
.reduce((memo, item) => Object.assign(memo, item), {})
}
}

View File

@ -1,9 +0,0 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": ["packages/renderer/src/*"]
}
}
}

View File

Before

Width:  |  Height:  |  Size: 9.6 MiB

After

Width:  |  Height:  |  Size: 9.6 MiB

View File

Before

Width:  |  Height:  |  Size: 3.4 MiB

After

Width:  |  Height:  |  Size: 3.4 MiB

View File

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -1,5 +0,0 @@
import { build } from 'vite'
await build({ configFile: 'packages/main/vite.config.ts' })
await build({ configFile: 'packages/preload/vite.config.ts' })
await build({ configFile: 'packages/renderer/vite.config.ts' })

View File

@ -1,70 +0,0 @@
import { spawn } from 'child_process'
import { createServer, build } from 'vite'
import electron from 'electron'
const query = new URLSearchParams(import.meta.url.split('?')[1])
const debug = query.has('debug')
/**
* @type {(server: import('vite').ViteDevServer) => Promise<import('rollup').RollupWatcher>}
*/
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,
})
/**
* @type {import('vite').Plugin}
*/
const startElectron = {
name: 'electron-main-watcher',
writeBundle() {
if (electronProcess) {
electronProcess.removeAllListeners()
electronProcess.kill()
}
electronProcess = spawn(electron, ['.'], { stdio: 'inherit', env })
electronProcess.once('exit', process.exit)
},
}
return build({
configFile: 'packages/main/vite.config.ts',
mode: 'development',
plugins: [!debug && startElectron].filter(Boolean),
build: {
watch: {},
},
})
}
/**
* @type {(server: import('vite').ViteDevServer) => Promise<import('rollup').RollupWatcher>}
*/
function watchPreload(server) {
return build({
configFile: 'packages/preload/vite.config.ts',
mode: 'development',
plugins: [{
name: 'electron-preload-watcher',
writeBundle() {
server.ws.send({ type: 'full-reload' })
},
}],
build: {
watch: {},
},
})
}
// bootstrap
const server = await createServer({ configFile: 'packages/renderer/vite.config.ts' })
await server.listen()
await watchPreload(server)
await watchMain(server)

View File

@ -1,10 +1,10 @@
import electron from '@/assets/electron.png'
import react from '@/assets/react.svg'
import vite from '@/assets/vite.svg'
import styles from '@/styles/app.module.scss'
import { useState } from 'react' import { useState } from 'react'
import electron from '/electron.png'
import react from '/react.svg'
import vite from '/vite.svg'
import styles from 'styles/app.module.scss'
const App = () => { const App: React.FC = () => {
const [count, setCount] = useState(0) const [count, setCount] = useState(0)
return ( return (
@ -60,8 +60,8 @@ const App = () => {
</a> </a>
<div className={styles.staticPublic}> <div className={styles.staticPublic}>
Place static files into the{' '} Place static files into the{' '}
<code>src/renderer/public</code> folder <code>/public</code> folder
<img style={{ width: 90 }} src="./images/node.png" /> <img style={{ width: 77 }} src="./node.png" />
</div> </div>
</div> </div>
</header> </header>

View File

@ -1,9 +1,8 @@
export { } export { }
declare global { declare global {
interface Window { interface Window {
// Expose some Api through preload script // Expose API through preload script
fs: typeof import('fs') fs: typeof import('fs')
ipcRenderer: import('electron').IpcRenderer ipcRenderer: import('electron').IpcRenderer
removeLoading: () => void removeLoading: () => void

12
src/main.tsx Normal file
View File

@ -0,0 +1,12 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import 'styles/index.css'
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>
)
window.removeLoading()

View File

@ -1,15 +1,27 @@
{ {
"extends": "./paths.json",
"compilerOptions": { "compilerOptions": {
"baseUrl": ".",
"target": "ESNext", "target": "ESNext",
"module": "ESNext", "useDefineForClassFields": true,
"allowJs": true, "lib": ["DOM", "DOM.Iterable", "ESNext"],
"paths": {
"@/*": ["src/*"],
"styles/*": ["src/assets/styles/*"]
},
"allowJs": false,
"skipLibCheck": true, "skipLibCheck": true,
"skipDefaultLibCheck": true, "esModuleInterop": false,
"esModuleInterop": true, "allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node", "moduleResolution": "Node",
"resolveJsonModule": true, "resolveJsonModule": true,
"strict": true, "isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx" "jsx": "react-jsx"
},
"include": ["src", "types.d.ts"],
"references": [{ "path": "./tsconfig.node.json" }]
} }
}

8
tsconfig.node.json Normal file
View File

@ -0,0 +1,8 @@
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node"
},
"include": ["vite.config.ts"]
}

1
types.d.ts vendored
View File

@ -1,4 +1,3 @@
declare namespace NodeJS { declare namespace NodeJS {
interface ProcessEnv { interface ProcessEnv {
NODE_ENV: 'development' | 'production' NODE_ENV: 'development' | 'production'

52
vite.config.ts Normal file
View File

@ -0,0 +1,52 @@
import { rmSync } from 'fs'
import { join } from 'path'
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import electron from 'vite-plugin-electron'
import renderer from 'vite-plugin-electron/renderer'
import pkg from './package.json'
rmSync(join(__dirname, 'dist'), { recursive: true, force: true }) // v14.14.0
// https://vitejs.dev/config/
export default defineConfig({
resolve: {
alias: {
'@': join(__dirname, 'src'),
'styles': join(__dirname, 'src/assets/styles'),
},
},
plugins: [
react(),
electron({
main: {
entry: 'electron/main/index.ts',
vite: {
build: {
sourcemap: false,
outDir: 'dist/electron/main',
},
},
},
preload: {
input: {
// You can configure multiple preload scripts here
splash: join(__dirname, 'electron/preload/splash.ts'),
},
vite: {
build: {
// For debug
sourcemap: 'inline',
outDir: 'dist/electron/preload',
}
},
},
}),
// Enables use of Node.js API in the Renderer-process
renderer(),
],
server: {
host: pkg.env.VITE_DEV_SERVER_HOST,
port: pkg.env.VITE_DEV_SERVER_PORT,
},
})