Skip to content

Commit 88ee8de

Browse files
committed
feat: Refactor the ipc module.
1 parent a3ccc3d commit 88ee8de

File tree

5 files changed

+229
-53
lines changed

5 files changed

+229
-53
lines changed

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

+68
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const fs = require('fs')
33
const path = require('path')
44
const LRU = require('lru-cache')
55
const semver = require('semver')
6+
const { Buffer } = require('buffer')
67

78
let _hasYarn
89
const _yarnProjects = new LRU({
@@ -14,6 +15,7 @@ const _gitProjects = new LRU({
1415
max: 10,
1516
maxAge: 1000
1617
})
18+
const CRLF = '\r\n'
1719

1820
// env detection
1921
exports.hasYarn = () => {
@@ -216,3 +218,69 @@ exports.getInstalledBrowsers = () => {
216218

217219
return browsers
218220
}
221+
222+
exports.getPipePath = (id) => {
223+
if (exports.isWindows && !id.startsWith('\\\\.\\pipe\\')) {
224+
id = id.replace(/^\//, '')
225+
id = id.replace(/\//g, '-')
226+
id = `\\\\.\\pipe\\${id}`
227+
}
228+
return id
229+
}
230+
231+
exports.encodeIpcData = (type, data) => {
232+
const jsonstr = JSON.stringify({
233+
data,
234+
type
235+
})
236+
const massage = `Content-Length: ${Buffer.byteLength(jsonstr)}${CRLF + CRLF}${jsonstr}`
237+
return Buffer.from(massage)
238+
}
239+
240+
exports.parseIpcData = (data, reserveData) => {
241+
let { contentLength, rawData } = reserveData
242+
rawData += data
243+
const messages = []
244+
while (true) {
245+
if (contentLength >= 0) {
246+
if (rawData.length >= contentLength) {
247+
const message = rawData.slice(0, contentLength)
248+
rawData = rawData.slice(contentLength)
249+
contentLength = -1
250+
if (message.length > 0) {
251+
let msg
252+
try {
253+
msg = JSON.parse(message)
254+
} catch (error) {
255+
msg = {
256+
type: 'error',
257+
data: `Error handling data: ${error}`
258+
}
259+
}
260+
messages.push(msg)
261+
}
262+
continue
263+
}
264+
} else {
265+
const idx = rawData.indexOf(CRLF + CRLF)
266+
if (idx !== -1) {
267+
const header = rawData.slice(0, idx)
268+
const lines = header.split(CRLF)
269+
for (let i = 0; i < lines.length; i++) {
270+
const pair = lines[i].split(/: +/)
271+
if (pair[0] === 'Content-Length') {
272+
contentLength = +pair[1]
273+
}
274+
}
275+
rawData = rawData.slice(idx + (CRLF + CRLF).length)
276+
continue
277+
}
278+
}
279+
break
280+
}
281+
282+
reserveData.contentLength = contentLength
283+
reserveData.rawData = rawData
284+
285+
return messages
286+
}

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

+83-29
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
const ipc = require('@achrinza/node-ipc')
1+
const { getPipePath, encodeIpcData, parseIpcData } = require('./env')
2+
const net = require('net')
23

34
const DEFAULT_ID = process.env.VUE_CLI_IPC || 'vue-cli'
45
const DEFAULT_IDLE_TIMEOUT = 3000
@@ -15,12 +16,18 @@ const PROJECT_ID = process.env.VUE_CLI_PROJECT_ID
1516
exports.IpcMessenger = class IpcMessenger {
1617
constructor (options = {}) {
1718
options = Object.assign({}, DEFAULT_OPTIONS, options)
18-
ipc.config.id = this.id = options.networkId
19-
ipc.config.retry = 1500
20-
ipc.config.silent = true
19+
this.id = options.networkId
20+
this.retry = 1500
21+
this.ipcTimer = null
22+
this.reserveData = {
23+
contentLength: -1,
24+
rawData: ''
25+
}
26+
this.socket = null
2127

2228
this.connected = false
2329
this.connecting = false
30+
this.disconnected = false
2431
this.disconnecting = false
2532
this.queue = null
2633
this.options = options
@@ -40,7 +47,7 @@ exports.IpcMessenger = class IpcMessenger {
4047
}
4148

4249
checkConnection () {
43-
if (!ipc.of[this.id]) {
50+
if (!this.socket) {
4451
this.connected = false
4552
}
4653
}
@@ -55,7 +62,8 @@ exports.IpcMessenger = class IpcMessenger {
5562
}
5663
}
5764

58-
ipc.of[this.id].emit(type, data)
65+
const massages = encodeIpcData(type, data)
66+
this.socket.write(massages)
5967

6068
clearTimeout(this.idleTimer)
6169
if (this.options.disconnectOnIdle) {
@@ -76,14 +84,7 @@ exports.IpcMessenger = class IpcMessenger {
7684
if (this.connected || this.connecting) return
7785
this.connecting = true
7886
this.disconnecting = false
79-
ipc.connectTo(this.id, () => {
80-
this.connected = true
81-
this.connecting = false
82-
this.queue && this.queue.forEach(data => this.send(data))
83-
this.queue = null
84-
85-
ipc.of[this.id].on('message', this._onMessage)
86-
})
87+
this._connectTo()
8788
}
8889

8990
disconnect () {
@@ -92,18 +93,11 @@ exports.IpcMessenger = class IpcMessenger {
9293
this.disconnecting = true
9394
this.connecting = false
9495

95-
const ipcTimer = setTimeout(() => {
96+
this.ipcTimer = setTimeout(() => {
9697
this._disconnect()
9798
}, this.disconnectTimeout)
9899

99100
this.send({ done: true }, 'ack')
100-
101-
ipc.of[this.id].on('ack', data => {
102-
if (data.ok) {
103-
clearTimeout(ipcTimer)
104-
this._disconnect()
105-
}
106-
})
107101
}
108102

109103
on (listener) {
@@ -118,25 +112,85 @@ exports.IpcMessenger = class IpcMessenger {
118112
_reset () {
119113
this.queue = []
120114
this.connected = false
115+
this.socket = null
121116
}
122117

123118
_disconnect () {
119+
if (!this.socket) {
120+
return
121+
}
124122
this.connected = false
125123
this.disconnecting = false
126-
ipc.disconnect(this.id)
124+
this.disconnected = true
125+
this.socket.destroy()
127126
this._reset()
128127
}
129128

130-
_onMessage (data) {
131-
this.listeners.forEach(fn => {
132-
if (this.options.namespaceOnProject && data._projectId) {
133-
if (data._projectId === PROJECT_ID) {
134-
data = data._data
129+
_onMessage (massage) {
130+
let { type, data } = massage
131+
if (type === 'ack') {
132+
if (data.ok) {
133+
clearTimeout(this.ipcTimer)
134+
this._disconnect()
135+
}
136+
} else {
137+
this.listeners.forEach((resolve, reject) => {
138+
if (this.options.namespaceOnProject && data._projectId) {
139+
if (data._projectId === PROJECT_ID) {
140+
data = data._data
141+
} else {
142+
return
143+
}
144+
}
145+
if (type === 'error') {
146+
reject(data)
135147
} else {
148+
resolve(data)
149+
}
150+
})
151+
}
152+
}
153+
154+
_connectTo () {
155+
const pipPath = getPipePath(this.id)
156+
const socket = net.createConnection({ path: pipPath })
157+
socket.setEncoding('utf-8')
158+
159+
socket.on('connect', () => {
160+
this.connected = true
161+
this.connecting = false
162+
this.queue && this.queue.forEach(data => this.send(data))
163+
this.queue = null
164+
})
165+
166+
socket.on('data', (massages) => {
167+
const queue = parseIpcData(massages, this.reserveData)
168+
queue.forEach(massage => {
169+
this._onMessage(massage)
170+
})
171+
})
172+
173+
socket.on('close', () => {
174+
if (this.disconnected) {
175+
return
176+
}
177+
setTimeout(() => {
178+
if (this.disconnected) {
179+
this._disconnect()
136180
return
137181
}
182+
this._connectTo()
183+
}, this.retry)
184+
})
185+
186+
socket.on('error', (error) => {
187+
const massage = {
188+
type: 'error',
189+
data: error
138190
}
139-
fn(data)
191+
this._onMessage(massage)
140192
})
193+
194+
this.socket = socket
141195
}
142196
}

packages/@vue/cli-shared-utils/package.json

-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
},
2121
"homepage": "https://github.com/vuejs/vue-cli/tree/dev/packages/@vue/cli-shared-utils#readme",
2222
"dependencies": {
23-
"@achrinza/node-ipc": "9.2.2",
2423
"chalk": "^4.1.2",
2524
"execa": "^1.0.0",
2625
"joi": "^17.4.0",

packages/@vue/cli-ui/apollo-server/util/ipc.js

+78-22
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,62 @@
1-
const ipc = require('@achrinza/node-ipc')
1+
const net = require('net')
2+
const fs = require('fs')
3+
24
// Utils
35
const { log, dumpObject } = require('../util/logger')
6+
const { getPipePath, encodeIpcData, parseIpcData } = require('@vue/cli-shared-utils')
47

5-
ipc.config.id = process.env.VUE_CLI_IPC || 'vue-cli'
6-
ipc.config.retry = 1500
7-
ipc.config.silent = true
8+
const id = process.env.VUE_CLI_IPC || 'vue-cli'
89

910
const listeners = []
1011

11-
ipc.serve(() => {
12-
ipc.server.on('message', (data, socket) => {
13-
log('IPC message', dumpObject(data))
14-
for (const listener of listeners) {
15-
listener({
16-
data,
17-
emit: data => {
18-
ipc.server.emit(socket, 'message', data)
19-
}
20-
})
12+
const pipePath = getPipePath(id)
13+
14+
let curSocket = null
15+
16+
let reserveData = {
17+
contentLength: -1,
18+
rawData: ''
19+
}
20+
21+
fs.unlink(pipePath, () => {
22+
const server = net.createServer((socket) => {
23+
curSocket = socket
24+
if (socket.setEncoding) {
25+
socket.setEncoding('utf-8')
2126
}
27+
28+
socket.on('data', (massages) => {
29+
const queue = parseIpcData(massages, reserveData)
30+
queue.forEach(massage => {
31+
_onMessage(massage)
32+
})
33+
})
34+
35+
socket.on('close', () => {
36+
if (curSocket && curSocket.destroy) {
37+
curSocket.destroy()
38+
}
39+
reserveData = {
40+
contentLength: -1,
41+
rawData: ''
42+
}
43+
curSocket = null
44+
})
45+
46+
socket.on('error', (error) => {
47+
const massage = {
48+
type: 'error',
49+
data: error
50+
}
51+
_onMessage(massage)
52+
})
2253
})
2354

24-
ipc.server.on('ack', (data, socket) => {
25-
log('IPC ack', dumpObject(data))
26-
if (data.done) {
27-
ipc.server.emit(socket, 'ack', { ok: true })
28-
}
55+
server.listen({
56+
path: pipePath
2957
})
3058
})
3159

32-
ipc.server.start()
33-
3460
function on (cb) {
3561
listeners.push(cb)
3662
return () => off(cb)
@@ -43,7 +69,37 @@ function off (cb) {
4369

4470
function send (data) {
4571
log('IPC send', dumpObject(data))
46-
ipc.server.broadcast('message', data)
72+
const massages = encodeIpcData('message', data)
73+
curSocket.write(massages)
74+
}
75+
76+
function _onMessage (massage) {
77+
const { type, data } = massage
78+
if (type === 'ack') {
79+
log('IPC ack', dumpObject(data))
80+
if (data.done) {
81+
curSocket.write(encodeIpcData('ack', { ok: true }))
82+
}
83+
} else {
84+
log('IPC message', dumpObject(data))
85+
listeners.forEach((resolve, reject) => {
86+
if (type === 'error') {
87+
reject({
88+
data,
89+
emit: data => {
90+
curSocket.write(encodeIpcData('error', data))
91+
}
92+
})
93+
} else {
94+
resolve({
95+
data,
96+
emit: data => {
97+
curSocket.write(encodeIpcData('message', data))
98+
}
99+
})
100+
}
101+
})
102+
}
47103
}
48104

49105
module.exports = {

0 commit comments

Comments
 (0)