Skip to content

Commit

Permalink
fix: extract incorrect emit types edge cases
Browse files Browse the repository at this point in the history
close #567
  • Loading branch information
johnsoncodehk committed Oct 8, 2021
1 parent 9e9879a commit 084269f
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 37 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -3,6 +3,7 @@
## Not yet released

- fix: don't report error `Its return type 'xxx' is not a valid JSX element.` to invalid functional component type ([#574](https://github.com/johnsoncodehk/volar/issues/574))
- fix: improve `$emit` types extract for events type-checking ([#567](https://github.com/johnsoncodehk/volar/issues/567))

## 0.28.0

Expand Down
4 changes: 4 additions & 0 deletions packages/server/src/features/customFeatures.ts
Expand Up @@ -32,6 +32,10 @@ export function register(
for (const fileName of localTypes.fileNames) {
fs.writeFile(fileName, localTypes.code, () => { });
}
const localVues = ls.__internal__.getLocalVueFiles();
for (const fileName of localVues.fileNames) {
fs.writeFile(fileName, localVues.code, () => { });
}
const { sourceFiles } = await ls.__internal__.getContext();
for (const [_, doc] of sourceFiles.getTsDocuments(lsType)) {
fs.writeFile(shared.uriToFsPath(doc.uri), doc.getText(), () => { });
Expand Down
4 changes: 2 additions & 2 deletions packages/vscode-vue-languageservice/src/generators/script.ts
Expand Up @@ -210,15 +210,15 @@ export function generate(
function writeExportComponent() {
if (shouldAddExportDefault) {
const start = codeGen.getText().length;
codeGen.addText(`export default (await import('./__VLS_types')).defineComponent({\n`);
codeGen.addText(`export default (await import('./__VLS_vue')).defineComponent({\n`);
overlapMapRanges.push({
start,
end: codeGen.getText().length,
});
}
else {
codeGen.addText(`\n`);
codeGen.addText(`export const __VLS_component = (await import('./__VLS_types')).defineComponent({\n`);
codeGen.addText(`export const __VLS_component = (await import('./__VLS_vue')).defineComponent({\n`);
}
if (script && scriptRanges?.exportDefault?.args) {
const args = scriptRanges.exportDefault.args;
Expand Down
32 changes: 25 additions & 7 deletions packages/vscode-vue-languageservice/src/languageService.ts
Expand Up @@ -154,8 +154,8 @@ export function createLanguageService(
const scriptTsLsRaw = vueHost.createTsLanguageService ? vueHost.createTsLanguageService(scriptTsHost) : ts.createLanguageService(scriptTsHost);
const templateTsLs = ts2.createLanguageService(ts, templateTsHost, templateTsLsRaw);
const scriptTsLs = ts2.createLanguageService(ts, scriptTsHost, scriptTsLsRaw);
const localTypesScript = ts.ScriptSnapshot.fromString(localTypes.code);
const localTypesScriptName = '__VLS_types.ts';
const localTypesScript = ts.ScriptSnapshot.fromString(localTypes.typesCode);
const localVueScript = ts.ScriptSnapshot.fromString(localTypes.vueCode);
const compilerHost = ts.createCompilerHost(vueHost.getCompilationSettings());
const documentContext: html.DocumentContext = {
resolveReference(ref: string, base: string) {
Expand Down Expand Up @@ -278,7 +278,15 @@ export function createLanguageService(
},
getLocalTypesFiles: () => {
const fileNames = getLocalTypesFiles();
const code = localTypes.code;
const code = localTypes.typesCode;
return {
fileNames,
code,
};
},
getLocalVueFiles: () => {
const fileNames = getLocalVueFiles();
const code = localTypes.vueCode;
return {
fileNames,
code,
Expand All @@ -293,7 +301,10 @@ export function createLanguageService(
};

function getLocalTypesFiles() {
return sourceFiles.getDirs().map(dir => upath.join(dir, localTypesScriptName));
return sourceFiles.getDirs().map(dir => upath.join(dir, localTypes.typesFileName));
}
function getLocalVueFiles() {
return sourceFiles.getDirs().map(dir => upath.join(dir, localTypes.vueFileName));
}
function createTsPluginProxy() {

Expand Down Expand Up @@ -549,7 +560,10 @@ export function createLanguageService(
return tsHost;

function getScriptFileNames() {
const tsFileNames = getLocalTypesFiles();
const tsFileNames = [
...getLocalTypesFiles(),
...getLocalVueFiles(),
];

for (const [tsUri] of sourceFiles.getTsDocuments(lsType)) {
tsFileNames.push(shared.uriToFsPath(tsUri)); // virtual .ts
Expand All @@ -566,7 +580,8 @@ export function createLanguageService(
}
function getScriptVersion(fileName: string) {
const uri = shared.fsPathToUri(fileName);
if (upath.basename(fileName) === localTypesScriptName) {
const basename = upath.basename(fileName);
if (basename === localTypes.typesFileName || basename === localTypes.vueFileName) {
return '0';
}
let doc = sourceFiles.getTsDocuments(lsType).get(uri);
Expand All @@ -582,9 +597,12 @@ export function createLanguageService(
return cache[1];
}
const basename = upath.basename(fileName);
if (basename === localTypesScriptName) {
if (basename === localTypes.typesFileName) {
return localTypesScript;
}
if (basename === localTypes.vueFileName) {
return localVueScript;
}
const uri = shared.fsPathToUri(fileName);
const doc = sourceFiles.getTsDocuments(lsType).get(uri);
if (doc) {
Expand Down
5 changes: 3 additions & 2 deletions packages/vscode-vue-languageservice/src/sourceFiles.ts
Expand Up @@ -6,6 +6,7 @@ import type { CssSourceMap, HtmlSourceMap, TeleportSourceMap, TsSourceMap } from
import { untrack } from './utils/untrack';
import * as shared from '@volar/shared';
import * as path from 'upath';
import * as localTypes from './utils/localTypes';

export type SourceFiles = ReturnType<typeof createSourceFiles>;

Expand Down Expand Up @@ -178,7 +179,7 @@ export function createSourceFiles() {
}),
fromTsLocation: untrack(function* (lsType: 'script' | 'template', uri: string, start: vscode.Position, end?: vscode.Position) {

if (uri.endsWith('/__VLS_types.ts') || uri.endsWith('/__VLS_globals.ts'))
if (uri.endsWith(`/${localTypes.typesFileName}`) || uri.endsWith(`/${localTypes.vueFileName}`))
return;

if (end === undefined)
Expand Down Expand Up @@ -208,7 +209,7 @@ export function createSourceFiles() {
}),
fromTsLocation2: untrack(function* (lsType: 'script' | 'template', uri: string, start: number, end?: number) {

if (uri.endsWith('/__VLS_types.ts') || uri.endsWith('/__VLS_globals.ts'))
if (uri.endsWith(`/${localTypes.typesFileName}`) || uri.endsWith(`/${localTypes.vueFileName}`))
return;

if (end === undefined)
Expand Down
Expand Up @@ -20,6 +20,7 @@ export function useSfcEntryForTemplateLs(
const tsScriptFileName = hasTsDoc.value ? '__VLS_script_ts' : '__VLS_script';
let content = '';
content += '// @ts-nocheck\n';
content += `import * as __VLS_vue from './__VLS_types';\n`;
content += `import * as __VLS_types from './__VLS_types';\n`;
if (scriptSetup.value || script.value) {
content += `import { __VLS_options as __VLS_options_ts, __VLS_name as __VLS_name_ts } from './${vueFileName}.${tsScriptFileName}';\n`;
Expand All @@ -44,8 +45,8 @@ export function useSfcEntryForTemplateLs(
else {
content += `export var __VLS_options = {};\n`;
content += `export var __VLS_name = undefined;\n`;
content += `var __VLS_component_ts = __VLS_types.defineComponent({});\n`;
content += `export var __VLS_component = __VLS_types.defineComponent({});\n`;
content += `var __VLS_component_ts = __VLS_vue.defineComponent({});\n`;
content += `export var __VLS_component = __VLS_vue.defineComponent({});\n`;
}
content += `declare var __VLS_ctx: __VLS_types.ComponentContext<typeof __VLS_component_ts>;\n`;
content += `declare var __VLS_ComponentsWrap: typeof __VLS_options & { components: { } };\n`;
Expand Down
49 changes: 25 additions & 24 deletions packages/vscode-vue-languageservice/src/utils/localTypes.ts
Expand Up @@ -6,9 +6,29 @@ const camelCaseText = [
': S',
].join('\n');

export const code = `
import type * as vue_1 from '@vue/runtime-dom';
import type * as vue_2 from 'vue';
export const vueFileName = '__VLS_vue.ts';
export const typesFileName = '__VLS_types.ts';

export const vueCode = `
export * from '@vue/runtime-dom';
export * from 'vue';
`.trim();

export const typesCode = `
import * as vue from './__VLS_vue';
import type {
FunctionalComponent,
HTMLAttributes,
VNodeProps,
AllowedComponentProps,
PropType,
EmitsOptions,
DefineComponent,
SetupContext,
ObjectDirective,
FunctionDirective,
GlobalComponents as GlobalComponents_0,
} from './__VLS_vue';
type IsAny<T> = boolean extends (T extends never ? true : false) ? true : false;
type IsFunctionalComponent<T> = T extends (...args: any) => JSX.Element ? true : false;
Expand All @@ -20,30 +40,14 @@ type AnyArray<T = any> = T[] | readonly T[];
type NonUndefinedable<T> = T extends undefined ? never : T;
${camelCaseText};
type FunctionalComponent<P = {}, E extends EmitsOptions = {}> = PickNotAny<vue_1.FunctionalComponent<P, E>, vue_2.FunctionalComponent<P, E>>;
type HTMLAttributes = PickNotAny<vue_1.HTMLAttributes, vue_2.HTMLAttributes>;
type VNodeProps = PickNotAny<vue_1.VNodeProps, vue_2.VNodeProps>;
type AllowedComponentProps = PickNotAny<vue_1.AllowedComponentProps, vue_2.AllowedComponentProps>;
type PropType<T> = PickNotAny<vue_1.PropType<T>, vue_2.PropType<T>>;
type EmitsOptions = PickNotAny<vue_1.EmitsOptions, vue_2.EmitsOptions>;
type DefineComponent_1<P, E extends EmitsOptions> = PickNotAny<vue_1.DefineComponent<P, any, any, any, any, any, any, E>, vue_2.DefineComponent<P, any, any, any, any, any, any, E>>;
type DefineComponent_2<P, E extends EmitsOptions> = DefineComponent_1<P, E>; // fix check extends failed if have no defineProps
type DefineComponent_3<P, E extends EmitsOptions> = PickNotAny<vue_1.DefineComponent<P, any, any, any, any, any, any, E, any, any, any, any>, vue_2.DefineComponent<P, any, any, any, any, any, any, E, any, any, any, any>>; // fix https://github.com/johnsoncodehk/volar/issues/495
type DefineComponent_4<P, E extends EmitsOptions> = DefineComponent_3<P, E>;
type GlobalComponents_0 = PickNotAny<PickNotAny<vue_1.GlobalComponents, vue_2.GlobalComponents>, {}>;
type SetupContext<T> = PickNotAny<vue_1.SetupContext<T>, vue_2.SetupContext<T>>;
type ObjectDirective<T, V> = PickNotAny<vue_1.ObjectDirective<T, V>, vue_2.ObjectDirective<T, V>>;
type FunctionDirective<T, V> = PickNotAny<vue_1.FunctionDirective<T, V>, vue_2.FunctionDirective<T, V>>;
export type GlobalComponents = GlobalComponents_0 & Pick<PickNotAny<typeof vue_1, typeof vue_2>,
export type GlobalComponents = PickNotAny<GlobalComponents_0, {}> & Pick<typeof vue,
'Transition'
| 'TransitionGroup'
| 'KeepAlive'
| 'Suspense'
| 'Teleport'
>;
export var defineComponent: PickNotAny<typeof vue_1.defineComponent, typeof vue_2.defineComponent>;
export declare function getVforSourceType<T>(source: T): T extends number ? number[] : T;
export declare function getVforKeyType<T>(source: T): typeof Symbol.iterator extends keyof T ? number : T extends T ? keyof T : never; // use "T extends T" support for union
export declare function getVforIndexType<T>(source: T): typeof Symbol.iterator extends keyof T ? undefined : number;
Expand Down Expand Up @@ -115,10 +119,7 @@ export type EmitEvent2<F, E> =
} ? (...payload: P) => R
: unknown | '[Type Warning] Volar cloud not infer $emit event more than 4 overloads without DefineComponent. see https://github.com/johnsoncodehk/volar/issues/60';
export type EmitEvent<T, E> =
T extends DefineComponent_1<infer _, infer E2> ? EmitEvent_3<E2, E>
: T extends DefineComponent_2<infer _, infer E2> ? EmitEvent_3<E2, E>
: T extends DefineComponent_3<infer _, infer E2> ? EmitEvent_3<E2, E>
: T extends DefineComponent_4<infer _, infer E2> ? EmitEvent_3<E2, E>
T extends DefineComponent<infer _, any, any, any, any, any, any, infer E2> ? EmitEvent_3<E2, E>
: T extends FunctionalComponent<infer _, infer E2> ? EmitEvent_3<E2, E>
: EmitEvent2<ExtractEmit2<T>, E>;
export type EmitEvent_3<E2, E> =
Expand Down

0 comments on commit 084269f

Please sign in to comment.