Skip to content

Commit b6254ec

Browse files
committed
fix(typescript-plugin): resolve component type without __VLS_components
1 parent 949b3e3 commit b6254ec

File tree

10 files changed

+170
-84
lines changed

10 files changed

+170
-84
lines changed

packages/language-core/lib/codegen/script/template.ts

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -153,22 +153,19 @@ function* generateBindings(
153153
{ templateComponents, templateCodegen, styleCodegen, setupBindingNames }: ScriptCodegenOptions,
154154
ctx: ScriptCodegenContext,
155155
): Generator<Code> {
156-
if (!setupBindingNames.size) {
157-
return;
158-
}
159-
ctx.generatedTypes.add(names.Bindings);
160-
161-
const usedVars = new Set([
156+
const testNames = new Set([
162157
...templateComponents.flatMap(c => [camelize(c), capitalize(camelize(c))]),
163158
...templateCodegen?.accessExternalVariables.keys() ?? [],
164159
...styleCodegen?.accessExternalVariables.keys() ?? [],
165160
]);
161+
const exposeNames = [...setupBindingNames].filter(name => testNames.has(name));
162+
if (exposeNames.length === 0) {
163+
return;
164+
}
165+
ctx.generatedTypes.add(names.Bindings);
166166

167167
yield `type ${names.Bindings} = __VLS_ProxyRefs<{${newLine}`;
168-
for (const bindingName of setupBindingNames) {
169-
if (!usedVars.has(bindingName)) {
170-
continue;
171-
}
168+
for (const bindingName of exposeNames) {
172169
const token = Symbol(bindingName.length);
173170
yield ['', undefined, 0, { __linkedToken: token }];
174171
yield `${bindingName}: typeof `;

packages/language-core/lib/plugins/vue-tsx.ts

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,24 +96,27 @@ function useCodegen(
9696
);
9797

9898
const getSetupBindingNames = computedSet(() => {
99-
const newNames = new Set<string>();
99+
const names = new Set<string>();
100100
const scriptSetupRanges = getScriptSetupRanges();
101101
if (!sfc.scriptSetup || !scriptSetupRanges) {
102-
return newNames;
102+
return names;
103103
}
104104
for (const { range } of scriptSetupRanges.bindings) {
105105
const name = sfc.scriptSetup.content.slice(range.start, range.end);
106106
if (!getImportComponentNames().has(name)) {
107-
newNames.add(name);
107+
names.add(name);
108108
}
109109
}
110110
const scriptRanges = getScriptRanges();
111111
if (sfc.script && scriptRanges) {
112112
for (const { range } of scriptRanges.bindings) {
113-
newNames.add(sfc.script.content.slice(range.start, range.end));
113+
const name = sfc.script.content.slice(range.start, range.end);
114+
if (!getImportComponentNames().has(name)) {
115+
names.add(name);
116+
}
114117
}
115118
}
116-
return newNames;
119+
return names;
117120
});
118121

119122
const getImportComponentNames = computedSet(() => {
@@ -130,6 +133,19 @@ function useCodegen(
130133
names.add(sfc.scriptSetup.content.slice(range.start, range.end));
131134
}
132135
}
136+
const scriptRange = getScriptRanges();
137+
if (sfc.script && scriptRange) {
138+
for (const { range, moduleName, isDefaultImport, isNamespace } of scriptRange.bindings) {
139+
if (
140+
moduleName
141+
&& isDefaultImport
142+
&& !isNamespace
143+
&& ctx.vueCompilerOptions.extensions.some(ext => moduleName.endsWith(ext))
144+
) {
145+
names.add(sfc.script.content.slice(range.start, range.end));
146+
}
147+
}
148+
}
133149
}
134150
return names;
135151
});
@@ -259,5 +275,6 @@ function useCodegen(
259275
getSetupSlotsAssignName,
260276
getGeneratedScript,
261277
getGeneratedTemplate,
278+
getImportComponentNames,
262279
};
263280
}

packages/tsc/bin/vue-tsc.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
#!/usr/bin/env node
2-
// @ts-check
32
require('../index.js').run();

packages/typescript-plugin/index.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -143,18 +143,18 @@ export = createLanguageServicePlugin(
143143
});
144144
session.addProtocolHandler('_vue:getComponentEvents', request => {
145145
const [fileName, tag]: Parameters<Requests['getComponentEvents']> = request.arguments;
146-
const { project } = getProject(fileName);
147-
return createResponse(getComponentEvents(ts, project.getLanguageService().getProgram()!, fileName, tag));
146+
const { project, virtualCode } = getProjectAndVirtualCode(fileName);
147+
return createResponse(getComponentEvents(ts, project.getLanguageService().getProgram()!, virtualCode, tag));
148148
});
149149
session.addProtocolHandler('_vue:getComponentNames', request => {
150150
const [fileName]: Parameters<Requests['getComponentNames']> = request.arguments;
151-
const { project } = getProject(fileName);
152-
return createResponse(getComponentNames(ts, project.getLanguageService().getProgram()!, fileName));
151+
const { project, virtualCode } = getProjectAndVirtualCode(fileName);
152+
return createResponse(getComponentNames(ts, project.getLanguageService().getProgram()!, virtualCode));
153153
});
154154
session.addProtocolHandler('_vue:getComponentProps', request => {
155155
const [fileName, tag]: Parameters<Requests['getComponentProps']> = request.arguments;
156-
const { project } = getProject(fileName);
157-
return createResponse(getComponentProps(ts, project.getLanguageService().getProgram()!, fileName, tag));
156+
const { project, virtualCode } = getProjectAndVirtualCode(fileName);
157+
return createResponse(getComponentProps(ts, project.getLanguageService().getProgram()!, virtualCode, tag));
158158
});
159159
session.addProtocolHandler('_vue:getComponentSlots', request => {
160160
const [fileName]: Parameters<Requests['getComponentSlots']> = request.arguments;

packages/typescript-plugin/lib/requests/getComponentDirectives.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,13 @@ export function getComponentDirectives(
1616
program: ts.Program,
1717
fileName: string,
1818
): string[] {
19-
const directives = getVariableType(ts, program, fileName, names.directives);
19+
const sourceFile = program.getSourceFile(fileName);
20+
if (!sourceFile) {
21+
return [];
22+
}
23+
24+
const checker = program.getTypeChecker();
25+
const directives = getVariableType(ts, checker, sourceFile, names.directives);
2026
if (!directives) {
2127
return [];
2228
}

packages/typescript-plugin/lib/requests/getComponentEvents.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
1-
import { names } from '@vue/language-core';
1+
import type { VueVirtualCode } from '@vue/language-core';
22
import type * as ts from 'typescript';
3-
import { getComponentType, getVariableType } from './utils';
3+
import { getComponentType } from './utils';
44

55
export function getComponentEvents(
66
ts: typeof import('typescript'),
77
program: ts.Program,
8-
fileName: string,
8+
virtualCode: VueVirtualCode,
99
tag: string,
1010
): string[] {
11-
const checker = program.getTypeChecker();
12-
const components = getVariableType(ts, program, fileName, names.components);
13-
if (!components) {
11+
const sourceFile = program.getSourceFile(virtualCode.fileName);
12+
if (!sourceFile) {
1413
return [];
1514
}
1615

17-
const componentType = getComponentType(ts, program, fileName, components, tag);
16+
const checker = program.getTypeChecker();
17+
const componentType = getComponentType(ts, checker, sourceFile, virtualCode, tag);
1818
if (!componentType) {
1919
return [];
2020
}
@@ -28,15 +28,15 @@ export function getComponentEvents(
2828
// }
2929
// }
3030

31-
for (const sig of componentType.getConstructSignatures()) {
31+
for (const sig of componentType.type.getConstructSignatures()) {
3232
const instanceType = sig.getReturnType();
3333
const emitSymbol = instanceType.getProperty('$emit');
3434
if (emitSymbol) {
35-
const emitType = checker.getTypeOfSymbolAtLocation(emitSymbol, components.node);
35+
const emitType = checker.getTypeOfSymbolAtLocation(emitSymbol, componentType.node);
3636
for (const call of emitType.getCallSignatures()) {
3737
if (call.parameters.length) {
3838
const eventNameParamSymbol = call.parameters[0]!;
39-
const eventNameParamType = checker.getTypeOfSymbolAtLocation(eventNameParamSymbol, components.node);
39+
const eventNameParamType = checker.getTypeOfSymbolAtLocation(eventNameParamSymbol, componentType.node);
4040
if (eventNameParamType.isStringLiteral()) {
4141
result.add(eventNameParamType.value);
4242
}
Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,27 @@
1-
import { names } from '@vue/language-core';
1+
import { names, tsCodegen, type VueVirtualCode } from '@vue/language-core';
22
import type * as ts from 'typescript';
33
import { getSelfComponentName, getVariableType } from './utils';
44

55
export function getComponentNames(
66
ts: typeof import('typescript'),
77
program: ts.Program,
8-
fileName: string,
8+
{ fileName, sfc }: VueVirtualCode,
99
): string[] {
10-
const componentNames = getVariableType(ts, program, fileName, names.components)
10+
const sourceFile = program.getSourceFile(fileName);
11+
if (!sourceFile) {
12+
return [];
13+
}
14+
15+
const checker = program.getTypeChecker();
16+
const componentNames = getVariableType(ts, checker, sourceFile, names.components)
1117
?.type
1218
.getProperties()
1319
.map(c => c.name)
1420
.filter(entry => !entry.includes('$') && !entry.startsWith('_'))
1521
?? [];
22+
1623
componentNames.push(getSelfComponentName(fileName));
17-
return componentNames;
24+
componentNames.push(...tsCodegen.get(sfc)?.getImportComponentNames() ?? []);
25+
26+
return [...new Set(componentNames)];
1827
}

packages/typescript-plugin/lib/requests/getComponentProps.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { names } from '@vue/language-core';
1+
import type { VueVirtualCode } from '@vue/language-core';
22
import type * as ts from 'typescript';
3-
import { getComponentType, getVariableType } from './utils';
3+
import { getComponentType } from './utils';
44

55
export interface ComponentPropInfo {
66
name: string;
@@ -14,38 +14,38 @@ export interface ComponentPropInfo {
1414
export function getComponentProps(
1515
ts: typeof import('typescript'),
1616
program: ts.Program,
17-
fileName: string,
17+
virtualCode: VueVirtualCode,
1818
tag: string,
1919
): ComponentPropInfo[] {
20-
const components = getVariableType(ts, program, fileName, names.components);
21-
if (!components) {
20+
const sourceFile = program.getSourceFile(virtualCode.fileName);
21+
if (!sourceFile) {
2222
return [];
2323
}
2424

25-
const componentType = getComponentType(ts, program, fileName, components, tag);
25+
const checker = program.getTypeChecker();
26+
const componentType = getComponentType(ts, checker, sourceFile, virtualCode, tag);
2627
if (!componentType) {
2728
return [];
2829
}
2930

3031
const result = new Map<string, ComponentPropInfo>();
31-
const checker = program.getTypeChecker();
3232

33-
for (const sig of componentType.getCallSignatures()) {
33+
for (const sig of componentType.type.getCallSignatures()) {
3434
if (sig.parameters.length) {
3535
const propParam = sig.parameters[0]!;
36-
const propsType = checker.getTypeOfSymbolAtLocation(propParam, components.node);
36+
const propsType = checker.getTypeOfSymbolAtLocation(propParam, componentType.node);
3737
const props = propsType.getProperties();
3838
for (const prop of props) {
3939
handlePropSymbol(prop);
4040
}
4141
}
4242
}
4343

44-
for (const sig of componentType.getConstructSignatures()) {
44+
for (const sig of componentType.type.getConstructSignatures()) {
4545
const instanceType = sig.getReturnType();
4646
const propsSymbol = instanceType.getProperty('$props');
4747
if (propsSymbol) {
48-
const propsType = checker.getTypeOfSymbolAtLocation(propsSymbol, components.node);
48+
const propsType = checker.getTypeOfSymbolAtLocation(propsSymbol, componentType.node);
4949
const props = propsType.getProperties();
5050
for (const prop of props) {
5151
handlePropSymbol(prop);

packages/typescript-plugin/lib/requests/getComponentSlots.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,14 @@ export function getComponentSlots(
1212
return [];
1313
}
1414

15+
const sourceFile = program.getSourceFile(virtualCode.fileName);
16+
if (!sourceFile) {
17+
return [];
18+
}
19+
20+
const checker = program.getTypeChecker();
1521
const assignName = codegen.getSetupSlotsAssignName() ?? names.slots;
16-
const slots = getVariableType(ts, program, virtualCode.fileName, assignName);
22+
const slots = getVariableType(ts, checker, sourceFile, assignName);
1723
if (!slots) {
1824
return [];
1925
}

0 commit comments

Comments
 (0)