Skip to content

Commit

Permalink
feat(language-core): type support of useSlots and $slots (vuejs#5055
Browse files Browse the repository at this point in the history
)
  • Loading branch information
KazariEX authored Dec 16, 2024
1 parent eee3309 commit 443ddf1
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 43 deletions.
13 changes: 12 additions & 1 deletion packages/language-core/lib/codegen/script/scriptSetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ function* generateSetupFunction(
[` as __VLS_TemplateResult['attrs'] & Record<string, unknown>)`],
callExp.end,
callExp.end
])
]);
}
for (const { callExp, exp, arg } of scriptSetupRanges.useCssModule) {
setupCodeModifies.push([
Expand All @@ -234,6 +234,17 @@ function* generateSetupFunction(
callExp.end
]);
}
for (const { callExp } of scriptSetupRanges.useSlots) {
setupCodeModifies.push([
[`(`],
callExp.start,
callExp.start
], [
[` as __VLS_TemplateResult['slots'])`],
callExp.end,
callExp.end
]);
}
const isTs = options.lang !== 'js' && options.lang !== 'jsx';
for (const { callExp, exp, arg } of scriptSetupRanges.useTemplateRef) {
const templateRefType = arg ? [
Expand Down
2 changes: 1 addition & 1 deletion packages/language-core/lib/codegen/script/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ function* generateTemplateComponents(options: ScriptCodegenOptions): Generator<C
exps.push(
`{} as { [K in ${nameType}]: typeof __VLS_self & (new () => { `
+ getSlotsPropertyName(options.vueCompilerOptions.target)
+ ` : typeof ${options.scriptSetupRanges?.defineSlots?.name ?? `__VLS_slots`} }) }`
+ `: typeof ${options.scriptSetupRanges?.defineSlots?.name ?? `__VLS_slots`} }) }`
);
}

Expand Down
73 changes: 37 additions & 36 deletions packages/language-core/lib/codegen/template/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as CompilerDOM from '@vue/compiler-dom';
import type * as ts from 'typescript';
import type { Code, Sfc, VueCompilerOptions } from '../../types';
import { getSlotsPropertyName } from '../../utils/shared';
import { endOfLine, newLine, wrapWith } from '../utils';
import { generateStringLiteralKey } from '../utils/stringLiteralKey';
import { TemplateCodegenContext, createTemplateCodegenContext } from './context';
Expand Down Expand Up @@ -34,9 +35,10 @@ export function* generateTemplate(options: TemplateCodegenOptions): Generator<Co
if (options.propsAssignName) {
ctx.addLocalVariable(options.propsAssignName);
}
ctx.addLocalVariable("$attrs");
ctx.addLocalVariable('$el');
ctx.addLocalVariable('$attrs');
ctx.addLocalVariable(getSlotsPropertyName(options.vueCompilerOptions.target));
ctx.addLocalVariable('$refs');
ctx.addLocalVariable('$el');

yield* generatePreResolveComponents(options);

Expand All @@ -45,50 +47,49 @@ export function* generateTemplate(options: TemplateCodegenOptions): Generator<Co
}

yield* generateStyleScopedClasses(ctx);

if (!options.hasDefineSlots) {
yield `var __VLS_slots!:`;
yield* generateSlotsType(options, ctx);
yield endOfLine;
}

yield* ctx.generateAutoImportCompletion();
yield* generateSlots(options, ctx);
yield* generateInheritedAttrs(ctx);
yield* generateRefs(ctx);
yield* generateRootEl(ctx);

yield* ctx.generateAutoImportCompletion();
return ctx;
}

function* generateSlotsType(options: TemplateCodegenOptions, ctx: TemplateCodegenContext): Generator<Code> {
for (const { expVar, varName } of ctx.dynamicSlots) {
ctx.hasSlot = true;
yield `Partial<Record<NonNullable<typeof ${expVar}>, (_: typeof ${varName}) => any>> &${newLine}`;
}
yield `{${newLine}`;
for (const slot of ctx.slots) {
ctx.hasSlot = true;
if (slot.name && slot.loc !== undefined) {
yield* generateObjectProperty(
options,
ctx,
slot.name,
slot.loc,
ctx.codeFeatures.withoutHighlightAndCompletion,
slot.nodeLoc
);
function* generateSlots(options: TemplateCodegenOptions, ctx: TemplateCodegenContext): Generator<Code> {
if (!options.hasDefineSlots) {
yield `var __VLS_slots!: `;
for (const { expVar, varName } of ctx.dynamicSlots) {
ctx.hasSlot = true;
yield `Partial<Record<NonNullable<typeof ${expVar}>, (_: typeof ${varName}) => any>> &${newLine}`;
}
else {
yield* wrapWith(
slot.tagRange[0],
slot.tagRange[1],
ctx.codeFeatures.withoutHighlightAndCompletion,
`default`
);
yield `{${newLine}`;
for (const slot of ctx.slots) {
ctx.hasSlot = true;
if (slot.name && slot.loc !== undefined) {
yield* generateObjectProperty(
options,
ctx,
slot.name,
slot.loc,
ctx.codeFeatures.withoutHighlightAndCompletion,
slot.nodeLoc
);
}
else {
yield* wrapWith(
slot.tagRange[0],
slot.tagRange[1],
ctx.codeFeatures.withoutHighlightAndCompletion,
`default`
);
}
yield `?(_: typeof ${slot.varName}): any,${newLine}`;
}
yield `?(_: typeof ${slot.varName}): any,${newLine}`;
yield `}${endOfLine}`;
}
yield `}`;
const name = getSlotsPropertyName(options.vueCompilerOptions.target);
yield `var ${name}!: typeof ${options.slotsAssignName ?? '__VLS_slots'}${endOfLine}`;
}

function* generateInheritedAttrs(ctx: TemplateCodegenContext): Generator<Code> {
Expand Down
7 changes: 7 additions & 0 deletions packages/language-core/lib/parsers/scriptSetupRanges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ type UseAttrs = CallExpressionRange;

type UseCssModule = CallExpressionRange;

type UseSlots = CallExpressionRange;

type UseTemplateRef = CallExpressionRange & {
name?: string;
}
Expand All @@ -74,6 +76,7 @@ export function parseScriptSetupRanges(
let defineOptions: DefineOptions | undefined;
const useAttrs: UseAttrs[] = [];
const useCssModule: UseCssModule[] = [];
const useSlots: UseSlots[] = [];
const useTemplateRef: UseTemplateRef[] = [];
const definePropProposalA = vueCompilerOptions.experimentalDefinePropProposal === 'kevinEdition' || ast.text.trimStart().startsWith('// @experimentalDefinePropProposal=kevinEdition');
const definePropProposalB = vueCompilerOptions.experimentalDefinePropProposal === 'johnsonEdition' || ast.text.trimStart().startsWith('// @experimentalDefinePropProposal=johnsonEdition');
Expand Down Expand Up @@ -145,6 +148,7 @@ export function parseScriptSetupRanges(
defineOptions,
useAttrs,
useCssModule,
useSlots,
useTemplateRef,
};

Expand Down Expand Up @@ -390,6 +394,9 @@ export function parseScriptSetupRanges(
else if (vueCompilerOptions.composables.useCssModule.includes(callText)) {
useCssModule.push(parseCallExpression(node));
}
else if (vueCompilerOptions.composables.useSlots.includes(callText)) {
useSlots.push(parseCallExpression(node));
}
else if (
vueCompilerOptions.composables.useTemplateRef.includes(callText)
&& !node.typeArguments?.length
Expand Down
1 change: 1 addition & 0 deletions packages/language-core/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export interface VueCompilerOptions {
composables: {
useAttrs: string[];
useCssModule: string[];
useSlots: string[];
useTemplateRef: string[];
};
plugins: VueLanguagePlugin[];
Expand Down
1 change: 1 addition & 0 deletions packages/language-core/lib/utils/ts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ export function resolveVueCompilerOptions(vueOptions: Partial<VueCompilerOptions
composables: {
useAttrs: ['useAttrs'],
useCssModule: ['useCssModule'],
useSlots: ['useSlots'],
useTemplateRef: ['useTemplateRef', 'templateRef'],
...vueOptions.composables,
},
Expand Down
16 changes: 13 additions & 3 deletions packages/language-core/schemas/vue-tsconfig.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,21 @@
"type": "object",
"default": {
"defineProps": [ "defineProps" ],
"defineSlots": [ "defineSlots" ],
"defineModel": [ "defineModel" ],
"defineEmits": [ "defineEmits" ],
"defineSlots": [ "defineSlots" ],
"defineExpose": [ "defineExpose" ],
"withDefaults": [ "withDefaults" ],
"templateRef": [ "templateRef", "useTemplateRef" ]
"defineOptions": [ "defineOptions" ],
"withDefaults": [ "withDefaults" ]
}
},
"composables": {
"type": "object",
"default": {
"useAttrs": [ "useAttrs" ],
"useCssModule": [ "useCssModule" ],
"useSlots": [ "useSlots" ],
"useTemplateRef": [ "useTemplateRef", "templateRef" ]
}
},
"experimentalResolveStyleCssClasses": {
Expand Down
10 changes: 8 additions & 2 deletions test-workspace/tsc/passedFixtures/vue3/slots/main.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
{{ exactType(num, {} as number) }}
</template>
</Self>

<!-- typed slot key -->
<slot :name="baz" str="str" :num="1"></slot>
<Self>
Expand All @@ -38,8 +37,15 @@ declare const Comp: new <T>(props: { value: T; }) => {
</script>

<script lang="ts" setup>
import { ref, VNode } from 'vue';
import { ref, useSlots, VNode } from 'vue';
import { exactType } from '../../shared';
const baz = ref('baz' as const);
const slots = useSlots();
exactType(slots, {} as {
bar?(_: { str: string; num: number; }): any;
} & {
baz?(_: { str: string; num: number; }): any;
});
</script>

0 comments on commit 443ddf1

Please sign in to comment.