@vue/typescript-plugin 2.1.10 → 2.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/utils.d.ts CHANGED
@@ -1,16 +1,31 @@
1
- import * as net from 'net';
1
+ import { FileMap } from '@vue/language-core';
2
+ import * as net from 'node:net';
2
3
  import type * as ts from 'typescript';
3
- import type { ProjectInfo, Request } from './server';
4
+ import type { ComponentPropInfo } from './requests/getComponentProps';
5
+ import type { NotificationData, ProjectInfo, RequestData } from './server';
4
6
  export { TypeScriptProjectHost } from '@volar/typescript';
5
- export declare const onSomePipeReadyCallbacks: (() => void)[];
6
- export declare function getNamedPipePath(projectKind: ts.server.ProjectKind.Configured | ts.server.ProjectKind.Inferred, key: number): string;
7
- export declare function getReadyNamedPipePaths(): {
8
- configured: string[];
9
- inferred: string[];
10
- };
11
- export declare function connect(namedPipePath: string, timeout?: number): Promise<net.Socket | "error" | "timeout">;
12
- export declare function searchNamedPipeServerForFile(fileName: string): Promise<{
13
- socket: net.Socket;
14
- projectInfo: ProjectInfo;
15
- } | undefined>;
16
- export declare function sendRequestWorker<T>(request: Request, socket: net.Socket): Promise<T | null | undefined>;
7
+ export declare function getServerPath(kind: ts.server.ProjectKind, id: number): string;
8
+ declare class NamedPipeServer {
9
+ path: string;
10
+ connecting: boolean;
11
+ projectInfo?: ProjectInfo;
12
+ containsFileCache: Map<string, Promise<boolean | null | undefined>>;
13
+ componentNamesAndProps: FileMap<Record<string, ComponentPropInfo[] | null>>;
14
+ constructor(kind: ts.server.ProjectKind, id: number);
15
+ containsFile(fileName: string): Promise<boolean | null | undefined> | undefined;
16
+ getComponentProps(fileName: string, tag: string): Promise<ComponentPropInfo[] | null | undefined>;
17
+ update(): void;
18
+ connect(): void;
19
+ close(): void;
20
+ socket?: net.Socket;
21
+ seq: number;
22
+ dataChunks: Buffer[];
23
+ requestHandlers: Map<number, (res: any) => void>;
24
+ onData(chunk: Buffer): void;
25
+ onNotification(type: NotificationData[0], fileName: string, data: any): void;
26
+ sendRequest<T>(requestType: RequestData[1], fileName: string, ...args: any[]): Promise<T | null | undefined>;
27
+ }
28
+ export declare const configuredServers: NamedPipeServer[];
29
+ export declare const inferredServers: NamedPipeServer[];
30
+ export declare const onServerReady: (() => void)[];
31
+ export declare function getBestServer(fileName: string): Promise<NamedPipeServer | undefined>;
package/lib/utils.js CHANGED
@@ -1,195 +1,232 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.onSomePipeReadyCallbacks = void 0;
4
- exports.getNamedPipePath = getNamedPipePath;
5
- exports.getReadyNamedPipePaths = getReadyNamedPipePaths;
6
- exports.connect = connect;
7
- exports.searchNamedPipeServerForFile = searchNamedPipeServerForFile;
8
- exports.sendRequestWorker = sendRequestWorker;
9
- const fs = require("fs");
10
- const net = require("net");
11
- const os = require("os");
12
- const path = require("path");
3
+ exports.onServerReady = exports.inferredServers = exports.configuredServers = void 0;
4
+ exports.getServerPath = getServerPath;
5
+ exports.getBestServer = getBestServer;
6
+ const language_core_1 = require("@vue/language-core");
7
+ const shared_1 = require("@vue/shared");
8
+ const fs = require("node:fs");
9
+ const net = require("node:net");
10
+ const os = require("node:os");
11
+ const path = require("node:path");
13
12
  const { version } = require('../package.json');
14
13
  const platform = os.platform();
15
14
  const pipeDir = platform === 'win32'
