Skip to content

Commit 3efa52d

Browse files
fix(typescript-plugin): more reliable connection to named pipe server (#3941)
1 parent c54f97e commit 3efa52d

File tree

4 files changed

+80
-75
lines changed

4 files changed

+80
-75
lines changed

packages/typescript-plugin/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ function createLanguageServicePlugin(): ts.server.PluginModuleFactory {
6161

6262
decorateLanguageService(files, info.languageService);
6363
decorateLanguageServiceHost(files, info.languageServiceHost, ts);
64-
startNamedPipeServer(info.project.projectKind);
64+
startNamedPipeServer(info.project.projectKind, info.project.getCurrentDirectory());
6565

6666
const getCompletionsAtPosition = info.languageService.getCompletionsAtPosition;
6767
const getCompletionEntryDetails = info.languageService.getCompletionEntryDetails;

packages/typescript-plugin/lib/client.ts

Lines changed: 37 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import * as net from 'net';
21
import * as fs from 'fs';
2+
import type * as net from 'net';
3+
import * as path from 'path';
34
import type * as ts from 'typescript';
45
import type { Request } from './server';
5-
import type { PipeTable } from './utils';
6-
import { pipeTable } from './utils';
6+
import type { NamedPipeServer } from './utils';
7+
import { connect, pipeTable } from './utils';
78

89
export function collectExtractProps(
910
...args: Parameters<typeof import('./requests/collectExtractProps.js')['collectExtractProps']>
@@ -80,58 +81,51 @@ export function getElementAttrs(
8081
}
8182

8283
async function sendRequest<T>(request: Request) {
83-
const pipeFile = await getPipeFile(request.args[0]);
84-
if (!pipeFile) {
85-
console.error('[Vue Named Pipe Client] pipeFile not found');
84+
const client = await connectForFile(request.args[0]);
85+
if (!client) {
86+
console.warn('[Vue Named Pipe Client] No server found for', request.args[0]);
8687
return;
8788
}
88-
return await _sendRequest<T>(request, pipeFile);
89+
const result = await sendRequestWorker<T>(request, client);
90+
client.end();
91+
return result;
8992
}
9093

91-
async function getPipeFile(fileName: string) {
92-
if (fs.existsSync(pipeTable)) {
93-
const table: PipeTable = JSON.parse(fs.readFileSync(pipeTable, 'utf8'));
94-
const all = Object.values(table);
95-
const configuredServers = all
96-
.filter(item => item.serverKind === 1 satisfies ts.server.ProjectKind.Configured)
97-
.sort((a, b) => Math.abs(process.pid - a.pid) - Math.abs(process.pid - b.pid));
98-
const inferredServers = all
99-
.filter(item => item.serverKind === 0 satisfies ts.server.ProjectKind.Inferred)
100-
.sort((a, b) => Math.abs(process.pid - a.pid) - Math.abs(process.pid - b.pid));
101-
for (const server of configuredServers) {
102-
const response = await _sendRequest<boolean>({ type: 'containsFile', args: [fileName] }, server.pipeFile);
94+
async function connectForFile(fileName: string) {
95+
if (!fs.existsSync(pipeTable)) {
96+
return;
97+
}
98+
const servers: NamedPipeServer[] = JSON.parse(fs.readFileSync(pipeTable, 'utf8'));
99+
const configuredServers = servers
100+
.filter(item => item.serverKind === 1 satisfies ts.server.ProjectKind.Configured);
101+
const inferredServers = servers
102+
.filter(item => item.serverKind === 0 satisfies ts.server.ProjectKind.Inferred)
103+
.sort((a, b) => b.currentDirectory.length - a.currentDirectory.length);
104+
for (const server of configuredServers) {
105+
const client = await connect(server.path);
106+
if (client) {
107+
const response = await sendRequestWorker<boolean>({ type: 'containsFile', args: [fileName] }, client);
103108
if (response) {
104-
return server.pipeFile;
109+
return client;
105110
}
106111
}
107-
for (const server of inferredServers) {
108-
const response = await _sendRequest<boolean>({ type: 'containsFile', args: [fileName] }, server.pipeFile);
109-
if (typeof response === 'boolean') {
110-
return server.pipeFile;
112+
}
113+
for (const server of inferredServers) {
114+
if (!path.relative(server.currentDirectory, fileName).startsWith('..')) {
115+
const client = await connect(server.path);
116+
if (client) {
117+
return client;
111118
}
112119
}
113120
}
114121
}
115122

116-
function _sendRequest<T>(request: Request, pipeFile: string) {
123+
function sendRequestWorker<T>(request: Request, client: net.Socket) {
117124
return new Promise<T | undefined | null>(resolve => {
118-
try {
119-
const client = net.connect(pipeFile);
120-
client.on('connect', () => {
121-
client.write(JSON.stringify(request));
122-
});
123-
client.on('data', data => {
124-
const text = data.toString();
125-
resolve(JSON.parse(text));
126-
client.end();
127-
});
128-
client.on('error', err => {
129-
console.error('[Vue Named Pipe Client]', err);
130-
return resolve(undefined);
131-
});
132-
} catch (e) {
133-
console.error('[Vue Named Pipe Client]', e);
134-
return resolve(undefined);
135-
}
125+
client.once('data', data => {
126+
const text = data.toString();
127+
resolve(JSON.parse(text));
128+
});
129+
client.write(JSON.stringify(request));
136130
});
137131
}

packages/typescript-plugin/lib/server.ts

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { getComponentEvents, getComponentNames, getComponentProps, getElementAtt
66
import { containsFile } from './requests/containsFile';
77
import { getPropertiesAtLocation } from './requests/getPropertiesAtLocation';
88
import { getQuickInfoAtPosition } from './requests/getQuickInfoAtPosition';
9-
import { PipeTable, pipeTable } from './utils';
9+
import { NamedPipeServer, connect, pipeTable } from './utils';
1010

1111
export interface Request {
1212
type: 'containsFile'
@@ -24,7 +24,7 @@ export interface Request {
2424

2525
let started = false;
2626

27-
export function startNamedPipeServer(serverKind: ts.server.ProjectKind) {
27+
export function startNamedPipeServer(serverKind: ts.server.ProjectKind, currentDirectory: string) {
2828

2929
if (started) return;
3030
started = true;
@@ -84,14 +84,14 @@ export function startNamedPipeServer(serverKind: ts.server.ProjectKind) {
8484
clearupPipeTable();
8585

8686
if (!fs.existsSync(pipeTable)) {
87-
fs.writeFileSync(pipeTable, JSON.stringify({}));
87+
fs.writeFileSync(pipeTable, JSON.stringify([] satisfies NamedPipeServer[]));
8888
}
89-
const table: PipeTable = JSON.parse(fs.readFileSync(pipeTable, 'utf8'));
90-
table[process.pid] = {
91-
pid: process.pid,
92-
pipeFile,
89+
const table: NamedPipeServer[] = JSON.parse(fs.readFileSync(pipeTable, 'utf8'));
90+
table.push({
91+
path: pipeFile,
9392
serverKind,
94-
};
93+
currentDirectory,
94+
});
9595
fs.writeFileSync(pipeTable, JSON.stringify(table, undefined, 2));
9696

9797
try {
@@ -102,21 +102,19 @@ export function startNamedPipeServer(serverKind: ts.server.ProjectKind) {
102102
}
103103

104104
function clearupPipeTable() {
105-
if (fs.existsSync(pipeTable)) {
106-
const table: PipeTable = JSON.parse(fs.readFileSync(pipeTable, 'utf8'));
107-
for (const pid in table) {
108-
const { pipeFile } = table[pid];
109-
try {
110-
const client = net.connect(pipeFile);
111-
client.on('connect', () => {
112-
client.end();
113-
});
114-
client.on('error', () => {
115-
const table = JSON.parse(fs.readFileSync(pipeTable, 'utf8'));
116-
delete table[pid];
117-
fs.writeFileSync(pipeTable, JSON.stringify(table, undefined, 2));
118-
});
119-
} catch { }
120-
}
105+
if (!fs.existsSync(pipeTable)) {
106+
return;
107+
}
108+
for (const server of JSON.parse(fs.readFileSync(pipeTable, 'utf8'))) {
109+
connect(server.path).then(client => {
110+
if (client) {
111+
client.end();
112+
}
113+
else {
114+
let table: NamedPipeServer[] = JSON.parse(fs.readFileSync(pipeTable, 'utf8'));
115+
table = table.filter(item => item.path !== server.path);
116+
fs.writeFileSync(pipeTable, JSON.stringify(table, undefined, 2));
117+
}
118+
});
121119
}
122120
}

packages/typescript-plugin/lib/utils.ts

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
import type { FileRegistry, VueCompilerOptions } from '@vue/language-core';
22
import * as os from 'os';
3+
import * as net from 'net';
34
import * as path from 'path';
45
import type * as ts from 'typescript';
56

6-
export interface PipeTable {
7-
[pid: string]: {
8-
pid: number;
9-
pipeFile: string;
10-
serverKind: ts.server.ProjectKind;
11-
};
7+
export interface NamedPipeServer {
8+
path: string;
9+
serverKind: ts.server.ProjectKind;
10+
currentDirectory: string;
1211
}
1312

14-
export const pipeTable = path.join(os.tmpdir(), 'vue-tsp-table.json');
13+
const { version } = require('../package.json');
14+
15+
export const pipeTable = path.join(os.tmpdir(), `vue-tsp-table-${version}.json`);
1516

1617
export const projects = new Map<ts.server.Project, {
1718
info: ts.server.PluginCreateInfo;
@@ -27,3 +28,15 @@ export function getProject(fileName: string) {
2728
}
2829
}
2930
}
31+
32+
export function connect(path: string) {
33+
return new Promise<net.Socket | undefined>(resolve => {
34+
const client = net.connect(path);
35+
client.on('connect', () => {
36+
resolve(client);
37+
});
38+
client.on('error', () => {
39+
return resolve(undefined);
40+
});
41+
});
42+
}

0 commit comments

Comments
 (0)