ソースを参照

wip: support electron

Gavin Chain 3 年 前
コミット
d4c4215b08
7 ファイル変更827 行追加32 行削除
  1. 2 0
      .env
  2. 13 0
      build/script/startElectron.ts
  3. 71 0
      config/electron/index.ts
  4. 60 1
      package.json
  5. 74 0
      script/build.js
  6. 63 0
      script/rollup.config.js
  7. 544 31
      yarn.lock

+ 2 - 0
.env

@@ -1,3 +1,5 @@
+PORT = 3100
+
 # port
 VITE_PORT = 3100
 

+ 13 - 0
build/script/startElectron.ts

@@ -0,0 +1,13 @@
+import { createServer } from 'vite';
+import { execSync } from 'child_process';
+import path from 'path';
+
+(async () => {
+  const server = await createServer({
+    root: path.resolve(__dirname, '../../'),
+  });
+
+  await server.listen();
+
+  execSync('node script/build --env=development --watch');
+})();

+ 71 - 0
config/electron/index.ts

@@ -0,0 +1,71 @@
+/**
+ * electron 主文件
+ */
+import dotenv from 'dotenv';
+import { app, BrowserWindow, ipcMain } from 'electron';
+import is_dev from 'electron-is-dev';
+// @ts-ignore
+import Store from 'electron-store';
+import { join } from 'path';
+
+const store = new Store();
+// @ts-ignore
+ipcMain.on('store:set', async (e, args) => {
+  store.set(args.key, args.value);
+});
+// @ts-ignore
+ipcMain.handle('store:get', async (e, args) => {
+  return await store.get(args);
+});
+// @ts-ignore
+ipcMain.on('store:delete', async (e, args) => {
+  store.delete(args);
+});
+
+dotenv.config({ path: join(__dirname, '../../.env') });
+
+let win = null;
+
+class createWin {
+  // 创建浏览器窗口
+  constructor() {
+    // @ts-ignore
+    win = new BrowserWindow({
+      width: 930,
+      height: 700,
+      frame: false,
+      transparent: true,
+      webPreferences: {
+        devTools: true,
+
+        nodeIntegration: true,
+        enableRemoteModule: true,
+      },
+    });
+    // win.webContents.openDevTools()
+    // @ts-ignore
+    win.maximize();
+    // win.maximize()
+
+    const URL = is_dev
+      ? `http://localhost:${process.env.PORT}` // vite 启动的服务器地址
+      : `file://${join(__dirname, '../../dist/render/index.html')}`; // vite 构建后的静态文件地址
+
+    // @ts-ignore
+    win.loadURL(URL);
+  }
+}
+
+app.whenReady().then(() => new createWin());
+
+app.on('window-all-closed', () => {
+  if (process.platform !== 'darwin') {
+    app.quit();
+  }
+});
+
+app.on('activate', () => {
+  if (BrowserWindow.getAllWindows().length === 0) {
+    new createWin();
+  }
+});

+ 60 - 1
package.json

@@ -6,10 +6,55 @@
     "email": "anncwb@126.com",
     "url": "https://github.com/anncwb"
   },
