hardware-example 0.3.40-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -0
- package/electron-builder.config.js +99 -0
- package/entitlements.mac.plist +10 -0
- package/forge.config.js +65 -0
- package/package.json +39 -0
- package/public/bin/bridge/linux-arm64/onekeyd +0 -0
- package/public/bin/bridge/linux-x64/onekeyd +0 -0
- package/public/bin/bridge/mac-arm64/onekeyd +0 -0
- package/public/bin/bridge/mac-x64/onekeyd +0 -0
- package/public/bin/bridge/win-x64/onekeyd.exe +0 -0
- package/public/icons/512x512.icns +0 -0
- package/public/icons/512x512.png +0 -0
- package/public/icons/background.png +0 -0
- package/public/icons/favicon/favicon.png +0 -0
- package/public/icons/icon.icns +0 -0
- package/public/icons/icon.png +0 -0
- package/public/icons/installerSidebar.bmp +0 -0
- package/scripts/copy-injected.js +38 -0
- package/src/index.ts +233 -0
- package/src/preload.ts +28 -0
- package/src/process/BaseProcess.ts +212 -0
- package/src/process/Bridge.ts +93 -0
- package/src/process/index.ts +36 -0
- package/tsconfig.json +30 -0
- package/webpack.config.ts +61 -0
package/README.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
# Q & A
|
|
3
|
+
## The mac running message is damaged and cannot be opened. You should move it to the trash.
|
|
4
|
+
### Intel Mac
|
|
5
|
+
1. Open settings
|
|
6
|
+
2. Security & Privacy
|
|
7
|
+
3. Security
|
|
8
|
+
4. Allow apps downloaded from: App Store and identified developers
|
|
9
|
+
5. Open the app again
|
|
10
|
+
|
|
11
|
+
### Apple Silicon Mac
|
|
12
|
+
1. Open terminal
|
|
13
|
+
2. Run the following command
|
|
14
|
+
```bash
|
|
15
|
+
sudo /usr/bin/xattr -c /Applications/YourAppName.app
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### If the above command does not work, try the following command
|
|
19
|
+
```bash
|
|
20
|
+
sudo xattr -r -d com.apple.quarantine /Applications/YourAppName.app
|
|
21
|
+
```
|
|
22
|
+
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
extraMetadata: {
|
|
3
|
+
main: 'dist/index.js',
|
|
4
|
+
version: '1',
|
|
5
|
+
},
|
|
6
|
+
appId: 'so.onekey.example.hardware-desktop',
|
|
7
|
+
productName: 'HardwareExample',
|
|
8
|
+
copyright: 'Copyright © OeKey 2024',
|
|
9
|
+
asar: true,
|
|
10
|
+
buildVersion: '1',
|
|
11
|
+
directories: {
|
|
12
|
+
output: 'out',
|
|
13
|
+
},
|
|
14
|
+
files: [
|
|
15
|
+
'web-build',
|
|
16
|
+
'public',
|
|
17
|
+
'bin',
|
|
18
|
+
'!public/bin/**/*',
|
|
19
|
+
'dist/**/*.js',
|
|
20
|
+
'!dist/__**',
|
|
21
|
+
'package.json',
|
|
22
|
+
],
|
|
23
|
+
extraResources: [
|
|
24
|
+
{
|
|
25
|
+
from: 'public/icons/512x512.png',
|
|
26
|
+
to: 'icons/512x512.png',
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
dmg: {
|
|
30
|
+
sign: false,
|
|
31
|
+
contents: [
|
|
32
|
+
{
|
|
33
|
+
x: 410,
|
|
34
|
+
y: 175,
|
|
35
|
+
type: 'link',
|
|
36
|
+
path: '/Applications',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
x: 130,
|
|
40
|
+
y: 175,
|
|
41
|
+
type: 'file',
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
background: 'public/icons/background.png',
|
|
45
|
+
},
|
|
46
|
+
nsis: {
|
|
47
|
+
oneClick: false,
|
|
48
|
+
installerSidebar: 'public/icons/installerSidebar.bmp',
|
|
49
|
+
},
|
|
50
|
+
mac: {
|
|
51
|
+
// skip code signing
|
|
52
|
+
identity: null,
|
|
53
|
+
extraResources: [
|
|
54
|
+
{
|
|
55
|
+
from: 'public/bin/bridge/mac-${arch}',
|
|
56
|
+
to: 'bin/bridge',
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
icon: 'public/icons/512x512.png',
|
|
60
|
+
artifactName: 'Hardware-Example-mac-${arch}.${ext}',
|
|
61
|
+
hardenedRuntime: true,
|
|
62
|
+
gatekeeperAssess: false,
|
|
63
|
+
darkModeSupport: false,
|
|
64
|
+
category: 'productivity',
|
|
65
|
+
target: [
|
|
66
|
+
// { target: 'dmg', arch: ['x64', 'arm64'] },
|
|
67
|
+
{ target: 'zip', arch: ['x64', 'arm64'] },
|
|
68
|
+
],
|
|
69
|
+
entitlements: 'entitlements.mac.plist',
|
|
70
|
+
extendInfo: {
|
|
71
|
+
NSCameraUsageDescription: 'Please allow OneKey to use your camera',
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
win: {
|
|
75
|
+
extraResources: [
|
|
76
|
+
{
|
|
77
|
+
from: 'public/bin/bridge/win-${arch}',
|
|
78
|
+
to: 'bin/bridge',
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
icon: 'public/icons/512x512.png',
|
|
82
|
+
artifactName: 'Hardware-Example-win-${arch}.${ext}',
|
|
83
|
+
verifyUpdateCodeSignature: false,
|
|
84
|
+
target: ['nsis'],
|
|
85
|
+
},
|
|
86
|
+
linux: {
|
|
87
|
+
extraResources: [
|
|
88
|
+
{
|
|
89
|
+
from: 'public/bin/bridge/linux-${arch}',
|
|
90
|
+
to: 'bin/bridge',
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
icon: 'public/icons/512x512.png',
|
|
94
|
+
artifactName: 'Hardware-Example-linux-${arch}.${ext}',
|
|
95
|
+
executableName: 'onekey-hardware-example',
|
|
96
|
+
category: 'Utility',
|
|
97
|
+
target: ['AppImage'],
|
|
98
|
+
},
|
|
99
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>com.apple.security.cs.allow-jit</key>
|
|
6
|
+
<true/>
|
|
7
|
+
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
|
8
|
+
<true/>
|
|
9
|
+
</dict>
|
|
10
|
+
</plist>
|
package/forge.config.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
const d = require('debug');
|
|
5
|
+
|
|
6
|
+
const debug = d('forge config');
|
|
7
|
+
|
|
8
|
+
module.exports = {
|
|
9
|
+
packagerConfig: {
|
|
10
|
+
name: 'HardwareExample',
|
|
11
|
+
appBundleId: 'so.onekey.example.hardware-desktop',
|
|
12
|
+
executableName: process.platform === 'linux' ? 'onekey-hardware-example' : 'HardwareExample',
|
|
13
|
+
appCategoryType: 'public.app-category.developer-tools',
|
|
14
|
+
appCopyright: 'Copyright 2024 OneKey Ltd',
|
|
15
|
+
asar: true,
|
|
16
|
+
icon: path.resolve(__dirname, 'public', 'icons', 'icon'),
|
|
17
|
+
},
|
|
18
|
+
rebuildConfig: {},
|
|
19
|
+
hooks: {
|
|
20
|
+
packageAfterCopy: async (forgeConfig, buildPath, electronVersion, platform, arch) => {
|
|
21
|
+
let originDir;
|
|
22
|
+
let binName;
|
|
23
|
+
const destDir = path.resolve(buildPath, '../', 'bin', 'bridge');
|
|
24
|
+
|
|
25
|
+
switch (platform) {
|
|
26
|
+
case 'darwin':
|
|
27
|
+
originDir = path.resolve(buildPath, 'public', 'bin', 'bridge', `mac-${arch}`);
|
|
28
|
+
binName = 'onekeyd';
|
|
29
|
+
break;
|
|
30
|
+
case 'win32':
|
|
31
|
+
originDir = path.resolve(buildPath, 'public', 'bin', 'bridge', 'win-x64');
|
|
32
|
+
binName = 'onekeyd.exe';
|
|
33
|
+
break;
|
|
34
|
+
case 'linux':
|
|
35
|
+
originDir = path.resolve(buildPath, 'public', 'bin', 'bridge', `linux-${arch}`);
|
|
36
|
+
binName = 'onekeyd';
|
|
37
|
+
break;
|
|
38
|
+
default:
|
|
39
|
+
originDir = '';
|
|
40
|
+
binName = 'onekeyd';
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
debug(`Copy bridge bin: originDir:${originDir}, platform:${platform}, arch:${arch}`);
|
|
45
|
+
await fs.ensureDir(destDir);
|
|
46
|
+
await fs.copy(path.resolve(originDir, binName), path.resolve(destDir, binName));
|
|
47
|
+
},
|
|
48
|
+
packageAfterPrune: async (forgeConfig, buildPath, electronVersion, platform, arch) => {
|
|
49
|
+
const bridgeDir = path.resolve(buildPath, 'public', 'bin');
|
|
50
|
+
const scrDir = path.resolve(buildPath, 'src');
|
|
51
|
+
const buildScriptDir = path.resolve(buildPath, 'scripts');
|
|
52
|
+
|
|
53
|
+
await fs.remove(bridgeDir);
|
|
54
|
+
await fs.remove(scrDir);
|
|
55
|
+
await fs.remove(buildScriptDir);
|
|
56
|
+
return null;
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
makers: [
|
|
60
|
+
{
|
|
61
|
+
name: '@electron-forge/maker-zip',
|
|
62
|
+
platforms: ['darwin', 'win32'],
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hardware-example",
|
|
3
|
+
"productName": "HardwareExample",
|
|
4
|
+
"executableName": "onekey-hardware-example",
|
|
5
|
+
"version": "0.3.40-alpha.1",
|
|
6
|
+
"author": "OneKey",
|
|
7
|
+
"description": "End-to-end encrypted workspaces for teams",
|
|
8
|
+
"main": "dist/index.js",
|
|
9
|
+
"license": "GPL-3.0-only",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"copy:inject": "node scripts/copy-injected.js",
|
|
12
|
+
"clean:build": "rimraf out",
|
|
13
|
+
"dev-electron-web": "cross-env NODE_ENV=development yarn copy:inject && yarn build:main && cd ../expo-example && yarn dev:electron-web",
|
|
14
|
+
"build-electron-web": "yarn copy:inject && yarn build:main && cd ../expo-example && yarn build:electron-web",
|
|
15
|
+
"dev": "npx concurrently \"yarn dev:electron\" \"cross-env TRANSFORM_REGENERATOR_DISABLED=true BROWSER=none yarn dev-electron-web\"",
|
|
16
|
+
"dev:electron": "electron --inspect=5858 dist/index.js",
|
|
17
|
+
"build:package": "yarn build-electron-web && electron-forge package",
|
|
18
|
+
"build:main": "webpack --config webpack.config.ts",
|
|
19
|
+
"make": "yarn clean:build && yarn build-electron-web && electron-builder build -mw --config electron-builder.config.js --publish never",
|
|
20
|
+
"lint": "eslint --ext .tsx --ext .ts ./",
|
|
21
|
+
"ts:check": "yarn tsc --noEmit"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"debug": "4.3.4",
|
|
25
|
+
"electron-is-dev": "^3.0.1",
|
|
26
|
+
"node-fetch": "^2.6.7"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/webpack": "^5.28.5",
|
|
30
|
+
"@types/webpack-node-externals": "^3.0.4",
|
|
31
|
+
"clean-webpack-plugin": "^4.0.0",
|
|
32
|
+
"cross-env": "^7.0.3",
|
|
33
|
+
"electron": "^28.0.0",
|
|
34
|
+
"electron-builder": "^24.9.1",
|
|
35
|
+
"webpack": "^5.90.2",
|
|
36
|
+
"webpack-node-externals": "^3.0.0"
|
|
37
|
+
},
|
|
38
|
+
"gitHead": "455e71107390795e90651ecb46ef32cd8c8be5e2"
|
|
39
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
const rootDir = path.join(__dirname, '../../../../');
|
|
6
|
+
|
|
7
|
+
const sourceDir = path.join(rootDir, 'node_modules/@onekeyfe/hd-web-sdk/build/');
|
|
8
|
+
const targetDir = path.join(rootDir, 'packages/connect-examples/electron-example/public/js-sdk/');
|
|
9
|
+
|
|
10
|
+
async function copyFiles() {
|
|
11
|
+
try {
|
|
12
|
+
await fs.remove(targetDir);
|
|
13
|
+
await fs.ensureDir(targetDir);
|
|
14
|
+
console.log(`Target directory ${targetDir} is ensured`);
|
|
15
|
+
|
|
16
|
+
await fs.copy(sourceDir, targetDir, {
|
|
17
|
+
recursive: true,
|
|
18
|
+
overwrite: true,
|
|
19
|
+
filter: (src, dest) => {
|
|
20
|
+
// Don't copy onekey-js-sdk
|
|
21
|
+
if (src.endsWith('onekey-js-sdk.min.js') || src.endsWith('onekey-js-sdk.js')) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Don't copy source maps
|
|
26
|
+
if (src.endsWith('.map')) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
return true;
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
console.log(`Files copied from ${sourceDir} to ${targetDir}`);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error('Error copying files:', error);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
copyFiles();
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { screen, app, BrowserWindow, session } from 'electron';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import isDevelopment from 'electron-is-dev';
|
|
4
|
+
import { format as formatUrl } from 'url';
|
|
5
|
+
import initProcess from './process';
|
|
6
|
+
|
|
7
|
+
const isMac = process.platform === 'darwin';
|
|
8
|
+
const isWin = process.platform === 'win32';
|
|
9
|
+
|
|
10
|
+
const APP_NAME = 'OneKey Example';
|
|
11
|
+
app.name = APP_NAME;
|
|
12
|
+
let mainWindow: BrowserWindow | null;
|
|
13
|
+
|
|
14
|
+
let isAppReady = false;
|
|
15
|
+
|
|
16
|
+
(global as any).resourcesPath = isDevelopment
|
|
17
|
+
? path.join(__dirname, '../public')
|
|
18
|
+
: path.join(process.resourcesPath, 'app');
|
|
19
|
+
const staticPath = isDevelopment
|
|
20
|
+
? path.join(__dirname, '../public')
|
|
21
|
+
: path.join((global as any).resourcesPath, 'public');
|
|
22
|
+
|
|
23
|
+
const sdkConnectSrc = isDevelopment
|
|
24
|
+
? `file://${path.join(staticPath, 'js-sdk/')}`
|
|
25
|
+
: path.join('public', 'js-sdk/');
|
|
26
|
+
|
|
27
|
+
function initChildProcess() {
|
|
28
|
+
return initProcess({ isDevelopment });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function showMainWindow() {
|
|
32
|
+
if (!mainWindow) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
mainWindow.show();
|
|
36
|
+
mainWindow.focus();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
40
|
+
function quitOrMinimizeApp(event?: Event) {
|
|
41
|
+
// On OS X it is common for applications and their menu bar
|
|
42
|
+
// to stay active until the user quits explicitly with Cmd + Q
|
|
43
|
+
if (isMac) {
|
|
44
|
+
// **** renderer app will reload after minimize, and keytar not working.
|
|
45
|
+
event?.preventDefault();
|
|
46
|
+
if (!mainWindow?.isDestroyed()) {
|
|
47
|
+
mainWindow?.hide();
|
|
48
|
+
}
|
|
49
|
+
// ****
|
|
50
|
+
// app.quit();
|
|
51
|
+
} else {
|
|
52
|
+
app.quit();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function createMainWindow() {
|
|
57
|
+
const display = screen.getPrimaryDisplay();
|
|
58
|
+
const dimensions = display.workAreaSize;
|
|
59
|
+
const ratio = 16 / 9;
|
|
60
|
+
|
|
61
|
+
const browserWindow = new BrowserWindow({
|
|
62
|
+
title: APP_NAME,
|
|
63
|
+
titleBarStyle: isWin ? 'default' : 'hidden',
|
|
64
|
+
trafficLightPosition: { x: 20, y: 12 },
|
|
65
|
+
autoHideMenuBar: true,
|
|
66
|
+
frame: true,
|
|
67
|
+
resizable: true,
|
|
68
|
+
width: Math.min(1920, dimensions.width),
|
|
69
|
+
height: Math.min(1920 / ratio, dimensions.height),
|
|
70
|
+
webPreferences: {
|
|
71
|
+
spellcheck: false,
|
|
72
|
+
webviewTag: true,
|
|
73
|
+
webSecurity: !isDevelopment,
|
|
74
|
+
nativeWindowOpen: true,
|
|
75
|
+
allowRunningInsecureContent: isDevelopment,
|
|
76
|
+
// webview injected js needs isolation=false, because property can not be exposeInMainWorld() when isolation enabled.
|
|
77
|
+
contextIsolation: false,
|
|
78
|
+
preload: path.join(__dirname, 'preload.js'),
|
|
79
|
+
sandbox: false,
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
if (isDevelopment) {
|
|
84
|
+
browserWindow.webContents.openDevTools();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
browserWindow.webContents.on('did-finish-load', () => {
|
|
88
|
+
console.log('browserWindow >>>> did-finish-load');
|
|
89
|
+
browserWindow.webContents.send('SET_ONEKEY_DESKTOP_GLOBALS', {
|
|
90
|
+
resourcesPath: (global as any).resourcesPath,
|
|
91
|
+
staticPath: `file://${staticPath}`,
|
|
92
|
+
sdkConnectSrc,
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const src = isDevelopment
|
|
97
|
+
? 'http://localhost:19006/'
|
|
98
|
+
: formatUrl({
|
|
99
|
+
pathname: 'index.html',
|
|
100
|
+
protocol: 'file',
|
|
101
|
+
slashes: true,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
browserWindow.loadURL(src);
|
|
105
|
+
|
|
106
|
+
browserWindow.on('closed', () => {
|
|
107
|
+
mainWindow = null;
|
|
108
|
+
isAppReady = false;
|
|
109
|
+
console.log('set isAppReady on browserWindow closed', isAppReady);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
browserWindow.webContents.on('devtools-opened', () => {
|
|
113
|
+
browserWindow.focus();
|
|
114
|
+
setImmediate(() => {
|
|
115
|
+
browserWindow.focus();
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// dom-ready is fired after ipcMain:app/ready
|
|
120
|
+
browserWindow.webContents.on('dom-ready', () => {
|
|
121
|
+
isAppReady = true;
|
|
122
|
+
console.log('set isAppReady on browserWindow dom-ready', isAppReady);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const filter = {
|
|
126
|
+
urls: ['http://127.0.0.1:21320/*', 'http://localhost:21320/*'],
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
session.defaultSession.webRequest.onBeforeSendHeaders(filter, (details, callback) => {
|
|
130
|
+
const { url } = details;
|
|
131
|
+
if (url.startsWith('http://127.0.0.1:21320/') || url.startsWith('http://localhost:21320/')) {
|
|
132
|
+
// resolve onekey bridge CORS error
|
|
133
|
+
details.requestHeaders.Origin = 'https://jssdk.onekey.so';
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
callback({ cancel: false, requestHeaders: details.requestHeaders });
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
if (!isDevelopment) {
|
|
140
|
+
const PROTOCOL = 'file';
|
|
141
|
+
session.defaultSession.protocol.interceptFileProtocol(PROTOCOL, (request, callback) => {
|
|
142
|
+
const isJsSdkFile = request.url.indexOf('/public/js-sdk') > -1;
|
|
143
|
+
const isIFrameHtml = request.url.indexOf('/public/js-sdk/iframe.html') > -1;
|
|
144
|
+
|
|
145
|
+
// resolve iframe path
|
|
146
|
+
if (isJsSdkFile && isIFrameHtml) {
|
|
147
|
+
callback({
|
|
148
|
+
path: path.join(__dirname, '..', 'public', 'js-sdk', 'iframe.html'),
|
|
149
|
+
});
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// resolve jssdk path
|
|
154
|
+
if (isJsSdkFile) {
|
|
155
|
+
const url = request.url.substr(PROTOCOL.length + 1);
|
|
156
|
+
callback(path.join(__dirname, '..', url));
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// resolve main app path
|
|
161
|
+
let url = request.url.substr(PROTOCOL.length + 1);
|
|
162
|
+
url = path.join(__dirname, '..', 'web-build', url);
|
|
163
|
+
callback(url);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
167
|
+
browserWindow.webContents.on('did-fail-load', (_, __, ___, validatedURL) => {
|
|
168
|
+
const redirectPath = validatedURL.replace(`${PROTOCOL}://`, '');
|
|
169
|
+
if (validatedURL.startsWith(PROTOCOL) && !redirectPath.includes('.')) {
|
|
170
|
+
browserWindow.loadURL(src);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// @ts-expect-error
|
|
176
|
+
browserWindow.on('close', (event: Event) => {
|
|
177
|
+
// hide() instead of close() on MAC
|
|
178
|
+
if (isMac) {
|
|
179
|
+
event.preventDefault();
|
|
180
|
+
if (!browserWindow.isDestroyed()) {
|
|
181
|
+
browserWindow.blur();
|
|
182
|
+
browserWindow.hide(); // hide window only
|
|
183
|
+
// browserWindow.minimize(); // hide window and minimize to Docker
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
return browserWindow;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const singleInstance = app.requestSingleInstanceLock();
|
|
192
|
+
|
|
193
|
+
if (!singleInstance && !process.mas) {
|
|
194
|
+
quitOrMinimizeApp();
|
|
195
|
+
} else {
|
|
196
|
+
app.on('second-instance', (e, argv) => {
|
|
197
|
+
if (mainWindow) {
|
|
198
|
+
if (mainWindow.isMinimized()) mainWindow.restore();
|
|
199
|
+
showMainWindow();
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
app.name = APP_NAME;
|
|
204
|
+
app.on('ready', () => {
|
|
205
|
+
if (!mainWindow) {
|
|
206
|
+
mainWindow = createMainWindow();
|
|
207
|
+
}
|
|
208
|
+
initChildProcess();
|
|
209
|
+
showMainWindow();
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// wuit when all windows are closed, except on macOS. There, it's common
|
|
214
|
+
// for applications and their menu bar to stay active until the user quits
|
|
215
|
+
// explicitly with Cmd + Q
|
|
216
|
+
app.on('window-all-closed', (event: Event) => {
|
|
217
|
+
quitOrMinimizeApp(event);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
app.on('activate', () => {
|
|
221
|
+
if (!mainWindow) {
|
|
222
|
+
mainWindow = createMainWindow();
|
|
223
|
+
}
|
|
224
|
+
showMainWindow();
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
app.on('before-quit', () => {
|
|
228
|
+
if (mainWindow) {
|
|
229
|
+
mainWindow?.removeAllListeners();
|
|
230
|
+
mainWindow?.removeAllListeners('close');
|
|
231
|
+
mainWindow?.close();
|
|
232
|
+
}
|
|
233
|
+
});
|
package/src/preload.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-unused-vars,@typescript-eslint/require-await */
|
|
3
|
+
import { ipcRenderer, contextBridge } from 'electron';
|
|
4
|
+
|
|
5
|
+
export type DesktopAPI = {
|
|
6
|
+
reloadBridgeProcess: () => void;
|
|
7
|
+
};
|
|
8
|
+
declare global {
|
|
9
|
+
interface Window {
|
|
10
|
+
desktopApi: DesktopAPI;
|
|
11
|
+
INJECT_PATH: string;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
ipcRenderer.on('SET_ONEKEY_DESKTOP_GLOBALS', (_, globals) => {
|
|
16
|
+
// @ts-expect-error
|
|
17
|
+
window.ONEKEY_DESKTOP_GLOBALS = globals;
|
|
18
|
+
// contextBridge.exposeInMainWorld('ONEKEY_DESKTOP_GLOBALS', globals);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const desktopApi = {
|
|
22
|
+
reloadBridgeProcess: () => {
|
|
23
|
+
ipcRenderer.send('app/reloadBridgeProcess');
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
window.desktopApi = desktopApi;
|
|
28
|
+
// contextBridge.exposeInMainWorld('desktopApi', desktopApi);
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import electron from 'electron';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
import type { ChildProcess } from 'child_process';
|
|
6
|
+
|
|
7
|
+
export type Status = {
|
|
8
|
+
service: boolean;
|
|
9
|
+
process: boolean;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type Options = {
|
|
13
|
+
startupThrottleTime?: number;
|
|
14
|
+
stopWaitTimes?: number;
|
|
15
|
+
autoRestart?: number;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const defaultOptions: Options = {
|
|
19
|
+
startupThrottleTime: 0,
|
|
20
|
+
stopWaitTimes: 10,
|
|
21
|
+
autoRestart: 2,
|
|
22
|
+
} as const;
|
|
23
|
+
|
|
24
|
+
export default abstract class BaseProcess {
|
|
25
|
+
process: ChildProcess | null;
|
|
26
|
+
|
|
27
|
+
resource: string;
|
|
28
|
+
|
|
29
|
+
processName: string;
|
|
30
|
+
|
|
31
|
+
options: Options;
|
|
32
|
+
|
|
33
|
+
launchThrottle: ReturnType<typeof setTimeout> | null;
|
|
34
|
+
|
|
35
|
+
supportedSystems = ['mac-x64', 'win-x64', 'linux-arm64', 'linux-x64'];
|
|
36
|
+
|
|
37
|
+
stopped = false;
|
|
38
|
+
|
|
39
|
+
constructor(resource: string, processName: string, options: Options = defaultOptions) {
|
|
40
|
+
this.process = null;
|
|
41
|
+
this.launchThrottle = null;
|
|
42
|
+
this.resource = resource;
|
|
43
|
+
this.processName = processName;
|
|
44
|
+
this.options = {
|
|
45
|
+
...defaultOptions,
|
|
46
|
+
...options,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const { system } = this.getPlatformInfo();
|
|
50
|
+
if (!this.isSystemSupported(system)) {
|
|
51
|
+
console.error('Unsupported system:', system);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
abstract getStatus(): Promise<{ service: boolean; process: boolean }>;
|
|
56
|
+
|
|
57
|
+
async start(params: string[] = [], isDev = false) {
|
|
58
|
+
const { system, ext } = this.getPlatformInfo();
|
|
59
|
+
if (this.launchThrottle) {
|
|
60
|
+
console.debug('Throttling launch, cancel process');
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const status = await this.getStatus();
|
|
65
|
+
|
|
66
|
+
if (status.service) {
|
|
67
|
+
console.debug('Service already running');
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (status.process) {
|
|
72
|
+
console.debug('Process already running, service not');
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (this.options.startupThrottleTime && this.options.startupThrottleTime > 0) {
|
|
77
|
+
console.debug('Throttling startup');
|
|
78
|
+
|
|
79
|
+
this.launchThrottle = setTimeout(() => {
|
|
80
|
+
console.debug('Cleaning up launch throttle');
|
|
81
|
+
this.launchThrottle = null;
|
|
82
|
+
}, this.options.startupThrottleTime * 1000);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
this.stopped = false;
|
|
86
|
+
|
|
87
|
+
const appPath = electron.app.getAppPath();
|
|
88
|
+
let processDir;
|
|
89
|
+
if (isDev) {
|
|
90
|
+
processDir = path.resolve(appPath, '../', 'public', 'bin', this.resource, system);
|
|
91
|
+
} else {
|
|
92
|
+
processDir = path.resolve(appPath, '../', 'bin', this.resource);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const processPath = path.join(processDir, `${this.processName}${ext}`);
|
|
96
|
+
const processEnv = { ...process.env };
|
|
97
|
+
// library search path for macOS
|
|
98
|
+
processEnv.DYLD_LIBRARY_PATH = processEnv.DYLD_LIBRARY_PATH
|
|
99
|
+
? `${processEnv.DYLD_LIBRARY_PATH}:${processDir}`
|
|
100
|
+
: `${processDir}`;
|
|
101
|
+
// library search path for Linux
|
|
102
|
+
processEnv.LD_LIBRARY_PATH = processEnv.LD_LIBRARY_PATH
|
|
103
|
+
? `${processEnv.LD_LIBRARY_PATH}:${processDir}`
|
|
104
|
+
: `${processDir}`;
|
|
105
|
+
|
|
106
|
+
console.info([
|
|
107
|
+
'Starting process:',
|
|
108
|
+
`- Path: ${processPath}`,
|
|
109
|
+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
110
|
+
`- Params: ${params}`,
|
|
111
|
+
`- CWD: ${processDir}`,
|
|
112
|
+
]);
|
|
113
|
+
this.process = spawn(processPath, params, {
|
|
114
|
+
cwd: processDir,
|
|
115
|
+
env: processEnv,
|
|
116
|
+
stdio: ['ignore', 'ignore', 'ignore'],
|
|
117
|
+
});
|
|
118
|
+
this.process.on('error', err => this.onError(err));
|
|
119
|
+
this.process.on('exit', code => this.onExit(code));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Stops the process
|
|
124
|
+
*/
|
|
125
|
+
stop() {
|
|
126
|
+
return new Promise<void>(resolve => {
|
|
127
|
+
this.stopped = true;
|
|
128
|
+
|
|
129
|
+
if (!this.process) {
|
|
130
|
+
console.warn('process already stopped');
|
|
131
|
+
resolve();
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
console.debug('Stopping process');
|
|
136
|
+
this.process.kill();
|
|
137
|
+
|
|
138
|
+
let timeout = 0;
|
|
139
|
+
const interval = setInterval(() => {
|
|
140
|
+
if (!this.process || this.process.killed) {
|
|
141
|
+
console.info('Process killed successfully');
|
|
142
|
+
clearInterval(interval);
|
|
143
|
+
this.process = null;
|
|
144
|
+
resolve();
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (this.options.stopWaitTimes && timeout < this.options.stopWaitTimes) {
|
|
149
|
+
console.info('Process Still alive, checking again...');
|
|
150
|
+
timeout += 1;
|
|
151
|
+
} else {
|
|
152
|
+
console.info('Process Still alive, going for the SIGKILL');
|
|
153
|
+
this.process.kill('SIGKILL');
|
|
154
|
+
}
|
|
155
|
+
}, 1000);
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async restart() {
|
|
160
|
+
console.info('Restarting');
|
|
161
|
+
await this.stop();
|
|
162
|
+
await this.start();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
onError(err: Error) {
|
|
166
|
+
console.error('Process exit error: ', err.message);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
onExit(code: number | null) {
|
|
170
|
+
console.info(
|
|
171
|
+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
172
|
+
`Exited, code: ${code ?? 'unknown'} (Stopped: ${this.stopped})`
|
|
173
|
+
);
|
|
174
|
+
this.process = null;
|
|
175
|
+
|
|
176
|
+
if (this.options.autoRestart && this.options.autoRestart > 0 && !this.stopped) {
|
|
177
|
+
console.debug('restarting...');
|
|
178
|
+
let restartDelay = this.options.autoRestart;
|
|
179
|
+
|
|
180
|
+
// Add throttle delay to prevent the process from never restarting if the throttle is hit
|
|
181
|
+
if (this.launchThrottle && this.options.startupThrottleTime) {
|
|
182
|
+
restartDelay += this.options.startupThrottleTime;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
setTimeout(() => this.start(), restartDelay * 1000);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
isSystemSupported(system: string) {
|
|
190
|
+
return this.supportedSystems.includes(system);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
getPlatformInfo() {
|
|
194
|
+
const { arch } = process;
|
|
195
|
+
const platform = this.getPlatform();
|
|
196
|
+
const ext = platform === 'win' ? '.exe' : '';
|
|
197
|
+
const system = `${platform}-${arch}`;
|
|
198
|
+
console.debug('arch: ', arch);
|
|
199
|
+
return { system, platform, arch, ext };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
getPlatform() {
|
|
203
|
+
switch (process.platform) {
|
|
204
|
+
case 'darwin':
|
|
205
|
+
return 'mac';
|
|
206
|
+
case 'win32':
|
|
207
|
+
return 'win';
|
|
208
|
+
default:
|
|
209
|
+
return process.platform;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
2
|
+
import fetch from 'node-fetch';
|
|
3
|
+
|
|
4
|
+
import BaseProcess from './BaseProcess';
|
|
5
|
+
|
|
6
|
+
import type { Status } from './BaseProcess';
|
|
7
|
+
|
|
8
|
+
class BridgeProcess extends BaseProcess {
|
|
9
|
+
constructor() {
|
|
10
|
+
super('bridge', 'onekeyd', {
|
|
11
|
+
startupThrottleTime: 3,
|
|
12
|
+
});
|
|
13
|
+
console.info('logger file name =====> :');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async getStatus(): Promise<Status> {
|
|
17
|
+
try {
|
|
18
|
+
const resp = await fetch(`http://127.0.0.1:21320/`, {
|
|
19
|
+
method: 'POST',
|
|
20
|
+
headers: {
|
|
21
|
+
Origin: 'https://electron.onekey.so',
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
console.debug(`Checking status (${resp.status})`);
|
|
25
|
+
if (resp.status === 200) {
|
|
26
|
+
const data = await resp.json();
|
|
27
|
+
if (data?.version) {
|
|
28
|
+
return {
|
|
29
|
+
service: true,
|
|
30
|
+
process: true,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
} catch (err: any) {
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
36
|
+
console.error(`Status error: ${err.message}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// process
|
|
40
|
+
return {
|
|
41
|
+
service: false,
|
|
42
|
+
process: Boolean(this.process),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function fetchWithTimeout(url: string, options: RequestInit & { timeout: number }) {
|
|
48
|
+
const { timeout = 3000 } = options;
|
|
49
|
+
|
|
50
|
+
const controller = new AbortController();
|
|
51
|
+
const id = setTimeout(() => controller.abort(), timeout);
|
|
52
|
+
// @ts-expect-error
|
|
53
|
+
const response = await fetch(url, {
|
|
54
|
+
...options,
|
|
55
|
+
signal: controller.signal as any,
|
|
56
|
+
});
|
|
57
|
+
clearTimeout(id);
|
|
58
|
+
return response;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const BridgeHeart = {
|
|
62
|
+
timer: null as ReturnType<typeof setInterval> | null,
|
|
63
|
+
start: (callback: () => void) => {
|
|
64
|
+
const checkBridge = async () => {
|
|
65
|
+
try {
|
|
66
|
+
const localBridgeUrl = 'http://127.0.0.1:21320/';
|
|
67
|
+
const resp = await fetchWithTimeout(localBridgeUrl, {
|
|
68
|
+
method: 'POST',
|
|
69
|
+
headers: {
|
|
70
|
+
Origin: 'https://electron.onekey.so',
|
|
71
|
+
},
|
|
72
|
+
timeout: 3000,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
if (resp.status !== 200) {
|
|
76
|
+
console.debug(`Bridge Heart Checking ${localBridgeUrl} (${resp.status})`);
|
|
77
|
+
// check bridge failed, restart it
|
|
78
|
+
callback?.();
|
|
79
|
+
}
|
|
80
|
+
} catch (err: any) {
|
|
81
|
+
console.error(
|
|
82
|
+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
83
|
+
`Bridge heart check error, will restart bridge process: ${err.message}`
|
|
84
|
+
);
|
|
85
|
+
callback?.();
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
BridgeHeart.timer = setInterval(checkBridge, 10000);
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export default BridgeProcess;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { app } from 'electron';
|
|
2
|
+
|
|
3
|
+
import BridgeProcess, { BridgeHeart } from './Bridge';
|
|
4
|
+
|
|
5
|
+
let bridgeInstance: BridgeProcess;
|
|
6
|
+
export const launchBridge = async (isDevelopment: boolean) => {
|
|
7
|
+
const bridge = new BridgeProcess();
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
console.info('bridge: Staring');
|
|
11
|
+
await bridge.start([], isDevelopment);
|
|
12
|
+
bridgeInstance = bridge;
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
14
|
+
BridgeHeart.start(() => restartBridge());
|
|
15
|
+
} catch (err) {
|
|
16
|
+
console.error(`bridge: Start failed: ${(err as Error).message}`);
|
|
17
|
+
console.error(err);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
app.on('before-quit', () => {
|
|
21
|
+
console.info('bridge', 'Stopping when app quit');
|
|
22
|
+
bridge.stop();
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const restartBridge = async () => {
|
|
27
|
+
console.debug('bridge: ', 'Restarting');
|
|
28
|
+
await bridgeInstance?.restart();
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const init = async ({ isDevelopment }: { isDevelopment: boolean }) => {
|
|
32
|
+
console.info('Electron main process log path: ');
|
|
33
|
+
await launchBridge(isDevelopment);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export default init;
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../../tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"allowSyntheticDefaultImports": true,
|
|
5
|
+
"jsx": "preserve",
|
|
6
|
+
"lib": [
|
|
7
|
+
"dom",
|
|
8
|
+
"esnext"
|
|
9
|
+
],
|
|
10
|
+
"moduleResolution": "node",
|
|
11
|
+
"noEmit": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"target": "esnext",
|
|
14
|
+
"esModuleInterop": true,
|
|
15
|
+
"module": "esnext",
|
|
16
|
+
"allowJs": true // Seems to be needed for index.js
|
|
17
|
+
},
|
|
18
|
+
"include": [
|
|
19
|
+
"**/*.ts",
|
|
20
|
+
"**/*.tsx",
|
|
21
|
+
"src/preload.ts"
|
|
22
|
+
],
|
|
23
|
+
"exclude": [
|
|
24
|
+
"node_modules",
|
|
25
|
+
"web-build/**/*",
|
|
26
|
+
"out/**/*",
|
|
27
|
+
"dist/**/*",
|
|
28
|
+
"public/**/*"
|
|
29
|
+
]
|
|
30
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
2
|
+
/* eslint-disable import/no-import-module-exports */
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import webpack from 'webpack';
|
|
5
|
+
import nodeExternals from 'webpack-node-externals';
|
|
6
|
+
import { CleanWebpackPlugin } from 'clean-webpack-plugin';
|
|
7
|
+
import childProcess from 'child_process';
|
|
8
|
+
|
|
9
|
+
const pkg = require('./package.json');
|
|
10
|
+
|
|
11
|
+
const gitRevision = childProcess.execSync('git rev-parse HEAD').toString().trim();
|
|
12
|
+
|
|
13
|
+
module.exports = {
|
|
14
|
+
mode: 'production',
|
|
15
|
+
entry: {
|
|
16
|
+
index: path.resolve(__dirname, 'src/index.ts'),
|
|
17
|
+
preload: path.resolve(__dirname, 'src/preload.ts'),
|
|
18
|
+
},
|
|
19
|
+
target: 'electron-main', // 针对Electron主进程
|
|
20
|
+
module: {
|
|
21
|
+
rules: [
|
|
22
|
+
{
|
|
23
|
+
test: /\.js$/,
|
|
24
|
+
exclude: /node_modules/,
|
|
25
|
+
use: ['babel-loader'],
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
test: /\.ts$/,
|
|
29
|
+
exclude: /node_modules/,
|
|
30
|
+
use: {
|
|
31
|
+
loader: 'babel-loader',
|
|
32
|
+
options: {
|
|
33
|
+
presets: ['@babel/preset-typescript'],
|
|
34
|
+
plugins: ['@babel/plugin-proposal-optional-chaining'],
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
resolve: {
|
|
41
|
+
extensions: ['.ts', '.js'],
|
|
42
|
+
},
|
|
43
|
+
externals: [
|
|
44
|
+
nodeExternals({
|
|
45
|
+
allowlist: Object.keys({
|
|
46
|
+
...pkg.dependencies,
|
|
47
|
+
...pkg.devDependencies,
|
|
48
|
+
}),
|
|
49
|
+
}),
|
|
50
|
+
],
|
|
51
|
+
output: {
|
|
52
|
+
path: path.resolve(__dirname, 'dist'),
|
|
53
|
+
filename: '[name].js',
|
|
54
|
+
},
|
|
55
|
+
plugins: [
|
|
56
|
+
new CleanWebpackPlugin(),
|
|
57
|
+
new webpack.DefinePlugin({
|
|
58
|
+
'process.env.COMMITHASH': JSON.stringify(gitRevision),
|
|
59
|
+
}),
|
|
60
|
+
],
|
|
61
|
+
};
|