Skip to content

Commit 948c4d4

Browse files
authored
feat: build watcher host (#96)
1 parent cc46d0f commit 948c4d4

File tree

4 files changed

+187
-0
lines changed

4 files changed

+187
-0
lines changed

build/webpack/ForgeWebpackPlugin.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import mainConfig from './webpack.main.config'
1515
import rendererConfig from './webpack.renderer.config'
1616
import nodeConfig from './webpack.node.config'
1717
import { extHostConfig, workerHostConfig } from './webpack.ext-host.config'
18+
import { watcherHostConfig } from './webpack.watcher-host.config'
1819
import webviewConfig from './webpack.webview.config'
1920

2021
const d = debug('electron-forge:plugin:webpack');
@@ -145,6 +146,16 @@ export class WebpackPlugin extends PluginBase<WebpackPluginConfig> {
145146
timer: { ...PRESET_TIMER },
146147
},
147148
},
149+
{
150+
title: 'Compiling wathcer host code',
151+
task: async () => {
152+
const tab = logger.createTab('Watcher Host')
153+
await this.compile(watcherHostConfig, 'watcher-host', 'watcher-host', false, tab)
154+
},
155+
rendererOptions: {
156+
timer: { ...PRESET_TIMER },
157+
},
158+
},
148159
{
149160
title: 'Compiling webview process code',
150161
task: async () => {
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import path from 'node:path';
2+
import { createConfig, webpackDir } from './webpack.base.config';
3+
import { asarDeps } from '../deps';
4+
5+
const srcDir = path.resolve('src/bootstrap/watcher-host');
6+
const outDir = path.join(webpackDir, 'watcher-host');
7+
8+
export const watcherHostConfig = createConfig(() => ({
9+
entry: srcDir,
10+
output: {
11+
filename: 'index.js',
12+
path: outDir,
13+
},
14+
externals: [
15+
({ request }, callback) => {
16+
if (asarDeps.includes(request!)) {
17+
return callback(null, 'commonjs ' + request);
18+
}
19+
callback();
20+
},
21+
],
22+
target: 'node',
23+
}));

src/bootstrap/node/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11

22
import '@/core/common/asar'
33
import * as net from 'node:net';
4+
import path from 'node:path';
45
import mri from 'mri'
56
import { IServerAppOpts, ServerApp, ConstructorOf, NodeModule } from '@opensumi/ide-core-node';
67
import { ServerCommonModule } from '@opensumi/ide-core-node';
@@ -49,6 +50,7 @@ async function startServer() {
4950
showBuiltinExtensions: true,
5051
extensionDir: process.env.IDE_EXTENSIONS_PATH!,
5152
},
53+
watcherHost: path.join(__dirname, '../watcher-host/index'),
5254
};
5355

5456
const server = net.createServer();

src/bootstrap/watcher-host/index.ts

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import '@/core/common/asar';
2+
import { createConnection } from 'net';
3+
4+
import { Injector } from '@opensumi/di';
5+
import { SumiConnectionMultiplexer } from '@opensumi/ide-connection';
6+
import { NetSocketConnection } from '@opensumi/ide-connection/lib/common/connection/drivers';
7+
import { argv } from '@opensumi/ide-core-common/lib/node/cli';
8+
import { suppressNodeJSEpipeError } from '@opensumi/ide-core-common/lib/node/utils';
9+
import { CommonProcessReporter, IReporter, ReporterProcessMessage } from '@opensumi/ide-core-common/lib/types';
10+
import { Emitter, isPromiseCanceledError } from '@opensumi/ide-utils';
11+
12+
import { SUMI_WATCHER_PROCESS_SOCK_KEY, WATCHER_INIT_DATA_KEY } from '@opensumi/ide-file-service/lib/common';
13+
14+
import { WatcherProcessLogger } from '@opensumi/ide-file-service/lib/node/hosted/watch-process-log';
15+
import { WatcherHostServiceImpl } from '@opensumi/ide-file-service/lib/node/hosted/watcher.host.service';
16+
import { LogServiceManager as LogServiceManagerToken } from '@opensumi/ide-logs/lib/node/log-manager';
17+
import { LogServiceManager } from '@/logger/node/log-manager';
18+
19+
Error.stackTraceLimit = 100;
20+
const logger: any = console;
21+
22+
async function initWatcherProcess() {
23+
patchConsole();
24+
patchProcess();
25+
const watcherInjector = new Injector();
26+
const reporterEmitter = new Emitter<ReporterProcessMessage>();
27+
28+
watcherInjector.addProviders({
29+
token: IReporter,
30+
useValue: new CommonProcessReporter(reporterEmitter),
31+
}, {
32+
token: LogServiceManagerToken,
33+
useClass: LogServiceManager
34+
});
35+
36+
const initData = JSON.parse(argv[WATCHER_INIT_DATA_KEY]);
37+
const connection = JSON.parse(argv[SUMI_WATCHER_PROCESS_SOCK_KEY]);
38+
39+
const socket = createConnection(connection);
40+
41+
const watcherProtocol = new SumiConnectionMultiplexer(new NetSocketConnection(socket), {
42+
timeout: -1,
43+
});
44+
45+
const logger = new WatcherProcessLogger(watcherInjector, initData.logDir, initData.logLevel);
46+
const watcherHostService = new WatcherHostServiceImpl(watcherProtocol, logger);
47+
watcherHostService.initWatcherServer();
48+
}
49+
50+
(async () => {
51+
await initWatcherProcess();
52+
})();
53+
54+
function getErrorLogger() {
55+
// eslint-disable-next-line no-console
56+
return (logger && logger.error.bind(logger)) || console.error.bind(console);
57+
}
58+
59+
function getWarnLogger() {
60+
// eslint-disable-next-line no-console
61+
return (logger && logger.warn.bind(logger)) || console.warn.bind(console);
62+
}
63+
64+
function patchProcess() {
65+
process.exit = function (code?: number) {
66+
const err = new Error(`An extension called process.exit(${code ?? ''}) and this was prevented.`);
67+
getWarnLogger()(err.stack);
68+
} as (code?: number) => never;
69+
70+
// override Electron's process.crash() method
71+
process.crash = function () {
72+
const err = new Error('An extension called process.crash() and this was prevented.');
73+
getWarnLogger()(err.stack);
74+
};
75+
}
76+
77+
function _wrapConsoleMethod(method: 'log' | 'info' | 'warn' | 'error') {
78+
// eslint-disable-next-line no-console
79+
const original = console[method].bind(console);
80+
81+
Object.defineProperty(console, method, {
82+
set: () => {
83+
// empty
84+
},
85+
get: () =>
86+
function (...args: any[]) {
87+
original(...args);
88+
},
89+
});
90+
}
91+
92+
function patchConsole() {
93+
_wrapConsoleMethod('info');
94+
_wrapConsoleMethod('log');
95+
_wrapConsoleMethod('warn');
96+
_wrapConsoleMethod('error');
97+
}
98+
99+
function unexpectedErrorHandler(e: any) {
100+
setTimeout(() => {
101+
getErrorLogger()('[Watcehr-Host]', e.message, e.stack && '\n\n' + e.stack);
102+
}, 0);
103+
}
104+
105+
function onUnexpectedError(e: any) {
106+
let err = e;
107+
if (!err) {
108+
getWarnLogger()(`Unknown Exception ${err}`);
109+
return;
110+
}
111+
112+
if (isPromiseCanceledError(err)) {
113+
getWarnLogger()(`Canceled ${err.message}`);
114+
return;
115+
}
116+
117+
if (!(err instanceof Error)) {
118+
err = new Error(e);
119+
}
120+
121+
unexpectedErrorHandler(err);
122+
}
123+
124+
suppressNodeJSEpipeError(process, (msg) => {
125+
getErrorLogger()(msg);
126+
});
127+
128+
process.on('uncaughtException', (err) => {
129+
onUnexpectedError(err);
130+
});
131+
132+
const unhandledPromises: Promise<any>[] = [];
133+
process.on('unhandledRejection', (reason, promise) => {
134+
unhandledPromises.push(promise);
135+
setTimeout(() => {
136+
const idx = unhandledPromises.indexOf(promise);
137+
if (idx >= 0) {
138+
promise.catch((e) => {
139+
unhandledPromises.splice(idx, 1);
140+
onUnexpectedError(e);
141+
});
142+
}
143+
}, 1000);
144+
});
145+
146+
process.on('rejectionHandled', (promise: Promise<any>) => {
147+
const idx = unhandledPromises.indexOf(promise);
148+
if (idx >= 0) {
149+
unhandledPromises.splice(idx, 1);
150+
}
151+
});

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy