Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(language-core): introduce options to control type inference of $attrs, $el, $refs and $slots #5135

Merged
merged 16 commits into from
Feb 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions packages/language-core/lib/codegen/script/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,18 @@ export function* generateComponent(
}
yield* generatePropsOption(options, ctx, scriptSetup, scriptSetupRanges, !!emitOptionCodes.length, true);
}
if (options.vueCompilerOptions.target >= 3.5 && options.templateCodegen?.templateRefs.size) {
if (
options.vueCompilerOptions.target >= 3.5
&& options.vueCompilerOptions.inferComponentDollarRefs
&& options.templateCodegen?.templateRefs.size
) {
yield `__typeRefs: {} as __VLS_TemplateRefs,${newLine}`;
}
if (options.vueCompilerOptions.target >= 3.5 && options.templateCodegen?.singleRootElType) {
if (
options.vueCompilerOptions.target >= 3.5
&& options.vueCompilerOptions.inferComponentDollarEl
&& options.templateCodegen?.singleRootElType
) {
yield `__typeEl: {} as __VLS_RootEl,${newLine}`;
}
if (options.sfc.script && options.scriptRanges?.exportDefault?.args) {
Expand Down
44 changes: 24 additions & 20 deletions packages/language-core/lib/codegen/script/scriptSetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,16 +171,18 @@ function* generateSetupFunction(
]);
}
}
for (const { callExp } of scriptSetupRanges.useAttrs) {
setupCodeModifies.push([
[`(`],
callExp.start,
callExp.start
], [
[` as typeof __VLS_special.$attrs)`],
callExp.end,
callExp.end
]);
if (options.vueCompilerOptions.inferTemplateDollarAttrs) {
for (const { callExp } of scriptSetupRanges.useAttrs) {
setupCodeModifies.push([
[`(`],
callExp.start,
callExp.start
], [
[` as typeof __VLS_dollars.$attrs)`],
callExp.end,
callExp.end
]);
}
}
for (const { callExp, exp, arg } of scriptSetupRanges.useCssModule) {
setupCodeModifies.push([
Expand Down Expand Up @@ -210,16 +212,18 @@ function* generateSetupFunction(
]);
}
}
for (const { callExp } of scriptSetupRanges.useSlots) {
setupCodeModifies.push([
[`(`],
callExp.start,
callExp.start
], [
[` as typeof __VLS_special.$slots)`],
callExp.end,
callExp.end
]);
if (options.vueCompilerOptions.inferTemplateDollarSlots) {
for (const { callExp } of scriptSetupRanges.useSlots) {
setupCodeModifies.push([
[`(`],
callExp.start,
callExp.start
], [
[` as typeof __VLS_dollars.$slots)`],
callExp.end,
callExp.end
]);
}
}
const isTs = options.lang !== 'js' && options.lang !== 'jsx';
for (const { callExp, exp, arg } of scriptSetupRanges.useTemplateRef) {
Expand Down
4 changes: 2 additions & 2 deletions packages/language-core/lib/codegen/template/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export function createTemplateCodegenContext(options: Pick<TemplateCodegenOption

const hoistVars = new Map<string, string>();
const localVars = new Map<string, number>();
const specialVars = new Set<string>();
const dollarVars = new Set<string>();
const accessExternalVariables = new Map<string, Set<number>>();
const slots: {
name: string;
Expand Down Expand Up @@ -83,7 +83,7 @@ export function createTemplateCodegenContext(options: Pick<TemplateCodegenOption
resolveCodeFeatures,
slots,
dynamicSlots,
specialVars,
dollarVars,
accessExternalVariables,
lastGenericComment,
blockConditions,
Expand Down
21 changes: 15 additions & 6 deletions packages/language-core/lib/codegen/template/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,20 @@ export function* generateTemplate(options: TemplateCodegenOptions): Generator<Co
if (options.propsAssignName) {
ctx.addLocalVariable(options.propsAssignName);
}

const slotsPropertyName = getSlotsPropertyName(options.vueCompilerOptions.target);
ctx.specialVars.add(slotsPropertyName);
ctx.specialVars.add('$attrs');
ctx.specialVars.add('$refs');
ctx.specialVars.add('$el');
if (options.vueCompilerOptions.inferTemplateDollarSlots) {
ctx.dollarVars.add(slotsPropertyName);
}
if (options.vueCompilerOptions.inferTemplateDollarAttrs) {
ctx.dollarVars.add('$attrs');
}
if (options.vueCompilerOptions.inferTemplateDollarRefs) {
ctx.dollarVars.add('$refs');
}
if (options.vueCompilerOptions.inferTemplateDollarEl) {
ctx.dollarVars.add('$el');
}

if (options.template.ast) {
yield* generateTemplateChild(options, ctx, options.template.ast, undefined);
Expand All @@ -55,7 +64,7 @@ export function* generateTemplate(options: TemplateCodegenOptions): Generator<Co
['$el', yield* generateRootEl(ctx)]
];

yield `var __VLS_special!: {${newLine}`;
yield `var __VLS_dollars!: {${newLine}`;
for (const [name, type] of speicalTypes) {
yield `${name}: ${type}${endOfLine}`;
}
Expand Down Expand Up @@ -114,7 +123,7 @@ function* generateInheritedAttrs(
if (ctx.bindingAttrLocs.length) {
yield `[`;
for (const loc of ctx.bindingAttrLocs) {
yield `__VLS_special.`;
yield `__VLS_dollars.`;
yield [
loc.source,
'template',
Expand Down
10 changes: 5 additions & 5 deletions packages/language-core/lib/codegen/template/interpolation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ function* forEachInterpolationSegment(
const curVar = ctxVars[i];
const nextVar = ctxVars[i + 1];

yield* generateVar(code, ctx.specialVars, destructuredPropNames, templateRefNames, curVar);
yield* generateVar(code, ctx.dollarVars, destructuredPropNames, templateRefNames, curVar);

if (nextVar.isShorthand) {
yield [code.slice(curVar.offset + curVar.text.length, nextVar.offset + nextVar.text.length), curVar.offset + curVar.text.length];
Expand All @@ -146,7 +146,7 @@ function* forEachInterpolationSegment(
}

const lastVar = ctxVars.at(-1)!;
yield* generateVar(code, ctx.specialVars, destructuredPropNames, templateRefNames, lastVar);
yield* generateVar(code, ctx.dollarVars, destructuredPropNames, templateRefNames, lastVar);
if (lastVar.offset + lastVar.text.length < code.length) {
yield [code.slice(lastVar.offset + lastVar.text.length), lastVar.offset + lastVar.text.length, 'endText'];
}
Expand All @@ -158,7 +158,7 @@ function* forEachInterpolationSegment(

function* generateVar(
code: string,
specialVars: Set<string>,
dollarVars: Set<string>,
destructuredPropNames: Set<string> | undefined,
templateRefNames: Set<string> | undefined,
curVar: CtxVar
Expand All @@ -175,8 +175,8 @@ function* generateVar(
yield [`)`, undefined];
}
else {
if (specialVars.has(curVar.text)) {
yield [`__VLS_special.`, undefined];
if (dollarVars.has(curVar.text)) {
yield [`__VLS_dollars.`, undefined];
}
else if (!isDestructuredProp) {
yield [`__VLS_ctx.`, undefined];
Expand Down
3 changes: 3 additions & 0 deletions packages/language-core/lib/plugins/file-md.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { parse } from '../utils/parseSfc';

const codeblockReg = /(`{3,})[\s\S]+?\1/g;
const inlineCodeblockReg = /`[^\n`]+?`/g;
const latexBlockReg = /(\${2,})[\s\S]+?\1/g;
const scriptSetupReg = /\\\<[\s\S]+?\>\n?/g;
const sfcBlockReg = /\<(script|style)\b[\s\S]*?\>([\s\S]*?)\<\/\1\>/g;
const angleBracketReg = /\<\S*\:\S*\>/g;
Expand Down Expand Up @@ -39,6 +40,8 @@ const plugin: VueLanguagePlugin = ({ vueCompilerOptions }) => {
.replace(codeblockReg, (match, quotes) => quotes + ' '.repeat(match.length - quotes.length * 2) + quotes)
// inline code block
.replace(inlineCodeblockReg, match => `\`${' '.repeat(match.length - 2)}\``)
// latex block
.replace(latexBlockReg, (match, quotes) => quotes + ' '.repeat(match.length - quotes.length * 2) + quotes)
// # \<script setup>
.replace(scriptSetupReg, match => ' '.repeat(match.length))
// <<< https://vitepress.dev/guide/markdown#import-code-snippets
Expand Down
6 changes: 6 additions & 0 deletions packages/language-core/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ export interface VueCompilerOptions {
checkUnknownEvents: boolean;
checkUnknownDirectives: boolean;
checkUnknownComponents: boolean;
inferComponentDollarEl: boolean;
inferComponentDollarRefs: boolean;
inferTemplateDollarAttrs: boolean;
inferTemplateDollarEl: boolean;
inferTemplateDollarRefs: boolean;
inferTemplateDollarSlots: boolean;
skipTemplateCodegen: boolean;
fallthroughAttributes: boolean;
dataAttributes: string[];
Expand Down
6 changes: 6 additions & 0 deletions packages/language-core/lib/utils/ts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,12 @@ export function getDefaultCompilerOptions(target = 99, lib = 'vue', strictTempla
checkUnknownEvents: strictTemplates,
checkUnknownDirectives: strictTemplates,
checkUnknownComponents: strictTemplates,
inferComponentDollarEl: false,
inferComponentDollarRefs: false,
inferTemplateDollarAttrs: false,
inferTemplateDollarEl: false,
inferTemplateDollarRefs: false,
inferTemplateDollarSlots: false,
skipTemplateCodegen: false,
fallthroughAttributes: false,
dataAttributes: [],
Expand Down
40 changes: 35 additions & 5 deletions packages/language-core/schemas/vue-tsconfig.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,36 @@
"default": false,
"markdownDescription": "Check unknown components. If not set, uses the 'strictTemplates' value."
},
"inferComponentDollarEl": {
"type": "boolean",
"default": false,
"markdownDescription": "Infer `$el` type on the component instance."
},
"inferComponentDollarRefs": {
"type": "boolean",
"default": false,
"markdownDescription": "Infer `$refs` type on the component instance."
},
"inferTemplateDollarAttrs": {
"type": "boolean",
"default": false,
"markdownDescription": "Infer `$attrs` type in the template and the return type of `useAttrs`."
},
"inferTemplateDollarEl": {
"type": "boolean",
"default": false,
"markdownDescription": "Infer `$el` type in the template."
},
"inferTemplateDollarRefs": {
"type": "boolean",
"default": false,
"markdownDescription": "Infer `$refs` type in the template."
},
"inferTemplateDollarSlots": {
"type": "boolean",
"default": false,
"markdownDescription": "Infer `$slots` type in the template and the return type of `useSlots`."
},
"skipTemplateCodegen": {
"type": "boolean",
"default": false,
Expand All @@ -85,11 +115,6 @@
"default": [ "aria-*" ],
"markdownDescription": "A glob matcher array that should always be recognizing as HTML Attributes rather than Component props. Attribute name will never convert to camelize case."
},
"plugins": {
"type": "array",
"default": [ ],
"markdownDescription": "Plugins to be used in the SFC compiler."
},
"optionsWrapper": {
"type": "array",
"default": [
Expand Down Expand Up @@ -119,6 +144,11 @@
"useTemplateRef": [ "useTemplateRef", "templateRef" ]
}
},
"plugins": {
"type": "array",
"default": [ ],
"markdownDescription": "Plugins to be used in the SFC compiler."
},
"experimentalResolveStyleCssClasses": {
"enum": [
"scoped",
Expand Down
2 changes: 1 addition & 1 deletion packages/tsc/tests/__snapshots__/dts.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ exports[`vue-tsc-dts > Input: reference-type-props/component-destructure.vue, Ou
"type __VLS_Props = {
text: string;
};
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, HTMLDivElement>;
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
export default _default;
"
`;
Expand Down
3 changes: 3 additions & 0 deletions test-workspace/tsc/passedFixtures/vue3/rootEl/base.vue
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
<!-- @inferComponentDollarEl true -->
<!-- @inferTemplateDollarEl true -->

<script setup lang="ts">
import { exactType } from '../../shared';
</script>
Expand Down
3 changes: 3 additions & 0 deletions test-workspace/tsc/passedFixtures/vue3/rootEl/child.vue
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
<!-- @inferComponentDollarEl true -->
<!-- @inferTemplateDollarEl true -->

<script setup lang="ts">
import { exactType } from '../../shared';
import Base from './base.vue';
Expand Down
2 changes: 2 additions & 0 deletions test-workspace/tsc/passedFixtures/vue3/rootEl/main.vue
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
<!-- @inferTemplateDollarEl true -->

<script setup lang="ts">
import { exactType } from '../../shared';
import Child from './child.vue';
Expand Down
2 changes: 2 additions & 0 deletions test-workspace/tsc/passedFixtures/vue3/slots/main.vue
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
<!-- @inferTemplateDollarSlots true -->

<template>
<!-- component slots type -->
<Comp value="1">
Expand Down
2 changes: 2 additions & 0 deletions test-workspace/tsc/passedFixtures/vue3/templateRef/main.vue
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
<!-- @inferTemplateDollarRefs true -->

<script setup lang="ts">
import { useTemplateRef } from 'vue';
import { exactType } from '../../shared';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
<!-- @inferComponentDollarRefs true -->

<script setup lang="ts">
import { useTemplateRef } from 'vue';
import { exactType } from '../../shared';
Expand Down