Compare commits

...

48 Commits

Author SHA1 Message Date
alex8088
1b411d3633 release: v3.1.0 2025-03-25 21:30:53 +08:00
alex8088
b56d3c2d21 fix(bytecodePlugin): optimize 'use strict' directive replacement (#681) 2025-03-16 19:24:22 +08:00
alex8088
f2eff25268 release: v3.1.0-beta.0 2025-03-12 22:05:32 +08:00
alex8088
2d8e513e07 chore(deps): update esbuild to v0.25 2025-03-12 21:52:14 +08:00
alex8088
d8063320dc chore(deps): update globals to v16 2025-03-12 21:43:32 +08:00
alex8088
f33c5b2abe chore(deps): update all non-major dependencies 2025-03-12 21:40:35 +08:00
alex.wei
e91e70c105
Merge pull request #729 from jonz94/electron-35
perf: build compatibility target for Electron 35
2025-03-11 10:10:09 +08:00
jonz94
ea144aef19
perf: build compatibility target for Electron 35 2025-03-09 04:16:52 +08:00
alex8088
987c55ee8b release: v3.0.0 2025-02-16 20:37:18 +08:00
alex8088
8064bd81ff release: v3.0.0-beta.0 2025-01-22 23:05:02 +08:00
alex8088
6e8572d9b7 feat: resolve conditions for preload 2025-01-22 22:24:04 +08:00
alex8088
4b47ef0bd4 perf: build compatilibity target for Electron 34 2025-01-22 21:16:13 +08:00
alex8088
5a5af050b2 chore(deps): update @type/node to v22 2025-01-22 21:13:05 +08:00
alex8088
96ae3c5cd9 chore(deps): update vite to v6 2025-01-22 01:24:58 +08:00
alex8088
79ac91dee2 chore(deps): update esbuild to v0.24 2025-01-22 01:15:46 +08:00
alex8088
1599d730f6 chore(deps): update @rollup/plugin-typescript to v12 2025-01-22 01:12:57 +08:00
alex8088
3c6e08b2f2 chore(deps): update @rollup/plugin-node-resolve to v16 2025-01-22 01:10:54 +08:00
alex8088
dfe6a3e3f8 chore(deps): update all non-major dependencies 2025-01-22 01:04:31 +08:00
alex8088
6c01417909 chore: move to eslint flat config 2025-01-22 00:47:48 +08:00
kye shimizu
5ffd49eddc
perf: build compatilibity target for Electron 33 (#651) 2024-11-11 23:49:02 +08:00
alex8088
bf1220875f perf: build compatilibity target for Electron 32 2024-08-21 21:49:44 +08:00
alex8088
02e0dff9f4 release: v2.3.0 2024-06-23 22:48:15 +08:00
alex8088
0a02ace008 chore(deps): update esbuild to v0.21 2024-06-22 23:22:41 +08:00
alex8088
e638dcae1b chore(deps): update @typescript-eslint/* to v7 2024-06-22 23:20:01 +08:00
alex8088
3264abc70b chore(deps): update all non-major dependencies 2024-06-22 23:09:09 +08:00
alex8088
19b42225f8 perf: improve cjs shim 2024-06-22 22:28:08 +08:00
alex8088
d9aaf24f84 fix: default mode should not overrite user config mode 2024-06-21 01:03:01 +08:00
Krystian Otto
3605aca1e8
fix: not using the mode from the config file (#539) 2024-06-21 00:49:24 +08:00
Justin Carrus
73dfee5a4f
fix: don't handle module ID that begin with \0 (#530) 2024-06-15 22:03:45 +08:00
alex8088
b3185d7fc5 feat: resolve import.meta.[dirname|filename] to support CommonJS format 2024-06-15 21:49:51 +08:00
alex8088
d79de5abb6 perf: build compatilibity target for Electron 31 2024-06-13 23:45:44 +08:00
alex8088
1838bdbf3e release: v2.2.0 2024-04-23 21:39:16 +08:00
alex8088
ff23f7d44d chore: use rollup-plugin-rm to clean dist 2024-04-21 21:46:06 +08:00
alex8088
a48e12a9df fix(types): narrow down the return type of defineConfig 2024-04-21 21:15:15 +08:00
alex8088
1abedce6c2 refactor(config): defineConfig types 2024-04-21 19:59:46 +08:00
alex8088
c2c655367f chore: fix camelcase typo 2024-04-21 19:04:33 +08:00
alex8088
6170705eae perf: build compatilibity target for Electron 30 2024-04-20 23:50:12 +08:00
alex8088
eeaa7de45c feat: export mergeConfig from vite (#471) 2024-04-20 23:42:45 +08:00
alex8088
f42a9d370f release: v2.1.0 2024-03-03 22:00:56 +08:00
alex8088
08ff86f2c4 feat: easy way to fork processes and use workers 2024-03-03 19:08:48 +08:00
alex8088
0ce505a12f perf(bytecodePlugin): warn that strings cannot be protected when minification is enabled (#417) 2024-03-03 16:45:50 +08:00
alex8088
ad891af811 fix: config via build.lib fails when default entry point not found (#393) 2024-03-01 23:06:09 +08:00
alex8088
27ade03acf chore: format 2024-03-01 22:33:56 +08:00
Luke Hagar
52fce25787
perf: allow integrating more complex render solutions (#412) 2024-02-24 01:01:26 +08:00
alex8088
19489a28c8 perf: build compatilibity target for Electron 29 2024-02-24 00:31:41 +08:00
alex8088
7f13ea2a84 release: v2.0.0 2024-01-09 00:05:04 +08:00
alex8088
932fc556f5 release: v2.0.0-beta.4 2024-01-06 23:57:05 +08:00
alex8088
a12646f25e fix: electorn's export subpaths also need to be externalized #372 2024-01-06 11:33:53 +08:00
20 changed files with 1312 additions and 1275 deletions

View File

@ -1,2 +0,0 @@
node_modules
dist

View File

@ -1,39 +0,0 @@
module.exports = {
root: true,
env: {
commonjs: true,
es6: true,
node: true
},
parser: '@typescript-eslint/parser',
parserOptions: {
sourceType: 'module',
ecmaVersion: 2022
},
plugins: ['@typescript-eslint'],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/eslint-recommended',
'plugin:prettier/recommended'
],
rules: {
'prettier/prettier': 'warn',
'no-empty': ['warn', { allowEmptyCatch: true }],
'@typescript-eslint/ban-ts-comment': ['error', { 'ts-ignore': 'allow-with-description' }],
'@typescript-eslint/explicit-function-return-type': 'error',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-empty-function': ['error', { allow: ['arrowFunctions'] }],
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-var-requires': 'off'
},
overrides: [
{
files: ['*.js'],
rules: {
'@typescript-eslint/explicit-function-return-type': 'off'
}
}
]
}

2
.gitignore vendored
View File

@ -1,5 +1,5 @@
node_modules node_modules
dist dist
.DS_Store .DS_Store
.eslintcache
*.log* *.log*

View File

@ -1,33 +1,84 @@
### v2.0.0-beta.3 (_2024-01-04_) ### v3.1.0 (_2025-03-25_)
- feat: config file supports "type": "module" in package.json - fix(bytecodePlugin): optimize 'use strict' directive replacement ([#681](https://github.com/alex8088/electron-vite/issues/681))
- perf: build compatilibity target for Electron 35 ([#729](https://github.com/alex8088/electron-vite/pull/729))
- chore(deps): update all non-major dependencies
- chore(deps): update globals to v16
- chore(deps): update esbuild to v0.25
### v2.0.0-beta.2 (_2023-12-19_) ### v3.1.0-beta.0 (_2025-03-12_)
- feat: support for passing arguments to electron in dev and preview commands ([#339](https://github.com/alex8088/electron-vite/pull/339)) See [v3.1.0-beta.0 changelog](https://github.com/alex8088/electron-vite/blob/v3.1.0-beta.0/CHANGELOG.md)
- perf(externalizeDepsPlugin): use cached package data to improve performance
- perf: loadEnv api also needs to load shared env variables prefixed with VITE\_
### v2.0.0-beta.1 (_2023-12-14_) ### v3.0.0 (_2025-02-16_)
- feat: env variables prefixed with VITE\_ will be shared in main process and renderer - feat: resolve conditions for preload
- fix: externalizeDepPlugin not work - perf: build compatilibity target for Electron 32
- perf: dev error message - perf: build compatilibity target for Electron 33 ([#651](https://github.com/alex8088/electron-vite/pull/651))
- perf: build compatilibity target for Electron 34
- chore: move to eslint flat config
- chore(deps): update all non-major dependencies
- chore(deps): update @rollup/plugin-node-resolve to v16
- chore(deps): update @rollup/plugin-typescript to v12
- chore(deps): update esbuild to v0.24
- chore(deps): update vite to v6
- chore(deps): update @type/node to v22
### v2.0.0-beta.0 (_2023-12-13_) ### v3.0.0-beta.0 (_2025-01-22_)
See [v3.0.0-beta.0 changelog](https://github.com/alex8088/electron-vite/blob/v3.0.0-beta.0/CHANGELOG.md)
### v2.3.0 (_2024-06-23_)
- feat: resolve import.meta.\[dirname|filename\] to support CommonJS format
- fix: don't handle module ID that begin with \0 ([#530](https://github.com/alex8088/electron-vite/pull/530))
- fix: not using the mode from the config file ([#539](https://github.com/alex8088/electron-vite/pull/539))
- fix: default mode should not overrite user config mode
- perf: build compatilibity target for Electron 31
- perf: improve cjs shim
- chore(deps): update all non-major dependencies
- chore(deps): update @typescript-eslint/\* to v7
- chore(deps): update esbuild to v0.21
### v2.2.0 (_2024-04-21_)
- feat: export mergeConfig from vite ([#471](https://github.com/alex8088/electron-vite/issues/471))
- fix(types): narrow down the return type of defineConfig
- perf: build compatilibity target for Electron 30
- refactor(config): defineConfig types
- chore: fix camelcase typo
- chore: use rollup-plugin-rm to clean dist
### v2.1.0 (_2024-03-03_)
- feat: easy way to fork processes and use workers
- fix: config via build.lib fails when default entry point not found ([#393](https://github.com/alex8088/electron-vite/issues/393))
- perf: build compatilibity target for Electron 29
- perf: allow integrating more complex render solutions ([#412](https://github.com/alex8088/electron-vite/pull/412))
- perf(bytecodePlugin): warn that strings cannot be protected when minification is enabled ([#417](https://github.com/alex8088/electron-vite/issues/417))
### v2.0.0 (_2024-01-09_)
- feat: bump minimum node version to 18 - feat: bump minimum node version to 18
- feat: migrate to ESM - feat: migrate to ESM
- feat: support vite 5 - feat: support vite 5
- feat: add package.json to export map - feat: add package.json to export map
- feat: support ESM in Electron - feat: support ESM in Electron
- feat: env variables prefixed with VITE\_ will be shared in main process and renderer
- feat: support for passing arguments to electron in dev and preview commands ([#339](https://github.com/alex8088/electron-vite/pull/339))
- feat: config file supports "type": "module" in package.json
- fix: emit assets when ssr is enabled - fix: emit assets when ssr is enabled
- fix: externalizeDepPlugin not work
- fix: electron's export subpaths also need to be externalized ([#372](https://github.com/alex8088/electron-vite/issues/372))
- perf: improve package.json resolve - perf: improve package.json resolve
- perf: use magic-string hires boundary for sourcemaps - perf: use magic-string hires boundary for sourcemaps
- perf: build compatilibity target for Electron 28 - perf: build compatilibity target for Electron 28
- pref: resolve import meta url in CommonJS format - pref: resolve import meta url in CommonJS format
- perf(worker): ESM syntax - perf(worker): ESM syntax
- perf: package version - perf: package version
- perf: dev error message
- perf(externalizeDepsPlugin): use cached package data to improve performance
- perf: loadEnv api also needs to load shared env variables prefixed with VITE\_
- refactor: build - refactor: build
- refactor: file hashes use url-safe base64 encoded hashes in vite 5 (rollup 4) - refactor: file hashes use url-safe base64 encoded hashes in vite 5 (rollup 4)
- refactor: remove Electron 11, 12 build compatilibity target - refactor: remove Electron 11, 12 build compatilibity target
@ -44,6 +95,26 @@
- chore: improve prettier config - chore: improve prettier config
- chore: update homepage - chore: update homepage
### v2.0.0-beta.4 (_2024-01-06_)
See [v2.0.0-beta.4 changelog](https://github.com/alex8088/electron-vite/blob/v2.0.0-beta.4/CHANGELOG.md)
### v2.0.0-beta.3 (_2024-01-04_)
See [v2.0.0-beta.3 changelog](https://github.com/alex8088/electron-vite/blob/v2.0.0-beta.3/CHANGELOG.md)
### v2.0.0-beta.2 (_2023-12-19_)
See [v2.0.0-beta.2 changelog](https://github.com/alex8088/electron-vite/blob/v2.0.0-beta.2/CHANGELOG.md)
### v2.0.0-beta.1 (_2023-12-14_)
See [v2.0.0-beta.1 changelog](https://github.com/alex8088/electron-vite/blob/v2.0.0-beta.1/CHANGELOG.md)
### v2.0.0-beta.0 (_2023-12-13_)
See [v2.0.0-beta.0 changelog](https://github.com/alex8088/electron-vite/blob/v2.0.0-beta.0/CHANGELOG.md)
### v1.0.29 (_2023-11-17_) ### v1.0.29 (_2023-11-17_)
- feat(cli): support --noSandbox option for dev and preview command - feat(cli): support --noSandbox option for dev and preview command

42
eslint.config.js Normal file
View File

@ -0,0 +1,42 @@
// ts-check
import eslint from '@eslint/js'
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'
import globals from 'globals'
import tseslint from 'typescript-eslint'
export default tseslint.config(
{ ignores: ['**/node_modules', '**/dist', '**/bin'] },
eslint.configs.recommended,
tseslint.configs.recommended,
eslintPluginPrettierRecommended,
{
languageOptions: {
parser: tseslint.parser,
parserOptions: {
sourceType: 'module',
ecmaVersion: 2022
},
globals: {
...globals.es2021,
...globals.node
}
},
rules: {
'prettier/prettier': 'warn',
'@typescript-eslint/ban-ts-comment': ['error', { 'ts-ignore': 'allow-with-description' }],
'@typescript-eslint/explicit-function-return-type': 'error',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-empty-function': ['error', { allow: ['arrowFunctions'] }],
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-var-requires': 'off'
}
},
{
files: ['*.js', '*.mjs'],
rules: {
'@typescript-eslint/explicit-function-return-type': 'off'
}
}
)

6
node.d.ts vendored
View File

@ -4,6 +4,12 @@ declare module '*?nodeWorker' {
export default function (options: WorkerOptions): Worker export default function (options: WorkerOptions): Worker
} }
// module path
declare module '*?modulePath' {
const src: string
export default src
}
// node asset // node asset
declare module '*?asset' { declare module '*?asset' {
const src: string const src: string

View File

@ -1,6 +1,6 @@
{ {
"name": "electron-vite", "name": "electron-vite",
"version": "2.0.0-beta.3", "version": "3.1.0",
"description": "Electron build tooling based on Vite", "description": "Electron build tooling based on Vite",
"type": "module", "type": "module",
"main": "dist/index.cjs", "main": "dist/index.cjs",
@ -47,7 +47,7 @@
], ],
"scripts": { "scripts": {
"format": "prettier --write .", "format": "prettier --write .",
"lint": "eslint --ext .ts src/**", "lint": "eslint --cache .",
"typecheck": "tsc --noEmit", "typecheck": "tsc --noEmit",
"build": "pnpm run lint && rollup -c rollup.config.ts --configPlugin typescript" "build": "pnpm run lint && rollup -c rollup.config.ts --configPlugin typescript"
}, },
@ -66,7 +66,7 @@
}, },
"peerDependencies": { "peerDependencies": {
"@swc/core": "^1.0.0", "@swc/core": "^1.0.0",
"vite": "^4.0.0 || ^5.0.0" "vite": "^4.0.0 || ^5.0.0 || ^6.0.0"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"@swc/core": { "@swc/core": {
@ -74,31 +74,33 @@
} }
}, },
"devDependencies": { "devDependencies": {
"@rollup/plugin-json": "^6.0.1", "@eslint/js": "^9.22.0",
"@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-typescript": "^11.1.5", "@rollup/plugin-node-resolve": "^16.0.1",
"@swc/core": "^1.3.100", "@rollup/plugin-typescript": "^12.1.2",
"@types/node": "^18.19.3", "@swc/core": "^1.11.9",
"@typescript-eslint/eslint-plugin": "^6.13.2", "@types/node": "^22.13.10",
"@typescript-eslint/parser": "^6.13.2", "eslint": "^9.22.0",
"eslint": "^8.55.0", "eslint-config-prettier": "^10.1.1",
"eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.2.3",
"eslint-plugin-prettier": "^5.0.1", "globals": "^16.0.0",
"lint-staged": "^15.2.0", "lint-staged": "^15.4.3",
"prettier": "^3.1.0", "prettier": "^3.5.3",
"rollup": "^4.6.1", "rollup": "^4.35.0",
"rollup-plugin-dts": "^6.1.0", "rollup-plugin-dts": "^6.1.1",
"simple-git-hooks": "^2.9.0", "rollup-plugin-rm": "^1.0.2",
"tslib": "^2.6.2", "simple-git-hooks": "^2.11.1",
"typescript": "^5.3.3", "tslib": "^2.8.1",
"vite": "^5.0.6" "typescript": "^5.7.3",
"typescript-eslint": "^8.26.1",
"vite": "^6.2.1"
}, },
"dependencies": { "dependencies": {
"@babel/core": "^7.23.5", "@babel/core": "^7.26.10",
"@babel/plugin-transform-arrow-functions": "^7.23.3", "@babel/plugin-transform-arrow-functions": "^7.25.9",
"cac": "^6.7.14", "cac": "^6.7.14",
"esbuild": "^0.19.8", "esbuild": "^0.25.1",
"magic-string": "^0.30.5", "magic-string": "^0.30.17",
"picocolors": "^1.0.0" "picocolors": "^1.1.1"
} }
} }

