Skip to content

Commit

Permalink
feat(typescript-plugin): define named pipe unique file name based on …
Browse files Browse the repository at this point in the history
…pid (#3916)
  • Loading branch information
johnsoncodehk committed Mar 1, 2024
1 parent 4b8fa93 commit e8bd161
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 9 deletions.
2 changes: 1 addition & 1 deletion packages/typescript-plugin/index.ts
Expand Up @@ -60,7 +60,7 @@ function createLanguageServicePlugin(): ts.server.PluginModuleFactory {

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

const getCompletionsAtPosition = info.languageService.getCompletionsAtPosition;
const getEncodedSemanticClassifications = info.languageService.getEncodedSemanticClassifications;
Expand Down
41 changes: 39 additions & 2 deletions packages/typescript-plugin/lib/client.ts
@@ -1,6 +1,9 @@
import * as net from 'net';
import * as fs from 'fs';
import type * as ts from 'typescript';
import type { Request } from './server';
import { pipeFile } from './utils';
import type { PipeTable } from './utils';
import { pipeTable } from './utils';

export function collectExtractProps(
...args: Parameters<typeof import('./requests/collectExtractProps.js')['collectExtractProps']>
Expand Down Expand Up @@ -76,7 +79,41 @@ export function getElementAttrs(
});
}

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

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);
if (response) {
return server.pipeFile;
}
}
for (const server of inferredServers) {
const response = await _sendRequest<boolean>({ type: 'containsFile', args: [fileName] }, server.pipeFile);
if (typeof response === 'boolean') {
return server.pipeFile;
}
}
}
}

function _sendRequest<T>(request: Request, pipeFile: string) {
return new Promise<T | undefined | null>(resolve => {
try {
const client = net.connect(pipeFile);
Expand Down
5 changes: 5 additions & 0 deletions packages/typescript-plugin/lib/requests/containsFile.ts
@@ -0,0 +1,5 @@
import { getProject } from '../utils';

export function containsFile(fileName: string) {
return !!getProject(fileName);
}
56 changes: 51 additions & 5 deletions packages/typescript-plugin/lib/server.ts
@@ -1,13 +1,16 @@
import * as fs from 'fs';
import * as net from 'net';
import type * as ts from 'typescript';
import { collectExtractProps } from './requests/collectExtractProps';
import { getComponentEvents, getComponentNames, getComponentProps, getElementAttrs, getTemplateContextProps } from './requests/componentInfos';
import { containsFile } from './requests/containsFile';
import { getPropertiesAtLocation } from './requests/getPropertiesAtLocation';
import { getQuickInfoAtPosition } from './requests/getQuickInfoAtPosition';
import { pipeFile } from './utils';
import { PipeTable, pipeTable } from './utils';

export interface Request {
type: 'collectExtractProps'
type: 'containsFile'
| 'collectExtractProps'
| 'getPropertiesAtLocation'
| 'getQuickInfoAtPosition'
// Component Infos
Expand All @@ -21,13 +24,23 @@ export interface Request {

let started = false;

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

if (started) return;
started = true;

const pipeFile = process.platform === 'win32'
? `\\\\.\\pipe\\vue-tsp-${process.pid}`
: `/tmp/vue-tsp-${process.pid}`;
const server = net.createServer(connection => {
connection.on('data', data => {
const request: Request = JSON.parse(data.toString());
if (request.type === 'collectExtractProps') {
const text = data.toString();
const request: Request = JSON.parse(text);
if (request.type === 'containsFile') {
const result = containsFile.apply(null, request.args);
connection.write(JSON.stringify(result ?? null));
}
else if (request.type === 'collectExtractProps') {
const result = collectExtractProps.apply(null, request.args);
connection.write(JSON.stringify(result ?? null));
}
Expand Down Expand Up @@ -68,9 +81,42 @@ export function startNamedPipeServer() {
connection.on('error', err => console.error('[Vue Named Pipe Server]', err.message));
});

clearupPipeTable();

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

try {
fs.unlinkSync(pipeFile);
} catch { }

server.listen(pipeFile);
}

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 { }
}
}
}
12 changes: 11 additions & 1 deletion packages/typescript-plugin/lib/utils.ts
@@ -1,7 +1,17 @@
import type * as ts from 'typescript';
import type { FileRegistry, VueCompilerOptions } from '@vue/language-core';

export const pipeFile = process.platform === 'win32' ? '\\\\.\\pipe\\vue-tsp' : '/tmp/vue-tsp';
export interface PipeTable {
[pid: string]: {
pid: number;
pipeFile: string;
serverKind: ts.server.ProjectKind;
};
}

export const pipeTable = process.platform === 'win32'
? `\\\\.\\pipe\\vue-tsp-table.json`
: `/tmp/vue-tsp-table.json`;

export const projects = new Map<ts.server.Project, {
info: ts.server.PluginCreateInfo;
Expand Down

0 comments on commit e8bd161

Please sign in to comment.