Skip to content

Commit

Permalink
perf(language-core): simplify intrinsic element virtual code
Browse files Browse the repository at this point in the history
  • Loading branch information
johnsoncodehk committed Apr 27, 2024
1 parent a402c27 commit ccc428c
Show file tree
Hide file tree
Showing 14 changed files with 333 additions and 281 deletions.
2 changes: 1 addition & 1 deletion packages/language-core/lib/codegen/script/component.ts
Expand Up @@ -75,7 +75,7 @@ export function* generateScriptSetupOptions(
yield `${ctx.helperTypes.WithDefaults.name}<`;
}
yield `${ctx.helperTypes.TypePropsToOption.name}<`;
yield `typeof __VLS_componentProps>`;
yield `__VLS_PublicProps>`;
if (scriptSetupRanges.props.withDefaults?.arg) {
yield `, typeof __VLS_withDefaultsArg>`;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/language-core/lib/codegen/script/globalTypes.ts
Expand Up @@ -79,7 +79,7 @@ declare global {
: T extends () => any ? (props: {}, ctx?: any) => ReturnType<T>
: T extends (...args: any) => any ? T
: (_: {}${vueCompilerOptions.strictTemplates ? '' : ' & Record<string, unknown>'}, ctx?: any) => { __ctx?: { attrs?: any, expose?: any, slots?: any, emit?: any, props?: {}${vueCompilerOptions.strictTemplates ? '' : ' & Record<string, unknown>'} } };
function __VLS_elementAsFunctionalComponent<T>(t: T): (_: T${vueCompilerOptions.strictTemplates ? '' : ' & Record<string, unknown>'}, ctx?: any) => { __ctx?: { attrs?: any, expose?: any, slots?: any, emit?: any, props?: T${vueCompilerOptions.strictTemplates ? '' : ' & Record<string, unknown>'} } };
function __VLS_elementAsFunction<T>(tag: T, endTag?: T): (_: T${vueCompilerOptions.strictTemplates ? '' : ' & Record<string, unknown>'}) => void;
function __VLS_functionalComponentArgsRest<T extends (...args: any) => any>(t: T): Parameters<T>['length'] extends 2 ? [any] : [];
function __VLS_pickEvent<E1, E2>(emitEvent: E1, propEvent: E2): __VLS_FillingEventArg<
__VLS_PickNotAny<
Expand Down
8 changes: 4 additions & 4 deletions packages/language-core/lib/codegen/script/scriptSetup.ts
Expand Up @@ -47,7 +47,7 @@ export function* generateScriptSetup(
+ ` __VLS_setup = (async () => {${newLine}`;
yield* generateSetupFunction(options, ctx, scriptSetup, scriptSetupRanges, undefined, definePropMirrors);
yield ` return {} as {${newLine}`
+ ` props: ${ctx.helperTypes.Prettify.name}<typeof __VLS_functionalComponentProps & typeof __VLS_componentProps> & typeof __VLS_publicProps,${newLine}`
+ ` props: ${ctx.helperTypes.Prettify.name}<typeof __VLS_functionalComponentProps & __VLS_PublicProps> & __VLS_BuiltInPublicProps,${newLine}`
+ ` expose(exposed: import('${options.vueCompilerOptions.lib}').ShallowUnwrapRef<${scriptSetupRanges.expose.define ? 'typeof __VLS_exposed' : '{}'}>): void,${newLine}`
+ ` attrs: any,${newLine}`
+ ` slots: ReturnType<typeof __VLS_template>,${newLine}`
Expand Down Expand Up @@ -246,14 +246,14 @@ function* generateComponentProps(
}
yield `})${endOfLine}`;
yield `let __VLS_functionalComponentProps!: `;
yield `${ctx.helperTypes.OmitKeepDiscriminatedUnion.name}<InstanceType<typeof __VLS_fnComponent>['$props'], keyof typeof __VLS_publicProps>`;
yield `${ctx.helperTypes.OmitKeepDiscriminatedUnion.name}<InstanceType<typeof __VLS_fnComponent>['$props'], keyof __VLS_BuiltInPublicProps>`;
yield endOfLine;
}
else {
yield `let __VLS_functionalComponentProps!: {}${endOfLine}`;
}

yield `let __VLS_publicProps!:${newLine}`
yield `type __VLS_BuiltInPublicProps =${newLine}`
+ ` import('${options.vueCompilerOptions.lib}').VNodeProps${newLine}`
+ ` & import('${options.vueCompilerOptions.lib}').AllowedComponentProps${newLine}`
+ ` & import('${options.vueCompilerOptions.lib}').ComponentCustomProps${endOfLine}`;
Expand All @@ -276,7 +276,7 @@ function* generateComponentProps(
yield `}${endOfLine}`;
}

yield `let __VLS_componentProps!: `;
yield `type __VLS_PublicProps = `;
if (scriptSetupRanges.slots.define && options.vueCompilerOptions.jsxSlots) {
if (ctx.generatedPropsType) {
yield ` & `;
Expand Down
1 change: 0 additions & 1 deletion packages/language-core/lib/codegen/template/context.ts
Expand Up @@ -167,7 +167,6 @@ export function createTemplateCodegenContext() {
generateAutoImportCompletion: function* (): Generator<Code> {
const all = [...accessGlobalVariables.entries()];
if (!all.some(([_, offsets]) => offsets.size)) {
yield `// no auto imports${endOfLine}`;
return;
}
yield `// @ts-ignore${newLine}`; // #2304
Expand Down
210 changes: 157 additions & 53 deletions packages/language-core/lib/codegen/template/element.ts
@@ -1,6 +1,7 @@
import * as CompilerDOM from '@vue/compiler-dom';
import { camelize, capitalize } from '@vue/shared';
import type { Code, VueCodeInformation } from '../../types';
import { hyphenateTag } from '../../utils/shared';
import { collectVars, createTsAst, endOfLine, newLine, variableNameRegex, wrapWith } from '../common';
import { generateCamelized } from './camelized';
import type { TemplateCodegenContext } from './context';
Expand All @@ -11,16 +12,15 @@ import { generateElementProps } from './elementProps';
import type { TemplateCodegenOptions } from './index';
import { generateInterpolation } from './interpolation';
import { generatePropertyAccess } from './propertyAccess';
import { generateStringLiteralKey } from './stringLiteralKey';
import { generateTemplateChild } from './templateChild';

const colonReg = /:/g;

export function* generateElement(
export function* generateComponent(
options: TemplateCodegenOptions,
ctx: TemplateCodegenContext,
node: CompilerDOM.ElementNode,
currentElement: CompilerDOM.ElementNode | undefined,
currentComponent: CompilerDOM.ElementNode | undefined,
componentCtxVar: string | undefined,
): Generator<Code> {
const startTagOffset = node.loc.start.offset + options.template.content.substring(node.loc.start.offset).indexOf(node.tag);
Expand All @@ -29,8 +29,6 @@ export function* generateElement(
const var_functionalComponent = ctx.getInternalVariable();
const var_componentInstance = ctx.getInternalVariable();
const var_componentEvents = ctx.getInternalVariable();
const isIntrinsicElement = node.tagType === CompilerDOM.ElementTypes.ELEMENT
|| node.tagType === CompilerDOM.ElementTypes.TEMPLATE;
const isComponentTag = node.tag.toLowerCase() === 'component';

let endTagOffset = !node.isSelfClosing && options.template.lang === 'html' ? node.loc.start.offset + node.loc.source.lastIndexOf(node.tag) : undefined;
Expand Down Expand Up @@ -69,16 +67,7 @@ export function* generateElement(
};
}

if (isIntrinsicElement) {
yield `const ${var_originalComponent} = __VLS_intrinsicElements[`;
yield* generateStringLiteralKey(
tag,
startTagOffset,
ctx.codeFeatures.verification,
);
yield `]${endOfLine}`;
}
else if (dynamicTagInfo) {
if (dynamicTagInfo) {
yield `const ${var_originalComponent} = `;
yield* generateInterpolation(
options,
Expand Down Expand Up @@ -113,20 +102,15 @@ export function* generateElement(
yield `const ${var_originalComponent} = {} as any${endOfLine}`;
}

if (isIntrinsicElement) {
yield `const ${var_functionalComponent} = __VLS_elementAsFunctionalComponent(${var_originalComponent})${endOfLine}`;
}
else {
yield `const ${var_functionalComponent} = __VLS_asFunctionalComponent(${var_originalComponent}, new ${var_originalComponent}({`;
yield* generateElementProps(options, ctx, node, props, false);
yield `}))${endOfLine}`;
}
yield `const ${var_functionalComponent} = __VLS_asFunctionalComponent(${var_originalComponent}, new ${var_originalComponent}({`;
yield* generateElementProps(options, ctx, node, props, false);
yield `}))${endOfLine}`;

if (
!dynamicTagInfo
&& !isIntrinsicElement
&& !isComponentTag
) {
// hover support
for (const offset of tagOffsets) {
yield `({} as { ${getCanonicalComponentName(tag)}: typeof ${var_originalComponent} }).`;
yield* generateCanonicalComponentName(
Expand All @@ -136,6 +120,45 @@ export function* generateElement(
);
yield endOfLine;
}
const camelizedTag = camelize(node.tag);
if (variableNameRegex.test(camelizedTag)) {
// renaming / find references support
for (const tagOffset of tagOffsets) {
for (const shouldCapitalize of (node.tag[0] === node.tag[0].toUpperCase() ? [false] : [true, false])) {
const expectName = shouldCapitalize ? capitalize(camelizedTag) : camelizedTag;
yield `__VLS_components.`;
yield* generateCamelized(
shouldCapitalize ? capitalize(node.tag) : node.tag,
tagOffset,
{
navigation: {
resolveRenameNewName: node.tag !== expectName ? camelizeComponentName : undefined,
resolveRenameEditText: getTagRenameApply(node.tag),
},
} as VueCodeInformation,
);
yield `;`;
}
}
yield `${newLine}`;
// auto import support
yield `// @ts-ignore${newLine}`; // #2304
yield `[`;
for (const tagOffset of tagOffsets) {
yield* generateCamelized(
capitalize(node.tag),
tagOffset,
{
completion: {
isAdditional: true,
onlyImport: true,
},
} as VueCodeInformation,
);
yield `,`;
}
yield `]${endOfLine}`;
}
}

if (options.vueCompilerOptions.strictTemplates) {
Expand Down Expand Up @@ -169,13 +192,89 @@ export function* generateElement(
yield `)${endOfLine}`;
}

if (node.tagType !== CompilerDOM.ElementTypes.TEMPLATE) {
defineComponentCtxVar = ctx.getInternalVariable();
componentCtxVar = defineComponentCtxVar;
currentElement = node;
defineComponentCtxVar = ctx.getInternalVariable();
componentCtxVar = defineComponentCtxVar;
currentComponent = node;

for (const failedExp of propsFailedExps) {
yield* generateInterpolation(
options,
ctx,
failedExp.loc.source,
failedExp.loc,
failedExp.loc.start.offset,
ctx.codeFeatures.all,
'(',
')',
);
yield endOfLine;
}

yield* generateVScope(options, ctx, node, props);

if (componentCtxVar) {
ctx.usedComponentCtxVars.add(componentCtxVar);
yield* generateElementEvents(options, ctx, node, var_functionalComponent, var_componentInstance, var_componentEvents, () => usedComponentEventsVar = true);
}

const slotDir = node.props.find(p => p.type === CompilerDOM.NodeTypes.DIRECTIVE && p.name === 'slot') as CompilerDOM.DirectiveNode;
if (slotDir && componentCtxVar) {
yield* generateComponentSlot(options, ctx, node, slotDir, currentComponent, componentCtxVar);
}
else {
yield* generateElementChildren(options, ctx, node, currentComponent, componentCtxVar);
}

if (defineComponentCtxVar && ctx.usedComponentCtxVars.has(defineComponentCtxVar)) {
yield `const ${componentCtxVar} = __VLS_pickFunctionalComponentCtx(${var_originalComponent}, ${var_componentInstance})!${endOfLine}`;
}
if (usedComponentEventsVar) {
yield `let ${var_componentEvents}!: __VLS_NormalizeEmits<typeof ${componentCtxVar}.emit>${endOfLine}`;
}
}

export function* generateElement(
options: TemplateCodegenOptions,
ctx: TemplateCodegenContext,
node: CompilerDOM.ElementNode,
currentComponent: CompilerDOM.ElementNode | undefined,
componentCtxVar: string | undefined,
): Generator<Code> {
const startTagOffset = node.loc.start.offset + options.template.content.substring(node.loc.start.offset).indexOf(node.tag);
const endTagOffset = !node.isSelfClosing && options.template.lang === 'html'
? node.loc.start.offset + node.loc.source.lastIndexOf(node.tag)
: undefined;
const propsFailedExps: CompilerDOM.SimpleExpressionNode[] = [];

yield `__VLS_elementAsFunction(__VLS_intrinsicElements`;
yield* generatePropertyAccess(
options,
ctx,
node.tag,
startTagOffset,
ctx.codeFeatures.withoutHighlightAndCompletion,
);
if (endTagOffset !== undefined) {
yield `, __VLS_intrinsicElements`;
yield* generatePropertyAccess(
options,
ctx,
node.tag,
endTagOffset,
ctx.codeFeatures.withoutHighlightAndCompletion,
);
}
yield `)(`;
yield* wrapWith(
startTagOffset,
startTagOffset + node.tag.length,
ctx.codeFeatures.verification,
`{`,
...generateElementProps(options, ctx, node, node.props, true, propsFailedExps),
`}`,
);
yield `)${endOfLine}`;

//#region fix #1775
for (const failedExp of propsFailedExps) {
yield* generateInterpolation(
options,
Expand All @@ -190,6 +289,23 @@ export function* generateElement(
yield endOfLine;
}

yield* generateVScope(options, ctx, node, node.props);

const slotDir = node.props.find(p => p.type === CompilerDOM.NodeTypes.DIRECTIVE && p.name === 'slot') as CompilerDOM.DirectiveNode;
if (slotDir && componentCtxVar) {
yield* generateComponentSlot(options, ctx, node, slotDir, currentComponent, componentCtxVar);
}
else {
yield* generateElementChildren(options, ctx, node, currentComponent, componentCtxVar);
}
}

function* generateVScope(
options: TemplateCodegenOptions,
ctx: TemplateCodegenContext,
node: CompilerDOM.ElementNode,
props: (CompilerDOM.AttributeNode | CompilerDOM.DirectiveNode)[],
): Generator<Code> {
const vScope = props.find(prop => prop.type === CompilerDOM.NodeTypes.DIRECTIVE && (prop.name === 'scope' || prop.name === 'data'));
let inScope = false;
let originalConditionsNum = ctx.blockConditions.length;
Expand Down Expand Up @@ -217,31 +333,11 @@ export function* generateElement(
if (options.shouldGenerateScopedClasses) {
yield* generateReferencesForScopedCssClasses(ctx, node);
}
if (componentCtxVar) {
ctx.usedComponentCtxVars.add(componentCtxVar);
yield* generateElementEvents(options, ctx, node, var_functionalComponent, var_componentInstance, var_componentEvents, () => usedComponentEventsVar = true);
}

if (inScope) {
yield `}${newLine}`;
ctx.blockConditions.length = originalConditionsNum;
}
//#endregion

const slotDir = node.props.find(p => p.type === CompilerDOM.NodeTypes.DIRECTIVE && p.name === 'slot') as CompilerDOM.DirectiveNode;
if (slotDir && componentCtxVar) {
yield* generateComponentSlot(options, ctx, node, slotDir, currentElement, componentCtxVar);
}
else {
yield* generateElementChildren(options, ctx, node, currentElement, componentCtxVar);
}

if (defineComponentCtxVar && ctx.usedComponentCtxVars.has(defineComponentCtxVar)) {
yield `const ${componentCtxVar} = __VLS_pickFunctionalComponentCtx(${var_originalComponent}, ${var_componentInstance})!${endOfLine}`;
}
if (usedComponentEventsVar) {
yield `let ${var_componentEvents}!: __VLS_NormalizeEmits<typeof ${componentCtxVar}.emit>${endOfLine}`;
}
}

export function getCanonicalComponentName(tagText: string) {
Expand Down Expand Up @@ -282,13 +378,13 @@ function* generateComponentSlot(
ctx: TemplateCodegenContext,
node: CompilerDOM.ElementNode,
slotDir: CompilerDOM.DirectiveNode,
currentElement: CompilerDOM.ElementNode | undefined,
currentComponent: CompilerDOM.ElementNode | undefined,
componentCtxVar: string,
): Generator<Code> {
yield `{${newLine}`;
ctx.usedComponentCtxVars.add(componentCtxVar);
if (currentElement) {
ctx.hasSlotElements.add(currentElement);
if (currentComponent) {
ctx.hasSlotElements.add(currentComponent);
}
const slotBlockVars: string[] = [];
let hasProps = false;
Expand Down Expand Up @@ -363,7 +459,7 @@ function* generateComponentSlot(

let prev: CompilerDOM.TemplateChildNode | undefined;
for (const childNode of node.children) {
yield* generateTemplateChild(options, ctx, childNode, currentElement, prev, componentCtxVar);
yield* generateTemplateChild(options, ctx, childNode, currentComponent, prev, componentCtxVar);
prev = childNode;
}

Expand Down Expand Up @@ -464,3 +560,11 @@ function* generateReferencesForScopedCssClasses(
}
}
}

function camelizeComponentName(newName: string) {
return camelize('-' + newName);
}

function getTagRenameApply(oldName: string) {
return oldName === hyphenateTag(oldName) ? hyphenateTag : undefined;
}

0 comments on commit ccc428c

Please sign in to comment.