Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(typescript-plugin): define named pipe unique file name based on pid #3916

Merged
merged 2 commits into from Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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