Skip to content

Commit

Permalink
fix(typescript-plugin): more reliable connection to named pipe server (
Browse files Browse the repository at this point in the history
  • Loading branch information
johnsoncodehk authored and so1ve committed Mar 4, 2024
1 parent 0cc2acb commit 3ae8dda
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 75 deletions.
2 changes: 1 addition & 1 deletion packages/typescript-plugin/index.ts
Expand Up @@ -61,7 +61,7 @@ function createLanguageServicePlugin(): ts.server.PluginModuleFactory {

decorateLanguageService(files, info.languageService);
decorateLanguageServiceHost(files, info.languageServiceHost, ts);
startNamedPipeServer(info.project.projectKind);
startNamedPipeServer(info.project.projectKind, info.project.getCurrentDirectory());

const getCompletionsAtPosition = info.languageService.getCompletionsAtPosition;
const getCompletionEntryDetails = info.languageService.getCompletionEntryDetails;
Expand Down
80 changes: 37 additions & 43 deletions packages/typescript-plugin/lib/client.ts
@@ -1,9 +1,10 @@
import * as net from 'net';
import * as fs from 'fs';
import type * as net from 'net';
import * as path from 'path';
import type * as ts from 'typescript';
import type { Request } from './server';
import type { PipeTable } from './utils';
import { pipeTable } from './utils';
import type { NamedPipeServer } from './utils';
import { connect, pipeTable } from './utils';

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

async function sendRequest<T>(request: Request) {
const pipeFile = await getPipeFile(request.args[0]);
if (!pipeFile) {
console.error('[Vue Named Pipe Client] pipeFile not found');
const client = await connectForFile(request.args[0]);
if (!client) {
console.warn('[Vue Named Pipe Client] No server found for', request.args[0]);
return;
}
return await _sendRequest<T>(request, pipeFile);
const result = await sendRequestWorker<T>(request, client);
client.end();
return result;
}

async function getPipeFile(fileName: string) {
if (fs.existsSync(pipeTable)) {
const table: PipeTable = JSON.parse(fs.readFileSync(pipeTable, 'utf8'));
const all = Object.values(table);
const configuredServers = all
.filter(item => item.serverKind === 1 satisfies ts.server.ProjectKind.Configured)
.sort((a, b) => Math.abs(process.pid - a.pid) - Math.abs(process.pid - b.pid));
const inferredServers = all
.filter(item => item.serverKind === 0 satisfies ts.server.ProjectKind.Inferred)
.sort((a, b) => Math.abs(process.pid - a.pid) - Math.abs(process.pid - b.pid));
for (const server of configuredServers) {
const response = await _sendRequest<boolean>({ type: 'containsFile', args: [fileName] }, server.pipeFile);
async function connectForFile(fileName: string) {
if (!fs.existsSync(pipeTable)) {
return;
}
const servers: NamedPipeServer[] = JSON.parse(fs.readFileSync(pipeTable, 'utf8'));
const configuredServers = servers
.filter(item => item.serverKind === 1 satisfies ts.server.ProjectKind.Configured);
const inferredServers = servers
.filter(item => item.serverKind === 0 satisfies ts.server.ProjectKind.Inferred)
.sort((a, b) => b.currentDirectory.length - a.currentDirectory.length);
for (const server of configuredServers) {
const client = await connect(server.path);
if (client) {
const response = await sendRequestWorker<boolean>({ type: 'containsFile', args: [fileName] }, client);
if (response) {
return server.pipeFile;
return client;
}
}
for (const server of inferredServers) {
const response = await _sendRequest<boolean>({ type: 'containsFile', args: [fileName] }, server.pipeFile);
if (typeof response === 'boolean') {
return server.pipeFile;
}
for (const server of inferredServers) {
if (!path.relative(server.currentDirectory, fileName).startsWith('..')) {
const client = await connect(server.path);
if (client) {
return client;
}
}
}
}

function _sendRequest<T>(request: Request, pipeFile: string) {
function sendRequestWorker<T>(request: Request, client: net.Socket) {
return new Promise<T | undefined | null>(resolve => {
try {
const client = net.connect(pipeFile);
client.on('connect', () => {
client.write(JSON.stringify(request));
});
client.on('data', data => {
const text = data.toString();
resolve(JSON.parse(text));
client.end();
});
client.on('error', err => {
console.error('[Vue Named Pipe Client]', err);
return resolve(undefined);
});
} catch (e) {
console.error('[Vue Named Pipe Client]', e);
return resolve(undefined);
}
client.once('data', data => {
const text = data.toString();
resolve(JSON.parse(text));
});
client.write(JSON.stringify(request));
});
}
46 changes: 22 additions & 24 deletions packages/typescript-plugin/lib/server.ts
Expand Up @@ -6,7 +6,7 @@ import { getComponentEvents, getComponentNames, getComponentProps, getElementAtt
import { containsFile } from './requests/containsFile';
import { getPropertiesAtLocation } from './requests/getPropertiesAtLocation';
import { getQuickInfoAtPosition } from './requests/getQuickInfoAtPosition';
import { PipeTable, pipeTable } from './utils';
import { NamedPipeServer, connect, pipeTable } from './utils';

export interface Request {
type: 'containsFile'
Expand All @@ -24,7 +24,7 @@ export interface Request {

let started = false;

export function startNamedPipeServer(serverKind: ts.server.ProjectKind) {
export function startNamedPipeServer(serverKind: ts.server.ProjectKind, currentDirectory: string) {

if (started) return;
started = true;
Expand Down Expand Up @@ -84,14 +84,14 @@ export function startNamedPipeServer(serverKind: ts.server.ProjectKind) {
clearupPipeTable();

if (!fs.existsSync(pipeTable)) {
fs.writeFileSync(pipeTable, JSON.stringify({}));
fs.writeFileSync(pipeTable, JSON.stringify([] satisfies NamedPipeServer[]));
}
const table: PipeTable = JSON.parse(fs.readFileSync(pipeTable, 'utf8'));
table[process.pid] = {
pid: process.pid,
pipeFile,
const table: NamedPipeServer[] = JSON.parse(fs.readFileSync(pipeTable, 'utf8'));
table.push({
path: pipeFile,
serverKind,
};
currentDirectory,
});
fs.writeFileSync(pipeTable, JSON.stringify(table, undefined, 2));

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

function clearupPipeTable() {
if (fs.existsSync(pipeTable)) {
const table: PipeTable = JSON.parse(fs.readFileSync(pipeTable, 'utf8'));
for (const pid in table) {
const { pipeFile } = table[pid];
try {
const client = net.connect(pipeFile);
client.on('connect', () => {
client.end();
});
client.on('error', () => {
const table = JSON.parse(fs.readFileSync(pipeTable, 'utf8'));
delete table[pid];
fs.writeFileSync(pipeTable, JSON.stringify(table, undefined, 2));
});
} catch { }
}
if (!fs.existsSync(pipeTable)) {
return;
}
for (const server of JSON.parse(fs.readFileSync(pipeTable, 'utf8'))) {
connect(server.path).then(client => {
if (client) {
client.end();
}
else {
let table: NamedPipeServer[] = JSON.parse(fs.readFileSync(pipeTable, 'utf8'));
table = table.filter(item => item.path !== server.path);
fs.writeFileSync(pipeTable, JSON.stringify(table, undefined, 2));
}
});
}
}
27 changes: 20 additions & 7 deletions packages/typescript-plugin/lib/utils.ts
@@ -1,17 +1,18 @@
import type { FileRegistry, VueCompilerOptions } from '@vue/language-core';
import * as os from 'os';
import * as net from 'net';
import * as path from 'path';
import type * as ts from 'typescript';

export interface PipeTable {
[pid: string]: {
pid: number;
pipeFile: string;
serverKind: ts.server.ProjectKind;
};
export interface NamedPipeServer {
path: string;
serverKind: ts.server.ProjectKind;
currentDirectory: string;
}

export const pipeTable = path.join(os.tmpdir(), 'vue-tsp-table.json');
const { version } = require('../package.json');

export const pipeTable = path.join(os.tmpdir(), `vue-tsp-table-${version}.json`);

export const projects = new Map<ts.server.Project, {
info: ts.server.PluginCreateInfo;
Expand All @@ -27,3 +28,15 @@ export function getProject(fileName: string) {
}
}
}

export function connect(path: string) {
return new Promise<net.Socket | undefined>(resolve => {
const client = net.connect(path);
client.on('connect', () => {
resolve(client);
});
client.on('error', () => {
return resolve(undefined);
});
});
}

0 comments on commit 3ae8dda

Please sign in to comment.