Skip to content

Commit 5936c82

Browse files
refactor(language-core): reduce virtual code generated by component tags (#4714)
1 parent 180af0b commit 5936c82

File tree

14 files changed

+123
-101
lines changed

14 files changed

+123
-101
lines changed

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,10 @@ declare global {
6767
function __VLS_nonNullable<T>(t: T): T extends null | undefined ? never : T;
6868
6969
type __VLS_SelfComponent<N, C> = string extends N ? {} : N extends string ? { [P in N]: C } : {};
70-
type __VLS_WithComponent<N0 extends string, LocalComponents, N1 extends string, N2 extends string, N3 extends string> =
70+
type __VLS_WithComponent<N0 extends string, Ctx, LocalComponents, N1 extends string, N2 extends string, N3 extends string> =
71+
N1 extends keyof Ctx ? N1 extends N0 ? Pick<Ctx, N0 extends keyof Ctx ? N0 : never> : { [K in N0]: Ctx[N1] } :
72+
N2 extends keyof Ctx ? N2 extends N0 ? Pick<Ctx, N0 extends keyof Ctx ? N0 : never> : { [K in N0]: Ctx[N2] } :
73+
N3 extends keyof Ctx ? N3 extends N0 ? Pick<Ctx, N0 extends keyof Ctx ? N0 : never> : { [K in N0]: Ctx[N3] } :
7174
N1 extends keyof LocalComponents ? N1 extends N0 ? Pick<LocalComponents, N0 extends keyof LocalComponents ? N0 : never> : { [K in N0]: LocalComponents[N1] } :
7275
N2 extends keyof LocalComponents ? N2 extends N0 ? Pick<LocalComponents, N0 extends keyof LocalComponents ? N0 : never> : { [K in N0]: LocalComponents[N2] } :
7376
N3 extends keyof LocalComponents ? N3 extends N0 ? Pick<LocalComponents, N0 extends keyof LocalComponents ? N0 : never> : { [K in N0]: LocalComponents[N3] } :
@@ -88,10 +91,10 @@ declare global {
8891
: (_: {}${vueCompilerOptions.strictTemplates ? '' : ' & Record<string, unknown>'}, ctx?: any) => { __ctx?: { attrs?: any, expose?: any, slots?: any, emit?: any, props?: {}${vueCompilerOptions.strictTemplates ? '' : ' & Record<string, unknown>'} } };
8992
function __VLS_elementAsFunction<T>(tag: T, endTag?: T): (_: T${vueCompilerOptions.strictTemplates ? '' : ' & Record<string, unknown>'}) => void;
9093
function __VLS_functionalComponentArgsRest<T extends (...args: any) => any>(t: T): Parameters<T>['length'] extends 2 ? [any] : [];
91-
function __VLS_pickFunctionalComponentCtx<T, K>(comp: T, compInstance: K): __VLS_PickNotAny<
94+
function __VLS_pickFunctionalComponentCtx<T, K>(comp: T, compInstance: K): NonNullable<__VLS_PickNotAny<
9295
'__ctx' extends keyof __VLS_PickNotAny<K, {}> ? K extends { __ctx?: infer Ctx } ? Ctx : never : any
9396
, T extends (props: any, ctx: infer Ctx) => any ? Ctx : any
94-
>;
97+
>>;
9598
type __VLS_FunctionalComponentProps<T, K> =
9699
'__ctx' extends keyof __VLS_PickNotAny<K, {}> ? K extends { __ctx?: { props?: infer P } } ? NonNullable<P> : never
97100
: T extends (props: infer P, ...args: any) => any ? P :

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export interface ScriptCodegenOptions {
4545
scriptSetupRanges: ScriptSetupRanges | undefined;
4646
templateCodegen: TemplateCodegenContext & { codes: Code[]; } | undefined;
4747
globalTypes: boolean;
48+
edited: boolean;
4849
getGeneratedLength: () => number;
4950
linkedCodeMappings: Mapping[];
5051
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export function* generateTemplate(
2323
else {
2424
yield `const __VLS_template = (() => {${newLine}`;
2525
}
26-
const templateCodegenCtx = createTemplateCodegenContext(new Set());
26+
const templateCodegenCtx = createTemplateCodegenContext({ scriptSetupBindingNames: new Set(), edited: options.edited });
2727
yield `const __VLS_template_return = () => {${newLine}`;
2828
yield* generateCtx(options, isClassComponent);
2929
yield* generateTemplateContext(options, templateCodegenCtx);

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ const _codeFeatures = {
5757

5858
export type TemplateCodegenContext = ReturnType<typeof createTemplateCodegenContext>;
5959

60-
export function createTemplateCodegenContext(scriptSetupBindingNames: TemplateCodegenOptions['scriptSetupBindingNames']) {
60+
export function createTemplateCodegenContext(options: Pick<TemplateCodegenOptions, 'scriptSetupBindingNames' | 'edited'>) {
6161
let ignoredError = false;
6262
let expectErrorToken: {
6363
errors: number;
@@ -190,6 +190,9 @@ export function createTemplateCodegenContext(scriptSetupBindingNames: TemplateCo
190190
}
191191
},
192192
generateAutoImportCompletion: function* (): Generator<Code> {
193+
if (!options.edited) {
194+
return;
195+
}
193196
const all = [...accessExternalVariables.entries()];
194197
if (!all.some(([_, offsets]) => offsets.size)) {
195198
return;
@@ -198,7 +201,7 @@ export function createTemplateCodegenContext(scriptSetupBindingNames: TemplateCo
198201
yield `[`;
199202
for (const [varName, offsets] of all) {
200203
for (const offset of offsets) {
201-
if (scriptSetupBindingNames.has(varName)) {
204+
if (options.scriptSetupBindingNames.has(varName)) {
202205
// #3409
203206
yield [
204207
varName,

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

Lines changed: 52 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@ export function* generateComponent(
2323
options: TemplateCodegenOptions,
2424
ctx: TemplateCodegenContext,
2525
node: CompilerDOM.ElementNode,
26-
currentComponent: CompilerDOM.ElementNode | undefined,
27-
componentCtxVar: string | undefined
26+
currentComponent: CompilerDOM.ElementNode | undefined
2827
): Generator<Code> {
2928
const startTagOffset = node.loc.start.offset + options.template.content.substring(node.loc.start.offset).indexOf(node.tag);
3029
const endTagOffset = !node.isSelfClosing && options.template.lang === 'html' ? node.loc.start.offset + node.loc.source.lastIndexOf(node.tag) : undefined;
@@ -137,40 +136,26 @@ export function* generateComponent(
137136
yield `)${endOfLine}`;
138137
}
139138
else if (!isComponentTag) {
140-
yield `// @ts-ignore${newLine}`;
141-
yield `const ${var_originalComponent} = ({} as `;
142-
for (const componentName of possibleOriginalNames) {
143-
yield `'${componentName}' extends keyof typeof __VLS_ctx ? { '${getCanonicalComponentName(node.tag)}': typeof __VLS_ctx`;
144-
yield* generatePropertyAccess(options, ctx, componentName);
145-
yield ` }: `;
146-
}
147-
yield `typeof __VLS_resolvedLocalAndGlobalComponents)${newLine}`;
148-
yield* generatePropertyAccess(
149-
options,
150-
ctx,
151-
getCanonicalComponentName(node.tag),
139+
yield `const ${var_originalComponent} = __VLS_resolvedLocalAndGlobalComponents.`;
140+
yield* generateCanonicalComponentName(
141+
node.tag,
152142
startTagOffset,
153-
ctx.codeFeatures.verification
143+
{
144+
// with hover support
145+
...ctx.codeFeatures.withoutHighlightAndCompletionAndNavigation,
146+
...ctx.codeFeatures.verification,
147+
}
154148
);
155-
yield endOfLine;
149+
yield `${endOfLine}`;
156150

157-
// hover support
158-
for (const offset of tagOffsets) {
159-
yield `({} as { ${getCanonicalComponentName(node.tag)}: typeof ${var_originalComponent} }).`;
160-
yield* generateCanonicalComponentName(
161-
node.tag,
162-
offset,
163-
ctx.codeFeatures.withoutHighlightAndCompletionAndNavigation
164-
);
165-
yield endOfLine;
166-
}
167151
const camelizedTag = camelize(node.tag);
168152
if (variableNameRegex.test(camelizedTag)) {
169153
// renaming / find references support
154+
yield `/** @type { [`;
170155
for (const tagOffset of tagOffsets) {
171156
for (const shouldCapitalize of (node.tag[0] === node.tag[0].toUpperCase() ? [false] : [true, false])) {
172157
const expectName = shouldCapitalize ? capitalize(camelizedTag) : camelizedTag;
173-
yield `__VLS_components.`;
158+
yield `typeof __VLS_components.`;
174159
yield* generateCamelized(
175160
shouldCapitalize ? capitalize(node.tag) : node.tag,
176161
tagOffset,
@@ -181,27 +166,25 @@ export function* generateComponent(
181166
},
182167
}
183168
);
184-
yield `;`;
169+
yield `, `;
185170
}
186171
}
187-
yield `${newLine}`;
172+
yield `] } */${newLine}`;
188173
// auto import support
189-
yield `// @ts-ignore${newLine}`; // #2304
190-
yield `[`;
191-
for (const tagOffset of tagOffsets) {
174+
if (options.edited) {
175+
yield `// @ts-ignore${newLine}`; // #2304
192176
yield* generateCamelized(
193177
capitalize(node.tag),
194-
tagOffset,
178+
startTagOffset,
195179
{
196180
completion: {
197181
isAdditional: true,
198182
onlyImport: true,
199183
},
200184
}
201185
);
202-
yield `,`;
186+
yield `${endOfLine}`;
203187
}
204-
yield `]${endOfLine}`;
205188
}
206189
}
207190
else {
@@ -213,38 +196,17 @@ export function* generateComponent(
213196
yield* generateElementProps(options, ctx, node, props, false);
214197
yield `}))${endOfLine}`;
215198

216-
if (options.vueCompilerOptions.strictTemplates) {
217-
// with strictTemplates, generate once for props type-checking + instance type
218-
yield `const ${var_componentInstance} = ${var_functionalComponent}(`;
219-
yield* wrapWith(
220-
startTagOffset,
221-
startTagOffset + node.tag.length,
222-
ctx.codeFeatures.verification,
223-
`{`,
224-
...generateElementProps(options, ctx, node, props, true, propsFailedExps),
225-
`}`
226-
);
227-
yield `, ...__VLS_functionalComponentArgsRest(${var_functionalComponent}))${endOfLine}`;
228-
}
229-
else {
230-
// without strictTemplates, this only for instance type
231-
yield `const ${var_componentInstance} = ${var_functionalComponent}({`;
232-
yield* generateElementProps(options, ctx, node, props, false);
233-
yield `}, ...__VLS_functionalComponentArgsRest(${var_functionalComponent}))${endOfLine}`;
234-
// and this for props type-checking
235-
yield `({} as (props: __VLS_FunctionalComponentProps<typeof ${var_originalComponent}, typeof ${var_componentInstance}> & Record<string, unknown>) => void)(`;
236-
yield* wrapWith(
237-
startTagOffset,
238-
startTagOffset + node.tag.length,
239-
ctx.codeFeatures.verification,
240-
`{`,
241-
...generateElementProps(options, ctx, node, props, true, propsFailedExps),
242-
`}`
243-
);
244-
yield `)${endOfLine}`;
245-
}
199+
yield `const ${var_componentInstance} = ${var_functionalComponent}(`;
200+
yield* wrapWith(
201+
startTagOffset,
202+
startTagOffset + node.tag.length,
203+
ctx.codeFeatures.verification,
204+
`{`,
205+
...generateElementProps(options, ctx, node, props, true, propsFailedExps),
206+
`}`
207+
);
208+
yield `, ...__VLS_functionalComponentArgsRest(${var_functionalComponent}))${endOfLine}`;
246209

247-
componentCtxVar = var_defineComponentCtx;
248210
currentComponent = node;
249211

250212
for (const failedExp of propsFailedExps) {
@@ -262,28 +224,14 @@ export function* generateComponent(
262224
}
263225

264226
const refName = yield* generateVScope(options, ctx, node, props);
227+
if (refName) {
228+
ctx.usedComponentCtxVars.add(var_defineComponentCtx);
229+
}
265230

266-
ctx.usedComponentCtxVars.add(componentCtxVar);
267231
const usedComponentEventsVar = yield* generateElementEvents(options, ctx, node, var_functionalComponent, var_componentInstance, var_componentEmit, var_componentEvents);
268-
269-
if (var_defineComponentCtx && ctx.usedComponentCtxVars.has(var_defineComponentCtx)) {
270-
yield `const ${componentCtxVar} = __VLS_nonNullable(__VLS_pickFunctionalComponentCtx(${var_originalComponent}, ${var_componentInstance}))${endOfLine}`;
271-
if (refName) {
272-
yield `// @ts-ignore${newLine}`;
273-
if (node.codegenNode?.type === CompilerDOM.NodeTypes.VNODE_CALL
274-
&& node.codegenNode.props?.type === CompilerDOM.NodeTypes.JS_OBJECT_EXPRESSION
275-
&& node.codegenNode.props.properties.find(({ key }) => key.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && key.content === 'ref_for')
276-
) {
277-
yield `(${refName} ??= []).push(${var_defineComponentCtx})`;
278-
} else {
279-
yield `${refName} = ${var_defineComponentCtx}`;
280-
}
281-
282-
yield endOfLine;
283-
}
284-
}
285232
if (usedComponentEventsVar) {
286-
yield `let ${var_componentEmit}!: typeof ${componentCtxVar}.emit${endOfLine}`;
233+
ctx.usedComponentCtxVars.add(var_defineComponentCtx);
234+
yield `let ${var_componentEmit}!: typeof ${var_defineComponentCtx}.emit${endOfLine}`;
287235
yield `let ${var_componentEvents}!: __VLS_NormalizeEmits<typeof ${var_componentEmit}>${endOfLine}`;
288236
}
289237

@@ -301,10 +249,27 @@ export function* generateComponent(
301249

302250
const slotDir = node.props.find(p => p.type === CompilerDOM.NodeTypes.DIRECTIVE && p.name === 'slot') as CompilerDOM.DirectiveNode;
303251
if (slotDir) {
304-
yield* generateComponentSlot(options, ctx, node, slotDir, currentComponent, componentCtxVar);
252+
yield* generateComponentSlot(options, ctx, node, slotDir, currentComponent, var_defineComponentCtx);
305253
}
306254
else {
307-
yield* generateElementChildren(options, ctx, node, currentComponent, componentCtxVar);
255+
yield* generateElementChildren(options, ctx, node, currentComponent, var_defineComponentCtx);
256+
}
257+
258+
if (ctx.usedComponentCtxVars.has(var_defineComponentCtx)) {
259+
yield `const ${var_defineComponentCtx} = __VLS_pickFunctionalComponentCtx(${var_originalComponent}, ${var_componentInstance})${endOfLine}`;
260+
if (refName) {
261+
yield `// @ts-ignore${newLine}`;
262+
if (node.codegenNode?.type === CompilerDOM.NodeTypes.VNODE_CALL
263+
&& node.codegenNode.props?.type === CompilerDOM.NodeTypes.JS_OBJECT_EXPRESSION
264+
&& node.codegenNode.props.properties.find(({ key }) => key.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && key.content === 'ref_for')
265+
) {
266+
yield `(${refName} ??= []).push(${var_defineComponentCtx})`;
267+
} else {
268+
yield `${refName} = ${var_defineComponentCtx}`;
269+
}
270+
271+
yield endOfLine;
272+
}
308273
}
309274
}
310275

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export function* generateElementChildren(
2828
&& node.tagType !== CompilerDOM.ElementTypes.ELEMENT
2929
&& node.tagType !== CompilerDOM.ElementTypes.TEMPLATE
3030
) {
31+
ctx.usedComponentCtxVars.add(componentCtxVar);
3132
yield `__VLS_nonNullable(${componentCtxVar}.slots).`;
3233
yield* wrapWith(
3334
node.children[0].loc.start.offset,

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

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ export function* generateElementProps(
109109
if (shouldSpread) {
110110
yield `...{ `;
111111
}
112+
const codeInfo = ctx.codeFeatures.withoutHighlightAndCompletion;
112113
const codes = wrapWith(
113114
prop.loc.start.offset,
114115
prop.loc.end.offset,
@@ -121,8 +122,20 @@ export function* generateElementProps(
121122
propName,
122123
prop.arg.loc.start.offset,
123124
{
124-
...ctx.codeFeatures.withoutHighlightAndCompletion,
125-
navigation: ctx.codeFeatures.withoutHighlightAndCompletion.navigation
125+
...codeInfo,
126+
verification: options.vueCompilerOptions.strictTemplates
127+
? codeInfo.verification
128+
: {
129+
shouldReport(_source, code) {
130+
if (String(code) === '2353' || String(code) === '2561') {
131+
return false;
132+
}
133+
return typeof codeInfo.verification === 'object'
134+
? codeInfo.verification.shouldReport?.(_source, code) ?? true
135+
: true;
136+
},
137+
},
138+
navigation: codeInfo.navigation
126139
? {
127140
resolveRenameNewName: camelize,
128141
resolveRenameEditText: shouldCamelize ? hyphenateAttr : undefined,
@@ -183,6 +196,7 @@ export function* generateElementProps(
183196
if (shouldSpread) {
184197
yield `...{ `;
185198
}
199+
const codeInfo = ctx.codeFeatures.withoutHighlightAndCompletion;
186200
const codes = conditionWrapWith(
187201
enableCodeFeatures,
188202
prop.loc.start.offset,
@@ -195,7 +209,19 @@ export function* generateElementProps(
195209
prop.loc.start.offset,
196210
shouldCamelize
197211
? {
198-
...ctx.codeFeatures.withoutHighlightAndCompletion,
212+
...codeInfo,
213+
verification: options.vueCompilerOptions.strictTemplates
214+
? codeInfo.verification
215+
: {
216+
shouldReport(_source, code) {
217+
if (String(code) === '2353' || String(code) === '2561') {
218+
return false;
219+
}
220+
return typeof codeInfo.verification === 'object'
221+
? codeInfo.verification.shouldReport?.(_source, code) ?? true
222+
: true;
223+
},
224+
},
199225
navigation: ctx.codeFeatures.withoutHighlightAndCompletion.navigation
200226
? {
201227
resolveRenameNewName: camelize,

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

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export interface TemplateCodegenOptions {
1515
template: NonNullable<Sfc['template']>;
1616
scriptSetupBindingNames: Set<string>;
1717
scriptSetupImportComponentNames: Set<string>;
18+
edited: boolean;
1819
templateRefNames: Map<string, string>;
1920
hasDefineSlots?: boolean;
2021
slotsAssignName?: string;
@@ -23,7 +24,7 @@ export interface TemplateCodegenOptions {
2324
}
2425

2526
export function* generateTemplate(options: TemplateCodegenOptions): Generator<Code, TemplateCodegenContext> {
26-
const ctx = createTemplateCodegenContext(options.scriptSetupBindingNames);
27+
const ctx = createTemplateCodegenContext(options);
2728

2829
if (options.slotsAssignName) {
2930
ctx.addLocalVariable(options.slotsAssignName);
@@ -105,23 +106,29 @@ export function* generateTemplate(options: TemplateCodegenOptions): Generator<Co
105106
}
106107

107108
function* generatePreResolveComponents(): Generator<Code> {
108-
yield `let __VLS_resolvedLocalAndGlobalComponents!: {}`;
109+
yield `let __VLS_resolvedLocalAndGlobalComponents!: Required<{}`;
109110
if (options.template.ast) {
111+
const components = new Set<string>();
110112
for (const node of forEachElementNode(options.template.ast)) {
111113
if (
112114
node.tagType === CompilerDOM.ElementTypes.COMPONENT
113115
&& node.tag.toLowerCase() !== 'component'
114116
&& !node.tag.includes('.') // namespace tag
115117
) {
116-
yield ` & __VLS_WithComponent<'${getCanonicalComponentName(node.tag)}', typeof __VLS_localComponents, `;
118+
if (components.has(node.tag)) {
119+
continue;
120+
}
121+
components.add(node.tag);
122+
yield newLine;
123+
yield ` & __VLS_WithComponent<'${getCanonicalComponentName(node.tag)}', typeof __VLS_ctx, typeof __VLS_localComponents, `;
117124
yield getPossibleOriginalComponentNames(node.tag, false)
118125
.map(name => `"${name}"`)
119126
.join(', ');
120127
yield `>`;
121128
}
122129
}
123130
}
124-
yield endOfLine;
131+
yield `>${endOfLine}`;
125132
}
126133
}
127134

0 commit comments

Comments
 (0)