Skip to content

Commit d8af540

Browse files
committed
refactor(wip): replace node-ipc with a cleanroom IPC implementation
The packet format is compatible with the node-ipc default. After this commit, no core plugins will be using node-ipc to do anything. TODOs: - Finish the IPC server implementation - Refactor `@vue/cli-ui` to get rid of `node-ipc`, too - Fully refactor the IpcMessenger module to address cross-platform issues, etc.
1 parent a1ee583 commit d8af540

File tree

5 files changed

+120
-6
lines changed

5 files changed

+120
-6
lines changed

packages/@vue/cli-service/lib/commands/serve.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const {
44
hasProjectYarn,
55
hasProjectPnpm,
66
openBrowser,
7-
IpcMessenger
7+
NewIpcMessenger
88
} = require('@vue/cli-shared-utils')
99

1010
const defaults = {
@@ -301,7 +301,7 @@ module.exports = (api, options) => {
301301

302302
// Send final app URL
303303
if (args.dashboard) {
304-
const ipc = new IpcMessenger()
304+
const ipc = new NewIpcMessenger()
305305
ipc.send({
306306
vueServe: {
307307
url: localUrlForBrowser

packages/@vue/cli-service/lib/webpack/DashboardPlugin.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@
77
const path = require('path')
88
const fs = require('fs-extra')
99
const webpack = require('webpack')
10-
const { IpcMessenger } = require('@vue/cli-shared-utils')
10+
const { NewIpcMessenger } = require('@vue/cli-shared-utils')
1111
const { analyzeBundle } = require('./analyzeBundle')
1212

1313
const ID = 'vue-cli-dashboard-plugin'
1414
const ONE_SECOND = 1000
1515
const FILENAME_QUERY_REGEXP = /\?.*$/
1616

17-
const ipc = new IpcMessenger()
17+
const ipc = new NewIpcMessenger()
1818

1919
function getTimeMessage (timer) {
2020
let time = Date.now() - timer

packages/@vue/cli-shared-utils/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
'env',
33
'exit',
44
'ipc',
5+
'NewIpcMessenger',
56
'logger',
67
'module',
78
'object',
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
const net = require('net')
2+
const path = require('path')
3+
4+
const DEFAULT_ID = process.env.VUE_CLI_IPC || 'vue-cli'
5+
const DEFAULT_IDLE_TIMEOUT = 3000
6+
const DEFAULT_OPTIONS = {
7+
networkId: DEFAULT_ID,
8+
autoConnect: true,
9+
disconnectOnIdle: false,
10+
idleTimeout: DEFAULT_IDLE_TIMEOUT,
11+
namespaceOnProject: true
12+
}
13+
const PROJECT_ID = process.env.VUE_CLI_PROJECT_ID
14+
15+
exports.NewIpcMessenger = class NewIpcMessenger {
16+
constructor (options = {}) {
17+
this.options = Object.assign({}, DEFAULT_OPTIONS, options)
18+
19+
this.id = this.options.networkId
20+
21+
// per the node-ipc documentation
22+
// TODO: windows socket path
23+
this.socketPath = path.join('/tmp/', `app.${this.id}`)
24+
25+
this.connected = false
26+
this.connecting = false
27+
this.disconnecting = false
28+
this.queue = null
29+
this.listeners = []
30+
31+
this.disconnectTimeout = 15000
32+
this.idleTimer = null
33+
34+
// Prevent forced process exit
35+
// (or else ipc messages may not be sent before kill)
36+
process.exit = code => {
37+
process.exitCode = code
38+
}
39+
}
40+
41+
checkConnection () {
42+
// TODO: not sure how to abstract this under the current implementation
43+
}
44+
45+
send (data, type = 'message') {
46+
if (this.connected) {
47+
if (this.options.namespaceOnProject && PROJECT_ID) {
48+
data = {
49+
_projectId: PROJECT_ID,
50+
_data: data
51+
}
52+
}
53+
54+
// the packet format is compatible with node-ipc default
55+
this._client.write(JSON.stringify({ type, data }) + '\f', 'utf8')
56+
57+
clearTimeout(this.idleTimer)
58+
if (this.options.disconnectOnIdle) {
59+
this.idleTimer = setTimeout(() => {
60+
this.disconnect()
61+
}, this.options.idleTimeout)
62+
}
63+
} else {
64+
this.queue.push(data)
65+
if (this.options.autoConnect && !this.connecting) {
66+
this.connect()
67+
}
68+
}
69+
}
70+
71+
connect () {
72+
if (this.connected || this.connecting) return
73+
74+
this.connecting = true
75+
this.disconnecting = false
76+
77+
// TODO: check the socketPath, unlink if existed
78+
// TODO: server side
79+
// net.createServer().listen(this.socketPath)
80+
81+
// client side
82+
this._client = net.createConnection({ path: this.socketPath }, () => {
83+
this.connected = true
84+
this.connecting = false
85+
this.queue && this.queue.forEach(data => this.send(data))
86+
this.queue = null
87+
})
88+
}
89+
90+
disconnect () {}
91+
92+
on (listener) {
93+
this.listeners.push(listener)
94+
}
95+
96+
off (listener) {
97+
const index = this.listeners.indexOf(listener)
98+
if (index !== -1) this.listeners.splice(index, 1)
99+
}
100+
101+
_onMessage (data) {
102+
this.listeners.forEach(fn => {
103+
if (this.options.namespaceOnProject && data._projectId) {
104+
if (data._projectId === PROJECT_ID) {
105+
data = data._data
106+
} else {
107+
return
108+
}
109+
}
110+
fn(data)
111+
})
112+
}
113+
}

packages/@vue/cli-shared-utils/lib/ipc.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ exports.IpcMessenger = class IpcMessenger {
2222
this.connected = false
2323
this.connecting = false
2424
this.disconnecting = false
25-
this.queue = null
25+
this.queue = []
2626
this.options = options
2727

2828
this.listeners = []
@@ -80,7 +80,7 @@ exports.IpcMessenger = class IpcMessenger {
8080
this.connected = true
8181
this.connecting = false
8282
this.queue && this.queue.forEach(data => this.send(data))
83-
this.queue = null
83+
this.queue = []
8484

8585
ipc.of[this.id].on('message', this._onMessage)
8686
})

0 commit comments

Comments
 (0)