2107
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,33 +1,16 @@
import { createRequire } from 'node:module' import { createRequire } from 'node:module'
import fs from 'node:fs/promises' import { defineConfig } from 'rollup'
import { type Plugin, defineConfig } from 'rollup'
import ts from '@rollup/plugin-typescript' import ts from '@rollup/plugin-typescript'
import resolve from '@rollup/plugin-node-resolve' import resolve from '@rollup/plugin-node-resolve'
import json from '@rollup/plugin-json' import json from '@rollup/plugin-json'
import dts from 'rollup-plugin-dts' import dts from 'rollup-plugin-dts'
import rm from 'rollup-plugin-rm'
const require = createRequire(import.meta.url) const require = createRequire(import.meta.url)
const pkg = require('./package.json') const pkg = require('./package.json')
const external = ['esbuild', ...Object.keys(pkg.dependencies || {}), ...Object.keys(pkg.peerDependencies || {})] const external = ['esbuild', ...Object.keys(pkg.dependencies || {}), ...Object.keys(pkg.peerDependencies || {})]
function clean(when: 'buildStart' | 'buildEnd', target: string): Plugin {
const _clean = async (target): Promise<void> => {
await fs.rm(target, { recursive: true, force: true }).catch(() => {})
}
return {
name: 'clean',
buildStart: async (): Promise<void> => {
if (when !== 'buildStart') return
await _clean(target)
},
buildEnd: async (): Promise<void> => {
if (when !== 'buildEnd') return
await _clean(target)
}
}
}
export default defineConfig([ export default defineConfig([
{ {
input: ['src/index.ts', 'src/cli.ts'], input: ['src/index.ts', 'src/cli.ts'],
@ -47,7 +30,7 @@ export default defineConfig([
], ],
external, external,
plugins: [ plugins: [
clean('buildStart', 'dist'), rm('dist', 'buildStart'),
json(), json(),
ts({ compilerOptions: { rootDir: 'src', declaration: true, declarationDir: 'dist/types' } }), ts({ compilerOptions: { rootDir: 'src', declaration: true, declarationDir: 'dist/types' } }),
resolve() resolve()
@ -59,6 +42,6 @@ export default defineConfig([
{ {
input: 'dist/types/index.d.ts', input: 'dist/types/index.d.ts',
output: [{ file: pkg.types, format: 'es' }], output: [{ file: pkg.types, format: 'es' }],
plugins: [dts(), clean('buildEnd', 'dist/types')] plugins: [dts(), rm('dist/types', 'buildEnd')]
} }
]) ])

View File

@ -5,7 +5,7 @@ import { createRequire } from 'node:module'
import colors from 'picocolors' import colors from 'picocolors'
import { import {
type UserConfig as ViteConfig, type UserConfig as ViteConfig,
type UserConfigExport as UserViteConfigExport, type UserConfigExport as ViteConfigExport,
type ConfigEnv, type ConfigEnv,
type Plugin, type Plugin,
type LogLevel, type LogLevel,
@ -18,8 +18,9 @@ import { build } from 'esbuild'
import { electronMainVitePlugin, electronPreloadVitePlugin, electronRendererVitePlugin } from './plugins/electron' import { electronMainVitePlugin, electronPreloadVitePlugin, electronRendererVitePlugin } from './plugins/electron'
import assetPlugin from './plugins/asset' import assetPlugin from './plugins/asset'
import workerPlugin from './plugins/worker' import workerPlugin from './plugins/worker'
import importMetaUrlPlugin from './plugins/importMetaUrl' import importMetaPlugin from './plugins/importMeta'
import esmShimPlugin from './plugins/esm' import esmShimPlugin from './plugins/esm'
import modulePathPlugin from './plugins/modulePath'
import { isObject, isFilePathESM } from './utils' import { isObject, isFilePathESM } from './utils'
export { defineConfig as defineViteConfig } from 'vite' export { defineConfig as defineViteConfig } from 'vite'
@ -45,25 +46,25 @@ export interface UserConfig {
preload?: ViteConfig & { configFile?: string | false } preload?: ViteConfig & { configFile?: string | false }
} }
export interface UserConfigSchema { export interface ElectronViteConfig {
/** /**
* Vite config options for electron main process * Vite config options for electron main process
* *
* https://vitejs.dev/config/ * https://vitejs.dev/config/
*/ */
main?: UserViteConfigExport main?: ViteConfigExport
/** /**
* Vite config options for electron renderer process * Vite config options for electron renderer process
* *
* https://vitejs.dev/config/ * https://vitejs.dev/config/
*/ */
renderer?: UserViteConfigExport renderer?: ViteConfigExport
/** /**
* Vite config options for electron preload files * Vite config options for electron preload files
* *
* https://vitejs.dev/config/ * https://vitejs.dev/config/
*/ */
preload?: UserViteConfigExport preload?: ViteConfigExport
} }
export type InlineConfig = Omit<ViteConfig, 'base'> & { export type InlineConfig = Omit<ViteConfig, 'base'> & {
@ -72,16 +73,28 @@ export type InlineConfig = Omit<ViteConfig, 'base'> & {
ignoreConfigWarning?: boolean ignoreConfigWarning?: boolean
} }
export type UserConfigFn = (env: ConfigEnv) => UserConfigSchema | Promise<UserConfigSchema> export type ElectronViteConfigFnObject = (env: ConfigEnv) => ElectronViteConfig
export type UserConfigExport = UserConfigSchema | Promise<UserConfigSchema> | UserConfigFn export type ElectronViteConfigFnPromise = (env: ConfigEnv) => Promise<ElectronViteConfig>
export type ElectronViteConfigFn = (env: ConfigEnv) => ElectronViteConfig | Promise<ElectronViteConfig>
export type ElectronViteConfigExport =
| ElectronViteConfig
| Promise<ElectronViteConfig>
| ElectronViteConfigFnObject
| ElectronViteConfigFnPromise
| ElectronViteConfigFn
/** /**
* Type helper to make it easier to use `electron.vite.config.*` * Type helper to make it easier to use `electron.vite.config.*`
* accepts a direct {@link UserConfig} object, or a function that returns it. * accepts a direct {@link ElectronViteConfig} object, or a function that returns it.
* The function receives a object that exposes two properties: * The function receives a object that exposes two properties:
* `command` (either `'build'` or `'serve'`), and `mode`. * `command` (either `'build'` or `'serve'`), and `mode`.
*/ */
export function defineConfig(config: UserConfigExport): UserConfigExport { export function defineConfig(config: ElectronViteConfig): ElectronViteConfig
export function defineConfig(config: Promise<ElectronViteConfig>): Promise<ElectronViteConfig>
export function defineConfig(config: ElectronViteConfigFnObject): ElectronViteConfigFnObject
export function defineConfig(config: ElectronViteConfigExport): ElectronViteConfigExport
export function defineConfig(config: ElectronViteConfigExport): ElectronViteConfigExport {
return config return config
} }
@ -99,8 +112,6 @@ export async function resolveConfig(
const config = inlineConfig const config = inlineConfig
const mode = inlineConfig.mode || defaultMode const mode = inlineConfig.mode || defaultMode
config.mode = mode
process.env.NODE_ENV = defaultMode process.env.NODE_ENV = defaultMode
let userConfig: UserConfig | undefined let userConfig: UserConfig | undefined
@ -129,6 +140,8 @@ export async function resolveConfig(
if (loadResult.config.main) { if (loadResult.config.main) {
const mainViteConfig: ViteConfig = mergeConfig(loadResult.config.main, deepClone(config)) const mainViteConfig: ViteConfig = mergeConfig(loadResult.config.main, deepClone(config))
mainViteConfig.mode = inlineConfig.mode || mainViteConfig.mode || defaultMode
if (outDir) { if (outDir) {
resetOutDir(mainViteConfig, outDir, 'main') resetOutDir(mainViteConfig, outDir, 'main')
} }
@ -137,7 +150,8 @@ export async function resolveConfig(
...electronMainVitePlugin({ root }), ...electronMainVitePlugin({ root }),
assetPlugin(), assetPlugin(),
workerPlugin(), workerPlugin(),
importMetaUrlPlugin(), modulePathPlugin(),
importMetaPlugin(),
esmShimPlugin() esmShimPlugin()
]) ])
@ -148,13 +162,15 @@ export async function resolveConfig(
if (loadResult.config.preload) { if (loadResult.config.preload) {
const preloadViteConfig: ViteConfig = mergeConfig(loadResult.config.preload, deepClone(config)) const preloadViteConfig: ViteConfig = mergeConfig(loadResult.config.preload, deepClone(config))
preloadViteConfig.mode = inlineConfig.mode || preloadViteConfig.mode || defaultMode
if (outDir) { if (outDir) {
resetOutDir(preloadViteConfig, outDir, 'preload') resetOutDir(preloadViteConfig, outDir, 'preload')
} }
mergePlugins(preloadViteConfig, [ mergePlugins(preloadViteConfig, [
...electronPreloadVitePlugin({ root }), ...electronPreloadVitePlugin({ root }),
assetPlugin(), assetPlugin(),
importMetaUrlPlugin(), importMetaPlugin(),
esmShimPlugin() esmShimPlugin()
]) ])
@ -165,6 +181,8 @@ export async function resolveConfig(
if (loadResult.config.renderer) { if (loadResult.config.renderer) {
const rendererViteConfig: ViteConfig = mergeConfig(loadResult.config.renderer, deepClone(config)) const rendererViteConfig: ViteConfig = mergeConfig(loadResult.config.renderer, deepClone(config))
rendererViteConfig.mode = inlineConfig.mode || rendererViteConfig.mode || defaultMode
if (outDir) { if (outDir) {
resetOutDir(rendererViteConfig, outDir, 'renderer') resetOutDir(rendererViteConfig, outDir, 'renderer')
} }
@ -388,7 +406,7 @@ async function loadConfigFormBundledFile(
configFile: string, configFile: string,
bundledCode: string, bundledCode: string,
isESM: boolean isESM: boolean
): Promise<UserConfigExport> { ): Promise<ElectronViteConfigExport> {
if (isESM) { if (isESM) {
const fileNameTmp = path.resolve(configRoot, `${CONFIG_FILE_NAME}.${Date.now()}.mjs`) const fileNameTmp = path.resolve(configRoot, `${CONFIG_FILE_NAME}.${Date.now()}.mjs`)
fs.writeFileSync(fileNameTmp, bundledCode) fs.writeFileSync(fileNameTmp, bundledCode)
@ -399,6 +417,7 @@ async function loadConfigFormBundledFile(
} finally { } finally {
try { try {
fs.unlinkSync(fileNameTmp) fs.unlinkSync(fileNameTmp)
// eslint-disable-next-line no-empty
} catch {} } catch {}
} }
} else { } else {

View File

@ -41,6 +41,11 @@ export function supportESM(): boolean {
return parseInt(majorVer) >= 28 return parseInt(majorVer) >= 28
} }
export function getElectronMajorVersion(): number {
const majorVer = getElectronMajorVer()
return parseInt(majorVer)
}
export function getElectronPath(): string { export function getElectronPath(): string {
let electronExecPath = process.env.ELECTRON_EXEC_PATH || '' let electronExecPath = process.env.ELECTRON_EXEC_PATH || ''
if (!electronExecPath) { if (!electronExecPath) {
@ -64,6 +69,13 @@ export function getElectronNodeTarget(): string {
const electronVer = getElectronMajorVer() const electronVer = getElectronMajorVer()
const nodeVer = { const nodeVer = {
'35': '22.14',
'34': '20.18',
'33': '20.18',
'32': '20.16',
'31': '20.14',
'30': '20.11',
'29': '20.9',
'28': '18.18', '28': '18.18',
'27': '18.17', '27': '18.17',
'26': '18.16', '26': '18.16',
@ -77,9 +89,7 @@ export function getElectronNodeTarget(): string {
'18': '16.13', '18': '16.13',
'17': '16.13', '17': '16.13',
'16': '16.9', '16': '16.9',
'15': '16.5', '15': '16.5'
'14': '14.17',
'13': '14.17'
} }
if (electronVer && parseInt(electronVer) > 10) { if (electronVer && parseInt(electronVer) > 10) {
let target = nodeVer[electronVer] let target = nodeVer[electronVer]
@ -93,6 +103,13 @@ export function getElectronChromeTarget(): string {
const electronVer = getElectronMajorVer() const electronVer = getElectronMajorVer()
const chromeVer = { const chromeVer = {
'35': '134',
'34': '132',
'33': '130',
'32': '128',
'31': '126',
'30': '124',
'29': '122',
'28': '120', '28': '120',
'27': '118', '27': '118',
'26': '116', '26': '116',
@ -106,9 +123,7 @@ export function getElectronChromeTarget(): string {
'18': '100', '18': '100',
'17': '98', '17': '98',
'16': '96', '16': '96',
'15': '94', '15': '94'
'14': '93',
'13': '91'
} }
if (electronVer && parseInt(electronVer) > 10) { if (electronVer && parseInt(electronVer) > 10) {
let target = chromeVer[electronVer] let target = chromeVer[electronVer]

View File

@ -1,4 +1,4 @@
export { type LogLevel, createLogger, splitVendorChunkPlugin, splitVendorChunk } from 'vite' export { type LogLevel, createLogger, mergeConfig, splitVendorChunkPlugin, splitVendorChunk } from 'vite'
export * from './config' export * from './config'
export { createServer } from './server' export { createServer } from './server'
export { build } from './build' export { build } from './build'

View File

@ -87,6 +87,12 @@ export default function assetPlugin(): Plugin {
return wasmHelperCode return wasmHelperCode
} }
if (id.startsWith('\0')) {
// Rollup convention, this id should be handled by the
// plugin that marked it with \0
return
}
const assetResolved = resolveAsset(id) const assetResolved = resolveAsset(id)
if (!assetResolved) { if (!assetResolved) {
return return
@ -109,7 +115,7 @@ export default function assetPlugin(): Plugin {
const hash = this.emitFile({ const hash = this.emitFile({
type: 'asset', type: 'asset',
name: path.basename(file), name: path.basename(file),
source source: source as unknown as Uint8Array
}) })
referenceId = `__VITE_NODE_ASSET__${hash}__` referenceId = `__VITE_NODE_ASSET__${hash}__`
assetCache.set(file, referenceId) assetCache.set(file, referenceId)

View File

@ -198,9 +198,12 @@ export function bytecodePlugin(options: BytecodeOptions = {}): Plugin | null {
if (useInRenderer) { if (useInRenderer) {
config.logger.warn(colors.yellow('bytecodePlugin does not support renderer.')) config.logger.warn(colors.yellow('bytecodePlugin does not support renderer.'))
} }
if (resolvedConfig.build.minify && protectedStrings.length > 0) {
config.logger.warn(colors.yellow('Strings cannot be protected when minification is enabled.'))
}
}, },
transform(code, id): void | { code: string; map: SourceMapInput } { transform(code, id): void | { code: string; map: SourceMapInput } {
if (protectedStrings.length === 0 || !filter(id)) return if (config.build.minify || protectedStrings.length === 0 || !filter(id)) return
let match: RegExpExecArray | null let match: RegExpExecArray | null
let s: MagicString | undefined let s: MagicString | undefined
@ -307,7 +310,7 @@ export function bytecodePlugin(options: BytecodeOptions = {}): Plugin | null {
const chunkFileName = path.resolve(outDir, name) const chunkFileName = path.resolve(outDir, name)
if (bytecodeChunks.includes(name)) { if (bytecodeChunks.includes(name)) {
const bytecodeBuffer = await compileToBytecode(_code) const bytecodeBuffer = await compileToBytecode(_code)
fs.writeFileSync(path.resolve(outDir, name + 'c'), bytecodeBuffer) fs.writeFileSync(path.resolve(outDir, name + 'c'), bytecodeBuffer as unknown as Uint8Array)
if (chunk.isEntry) { if (chunk.isEntry) {
if (!removeBundleJS) { if (!removeBundleJS) {
keepBundle(chunkFileName) keepBundle(chunkFileName)
@ -341,7 +344,9 @@ export function bytecodePlugin(options: BytecodeOptions = {}): Plugin | null {
} }
} }
const bytecodeLoaderBlock = getBytecodeLoaderBlock(chunk.fileName) const bytecodeLoaderBlock = getBytecodeLoaderBlock(chunk.fileName)
_code = hasBytecodeMoudle ? _code.replace(useStrict, `${useStrict}\n${bytecodeLoaderBlock}`) : _code _code = hasBytecodeMoudle
? _code.replace(/("use strict";)|('use strict';)/, `${useStrict}\n${bytecodeLoaderBlock}`)
: _code
} }
fs.writeFileSync(chunkFileName, _code) fs.writeFileSync(chunkFileName, _code)
} }

View File

@ -11,7 +11,7 @@ export interface ElectronPluginOptions {
root?: string root?: string
} }
function findLibEntry(root: string, scope: string): string { function findLibEntry(root: string, scope: string): string | undefined {
for (const name of ['index', scope]) { for (const name of ['index', scope]) {
for (const ext of ['js', 'ts', 'mjs', 'cjs']) { for (const ext of ['js', 'ts', 'mjs', 'cjs']) {
const entryFile = path.resolve(root, 'src', scope, `${name}.${ext}`) const entryFile = path.resolve(root, 'src', scope, `${name}.${ext}`)
@ -20,7 +20,7 @@ function findLibEntry(root: string, scope: string): string {
} }
} }
} }
return '' return undefined
} }
function findInput(root: string, scope = 'renderer'): string { function findInput(root: string, scope = 'renderer'): string {
@ -76,7 +76,7 @@ export function electronMainVitePlugin(options?: ElectronPluginOptions): Plugin[
target: nodeTarget, target: nodeTarget,
assetsDir: 'chunks', assetsDir: 'chunks',
rollupOptions: { rollupOptions: {
external: ['electron', ...builtinModules.flatMap(m => [m, `node:${m}`])], external: ['electron', /^electron\/.+/, ...builtinModules.flatMap(m => [m, `node:${m}`])],
output: {} output: {}
}, },
reportCompressedSize: false, reportCompressedSize: false,
@ -198,12 +198,18 @@ export function electronPreloadVitePlugin(options?: ElectronPluginOptions): Plug
const format = pkg.type && pkg.type === 'module' && supportESM() ? 'es' : 'cjs' const format = pkg.type && pkg.type === 'module' && supportESM() ? 'es' : 'cjs'
const defaultConfig = { const defaultConfig = {
ssr: {
resolve: {
conditions: ['module', 'browser', 'development|production'],
mainFields: ['browser', 'module', 'jsnext:main', 'jsnext']
}
},
build: { build: {
outDir: path.resolve(root, 'out', 'preload'), outDir: path.resolve(root, 'out', 'preload'),
target: nodeTarget, target: nodeTarget,
assetsDir: 'chunks', assetsDir: 'chunks',
rollupOptions: { rollupOptions: {
external: ['electron', ...builtinModules.flatMap(m => [m, `node:${m}`])], external: ['electron', /^electron\/.+/, ...builtinModules.flatMap(m => [m, `node:${m}`])],
output: {} output: {}
}, },
reportCompressedSize: false, reportCompressedSize: false,
@ -272,7 +278,8 @@ export function electronPreloadVitePlugin(options?: ElectronPluginOptions): Plug
// enable ssr build // enable ssr build
config.build.ssr = true config.build.ssr = true
config.build.ssrEmitAssets = true config.build.ssrEmitAssets = true
config.ssr = { ...config.ssr, ...{ noExternal: true } } config.ssr = mergeConfig(defaultConfig.ssr, config.ssr || {})
config.ssr.noExternal = true
} }
}, },
{ {
@ -393,7 +400,9 @@ export function electronRendererVitePlugin(options?: ElectronPluginOptions): Plu
} else { } else {
const targets = Array.isArray(build.target) ? build.target : [build.target] const targets = Array.isArray(build.target) ? build.target : [build.target]
if (targets.some(t => !t.startsWith('chrome') && !/^es((202\d{1})|next)$/.test(t))) { if (targets.some(t => !t.startsWith('chrome') && !/^es((202\d{1})|next)$/.test(t))) {
throw new Error('The electron vite renderer config build.target must be "chrome?" or "es?".') config.logger.warn(
'The electron vite renderer config build.target is not "chrome?" or "es?". This could be a mistake.'
)
} }
} }

View File

@ -8,9 +8,11 @@ import MagicString from 'magic-string'
import type { SourceMapInput } from 'rollup' import type { SourceMapInput } from 'rollup'
import type { Plugin } from 'vite' import type { Plugin } from 'vite'
import { getElectronMajorVersion } from '../electron'
const CJSyntaxRe = /__filename|__dirname|require\(|require\.resolve\(/ const CJSyntaxRe = /__filename|__dirname|require\(|require\.resolve\(/
const CJSShim = ` const CJSShim_normal = `
// -- CommonJS Shims -- // -- CommonJS Shims --
import __cjs_url__ from 'node:url'; import __cjs_url__ from 'node:url';
import __cjs_path__ from 'node:path'; import __cjs_path__ from 'node:path';
@ -20,6 +22,14 @@ const __dirname = __cjs_path__.dirname(__filename);
const require = __cjs_mod__.createRequire(import.meta.url); const require = __cjs_mod__.createRequire(import.meta.url);
` `
const CJSShim_node_20_11 = `
// -- CommonJS Shims --
import __cjs_mod__ from 'node:module';
const __filename = import.meta.filename;
const __dirname = import.meta.dirname;
const require = __cjs_mod__.createRequire(import.meta.url);
`
const ESMStaticImportRe = const ESMStaticImportRe =
/(?<=\s|^|;)import\s*([\s"']*(?<imports>[\p{L}\p{M}\w\t\n\r $*,/{}@.]+)from\s*)?["']\s*(?<specifier>(?<="\s*)[^"]*[^\s"](?=\s*")|(?<='\s*)[^']*[^\s'](?=\s*'))\s*["'][\s;]*/gmu /(?<=\s|^|;)import\s*([\s"']*(?<imports>[\p{L}\p{M}\w\t\n\r $*,/{}@.]+)from\s*)?["']\s*(?<specifier>(?<="\s*)[^"]*[^\s"](?=\s*")|(?<='\s*)[^']*[^\s'](?=\s*'))\s*["'][\s;]*/gmu
@ -37,6 +47,9 @@ function findStaticImports(code: string): StaticImport[] {
export default function esmShimPlugin(): Plugin { export default function esmShimPlugin(): Plugin {
let sourcemap: boolean | 'inline' | 'hidden' = false let sourcemap: boolean | 'inline' | 'hidden' = false
const CJSShim = getElectronMajorVersion() >= 30 ? CJSShim_node_20_11 : CJSShim_normal
return { return {
name: 'vite:esm-shim', name: 'vite:esm-shim',
apply: 'build', apply: 'build',

View File

@ -1,14 +1,20 @@
import type { Plugin } from 'vite' import type { Plugin } from 'vite'
export default function importMetaUrlPlugin(): Plugin { export default function importMetaPlugin(): Plugin {
return { return {
name: 'vite:import-meta-url', name: 'vite:import-meta',
apply: 'build', apply: 'build',
enforce: 'pre', enforce: 'pre',
resolveImportMeta(property, { format }): string | null { resolveImportMeta(property, { format }): string | null {
if (property === 'url' && format === 'cjs') { if (property === 'url' && format === 'cjs') {
return `require("url").pathToFileURL(__filename).href` return `require("url").pathToFileURL(__filename).href`
} }
if (property === 'filename' && format === 'cjs') {
return `__filename`
}
if (property === 'dirname' && format === 'cjs') {
return `__dirname`
}
return null return null
} }
} }

65
src/plugins/modulePath.ts Normal file
View File

@ -0,0 +1,65 @@
import type { Plugin } from 'vite'
import type { SourceMapInput } from 'rollup'
import MagicString from 'magic-string'
import { cleanUrl, parseRequest, toRelativePath } from '../utils'
const modulePathRE = /__VITE_MODULE_PATH__([\w$]+)__/g
/**
* Resolve `?modulePath` import and return the module bundle path.
*/
export default function modulePathPlugin(): Plugin {
let sourcemap: boolean | 'inline' | 'hidden' = false
return {
name: 'vite:module-path',
apply: 'build',
enforce: 'pre',
configResolved(config): void {
sourcemap = config.build.sourcemap
},
resolveId(id, importer): string | void {
const query = parseRequest(id)
if (query && typeof query.modulePath === 'string') {
return id + `&importer=${importer}`
}
},
load(id): string | void {
const query = parseRequest(id)
if (query && typeof query.modulePath === 'string' && typeof query.importer === 'string') {
const cleanPath = cleanUrl(id)
const hash = this.emitFile({
type: 'chunk',
id: cleanPath,
importer: query.importer
})
const refId = `__VITE_MODULE_PATH__${hash}__`
return `
import { join } from 'path'
export default join(__dirname, ${refId})`
}
},
renderChunk(code, chunk): { code: string; map: SourceMapInput } | null {
if (code.match(modulePathRE)) {
let match: RegExpExecArray | null
const s = new MagicString(code)
while ((match = modulePathRE.exec(code))) {
const [full, hash] = match
const filename = this.getFileName(hash)
const outputFilepath = toRelativePath(filename, chunk.fileName)
const replacement = JSON.stringify(outputFilepath)
s.overwrite(match.index, match.index + full.length, replacement, {
contentOnly: true
})
}
return {
code: s.toString(),
map: sourcemap ? s.generateMap({ hires: 'boundary' }) : null
}
}
return null
}
}
}

View File

@ -2,7 +2,7 @@ import type { ChildProcess } from 'node:child_process'
import { import {
type UserConfig as ViteConfig, type UserConfig as ViteConfig,
type ViteDevServer, type ViteDevServer,
createServer as ViteCreateServer, createServer as viteCreateServer,
build as viteBuild, build as viteBuild,
createLogger, createLogger,
mergeConfig mergeConfig
@ -80,7 +80,7 @@ export async function createServer(
if (rendererViteConfig) { if (rendererViteConfig) {
logger.info(colors.gray(`\n-----\n`)) logger.info(colors.gray(`\n-----\n`))
server = await ViteCreateServer(rendererViteConfig) server = await viteCreateServer(rendererViteConfig)
if (!server.httpServer) { if (!server.httpServer) {
throw new Error('HTTP server not available') throw new Error('HTTP server not available')

View File

@ -29,7 +29,10 @@ export function parseRequest(id: string): Record<string, string> | null {
} }
export function getHash(text: Buffer | string): string { export function getHash(text: Buffer | string): string {
return createHash('sha256').update(text).digest('hex').substring(0, 8) return createHash('sha256')
.update(text as unknown as Uint8Array)
.digest('hex')
.substring(0, 8)
} }
export function toRelativePath(filename: string, importer: string): string { export function toRelativePath(filename: string, importer: string): string {