16
- ? `\\\\.\\pipe`
17
- : `/tmp`;
18
- const toFullPath = (file) => {
19
- if (platform === 'win32') {
20
- return pipeDir + '\\' + file;
15
+ ? `\\\\.\\pipe\\`
16
+ : `/tmp/`;
17
+ function getServerPath(kind, id) {
18
+ if (kind === 1) {
19
+ return `${pipeDir}vue-named-pipe-${version}-configured-${id}`;
21
20
  }
22
21
  else {
23
- return pipeDir + '/' + file;
24
- }
25
- };
26
- const configuredNamedPipePathPrefix = toFullPath(`vue-named-pipe-${version}-configured-`);
27
- const inferredNamedPipePathPrefix = toFullPath(`vue-named-pipe-${version}-inferred-`);
28
- const pipes = new Map();
29
- exports.onSomePipeReadyCallbacks = [];
30
- function waitingForNamedPipeServerReady(namedPipePath) {
31
- const socket = net.connect(namedPipePath);
32
- const start = Date.now();
33
- socket.on('connect', () => {
34
- console.log('[Vue Named Pipe Client] Connected:', namedPipePath, 'in', (Date.now() - start) + 'ms');
35
- socket.write('ping');
36
- });
37
- socket.on('data', () => {
38
- console.log('[Vue Named Pipe Client] Ready:', namedPipePath, 'in', (Date.now() - start) + 'ms');
39
- pipes.set(namedPipePath, 'ready');
40
- socket.end();
41
- exports.onSomePipeReadyCallbacks.forEach(cb => cb());
42
- });
43
- socket.on('error', err => {
44
- if (err.code === 'ECONNREFUSED') {
45
- try {
46
- console.log('[Vue Named Pipe Client] Deleting:', namedPipePath);
47
- fs.promises.unlink(namedPipePath);
48
- }
49
- catch { }
50
- }
51
- pipes.delete(namedPipePath);
52
- socket.end();
53
- });
54
- socket.on('timeout', () => {
55
- pipes.delete(namedPipePath);
56
- socket.end();
57
- });
58
- }
59
- function getNamedPipePath(projectKind, key) {
60
- return projectKind === 1
61
- ? `${configuredNamedPipePathPrefix}${key}`
62
- : `${inferredNamedPipePathPrefix}${key}`;
22
+ return `${pipeDir}vue-named-pipe-${version}-inferred-${id}`;
23
+ }
63
24
  }
64
- function getReadyNamedPipePaths() {
65
- const configuredPipes = [];
66
- const inferredPipes = [];
67
- for (let i = 0; i < 20; i++) {
68
- const configuredPipe = getNamedPipePath(1, i);
69
- const inferredPipe = getNamedPipePath(0, i);
70
- if (pipes.get(configuredPipe) === 'ready') {
71
- configuredPipes.push(configuredPipe);
72
- }
73
- else if (!pipes.has(configuredPipe)) {
74
- pipes.set(configuredPipe, 'unknown');
75
- waitingForNamedPipeServerReady(configuredPipe);
25
+ class NamedPipeServer {
26
+ constructor(kind, id) {
27
+ this.connecting = false;
28
+ this.containsFileCache = new Map();
29
+ this.componentNamesAndProps = new language_core_1.FileMap(false);
30
+ this.seq = 0;
31
+ this.dataChunks = [];
32
+ this.requestHandlers = new Map();
33
+ this.path = getServerPath(kind, id);
34
+ }
35
+ containsFile(fileName) {
36
+ if (this.projectInfo) {
37
+ if (!this.containsFileCache.has(fileName)) {
38
+ this.containsFileCache.set(fileName, (async () => {
39
+ const res = await this.sendRequest('containsFile', fileName);
40
+ if (typeof res !== 'boolean') {
41
+ // If the request fails, delete the cache
42
+ this.containsFileCache.delete(fileName);
43
+ }
44
+ return res;
45
+ })());
46
+ }
47
+ return this.containsFileCache.get(fileName);
76
48
  }
77
- if (pipes.get(inferredPipe) === 'ready') {
78
- inferredPipes.push(inferredPipe);
49
+ }
50
+ async getComponentProps(fileName, tag) {
51
+ const componentAndProps = this.componentNamesAndProps.get(fileName);
52
+ if (!componentAndProps) {
53
+ return;
79
54
  }
80
- else if (!pipes.has(inferredPipe)) {
81
- pipes.set(inferredPipe, 'unknown');
82
- waitingForNamedPipeServerReady(inferredPipe);
55
+ const props = componentAndProps[tag]
56
+ ?? componentAndProps[(0, shared_1.camelize)(tag)]
57
+ ?? componentAndProps[(0, shared_1.capitalize)((0, shared_1.camelize)(tag))];
58
+ if (props) {
59
+ return props;
83
60
  }
61
+ return await this.sendRequest('subscribeComponentProps', fileName, tag);
84
62
  }
85
- return {
86
- configured: configuredPipes,
87
- inferred: inferredPipes,
88
- };
89
- }
90
- function connect(namedPipePath, timeout) {
91
- return new Promise(resolve => {
92
- const socket = net.connect(namedPipePath);
93
- if (timeout) {
94
- socket.setTimeout(timeout);
63
+ update() {
64
+ if (!this.connecting && !this.projectInfo) {
65
+ this.connecting = true;
66
+ this.connect();
95
67
  }
96
- const onConnect = () => {
97
- cleanup();
98
- resolve(socket);
99
- };
100
- const onError = (err) => {
68
+ }
69
+ connect() {
70
+ this.socket = net.connect(this.path);
71
+ this.socket.on('data', this.onData.bind(this));
72
+ this.socket.on('connect', async () => {
73
+ const projectInfo = await this.sendRequest('projectInfo', '');
74
+ if (projectInfo) {
75
+ console.log('TSServer project ready:', projectInfo.name);
76
+ this.projectInfo = projectInfo;
77
+ this.containsFileCache.clear();
78
+ exports.onServerReady.forEach(cb => cb());
79
+ }
80
+ else {
81
+ this.close();
82
+ }
83
+ });
84
+ this.socket.on('error', err => {
101
85
  if (err.code === 'ECONNREFUSED') {
102
86
  try {
103
- console.log('[Vue Named Pipe Client] Deleting:', namedPipePath);
104
- fs.promises.unlink(namedPipePath);
87
+ console.log('Deleteing invalid named pipe file:', this.path);
88
+ fs.promises.unlink(this.path);
105
89
  }
106
90
  catch { }
107
91
  }
108
- pipes.delete(namedPipePath);
109
- cleanup();
110
- resolve('error');
111
- socket.end();
112
- };
113
- const onTimeout = () => {
114
- cleanup();
115
- resolve('timeout');
116
- socket.end();
117
- };
118
- const cleanup = () => {
119
- socket.off('connect', onConnect);
120
- socket.off('error', onError);
121
- socket.off('timeout', onTimeout);
122
- };
123
- socket.on('connect', onConnect);
124
- socket.on('error', onError);
125
- socket.on('timeout', onTimeout);
126
- });
92
+ this.close();
93
+ });
94
+ this.socket.on('timeout', () => {
95
+ this.close();
96
+ });
97
+ }
98
+ close() {
99
+ this.connecting = false;
100
+ this.projectInfo = undefined;
101
+ this.socket?.end();
102
+ }
103
+ onData(chunk) {
104
+ this.dataChunks.push(chunk);
105
+ const data = Buffer.concat(this.dataChunks);
106
+ const text = data.toString();
107
+ if (text.endsWith('\n\n')) {
108
+ this.dataChunks.length = 0;
109
+ const results = text.split('\n\n');
110
+ for (let result of results) {
111
+ result = result.trim();
112
+ if (!result) {
113
+ continue;
114
+ }
115
+ try {
116
+ const data = JSON.parse(result.trim());
117
+ if (typeof data[0] === 'number') {
118
+ const [seq, res] = data;
119
+ this.requestHandlers.get(seq)?.(res);
120
+ }
121
+ else {
122
+ const [type, fileName, res] = data;
123
+ this.onNotification(type, fileName, res);
124
+ }
125
+ }
126
+ catch (e) {
127
+ console.error('JSON parse error:', e);
128
+ }
129
+ }
130
+ }
131
+ }
132
+ onNotification(type, fileName, data) {
133
+ // console.log(`[${type}] ${fileName} ${JSON.stringify(data)}`);
134
+ if (type === 'componentNamesUpdated') {
135
+ let components = this.componentNamesAndProps.get(fileName);
136
+ if (!components) {
137
+ components = {};
138
+ this.componentNamesAndProps.set(fileName, components);
139
+ }
140
+ const newNames = data;
141
+ const newNameSet = new Set(newNames);
142
+ for (const name in components) {
143
+ if (!newNameSet.has(name)) {
144
+ delete components[name];
145
+ }
146
+ }
147
+ for (const name of newNames) {
148
+ if (!components[name]) {
149
+ components[name] = null;
150
+ }
151
+ }
152
+ }
153
+ else if (type === 'componentPropsUpdated') {
154
+ const components = this.componentNamesAndProps.get(fileName) ?? {};
155
+ const [name, props] = data;
156
+ components[name] = props;
157
+ }
158
+ else {
159
+ console.error('Unknown notification type:', type);
160
+ debugger;
161
+ }
162
+ }
163
+ sendRequest(requestType, fileName, ...args) {
164
+ return new Promise(resolve => {
165
+ const seq = this.seq++;
166
+ // console.time(`[${seq}] ${requestType} ${fileName}`);
167
+ this.requestHandlers.set(seq, data => {
168
+ // console.timeEnd(`[${seq}] ${requestType} ${fileName}`);
169
+ this.requestHandlers.delete(seq);
170
+ resolve(data);
171
+ clearInterval(retryTimer);
172
+ });
173
+ const retry = () => {
174
+ const data = [seq, requestType, fileName, ...args];
175
+ this.socket.write(JSON.stringify(data) + '\n\n');
176
+ };
177
+ retry();
178
+ const retryTimer = setInterval(retry, 1000);
179
+ });
180
+ }
127
181
  }
128
- async function searchNamedPipeServerForFile(fileName) {
129
- const paths = await getReadyNamedPipePaths();
130
- const configuredServers = (await Promise.all(paths.configured.map(async (path) => {
131
- // Find existing servers
132
- const socket = await connect(path);
133
- if (typeof socket !== 'object') {
182
+ exports.configuredServers = [];
183
+ exports.inferredServers = [];
184
+ exports.onServerReady = [];
185
+ for (let i = 0; i < 10; i++) {
186
+ exports.configuredServers.push(new NamedPipeServer(1, i));
187
+ exports.inferredServers.push(new NamedPipeServer(0, i));
188
+ }
189
+ async function getBestServer(fileName) {
190
+ for (const server of exports.configuredServers) {
191
+ server.update();
192
+ }
193
+ let servers = (await Promise.all(exports.configuredServers.map(async (server) => {
194
+ const projectInfo = server.projectInfo;
195
+ if (!projectInfo) {
134
196
  return;
135
197
  }
136
- // Find servers containing the current file
137
- const containsFile = await sendRequestWorker({ type: 'containsFile', args: [fileName] }, socket);
198
+ const containsFile = await server.containsFile(fileName);
138
199
  if (!containsFile) {
139
- socket.end();
140
- return;
141
- }
142
- // Get project info for each server
143
- const projectInfo = await sendRequestWorker({ type: 'projectInfo', args: [fileName] }, socket);
144
- if (!projectInfo) {
145
- socket.end();
146
200
  return;
147
201
  }
148
- return {
149
- socket,
150
- projectInfo,
151
- };
202
+ return server;
152
203
  }))).filter(server => !!server);
153
204
  // Sort servers by tsconfig
154
- configuredServers.sort((a, b) => sortTSConfigs(fileName, a.projectInfo.name, b.projectInfo.name));
155
- if (configuredServers.length) {
156
- // Close all but the first server
157
- for (let i = 1; i < configuredServers.length; i++) {
158
- configuredServers[i].socket.end();
159
- }
205
+ servers.sort((a, b) => sortTSConfigs(fileName, a.projectInfo.name, b.projectInfo.name));
206
+ if (servers.length) {
160
207
  // Return the first server
161
- return configuredServers[0];
208
+ return servers[0];
162
209
  }
163
- const inferredServers = (await Promise.all(paths.inferred.map(async (namedPipePath) => {
164
- // Find existing servers
165
- const socket = await connect(namedPipePath);
166
- if (typeof socket !== 'object') {
167
- return;
168
- }
169
- // Get project info for each server
170
- const projectInfo = await sendRequestWorker({ type: 'projectInfo', args: [fileName] }, socket);
210
+ for (const server of exports.inferredServers) {
211
+ server.update();
212
+ }
213
+ servers = (await Promise.all(exports.inferredServers.map(server => {
214
+ const projectInfo = server.projectInfo;
171
215
  if (!projectInfo) {
172
- socket.end();
173
216
  return;
174
217
  }
175
218
  // Check if the file is in the project's directory
176
- if (!path.relative(projectInfo.currentDirectory, fileName).startsWith('..')) {
177
- return {
178
- socket,
179
- projectInfo,
180
- };
219
+ if (path.relative(projectInfo.currentDirectory, fileName).startsWith('..')) {
220
+ return;
181
221
  }
222
+ return server;
182
223
  }))).filter(server => !!server);
183
224
  // Sort servers by directory
184
- inferredServers.sort((a, b) => b.projectInfo.currentDirectory.replace(/\\/g, '/').split('/').length
225
+ servers.sort((a, b) => b.projectInfo.currentDirectory.replace(/\\/g, '/').split('/').length
185
226
  - a.projectInfo.currentDirectory.replace(/\\/g, '/').split('/').length);
186
- if (inferredServers.length) {
187
- // Close all but the first server
188
- for (let i = 1; i < inferredServers.length; i++) {
189
- inferredServers[i].socket.end();
190
- }
227
+ if (servers.length) {
191
228
  // Return the first server
192
- return inferredServers[0];
229
+ return servers[0];
193
230
  }
194
231
  }
195
232
  function sortTSConfigs(file, a, b) {
@@ -208,37 +245,4 @@ function isFileInDir(fileName, dir) {
208
245
  const relative = path.relative(dir, fileName);
209
246
  return !!relative && !relative.startsWith('..') && !path.isAbsolute(relative);
210
247
  }
211
- function sendRequestWorker(request, socket) {
212
- return new Promise(resolve => {
213
- let dataChunks = [];
214
- const onData = (chunk) => {
215
- dataChunks.push(chunk);
216
- const data = Buffer.concat(dataChunks);
217
- const text = data.toString();
218
- if (text.endsWith('\n\n')) {
219
- let json = null;
220
- try {
221
- json = JSON.parse(text);
222
- }
223
- catch (e) {
224
- console.error('[Vue Named Pipe Client] Failed to parse response:', text);
225
- }
226
- cleanup();
227
- resolve(json);
228
- }
229
- };
230
- const onError = (err) => {
231
- console.error('[Vue Named Pipe Client] Error:', err.message);
232
- cleanup();
233
- resolve(undefined);
234
- };
235
- const cleanup = () => {
236
- socket.off('data', onData);
237
- socket.off('error', onError);
238
- };
239
- socket.on('data', onData);
240
- socket.on('error', onError);
241
- socket.write(JSON.stringify(request));
242
- });
243
- }
244
248
  //# sourceMappingURL=utils.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vue/typescript-plugin",
3
- "version": "2.1.10",
3
+ "version": "2.2.2",
4
4
  "license": "MIT",
5
5
  "files": [
6
6
  "**/*.js",
@@ -13,12 +13,12 @@
13
13
  "directory": "packages/typescript-plugin"
14
14
  },
15
15
  "dependencies": {
16
- "@volar/typescript": "~2.4.8",
17
- "@vue/language-core": "2.1.10",
16
+ "@volar/typescript": "~2.4.11",
17
+ "@vue/language-core": "2.2.2",
18
18
  "@vue/shared": "^3.5.0"
19
19
  },
20
20
  "devDependencies": {
21
- "@types/node": "latest"
21
+ "@types/node": "^22.10.4"
22
22
  },
23
- "gitHead": "b0af30caee2f8dfb1a8393c1b400f38e31fa4883"
23
+ "gitHead": "30757908b67f40f779c36795665163634fb81868"
24
24
  }