Skip to content

Commit 74bd7de

Browse files
authored
feat(language-core): navigation support for $attrs, $slots, $refs and $el in the template (#5056)
1 parent f76c5eb commit 74bd7de

File tree

7 files changed

+88
-70
lines changed

7 files changed

+88
-70
lines changed

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,15 +127,15 @@ function* generateTemplateBody(
127127
yield `const __VLS_slots = {}${endOfLine}`;
128128
}
129129
yield `const __VLS_inheritedAttrs = {}${endOfLine}`;
130-
yield `const $refs = {}${endOfLine}`;
131-
yield `const $el = {} as any${endOfLine}`;
130+
yield `const __VLS_refs = {}${endOfLine}`;
131+
yield `const __VLS_rootEl = {} as any${endOfLine}`;
132132
}
133133

134134
yield `return {${newLine}`;
135135
yield ` attrs: {} as Partial<typeof __VLS_inheritedAttrs>,${newLine}`;
136136
yield ` slots: ${options.scriptSetupRanges?.defineSlots?.name ?? '__VLS_slots'},${newLine}`;
137-
yield ` refs: $refs,${newLine}`;
138-
yield ` rootEl: $el,${newLine}`;
137+
yield ` refs: __VLS_refs,${newLine}`;
138+
yield ` rootEl: __VLS_rootEl,${newLine}`;
139139
yield `}${endOfLine}`;
140140
}
141141

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ export function createTemplateCodegenContext(options: Pick<TemplateCodegenOption
103103
},
104104
});
105105
const localVars = new Map<string, number>();
106+
const specialVars = new Set<string>();
106107
const accessExternalVariables = new Map<string, Set<number>>();
107108
const slots: {
108109
name: string;
@@ -132,6 +133,7 @@ export function createTemplateCodegenContext(options: Pick<TemplateCodegenOption
132133
slots,
133134
dynamicSlots,
134135
codeFeatures,
136+
specialVars,
135137
accessExternalVariables,
136138
lastGenericComment,
137139
hasSlotElements,

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

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -277,16 +277,10 @@ export function* generateComponent(
277277
yield `let ${var_componentEvents}!: __VLS_NormalizeEmits<typeof ${var_componentEmit}>${endOfLine}`;
278278
}
279279

280-
if (
281-
options.vueCompilerOptions.fallthroughAttributes
282-
&& (
283-
node.props.some(prop => prop.type === CompilerDOM.NodeTypes.DIRECTIVE && prop.name === 'bind' && prop.exp?.loc.source === '$attrs')
284-
|| node === ctx.singleRootNode
285-
)
286-
) {
287-
const varAttrs = ctx.getInternalVariable();
288-
ctx.inheritedAttrVars.add(varAttrs);
289-
yield `var ${varAttrs}!: Parameters<typeof ${var_functionalComponent}>[0];\n`;
280+
if (hasVBindAttrs(options, ctx, node)) {
281+
const attrsVar = ctx.getInternalVariable();
282+
ctx.inheritedAttrVars.add(attrsVar);
283+
yield `let ${attrsVar}!: Parameters<typeof ${var_functionalComponent}>[0];\n`;
290284
}
291285

292286
const slotDir = node.props.find(p => p.type === CompilerDOM.NodeTypes.DIRECTIVE && p.name === 'slot') as CompilerDOM.DirectiveNode;
@@ -365,13 +359,7 @@ export function* generateElement(
365359
yield* generateElementChildren(options, ctx, node);
366360
}
367361

368-
if (
369-
options.vueCompilerOptions.fallthroughAttributes
370-
&& (
371-
node.props.some(prop => prop.type === CompilerDOM.NodeTypes.DIRECTIVE && prop.name === 'bind' && prop.exp?.loc.source === '$attrs')
372-
|| node === ctx.singleRootNode
373-
)
374-
) {
362+
if (hasVBindAttrs(options, ctx, node)) {
375363
ctx.inheritedAttrVars.add(`__VLS_intrinsicElements.${node.tag}`);
376364
}
377365
}
@@ -625,6 +613,21 @@ function* generateReferencesForElements(
625613
return [];
626614
}
627615

616+
function hasVBindAttrs(
617+
options: TemplateCodegenOptions,
618+
ctx: TemplateCodegenContext,
619+
node: CompilerDOM.ElementNode
620+
) {
621+
return options.vueCompilerOptions.fallthroughAttributes && (
622+
node === ctx.singleRootNode ||
623+
node.props.some(prop =>
624+
prop.type === CompilerDOM.NodeTypes.DIRECTIVE
625+
&& prop.name === 'bind'
626+
&& prop.exp?.loc.source === '$attrs'
627+
)
628+
);
629+
}
630+
628631
function camelizeComponentName(newName: string) {
629632
return camelize('-' + newName);
630633
}

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,6 @@ export function* generateElementProps(
145145
prop,
146146
prop.exp,
147147
ctx.codeFeatures.all,
148-
prop.arg?.loc.start.offset === prop.exp?.loc.start.offset,
149148
enableCodeFeatures
150149
),
151150
`)`
@@ -257,7 +256,6 @@ export function* generateElementProps(
257256
prop,
258257
prop.exp,
259258
ctx.codeFeatures.all,
260-
false,
261259
enableCodeFeatures
262260
)
263261
);
@@ -279,9 +277,10 @@ function* generatePropExp(
279277
prop: CompilerDOM.DirectiveNode,
280278
exp: CompilerDOM.SimpleExpressionNode | undefined,
281279
features: VueCodeInformation,
282-
isShorthand: boolean,
283280
enableCodeFeatures: boolean
284281
): Generator<Code> {
282+
const isShorthand = prop.arg?.loc.start.offset === prop.exp?.loc.start.offset;
283+
285284
if (isShorthand && features.completion) {
286285
features = {
287286
...features,

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

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -35,26 +35,38 @@ export function* generateTemplate(options: TemplateCodegenOptions): Generator<Co
3535
if (options.propsAssignName) {
3636
ctx.addLocalVariable(options.propsAssignName);
3737
}
38-
ctx.addLocalVariable(getSlotsPropertyName(options.vueCompilerOptions.target));
39-
ctx.addLocalVariable('$attrs');
40-
ctx.addLocalVariable('$refs');
41-
ctx.addLocalVariable('$el');
38+
const slotsPropertyName = getSlotsPropertyName(options.vueCompilerOptions.target);
39+
ctx.specialVars.add(slotsPropertyName);
40+
ctx.specialVars.add('$attrs');
41+
ctx.specialVars.add('$refs');
42+
ctx.specialVars.add('$el');
4243

4344
if (options.template.ast) {
4445
yield* generateTemplateChild(options, ctx, options.template.ast, undefined);
4546
}
4647

4748
yield* generateStyleScopedClassReferences(ctx);
48-
yield* generateSlots(options, ctx);
49-
yield* generateInheritedAttrs(ctx);
50-
yield* generateRefs(ctx);
51-
yield* generateRootEl(ctx);
49+
const speicalTypes = [
50+
[slotsPropertyName, yield* generateSlots(options, ctx)],
51+
['$attrs', yield* generateInheritedAttrs(ctx)],
52+
['$refs', yield* generateRefs(ctx)],
53+
['$el', yield* generateRootEl(ctx)]
54+
];
55+
56+
yield `var __VLS_special!: {${newLine}`;
57+
for (const [name, type] of speicalTypes) {
58+
yield `${name}: ${type}${endOfLine}`;
59+
}
60+
yield `} & { [K in keyof typeof __VLS_ctx]: unknown }${endOfLine}`;
5261

5362
yield* ctx.generateAutoImportCompletion();
5463
return ctx;
5564
}
5665

57-
function* generateSlots(options: TemplateCodegenOptions, ctx: TemplateCodegenContext): Generator<Code> {
66+
function* generateSlots(
67+
options: TemplateCodegenOptions,
68+
ctx: TemplateCodegenContext
69+
): Generator<Code> {
5870
if (!options.hasDefineSlots) {
5971
yield `var __VLS_slots!: `;
6072
for (const { expVar, varName } of ctx.dynamicSlots) {
@@ -86,21 +98,22 @@ function* generateSlots(options: TemplateCodegenOptions, ctx: TemplateCodegenCon
8698
}
8799
yield `}${endOfLine}`;
88100
}
89-
const name = getSlotsPropertyName(options.vueCompilerOptions.target);
90-
yield `var ${name}!: typeof ${options.slotsAssignName ?? '__VLS_slots'}${endOfLine}`;
101+
return `typeof ${options.slotsAssignName ?? `__VLS_slots`}`;
91102
}
92103

93-
function* generateInheritedAttrs(ctx: TemplateCodegenContext): Generator<Code> {
104+
function* generateInheritedAttrs(
105+
ctx: TemplateCodegenContext
106+
): Generator<Code> {
94107
yield 'let __VLS_inheritedAttrs!: {}';
95108
for (const varName of ctx.inheritedAttrVars) {
96109
yield ` & typeof ${varName}`;
97110
}
98111
yield endOfLine;
99-
yield `var $attrs!: Partial<typeof __VLS_inheritedAttrs> & Record<string, unknown>${endOfLine}`;
100112

101113
if (ctx.bindingAttrLocs.length) {
102114
yield `[`;
103115
for (const loc of ctx.bindingAttrLocs) {
116+
yield `__VLS_special.`;
104117
yield [
105118
loc.source,
106119
'template',
@@ -111,9 +124,12 @@ function* generateInheritedAttrs(ctx: TemplateCodegenContext): Generator<Code> {
111124
}
112125
yield `]${endOfLine}`;
113126
}
127+
return `Partial<typeof __VLS_inheritedAttrs> & Record<string, unknown>`;
114128
}
115129

116-
function* generateRefs(ctx: TemplateCodegenContext): Generator<Code> {
130+
function* generateRefs(
131+
ctx: TemplateCodegenContext
132+
): Generator<Code> {
117133
yield `const __VLS_refs = {${newLine}`;
118134
for (const [name, [varName, offset]] of ctx.templateRefs) {
119135
yield* generateStringLiteralKey(
@@ -124,16 +140,16 @@ function* generateRefs(ctx: TemplateCodegenContext): Generator<Code> {
124140
yield `: ${varName},${newLine}`;
125141
}
126142
yield `}${endOfLine}`;
127-
yield `var $refs!: typeof __VLS_refs${endOfLine}`;
143+
return `typeof __VLS_refs`;
128144
}
129145

130-
function* generateRootEl(ctx: TemplateCodegenContext): Generator<Code> {
131-
if (ctx.singleRootElType) {
132-
yield `var $el!: ${ctx.singleRootElType}${endOfLine}`;
133-
}
134-
else {
135-
yield `var $el!: any${endOfLine}`;
136-
}
146+
function* generateRootEl(
147+
ctx: TemplateCodegenContext
148+
): Generator<Code> {
149+
yield `let __VLS_rootEl!: `;
150+
yield ctx.singleRootElType ?? `any`;
151+
yield endOfLine;
152+
return `typeof __VLS_rootEl`;
137153
}
138154

139155
export function* forEachElementNode(node: CompilerDOM.RootNode | CompilerDOM.TemplateChildNode): Generator<CompilerDOM.ElementNode> {

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

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ export function* generateInterpolation(
7171
}
7272
}
7373

74+
interface CtxVar {
75+
text: string;
76+
isShorthand: boolean;
77+
offset: number;
78+
};
79+
7480
function* forEachInterpolationSegment(
7581
ts: typeof import('typescript'),
7682
destructuredPropNames: Set<string> | undefined,
@@ -80,20 +86,16 @@ function* forEachInterpolationSegment(
8086
offset: number | undefined,
8187
ast: ts.SourceFile
8288
): Generator<[fragment: string, offset: number | undefined, type?: 'errorMappingOnly' | 'startText' | 'endText']> {
83-
let ctxVars: {
84-
text: string,
85-
isShorthand: boolean,
86-
offset: number,
87-
}[] = [];
89+
let ctxVars: CtxVar[] = [];
8890

8991
const varCb = (id: ts.Identifier, isShorthand: boolean) => {
9092
const text = getNodeText(ts, id, ast);
9193
if (
92-
ctx.hasLocalVariable(text) ||
94+
ctx.hasLocalVariable(text)
9395
// https://github.com/vuejs/core/blob/245230e135152900189f13a4281302de45fdcfaa/packages/compiler-core/src/transforms/transformExpression.ts#L342-L352
94-
isGloballyAllowed(text) ||
95-
text === 'require' ||
96-
text.startsWith('__VLS_')
96+
|| isGloballyAllowed(text)
97+
|| text === 'require'
98+
|| text.startsWith('__VLS_')
9799
) {
98100
// localVarOffsets.push(localVar.getStart(ast));
99101
}
@@ -132,7 +134,7 @@ function* forEachInterpolationSegment(
132134
const curVar = ctxVars[i];
133135
const nextVar = ctxVars[i + 1];
134136

135-
yield* generateVar(code, destructuredPropNames, templateRefNames, curVar, nextVar);
137+
yield* generateVar(code, ctx.specialVars, destructuredPropNames, templateRefNames, curVar, nextVar);
136138

137139
if (nextVar.isShorthand) {
138140
yield [code.slice(curVar.offset + curVar.text.length, nextVar.offset + nextVar.text.length), curVar.offset + curVar.text.length];
@@ -144,7 +146,7 @@ function* forEachInterpolationSegment(
144146
}
145147

146148
const lastVar = ctxVars.at(-1)!;
147-
yield* generateVar(code, destructuredPropNames, templateRefNames, lastVar);
149+
yield* generateVar(code, ctx.specialVars, destructuredPropNames, templateRefNames, lastVar);
148150
if (lastVar.offset + lastVar.text.length < code.length) {
149151
yield [code.slice(lastVar.offset + lastVar.text.length), lastVar.offset + lastVar.text.length, 'endText'];
150152
}
@@ -156,18 +158,11 @@ function* forEachInterpolationSegment(
156158

157159
function* generateVar(
158160
code: string,
161+
specialVars: Set<string>,
159162
destructuredPropNames: Set<string> | undefined,
160163
templateRefNames: Set<string> | undefined,
161-
curVar: {
162-
text: string,
163-
isShorthand: boolean,
164-
offset: number,
165-
},
166-
nextVar: {
167-
text: string,
168-
isShorthand: boolean,
169-
offset: number,
170-
} = curVar
164+
curVar: CtxVar,
165+
nextVar: CtxVar = curVar
171166
): Generator<[fragment: string, offset: number | undefined, type?: 'errorMappingOnly']> {
172167
// fix https://github.com/vuejs/language-tools/issues/1205
173168
// fix https://github.com/vuejs/language-tools/issues/1264
@@ -181,7 +176,10 @@ function* generateVar(
181176
yield [`)`, undefined];
182177
}
183178
else {
184-
if (!isDestructuredProp) {
179+
if (specialVars.has(curVar.text)) {
180+
yield [`__VLS_special.`, undefined];
181+
}
182+
else if (!isDestructuredProp) {
185183
yield [`__VLS_ctx.`, undefined];
186184
}
187185
yield [code.slice(curVar.offset, curVar.offset + curVar.text.length), curVar.offset];

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ function createTsx(
189189
slotsAssignName: slotsAssignName.get(),
190190
propsAssignName: propsAssignName.get(),
191191
inheritAttrs: inheritAttrs.get(),
192-
selfComponentName: selfComponentName.get()
192+
selfComponentName: selfComponentName.get(),
193193
});
194194

195195
let current = codegen.next();
@@ -202,7 +202,7 @@ function createTsx(
202202

203203
return {
204204
...current.value,
205-
codes: codes,
205+
codes,
206206
};
207207
});
208208
const generatedScript = computed(() => {

0 commit comments

Comments
 (0)