Skip to content

Commit

Permalink
feat(typescript-plugin): add support for subscribe component props
Browse files Browse the repository at this point in the history
  • Loading branch information
johnsoncodehk committed Dec 23, 2024
1 parent 2439489 commit d17eda8
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 52 deletions.
9 changes: 1 addition & 8 deletions packages/typescript-plugin/lib/client.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { camelize, capitalize } from '@vue/shared';
import type { RequestData } from './server';
import { getBestServer } from './utils';

Expand Down Expand Up @@ -45,13 +44,7 @@ export async function getComponentProps(fileName: string, componentName: string)
if (!server) {
return;
}
const componentAndProps = await server.componentNamesAndProps.get(fileName);
if (!componentAndProps) {
return;
}
return componentAndProps[componentName]
?? componentAndProps[camelize(componentName)]
?? componentAndProps[capitalize(camelize(componentName))];
return await server.getComponentProps(fileName, componentName);
}

export function getComponentEvents(
Expand Down
103 changes: 66 additions & 37 deletions packages/typescript-plugin/lib/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,23 @@ import { getQuickInfoAtPosition } from './requests/getQuickInfoAtPosition';
import type { RequestContext } from './requests/types';
import { getServerPath } from './utils';

export type RequestType = 'containsFile'
export type RequestType =
'containsFile'
| 'projectInfo'
| 'collectExtractProps'
| 'getImportPathForFile'
| 'getPropertiesAtLocation'
| 'getQuickInfoAtPosition'
// Component Infos
| 'getComponentProps'
| 'subscribeComponentProps'
| 'getComponentEvents'
| 'getTemplateContextProps'
| 'getElementAttrs';

export type NotificationType =
'componentNamesUpdated'
| 'componentPropsUpdated';

export type RequestData = [
seq: number,
type: RequestType,
Expand All @@ -35,7 +40,7 @@ export type ResponseData = [
];

export type NotificationData = [
type: 'componentAndPropsUpdated',
type: NotificationType,
fileName: string,
data: any,
];
Expand Down Expand Up @@ -63,7 +68,20 @@ export async function startNamedPipeServer(
getFileId: (fileName: string) => fileName,
};
const dataChunks: Buffer[] = [];
const componentNamesAndProps = new Map<string, string>();
const currentData = new Map<
string,
[
componentNames: string[],
Record<
string,
{
name: string;
required?: true;
commentMarkdown?: string;
}[]
>,
]
>();
const allConnections = new Set<net.Socket>();
const pendingRequests = new Set<number>();
const server = net.createServer(connection => {
Expand Down Expand Up @@ -93,8 +111,12 @@ export async function startNamedPipeServer(
});
connection.on('error', err => console.error('[Vue Named Pipe Server]', err.message));

for (const [fileName, data] of componentNamesAndProps) {
notify(connection, 'componentAndPropsUpdated', fileName, data);
for (const [fileName, [componentNames, componentProps]] of currentData) {
notify(connection, 'componentNamesUpdated', fileName, Object.keys(componentNames));

for (const [name, props] of Object.entries(componentProps)) {
notify(connection, 'componentPropsUpdated', fileName, [name, props]);
}
}
});

Expand Down Expand Up @@ -137,37 +159,34 @@ export async function startNamedPipeServer(
if (token?.isCancellationRequested()) {
break;
}
let newData: Record<string, {
name: string;
required?: true;
commentMarkdown?: string;
}[]> | undefined = {};
const componentNames = getComponentNames.apply(requestContext, [scriptInfo.fileName]);
// const testProps = getComponentProps.apply(requestContext, [scriptInfo.fileName, 'HelloWorld']);
// debugger;
for (const component of componentNames ?? []) {

let data = currentData.get(scriptInfo.fileName);
if (!data) {
data = [[], {}];
currentData.set(scriptInfo.fileName, data);
}

const [oldComponentNames, componentProps] = data;
const newComponentNames = getComponentNames.apply(requestContext, [scriptInfo.fileName]) ?? [];

if (JSON.stringify(oldComponentNames) !== JSON.stringify(newComponentNames)) {
data[0] = newComponentNames;
for (const connection of connections) {
notify(connection, 'componentNamesUpdated', scriptInfo.fileName, newComponentNames);
}
}

for (const [name, props] of Object.entries(componentProps)) {
await sleep(10);
if (token?.isCancellationRequested()) {
newData = undefined;
break;
}
const props = getComponentProps.apply(requestContext, [scriptInfo.fileName, component]);
if (props) {
newData[component] = props;
}
}
if (!newData) {
// Canceled
break;
}
const oldDataJson = componentNamesAndProps.get(scriptInfo.fileName);
const newDataJson = JSON.stringify(newData);
if (oldDataJson !== newDataJson) {
// Update cache
componentNamesAndProps.set(scriptInfo.fileName, newDataJson);
// Notify
for (const connection of connections) {
notify(connection, 'componentAndPropsUpdated', scriptInfo.fileName, newData);
const newProps = getComponentProps.apply(requestContext, [scriptInfo.fileName, name]) ?? [];
if (JSON.stringify(props) !== JSON.stringify(newProps)) {
componentProps[name] = newProps;
for (const connection of connections) {
notify(connection, 'componentPropsUpdated', scriptInfo.fileName, [name, newProps]);
}
}
}
}
Expand Down Expand Up @@ -200,7 +219,9 @@ export async function startNamedPipeServer(
connection.write(JSON.stringify([seq, data ?? null]) + '\n\n');
}

function handleRequest(requestType: RequestType, ...args: any[]) {
function handleRequest(requestType: RequestType, ...args: [fileName: string, ...any[]]) {
const fileName = args[0];

if (requestType === 'projectInfo') {
return {
name: info.project.getProjectName(),
Expand All @@ -209,7 +230,7 @@ export async function startNamedPipeServer(
} satisfies ProjectInfo;
}
else if (requestType === 'containsFile') {
return info.project.containsFile(ts.server.toNormalizedPath(args[0]));
return info.project.containsFile(ts.server.toNormalizedPath(fileName));
}
else if (requestType === 'collectExtractProps') {
return collectExtractProps.apply(requestContext, args as any);
Expand All @@ -223,8 +244,16 @@ export async function startNamedPipeServer(
else if (requestType === 'getQuickInfoAtPosition') {
return getQuickInfoAtPosition.apply(requestContext, args as any);
}
else if (requestType === 'getComponentProps') {
return getComponentProps.apply(requestContext, args as any);
else if (requestType === 'subscribeComponentProps') {
const tag = args[1];
const props = getComponentProps.apply(requestContext, [fileName, tag]) ?? [];
let data = currentData.get(fileName);
if (!data) {
data = [[], {}];
currentData.set(fileName, data);
}
data[1][tag] = props;
return props;
}
else if (requestType === 'getComponentEvents') {
return getComponentEvents.apply(requestContext, args as any);
Expand Down
65 changes: 58 additions & 7 deletions packages/typescript-plugin/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { camelize, capitalize } from '@vue/shared';
import * as fs from 'node:fs';
import * as net from 'node:net';
import * as os from 'node:os';
Expand Down Expand Up @@ -29,11 +30,14 @@ class NamedPipeServer {
connecting = false;
projectInfo?: ProjectInfo;
containsFileCache = new Map<string, Promise<boolean | undefined | null>>();
componentNamesAndProps = new Map<string, Record<string, {
name: string;
required?: true;
commentMarkdown?: string;
}[]>>();
componentNamesAndProps = new Map<
string,
Record<string, null | {
name: string;
required?: true;
commentMarkdown?: string;
}[]>
>();

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

async getComponentProps(fileName: string, tag: string) {
const componentAndProps = this.componentNamesAndProps.get(fileName);
if (!componentAndProps) {
return;
}
const props = componentAndProps[tag]
?? componentAndProps[camelize(tag)]
?? componentAndProps[capitalize(camelize(tag))];
if (props) {
return props;
}
return await this.sendRequest<ReturnType<typeof import('./requests/componentInfos')['getComponentProps']>>('subscribeComponentProps', fileName, tag);
}

update() {
if (!this.connecting && !this.projectInfo) {
this.connecting = true;
Expand Down Expand Up @@ -131,8 +149,41 @@ class NamedPipeServer {

onNotification(type: NotificationData[0], fileName: string, data: any) {
// console.log(`[${type}] ${fileName} ${JSON.stringify(data)}`);
if (type === 'componentAndPropsUpdated') {
this.componentNamesAndProps.set(fileName, data);

if (type === 'componentNamesUpdated') {
let components = this.componentNamesAndProps.get(fileName);
if (!components) {
components = {};
this.componentNamesAndProps.set(fileName, components);
}
const newNames: string[] = data;
const newNameSet = new Set(newNames);
for (const name in components) {
if (!newNameSet.has(name)) {
delete components[name];
}
}
for (const name of newNames) {
if (!components[name]) {
components[name] = null;
}
}
}
else if (type === 'componentPropsUpdated') {
const components = this.componentNamesAndProps.get(fileName) ?? {};
const [name, props]: [
name: string,
props: {
name: string;
required?: true;
commentMarkdown?: string;
}[],
] = data;
components[name] = props;
}
else {
console.error('Unknown notification type:', type);
debugger;
}
}

Expand Down

0 comments on commit d17eda8

Please sign in to comment.