vite-plugin-electron-renderer
Advanced tools
Comparing version 0.1.4 to 0.2.0
@@ -1,2 +0,2 @@ | ||
import { Plugin, UserConfig } from 'vite'; | ||
import { Plugin } from 'vite'; | ||
@@ -6,41 +6,4 @@ declare const electronRenderer: VitePluginElectronRenderer; | ||
export interface Resolve { | ||
[filename: string]: string | (() => string); | ||
} | ||
export interface VitePluginElectronRenderer { | ||
(options?: { | ||
/** | ||
* @example | ||
* { | ||
* resolve: { | ||
* 'electron-store': 'const Store=require("electron-store"); export { Store as default }', | ||
* } | ||
* } | ||
*/ | ||
resolve?: Resolve; | ||
}): Plugin; | ||
(): Plugin[]; | ||
} | ||
export interface GenerateESModule { | ||
(cacheDir: string, resolve: Resolve): void; | ||
} | ||
export interface ModifyAlias { | ||
( | ||
config: UserConfig, | ||
aliaList: { [moduleId: string]: string; }[], | ||
): void; | ||
} | ||
export interface ModifyOptimizeDepsExclude { | ||
(config: UserConfig, exclude: string[]): void; | ||
} | ||
export interface ModifyRollupExternal { | ||
(config: UserConfig): void; | ||
} | ||
export interface ModifyOptionsForElectron { | ||
(config: UserConfig): void; | ||
} |
232
index.js
@@ -1,186 +0,78 @@ | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
const { builtinModules } = require('module'); | ||
const optimizer = require('vite-plugin-optimizer'); | ||
/** @type {import('.').VitePluginElectronRenderer} */ | ||
module.exports = function (options = {}) { | ||
const { resolve } = options; | ||
let root = process.cwd(); | ||
/** | ||
* @type {import('.').VitePluginElectronRenderer} | ||
*/ | ||
module.exports = function () { | ||
const name = 'vite-plugin-electron-renderer'; | ||
const browserExternalId = '__vite-browser-external' | ||
const ElectronRendererModule = path.join(__dirname, 'modules/electron-renderer.js'); | ||
const plugin = optimizer(builtinModulesExport(builtinModules), { dir: `.${name}` }); | ||
plugin.name = name; | ||
return { | ||
name, | ||
config(config, env) { | ||
if (config.root && path.isAbsolute(config.root)) { | ||
// TODO: config.root is relative path | ||
root = config.root; | ||
} | ||
return [ | ||
{ | ||
name: `${name}:config`, | ||
config(config) { | ||
// make sure that Electron can be loaded into the local file using `loadFile` after packaging | ||
if (!config.base) config.base = './'; | ||
modifyOptionsForElectron(config); | ||
modifyOptimizeDepsExclude(config, ['electron', ...(resolve ? Object.keys(resolve) : [])]); | ||
if (!config.build) config.build = {}; | ||
// in 'vite serve' phase ensure the 'electron' and NodeJs built-in modules are loaded correctly by `resolve.alias` | ||
if (env.command === 'serve') { | ||
modifyAlias(config, [{ electron: ElectronRendererModule }]); | ||
} | ||
// in 'vite build' phase insert 'electron' and NodeJs built-in modules into Rollup `output.external` | ||
else if (env.command === 'build') { | ||
modifyRollupExternal(config); | ||
} | ||
// ensure that static resources are loaded normally | ||
if (!config.build.assetsDir) config.build.assetsDir = ''; | ||
// generate resolve-module file to `node_modules/.vite-plugin-electron-renderer` | ||
// point resolve-module to this path by `resolve.alias` | ||
if (resolve) { | ||
const cacheDir = path.join(node_modules(root), `.${name}`); | ||
generateESModule(cacheDir, resolve); | ||
modifyAlias( | ||
config, | ||
Object.keys(resolve).map(moduleId => ({ [moduleId]: path.join(cacheDir, moduleId) })), | ||
); | ||
} | ||
}, | ||
configureServer(server) { | ||
// intercept node built-in modules require, convert to ESModule respond | ||
server.middlewares.use((req, res, next) => { | ||
if ((req.url || '').includes(browserExternalId)) { | ||
const builtinId = req.url.split(`${browserExternalId}:`, 2)[1]; | ||
if (builtinModules.includes(builtinId)) { | ||
const builtinModule = require(builtinId); | ||
const members = Object.keys(builtinModule); | ||
const code = ` | ||
const _M = require("${builtinId}"); | ||
const { | ||
${members.join(',\n ')} | ||
} = _M; | ||
export { _M as default, ${members.join(', ')} } | ||
`; | ||
res.setHeader('Access-Control-Allow-Origin', '*'); | ||
res.setHeader('Content-Type', 'application/javascript'); | ||
res.setHeader('Cache-Control', 'max-age=3600'); // an hour of cache | ||
res.end(code); | ||
return; | ||
if (!config.build.rollupOptions) config.build.rollupOptions = {}; | ||
if (!config.build.rollupOptions.output) config.build.rollupOptions.output = {}; | ||
if (Array.isArray(config.build.rollupOptions.output)) { | ||
config.build.rollupOptions.output.forEach(output => { | ||
if (!output.format) { | ||
// the packaged Electron app should use "cjs" | ||
output.format = 'cjs'; | ||
} | ||
}); | ||
} else { | ||
if (!config.build.rollupOptions.output.format) { | ||
// the packaged Electron app should use "cjs" | ||
config.build.rollupOptions.output.format = 'cjs'; | ||
} | ||
} | ||
next(); | ||
}); | ||
}, | ||
}; | ||
} | ||
/** @type {import('.').GenerateESModule} */ | ||
function generateESModule(cacheDir, resolve) { | ||
// generate custom-resolve module file | ||
for (const [module, strOrFn] of Object.entries(resolve)) { | ||
const moduleId = path.join(cacheDir, module + '.js'); | ||
const moduleContent = typeof strOrFn === 'function' ? strOrFn() : strOrFn; | ||
// ---------------------------------------- | ||
// supported nest module ('@scope/name') | ||
ensureDir(path.parse(moduleId).dir); | ||
// write custom-resolve | ||
fs.writeFileSync(moduleId, moduleContent); | ||
} | ||
} | ||
if (!config.resolve) config.resolve = {}; | ||
if (!config.resolve.alias) config.resolve.alias = []; | ||
const electronjs = path.join(__dirname, 'modules/electron-renderer.js'); | ||
/** @type {import('.').ModifyAlias} */ | ||
function modifyAlias(config, aliaList) { | ||
if (!config.resolve) config.resolve = {}; | ||
if (Array.isArray(config.resolve.alias)) { | ||
config.resolve.alias.push({ | ||
find: 'electron', | ||
replacement: electronjs, | ||
}); | ||
} else { | ||
config.resolve.alias['electron'] = electronjs; | ||
} | ||
}, | ||
}, | ||
plugin, | ||
]; | ||
}; | ||
let alias = config.resolve.alias || []; | ||
if (!Array.isArray(alias)) { | ||
// keep the the original alias | ||
alias = Object.entries(alias).map(([k, v]) => ({ find: k, replacement: v })); | ||
} | ||
alias.push(...aliaList.map(item => { | ||
const [find, replacement] = Object.entries(item)[0]; | ||
return { find, replacement }; | ||
})); | ||
/** | ||
* @type {(modules: string[]) => Record<string, string>} | ||
*/ | ||
function builtinModulesExport(modules) { | ||
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} | ||
` | ||
config.resolve.alias = alias; | ||
return { [moduleId]: nodeModuleCode } | ||
}).reduce((memo, item) => Object.assign(memo, item), {}) | ||
} | ||
/** @type {import('.').ModifyOptimizeDepsExclude} */ | ||
function modifyOptimizeDepsExclude(config, exclude) { | ||
if (!config.optimizeDeps) config.optimizeDeps = {}; | ||
if (!config.optimizeDeps.exclude) config.optimizeDeps.exclude = []; | ||
config.optimizeDeps.exclude.push(...exclude); | ||
} | ||
/** @type {import('.').ModifyRollupExternal} */ | ||
function modifyRollupExternal(config) { | ||
if (!config.build) config.build = {}; | ||
if (!config.build.rollupOptions) config.build.rollupOptions = {}; | ||
if (!config.build.rollupOptions.external) config.build.rollupOptions.external = []; | ||
let external = config.build.rollupOptions.external; | ||
if (Array.isArray(external)) { | ||
external = ['electron', ...builtinModules, ...external]; | ||
} else if (external instanceof RegExp) { | ||
external = ['electron', ...builtinModules, external]; | ||
} else if (typeof external === 'string') { | ||
external = ['electron', ...builtinModules, external]; | ||
} else if (typeof external === 'function') { | ||
const fn = external; | ||
external = (source, importer, isResolved) => { | ||
if (source === 'electron' || builtinModules.includes(source)) { | ||
return true; | ||
} | ||
return fn.call(fn, source, importer, isResolved); | ||
} | ||
} | ||
config.build.rollupOptions.external = external; | ||
} | ||
/** @type {import('.').ModifyOptionsForElectron} */ | ||
function modifyOptionsForElectron(config) { | ||
// electron use `loadFile` load Renderer-process file | ||
if (!config.base) config.base = './'; | ||
if (!config.build) config.build = {}; | ||
// ensure `require('file-paht')` corre | ||
if (!config.build.assetsDir) config.build.assetsDir = ''; | ||
if (!config.build.rollupOptions) config.build.rollupOptions = {}; | ||
if (!config.build.rollupOptions.output) config.build.rollupOptions.output = {}; | ||
if (Array.isArray(config.build.rollupOptions.output)) { | ||
config.build.rollupOptions.output.forEach(output => { | ||
if (!output.format) { | ||
// the packaged "electron app" should use "cjs" | ||
output.format = 'cjs'; | ||
} | ||
}); | ||
} else { | ||
if (!config.build.rollupOptions.output.format) { | ||
// the packaged "electron app" should use "cjs" | ||
config.build.rollupOptions.output.format = 'cjs'; | ||
} | ||
} | ||
} | ||
// --------- utils --------- | ||
function ensureDir(dir) { | ||
if (!(fs.existsSync(dir) && fs.statSync(dir).isDirectory())) { | ||
fs.mkdirSync(dir, { recursive: true }); | ||
} | ||
return dir; | ||
} | ||
function node_modules(root, count = 0) { | ||
if (node_modules.p) { | ||
return node_modules.p; | ||
} | ||
const p = path.join(root, 'node_modules'); | ||
if (fs.existsSync(p)) { | ||
return node_modules.p = p; | ||
} | ||
if (count >= 19) { | ||
throw new Error('Can not found node_modules directory.'); | ||
} | ||
return node_modules(path.join(root, '..'), count + 1); | ||
} |
{ | ||
"name": "vite-plugin-electron-renderer", | ||
"version": "0.1.4", | ||
"version": "0.2.0", | ||
"description": "Use Electron and NodeJs API in Renderer-process", | ||
@@ -10,4 +10,6 @@ "main": "index.js", | ||
"license": "MIT", | ||
"dependencies": { | ||
"vite-plugin-optimizer": "^1.1.4" | ||
}, | ||
"devDependencies": { | ||
"@types/node": "^16.x", | ||
"vite": "^2.x" | ||
@@ -14,0 +16,0 @@ }, |
@@ -0,13 +1,22 @@ | ||
# vite-plugin-electron-renderer | ||
[![npm package](https://nodei.co/npm/vite-plugin-electron-renderer.png?downloads=true&downloadRank=true&stars=true)](https://www.npmjs.com/package/vite-plugin-electron-renderer) | ||
# Use Electron and NodeJs API in Renderer-process | [简体中文](https://github.com/caoxiemeihao/vite-plugins/blob/main/packages/electron-renderer/README.zh-CN.md) | ||
<br/> | ||
[![NPM version](https://img.shields.io/npm/v/vite-plugin-electron-renderer.svg?style=flat)](https://npmjs.org/package/vite-plugin-electron-renderer) | ||
[![NPM Downloads](https://img.shields.io/npm/dm/vite-plugin-electron-renderer.svg?style=flat)](https://npmjs.org/package/vite-plugin-electron-renderer) | ||
### Example 👉 [electron-vite-boilerplate](https://github.com/caoxiemeihao/electron-vite-boilerplate) | ||
Use Electron and Node.js API in Renderer-process | ||
English | [简体中文](https://github.com/caoxiemeihao/vite-plugins/blob/main/packages/electron-renderer/README.zh-CN.md) | ||
### Usage | ||
## Example 👉 [electron-vite-boilerplate](https://github.com/caoxiemeihao/electron-vite-boilerplate) | ||
## Install | ||
```bash | ||
npm i vite-plugin-electron-renderer -D | ||
``` | ||
## Usage | ||
**vite.config.ts** | ||
@@ -26,53 +35,14 @@ | ||
**vrenderer/foo.ts** | ||
**renderer.js** | ||
```ts | ||
import { readFile } from 'fs' | ||
import { ipcRenderer } from 'electron' | ||
ipcRenderer.on('event-name', () => { | ||
// somethine code... | ||
}) | ||
readFile(/* something code... */) | ||
ipcRenderer.on('event-name', () => {/* something code... */}) | ||
``` | ||
--- | ||
## How to work | ||
### Options.resolve | ||
In some cases, you just want "vite" to load a module like NodeJs. | ||
You can custom-resolve the module **eg:** | ||
**vite.config.ts** | ||
```ts | ||
import { defineConfig } from 'vite' | ||
import electron from 'vite-plugin-electron-renderer' | ||
export default defineConfig({ | ||
plugins: [ | ||
electron({ | ||
resolve: { | ||
// In 'vite serve' phase 'electron-store' will generate file to `node_modules/.vite-plugin-electron-renderer/electron-store.js` | ||
// Then point 'electron-store' to this path through 'resolve.alias' | ||
'electron-store': `const Store=require('electron-store'); export default Store;`; | ||
sqlite3: () => { | ||
// dynamic calculate module exported members | ||
const sqlite3 = require('sqlite3'); | ||
const members = Object.keys(sqlite3); | ||
const code = ` | ||
const sqlite3 = require("sqlite3"); | ||
const { ${members.join(', ')} } = sqlite3; | ||
export { ${members.join(', ')}, sqlite3 as default } | ||
`; | ||
return code; | ||
}, | ||
}, | ||
}), | ||
], | ||
}) | ||
``` | ||
--- | ||
### How to work | ||
1. Fist, the plugin will configuration something. | ||
@@ -83,25 +53,15 @@ | ||
* `base = './'` | ||
* `build.assetsDir = ''` | ||
* `build.assetsDir = ''` -> *TODO: Automatic splicing "build.assetsDir"* | ||
* `build.rollupOptions.output.format = 'cjs'` | ||
- Add "electron", NodeJs built-in modules and "options.resolve" to "optimizeDeps.exclude" | ||
2. The plugin transform Electron and Node.js built-in modules to ESModule format in "vite serve" phase. | ||
```js | ||
{ | ||
optimizeDeps: { | ||
exclude: [ | ||
'electron', | ||
...'built-in modules', | ||
...Object.keys(options.resolve), | ||
], | ||
}, | ||
} | ||
``` | ||
3. Add Electron and Node.js built-in modules to Rollup "output.external" option in the "vite build" phase. | ||
2. The plugin transform "electron" and NodeJs built-in modules to ESModule format in "vite serve" phase. | ||
**Using Electron API in Renderer-process** `import { ipcRenderer } from 'electron` | ||
3. Add "electron" and NodeJs built-in modules to Rollup "output.external" option in the "vite build" phase. | ||
Actually redirect to "[node_modules/vite-plugin-electron-renderer/modules/electron-renderer.js](modules/electron-renderer.js)" by `resolve.alias` | ||
**Using electron in Renderer-process** `import { ipcRenderer } from 'electron` | ||
**Using Node.js API in Renderer-process** `import { readFile } from 'fs'` | ||
Actually redirect to "[node_modules/vite-plugin-electron-renderer/modules/electron-renderer.js](modules/electron-renderer.js)" through "resolve.alias". | ||
All Node.js API will be built into the `node_modules/.vite-plugin-electron-renderer` directory by [vite-plugin-optimizer](https://www.npmjs.com/package/vite-plugin-optimizer) |
@@ -0,12 +1,21 @@ | ||
# vite-plugin-electron-renderer | ||
[![npm package](https://nodei.co/npm/vite-plugin-electron-renderer.png?downloads=true&downloadRank=true&stars=true)](https://www.npmjs.com/package/vite-plugin-electron-renderer) | ||
# 支持在渲染进程中使用 Electron and NodeJs API | [English](https://github.com/caoxiemeihao/vite-plugins/tree/main/packages/electron-renderer#readme) | ||
<br/> | ||
[![NPM version](https://img.shields.io/npm/v/vite-plugin-electron-renderer.svg?style=flat)](https://npmjs.org/package/vite-plugin-electron-renderer) | ||
[![NPM Downloads](https://img.shields.io/npm/dm/vite-plugin-electron-renderer.svg?style=flat)](https://npmjs.org/package/vite-plugin-electron-renderer) | ||
支持在渲染进程中使用 Electron and Node.Js API | ||
[English](https://github.com/caoxiemeihao/vite-plugins/tree/main/packages/electron-renderer#readme) | 简体中文 | ||
## 示例 👉 [electron-vite-boilerplate](https://github.com/caoxiemeihao/electron-vite-boilerplate) | ||
### 使用 | ||
## 安装 | ||
```bash | ||
npm i | ||
## 使用 | ||
**vite.config.ts** | ||
@@ -25,53 +34,14 @@ | ||
**vrenderer/foo.ts** | ||
**renderer.js** | ||
```ts | ||
import { readFile } from 'fs' | ||
import { ipcRenderer } from 'electron' | ||
ipcRenderer.on('event-name', () => { | ||
// somethine code... | ||
}) | ||
readFile(/* something code... */) | ||
ipcRenderer.on('event-name', () => {/* something code... */}) | ||
``` | ||
--- | ||
## 工作原理 | ||
### Options.resolve | ||
很多时候, 你只想在 Vite 中用 NodeJs 的方式加载模块 | ||
通过 "resolve" 配置实现 **例如:** | ||
**vite.config.ts** | ||
```ts | ||
import { defineConfig } from 'vite' | ||
import electron from 'vite-plugin-electron-renderer' | ||
export default defineConfig({ | ||
plugins: [ | ||
electron({ | ||
resolve: { | ||
// 在 'vite serve' 阶段 'electron-store' 会生成到 `node_modules/.vite-plugin-electron-renderer/electron-store.js` 中 | ||
// 然后配置 `resolve.alias` 指向这个路径 | ||
'electron-store': `const Store=require('electron-store'); export default Store;`; | ||
sqlite3: () => { | ||
// 动态计算出模块中导出的成员 | ||
const sqlite3 = require('sqlite3'); | ||
const members = Object.keys(sqlite3); | ||
const code = ` | ||
const sqlite3 = require("sqlite3"); | ||
const { ${members.join(', ')} } = sqlite3; | ||
export { ${members.join(', ')}, sqlite3 as default } | ||
`; | ||
return code; | ||
}, | ||
}, | ||
}), | ||
], | ||
}) | ||
``` | ||
--- | ||
### 工作原理 | ||
1. 首先,插件会修改一些配置 | ||
@@ -85,10 +55,15 @@ | ||
- 将 'electron',NodeJs 内置模块和 `options.resolve` 插入到 "optimizeDeps.exclude" 中 | ||
- 将 Electron,Node.Js 内置模块和 `options.resolve` 插入到 "optimizeDeps.exclude" 中 | ||
2. 开发阶段(`vite serve`) 将 Electron 和 NodeJs 内置模块转换成 ESModule 格式 | ||
2. 开发阶段(`vite serve`) 将 Electron 和 Node.Js 内置模块转换成 ESModule 格式 | ||
3. 打包阶段(`vite build`) 将 "electron" 和 NodeJs 内置模块插入到 Rollup 的 "output.external" 中 | ||
3. 打包阶段(`vite build`) 将 Electron 和 Node.Js 内置模块插入到 Rollup 的 `output.external` 中 | ||
**在想染进程中使用 electron** `import { ipcRenderer } from 'electron` | ||
**在染进程中使用 electron** `import { ipcRenderer } from 'electron` | ||
实际上通过 "resolve.alias" 重定向到 "[node_modules/vite-plugin-electron-renderer/modules/electron-renderer.js](modules/electron-renderer.js)" | ||
实际上通过 `resolve.alias` 重定向到 "[node_modules/vite-plugin-electron-renderer/modules/electron-renderer.js](modules/electron-renderer.js)" | ||
**在染进程中使用 Node.js API** `import { readFile } from 'fs'` | ||
所有的 Node.js API 将会通过 [vite-plugin-optimizer](https://www.npmjs.com/package/vite-plugin-optimizer) 构建到 `node_modules/.vite-plugin-electron-renderer` 目录 | ||
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
1
3
8309
1
101
66
1
+ Addedvite-plugin-optimizer@^1.1.4
+ Addedvite-plugin-optimizer@1.4.3(transitive)