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

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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 @@ -40,10 +40,18 @@ export function* generateComponent(
const { args } = options.scriptRanges.exportDefault;
yield generateSfcBlockSection(options.sfc.script, args.start + 1, args.end - 1, codeFeatures.all);
}
if (options.vueCompilerOptions.target >= 3.5 && options.templateCodegen?.templateRefs.size) {
if (
options.vueCompilerOptions.target >= 3.5
&& options.vueCompilerOptions.typedDollarRefs.expose
&& 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.typedDollarEl.expose
&& options.templateCodegen?.singleRootElType
) {
yield `__typeEl: {} as __VLS_TemplateEl,${newLine}`;
}
yield `})`;
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 @@ -170,16 +170,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.typedDollarAttrs.self) {
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 @@ -209,16 +211,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.typedDollarSlots.self) {
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 @@ -103,7 +103,7 @@ export function createTemplateCodegenContext(options: Pick<TemplateCodegenOption
},
});
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 @@ -132,7 +132,7 @@ export function createTemplateCodegenContext(options: Pick<TemplateCodegenOption
slots,
dynamicSlots,
codeFeatures,
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 @@ -35,11 +35,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.typedDollarSlots.self) {
ctx.dollarVars.add(slotsPropertyName);
}
if (options.vueCompilerOptions.typedDollarAttrs.self) {
ctx.dollarVars.add('$attrs');
}
if (options.vueCompilerOptions.typedDollarRefs.self) {
ctx.dollarVars.add('$refs');
}
if (options.vueCompilerOptions.typedDollarEl.self) {
ctx.dollarVars.add('$el');
}

if (options.template.ast) {
yield* generateTemplateChild(options, ctx, options.template.ast, undefined);
Expand All @@ -53,7 +62,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, nextVar);
yield* generateVar(code, ctx.dollarVars, destructuredPropNames, templateRefNames, curVar, nextVar);

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 @@ -176,8 +176,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
14 changes: 14 additions & 0 deletions packages/language-core/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,20 @@ export interface VueCompilerOptions {
useSlots: string[];
useTemplateRef: string[];
};
typedDollarAttrs: {
self: boolean;
},
typedDollarEl: {
self: boolean;
expose: boolean;
},
typedDollarRefs: {
self: boolean;
expose: boolean;
},
typedDollarSlots: {
self: boolean;
},
plugins: VueLanguagePlugin[];

// experimental
Expand Down
43 changes: 43 additions & 0 deletions packages/language-core/lib/utils/ts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,20 @@ function getDefaultOptions(options: Partial<VueCompilerOptions>): VueCompilerOpt
useSlots: ['useSlots'],
useTemplateRef: ['useTemplateRef', 'templateRef'],
},
typedDollarAttrs: {
self: true
},
typedDollarEl: {
self: true,
expose: false
},
typedDollarRefs: {
self: true,
expose: false
},
typedDollarSlots: {
self: true
},
plugins: [],
experimentalDefinePropProposal: false,
experimentalResolveStyleCssClasses: 'scoped',
Expand All @@ -275,6 +289,22 @@ export function resolveVueCompilerOptions(
...defaults.composables,
...options.composables,
},
typedDollarAttrs: mergeTypedDollar(
options.typedDollarAttrs,
defaults.typedDollarAttrs
),
typedDollarEl: mergeTypedDollar(
options.typedDollarEl,
defaults.typedDollarEl
),
typedDollarRefs: mergeTypedDollar(
options.typedDollarRefs,
defaults.typedDollarRefs
),
typedDollarSlots: mergeTypedDollar(
options.typedDollarSlots,
defaults.typedDollarSlots
),

// https://github.com/vuejs/vue-next/blob/master/packages/compiler-dom/src/transforms/vModel.ts#L49-L51
// https://vuejs.org/guide/essentials/forms.html#form-input-bindings
Expand Down Expand Up @@ -315,3 +345,16 @@ export function setupGlobalTypes(rootDir: string, vueOptions: VueCompilerOptions
return { absolutePath: globalTypesPath };
} catch { }
}

function mergeTypedDollar<T>(
value: T | undefined,
defaults: { self: boolean; expose?: boolean; }
) {
return (typeof value === "boolean" ? {
self: value,
expose: value
} : {
...defaults,
...value
}) as T;
}
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,11 +65,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 @@ -99,6 +94,41 @@
"useTemplateRef": [ "useTemplateRef", "templateRef" ]
}
},
"typedDollarAttrs": {
"type": ["boolean", "object"],
"default": {
"self": true
},
"markdownDescription": "\"self\" to control the type inference of `$attrs` in the template and the return type of `useAttrs`."
},
"typedDollarEl": {
"type": ["boolean", "object"],
"default": {
"self": true,
"expose": false
},
"markdownDescription": "\"self\" to control the type inference of `$el` in the template, \"expose\" to control the type inference of `$el` on the component instance."
},
"typedDollarRefs": {
"type": ["boolean", "object"],
"default": {
"self": true,
"expose": false
},
"markdownDescription": "\"self\" to control the type inference of `$refs` in the template, \"expose\" to control the type inference of `$refs` on the component instance."
},
"typedDollarSlots": {
"type": ["boolean", "object"],
"default": {
"self": true
},
"markdownDescription": "\"self\" to control the type inference of `$slots` in the template and the return type of `useSlots`."
},
"plugins": {
"type": "array",
"default": [ ],
"markdownDescription": "Plugins to be used in the SFC compiler."
},
"experimentalResolveStyleCssClasses": {
"enum": [
"scoped",
Expand Down
2 changes: 2 additions & 0 deletions test-workspace/tsc/passedFixtures/vue3/rootEl/base.vue
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
<!-- @typedDollarEl true -->

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

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

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