Skip to content

Commit d17eda8

Browse files
committed
feat(typescript-plugin): add support for subscribe component props
1 parent 2439489 commit d17eda8

File tree

3 files changed

+125
-52
lines changed

3 files changed

+125
-52
lines changed

packages/typescript-plugin/lib/client.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { camelize, capitalize } from '@vue/shared';
21
import type { RequestData } from './server';
32
import { getBestServer } from './utils';
43

@@ -45,13 +44,7 @@ export async function getComponentProps(fileName: string, componentName: string)
4544
if (!server) {
4645
return;
4746
}
48-
const componentAndProps = await server.componentNamesAndProps.get(fileName);
49-
if (!componentAndProps) {
50-
return;
51-
}
52-
return componentAndProps[componentName]
53-
?? componentAndProps[camelize(componentName)]
54-
?? componentAndProps[capitalize(camelize(componentName))];
47+
return await server.getComponentProps(fileName, componentName);
5548
}
5649

5750
export function getComponentEvents(

packages/typescript-plugin/lib/server.ts

Lines changed: 66 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,23 @@ import { getQuickInfoAtPosition } from './requests/getQuickInfoAtPosition';
1010
import type { RequestContext } from './requests/types';
1111
import { getServerPath } from './utils';
1212

13-
export type RequestType = 'containsFile'
13+
export type RequestType =
14+
'containsFile'
1415
| 'projectInfo'
1516
| 'collectExtractProps'
1617
| 'getImportPathForFile'
1718
| 'getPropertiesAtLocation'
1819
| 'getQuickInfoAtPosition'
1920
// Component Infos
20-
| 'getComponentProps'
21+
| 'subscribeComponentProps'
2122
| 'getComponentEvents'
2223
| 'getTemplateContextProps'
2324
| 'getElementAttrs';
2425

26+
export type NotificationType =
27+
'componentNamesUpdated'
28+
| 'componentPropsUpdated';
29+
2530
export type RequestData = [
2631
seq: number,
2732
type: RequestType,
@@ -35,7 +40,7 @@ export type ResponseData = [
3540
];
3641

3742
export type NotificationData = [
38-
type: 'componentAndPropsUpdated',
43+
type: NotificationType,
3944
fileName: string,
4045
data: any,
4146
];
@@ -63,7 +68,20 @@ export async function startNamedPipeServer(
6368
getFileId: (fileName: string) => fileName,
6469
};
6570
const dataChunks: Buffer[] = [];
66-
const componentNamesAndProps = new Map<string, string>();
71+
const currentData = new Map<
72+
string,
73+
[
74+
componentNames: string[],
75+
Record<
76+
string,
77+
{
78+
name: string;
79+
required?: true;
80+
commentMarkdown?: string;
81+
}[]
82+
>,
83+
]
84+
>();
6785
const allConnections = new Set<net.Socket>();
6886
const pendingRequests = new Set<number>();
6987
const server = net.createServer(connection => {
@@ -93,8 +111,12 @@ export async function startNamedPipeServer(
93111
});
94112
connection.on('error', err => console.error('[Vue Named Pipe Server]', err.message));
95113

96-
for (const [fileName, data] of componentNamesAndProps) {
97-
notify(connection, 'componentAndPropsUpdated', fileName, data);
114+
for (const [fileName, [componentNames, componentProps]] of currentData) {
115+
notify(connection, 'componentNamesUpdated', fileName, Object.keys(componentNames));
116+
117+
for (const [name, props] of Object.entries(componentProps)) {
118+
notify(connection, 'componentPropsUpdated', fileName, [name, props]);
119+
}
98120
}
99121
});
100122

@@ -137,37 +159,34 @@ export async function startNamedPipeServer(
137159
if (token?.isCancellationRequested()) {
138160
break;
139161
}
140-
let newData: Record<string, {
141-
name: string;
142-
required?: true;
143-
commentMarkdown?: string;
144-
}[]> | undefined = {};
145-
const componentNames = getComponentNames.apply(requestContext, [scriptInfo.fileName]);
146-
// const testProps = getComponentProps.apply(requestContext, [scriptInfo.fileName, 'HelloWorld']);
147-
// debugger;
148-
for (const component of componentNames ?? []) {
162+
163+
let data = currentData.get(scriptInfo.fileName);
164+
if (!data) {
165+
data = [[], {}];
166+
currentData.set(scriptInfo.fileName, data);
167+
}
168+
169+
const [oldComponentNames, componentProps] = data;
170+
const newComponentNames = getComponentNames.apply(requestContext, [scriptInfo.fileName]) ?? [];
171+
172+
if (JSON.stringify(oldComponentNames) !== JSON.stringify(newComponentNames)) {
173+
data[0] = newComponentNames;
174+
for (const connection of connections) {
175+
notify(connection, 'componentNamesUpdated', scriptInfo.fileName, newComponentNames);
176+
}
177+
}
178+
179+
for (const [name, props] of Object.entries(componentProps)) {
149180
await sleep(10);
150181
if (token?.isCancellationRequested()) {
151-
newData = undefined;
152182
break;
153183
}
154-
const props = getComponentProps.apply(requestContext, [scriptInfo.fileName, component]);
155-
if (props) {
156-
newData[component] = props;
157-
}
158-
}
159-
if (!newData) {
160-
// Canceled
161-
break;
162-
}
163-
const oldDataJson = componentNamesAndProps.get(scriptInfo.fileName);
164-
const newDataJson = JSON.stringify(newData);
165-
if (oldDataJson !== newDataJson) {
166-
// Update cache
167-
componentNamesAndProps.set(scriptInfo.fileName, newDataJson);
168-
// Notify
169-
for (const connection of connections) {
170-
notify(connection, 'componentAndPropsUpdated', scriptInfo.fileName, newData);
184+
const newProps = getComponentProps.apply(requestContext, [scriptInfo.fileName, name]) ?? [];
185+
if (JSON.stringify(props) !== JSON.stringify(newProps)) {
186+
componentProps[name] = newProps;
187+
for (const connection of connections) {
188+
notify(connection, 'componentPropsUpdated', scriptInfo.fileName, [name, newProps]);
189+
}
171190
}
172191
}
173192
}
@@ -200,7 +219,9 @@ export async function startNamedPipeServer(
200219
connection.write(JSON.stringify([seq, data ?? null]) + '\n\n');
201220
}
202221

203-
function handleRequest(requestType: RequestType, ...args: any[]) {
222+
function handleRequest(requestType: RequestType, ...args: [fileName: string, ...any[]]) {
223+
const fileName = args[0];
224+
204225
if (requestType === 'projectInfo') {
205226
return {
206227
name: info.project.getProjectName(),
@@ -209,7 +230,7 @@ export async function startNamedPipeServer(
209230
} satisfies ProjectInfo;
210231
}
211232
else if (requestType === 'containsFile') {
212-
return info.project.containsFile(ts.server.toNormalizedPath(args[0]));
233+
return info.project.containsFile(ts.server.toNormalizedPath(fileName));
213234
}
214235
else if (requestType === 'collectExtractProps') {
215236
return collectExtractProps.apply(requestContext, args as any);
@@ -223,8 +244,16 @@ export async function startNamedPipeServer(
223244
else if (requestType === 'getQuickInfoAtPosition') {
224245
return getQuickInfoAtPosition.apply(requestContext, args as any);
225246
}
226-
else if (requestType === 'getComponentProps') {
227-
return getComponentProps.apply(requestContext, args as any);
247+
else if (requestType === 'subscribeComponentProps') {
248+
const tag = args[1];
249+
const props = getComponentProps.apply(requestContext, [fileName, tag]) ?? [];
250+
let data = currentData.get(fileName);
251+
if (!data) {
252+
data = [[], {}];
253+
currentData.set(fileName, data);
254+
}
255+
data[1][tag] = props;
256+
return props;
228257
}
229258
else if (requestType === 'getComponentEvents') {
230259
return getComponentEvents.apply(requestContext, args as any);

packages/typescript-plugin/lib/utils.ts

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { camelize, capitalize } from '@vue/shared';
12
import * as fs from 'node:fs';
23
import * as net from 'node:net';
34
import * as os from 'node:os';
@@ -29,11 +30,14 @@ class NamedPipeServer {
2930
connecting = false;
3031
projectInfo?: ProjectInfo;
3132
containsFileCache = new Map<string, Promise<boolean | undefined | null>>();
32-
componentNamesAndProps = new Map<string, Record<string, {
33-
name: string;
34-
required?: true;
35-
commentMarkdown?: string;
36-
}[]>>();
33+
componentNamesAndProps = new Map<
34+
string,
35+
Record<string, null | {
36+
name: string;
37+
required?: true;
38+
commentMarkdown?: string;
39+
}[]>
40+
>();
3741

3842
constructor(kind: ts.server.ProjectKind, id: number) {
3943
this.path = getServerPath(kind, id);
@@ -55,6 +59,20 @@ class NamedPipeServer {
5559
}
5660
}
5761

62+
async getComponentProps(fileName: string, tag: string) {
63+
const componentAndProps = this.componentNamesAndProps.get(fileName);
64+
if (!componentAndProps) {
65+
return;
66+
}
67+
const props = componentAndProps[tag]
68+
?? componentAndProps[camelize(tag)]
69+
?? componentAndProps[capitalize(camelize(tag))];
70+
if (props) {
71+
return props;
72+
}
73+
return await this.sendRequest<ReturnType<typeof import('./requests/componentInfos')['getComponentProps']>>('subscribeComponentProps', fileName, tag);
74+
}
75+
5876
update() {
5977
if (!this.connecting && !this.projectInfo) {
6078
this.connecting = true;
@@ -131,8 +149,41 @@ class NamedPipeServer {
131149

132150
onNotification(type: NotificationData[0], fileName: string, data: any) {
133151
// console.log(`[${type}] ${fileName} ${JSON.stringify(data)}`);
134-
if (type === 'componentAndPropsUpdated') {
135-
this.componentNamesAndProps.set(fileName, data);
152+
153+
if (type === 'componentNamesUpdated') {
154+
let components = this.componentNamesAndProps.get(fileName);
155+
if (!components) {
156+
components = {};
157+
this.componentNamesAndProps.set(fileName, components);
158+
}
159+
const newNames: string[] = data;
160+
const newNameSet = new Set(newNames);
161+
for (const name in components) {
162+
if (!newNameSet.has(name)) {
163+
delete components[name];
164+
}
165+
}
166+
for (const name of newNames) {
167+
if (!components[name]) {
168+
components[name] = null;
169+
}
170+
}
171+
}
172+
else if (type === 'componentPropsUpdated') {
173+
const components = this.componentNamesAndProps.get(fileName) ?? {};
174+
const [name, props]: [
175+
name: string,
176+
props: {
177+
name: string;
178+
required?: true;
179+
commentMarkdown?: string;
180+
}[],
181+
] = data;
182+
components[name] = props;
183+
}
184+
else {
185+
console.error('Unknown notification type:', type);
186+
debugger;
136187
}
137188
}
138189

0 commit comments

Comments
 (0)