+  "build": {
+    "appId": "xxx@gmail.com",
+    "electronDownload": {
+      "mirror": "https://npm.taobao.org/mirrors/electron/"
+    },
+    "files": [
+      "!node_modules",
+      "dist/**"
+    ],
+    "asar": false,
+    "mac": {
+      "artifactName": "${productName}_setup_${version}.${ext}",
+      "target": [
+        "dmg"
+      ]
+    },
+    "linux": {
+      "icon": "build/icons/512x512.png",
+      "target": [
+        "deb"
+      ]
+    },
+    "win": {
+      "target": [
+        {
+          "target": "nsis",
+          "arch": [
+            "x64"
+          ]
+        }
+      ],
+      "artifactName": "${productName}_setup_${version}.${ext}"
+    },
+    "nsis": {
+      "oneClick": false,
+      "perMachine": false,
+      "allowToChangeInstallationDirectory": true,
+      "deleteAppDataOnUninstall": false
+    }
+  },
+  "main": "dist/main/build.js",
   "scripts": {
+    "dev": "vite",
+    "app": "esno ./build/script/runApp.ts",
+    "app:build": "npm run build && node script/build && electron-builder ",
+    "app:vue": "vite --force",
+    "app:ele": "node script/build --env=development --watch",
     "bootstrap": "yarn install",
     "serve": "npm run dev",
-    "dev": "vite",
     "build": "vite build && esno ./build/script/postBuild.ts",
     "build:no-cache": "yarn clean:cache && npm run build",
     "report": "cross-env REPORT=true npm run build",
@@ -32,6 +77,8 @@
     "postinstall": "npm run install:husky"
   },
   "dependencies": {
+    "electron-is-dev": "^1.2.0",
+    "electron-store": "^6.0.0",
     "@iconify/iconify": "^2.0.1",
     "@logicflow/core": "^0.4.11",
     "@logicflow/extension": "^0.4.12",
@@ -61,6 +108,18 @@
     "xlsx": "^0.17.0"
   },
   "devDependencies": {
+    "rollup-plugin-esbuild": "^3.0.2",
+    "@rollup/plugin-alias": "^3.1.1",
+    "@rollup/plugin-commonjs": "^15.0.0",
+    "@rollup/plugin-json": "^4.1.0",
+    "@rollup/plugin-node-resolve": "^9.0.0",
+    "wait-on": "^5.2.1",
+    "electron-contextmenu-middleware": "^1.0.3",
+    "electron-input-menu": "^2.1.0",
+    "electron": "^11.0.0",
+    "electron-builder": "^22.8.0",
+    "electron-connect": "^0.6.3",
+    "concurrently": "^5.3.0",
     "@commitlint/cli": "^12.1.4",
     "@commitlint/config-conventional": "^12.1.4",
     "@iconify/json": "^1.1.350",

+ 74 - 0
script/build.js

@@ -0,0 +1,74 @@
+/**
+ * electron 打包
+ */
+const path = require('path');
+const rollup = require('rollup');
+const argv = require('minimist')(process.argv.slice(2));
+const chalk = require('chalk');
+const ora = require('ora');
+const waitOn = require('wait-on');
+const electron = require('electron-connect').server.create({ stopOnClose: true });
+require('dotenv').config({ path: path.join(__dirname, '../.env') });
+const options = require('./rollup.config');
+const net = require('net');
+const { URL } = require('url');
+
+const opt = options(argv.env);
+const TAG = '[script/build.js]';
+const spinner = ora(`${TAG} Electron build...`);
+
+const watchFunc = function () {
+  // once here, all resources are available
+  const watcher = rollup.watch(opt);
+  watcher.on('change', (filename) => {
+    const log = chalk.green(`change -- ${filename}`);
+    console.log(TAG, log);
+  });
+  watcher.on('event', (ev) => {
+    if (ev.code === 'END') {
+      // init-未启动、started-第一次启动、restarted-重新启动
+      electron.electronState === 'init' ? electron.start() : electron.restart();
+    } else if (ev.code === 'ERROR') {
+      console.log(ev.error);
+    }
+  });
+};
+
+const resource = `http://localhost:${process.env.PORT}/index.html`; // 因为 vite 不会重定向到 index.html,所以直接写 index.html 路由。
+
+if (argv.watch) {
+  waitOn(
+    {
+      resources: [resource],
+      timeout: 5000,
+    },
+    (err) => {
+      if (err) {
+        const { port, hostname } = new URL(resource);
+        const serverSocket = net.connect(port || 80, hostname, () => {
+          watchFunc();
+        });
+        serverSocket.on('error', (e) => {
+          console.log(err);
+          console.log(e);
+          process.exit(1);
+        });
+      } else {
+        watchFunc();
+      }
+    }
+  );
+} else {
+  spinner.start();
+  rollup
+    .rollup(opt)
+    .then((build) => {
+      spinner.stop();
+      console.log(TAG, chalk.green('Electron build successed.'));
+      build.write(opt.output);
+    })
+    .catch((error) => {
+      spinner.stop();
+      console.log(`\n${TAG} ${chalk.red('构建报错')}\n`, error, '\n');
+    });
+}

+ 63 - 0
script/rollup.config.js

@@ -0,0 +1,63 @@
+const path = require('path');
+const { nodeResolve } = require('@rollup/plugin-node-resolve');
+const commonjs = require('@rollup/plugin-commonjs');
+const esbuild = require('rollup-plugin-esbuild');
+const alias = require('@rollup/plugin-alias');
+const json = require('@rollup/plugin-json');
+
+module.exports = (env = 'production') => {
+  console.log('环境:' + env);
+  return {
+    input: path.join(__dirname, '../config/electron/index.ts'),
+    output: {
+      file: path.join(__dirname, '../dist/main/build.js'),
+      format: 'cjs',
+      name: 'ElectronMainBundle',
+      sourcemap: true,
+    },
+    plugins: [
+      nodeResolve({ jsnext: true, preferBuiltins: true, browser: true }), // 消除碰到 node.js 模块时⚠警告
+      commonjs(),
+      json(),
+      esbuild({
+        // All options are optional
+        include: /\.[jt]sx?$/, // default, inferred from `loaders` option
+        exclude: /node_modules/, // default
+        // watch: process.argv.includes('--watch'), // rollup 中有配置
+        sourceMap: false, // default
+        minify: process.env.NODE_ENV === 'production',
+        target: 'es2017', // default, or 'es20XX', 'esnext'
+        jsxFactory: 'React.createElement',
+        jsxFragment: 'React.Fragment',
+        // Like @rollup/plugin-replace
+        define: {
+          __VERSION__: '"x.y.z"',
+        },
+        // Add extra loaders
+        loaders: {
+          // Add .json files support
+          // require @rollup/plugin-commonjs
+          '.json': 'json',
+          // Enable JSX in .js files too
+          '.js': 'jsx',
+        },
+      }),
+      alias({
+        entries: [{ find: '@main', replacement: path.join(__dirname, '../src/main') }],
+      }),
+    ],
+    external: [
+      'crypto',
+      'assert',
+      'fs',
+      'util',
+      'os',
+      'events',
+      'child_process',
+      'http',
+      'https',
+      'path',
+      'electron',
+    ],
+  };
+};

ファイルの差分が大きいため隠しています
+ 544 - 31
yarn.lock


この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません