Skip to content

Commit

Permalink
feat(typescript-plugin): add emit JSDoc support (vuejs#4365)
Browse files Browse the repository at this point in the history
  • Loading branch information
johnsoncodehk committed May 10, 2024
1 parent 62c65ae commit 635f4fe
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 4 deletions.
6 changes: 4 additions & 2 deletions packages/language-core/lib/codegen/template/element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export function* generateComponent(
const var_originalComponent = matchImportName ?? ctx.getInternalVariable();
const var_functionalComponent = ctx.getInternalVariable();
const var_componentInstance = ctx.getInternalVariable();
const var_componentEmit = ctx.getInternalVariable();
const var_componentEvents = ctx.getInternalVariable();
const var_defineComponentCtx = ctx.getInternalVariable();
const isComponentTag = node.tag.toLowerCase() === 'component';
Expand Down Expand Up @@ -238,7 +239,7 @@ export function* generateComponent(
yield* generateVScope(options, ctx, node, props);

ctx.usedComponentCtxVars.add(componentCtxVar);
yield* generateElementEvents(options, ctx, node, var_functionalComponent, var_componentInstance, var_componentEvents, () => usedComponentEventsVar = true);
yield* generateElementEvents(options, ctx, node, var_functionalComponent, var_componentInstance, var_componentEmit, var_componentEvents, () => usedComponentEventsVar = true);

const slotDir = node.props.find(p => p.type === CompilerDOM.NodeTypes.DIRECTIVE && p.name === 'slot') as CompilerDOM.DirectiveNode;
if (slotDir) {
Expand All @@ -252,7 +253,8 @@ export function* generateComponent(
yield `const ${componentCtxVar} = __VLS_pickFunctionalComponentCtx(${var_originalComponent}, ${var_componentInstance})!${endOfLine}`;
}
if (usedComponentEventsVar) {
yield `let ${var_componentEvents}!: __VLS_NormalizeEmits<typeof ${componentCtxVar}.emit>${endOfLine}`;
yield `let ${var_componentEmit}!: typeof ${componentCtxVar}.emit${endOfLine}`;
yield `let ${var_componentEvents}!: __VLS_NormalizeEmits<typeof ${var_componentEmit}>${endOfLine}`;
}
}

Expand Down
8 changes: 6 additions & 2 deletions packages/language-core/lib/codegen/template/elementEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export function* generateElementEvents(
node: CompilerDOM.ElementNode,
componentVar: string,
componentInstanceVar: string,
emitVar: string,
eventsVar: string,
used: () => void,
): Generator<Code> {
Expand All @@ -27,7 +28,9 @@ export function* generateElementEvents(
) {
used();
const eventVar = ctx.getInternalVariable();
yield `let ${eventVar} = { '${prop.arg.loc.source}': __VLS_pickEvent(`;
yield `let ${eventVar} = {${newLine}`;
yield `/**__VLS_emit,${emitVar},${prop.arg.loc.source}*/${newLine}`;
yield `'${prop.arg.loc.source}': __VLS_pickEvent(`;
yield `${eventsVar}['${prop.arg.loc.source}'], `;
yield `({} as __VLS_FunctionalComponentProps<typeof ${componentVar}, typeof ${componentInstanceVar}>)`;
yield* generateEventArg(options, ctx, prop.arg, true, false);
Expand Down Expand Up @@ -59,7 +62,8 @@ export function* generateElementEvents(
}
yield `: `;
yield* generateEventExpression(options, ctx, prop);
yield ` }${endOfLine}`;
yield newLine;
yield `}${endOfLine}`;
}
else if (
prop.type === CompilerDOM.NodeTypes.DIRECTIVE
Expand Down
41 changes: 41 additions & 0 deletions packages/typescript-plugin/lib/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export function decorateLanguageServiceForVue(
getCompletionEntryDetails,
getCodeFixesAtPosition,
getEncodedSemanticClassifications,
getQuickInfoAtPosition,
} = languageService;

languageService.getCompletionsAtPosition = (fileName, position, options, formattingSettings) => {
Expand Down Expand Up @@ -105,6 +106,46 @@ export function decorateLanguageServiceForVue(
result = result.filter(entry => entry.description.indexOf('__VLS_') === -1);
return result;
};
languageService.getQuickInfoAtPosition = (...args) => {
const result = getQuickInfoAtPosition(...args);
if (result && result.documentation?.length === 1 && result.documentation[0].text.startsWith('__VLS_emit,')) {
const [_, emitVarName, eventName] = result.documentation[0].text.split(',');
const program = languageService.getProgram()!;
const typeChecker = program.getTypeChecker();
const sourceFile = program.getSourceFile(args[0]);

result.documentation = undefined;

let symbolNode: ts.Identifier | undefined;

sourceFile?.forEachChild(function visit(node) {
if (ts.isIdentifier(node) && node.text === emitVarName) {
symbolNode = node;
}
if (symbolNode) {
return;
}
ts.forEachChild(node, visit);
});

if (symbolNode) {
const emitSymbol = typeChecker.getSymbolAtLocation(symbolNode);
if (emitSymbol) {
const type = typeChecker.getTypeOfSymbolAtLocation(emitSymbol, symbolNode);
const calls = type.getCallSignatures();
for (const call of calls) {
const callEventName = (typeChecker.getTypeOfSymbolAtLocation(call.parameters[0], symbolNode) as ts.StringLiteralType).value;
call.getJsDocTags();
if (callEventName === eventName) {
result.documentation = call.getDocumentationComment(typeChecker);
result.tags = call.getJsDocTags();
}
}
}
}
}
return result;
};
if (isTsPlugin) {
languageService.getEncodedSemanticClassifications = (fileName, span, format) => {
const result = getEncodedSemanticClassifications(fileName, span, format);
Expand Down

0 comments on commit 635f4fe

Please sign in to comment.