Skip to content

Commit

Permalink
refactor(language-core): reduce virtual code generated by component t…
Browse files Browse the repository at this point in the history
…ags (#4714)
  • Loading branch information
johnsoncodehk authored Aug 27, 2024
1 parent 180af0b commit 5936c82
Show file tree
Hide file tree
Showing 14 changed files with 123 additions and 101 deletions.
9 changes: 6 additions & 3 deletions packages/language-core/lib/codegen/script/globalTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,10 @@ declare global {
function __VLS_nonNullable<T>(t: T): T extends null | undefined ? never : T;
type __VLS_SelfComponent<N, C> = string extends N ? {} : N extends string ? { [P in N]: C } : {};
type __VLS_WithComponent<N0 extends string, LocalComponents, N1 extends string, N2 extends string, N3 extends string> =
type __VLS_WithComponent<N0 extends string, Ctx, LocalComponents, N1 extends string, N2 extends string, N3 extends string> =
N1 extends keyof Ctx ? N1 extends N0 ? Pick<Ctx, N0 extends keyof Ctx ? N0 : never> : { [K in N0]: Ctx[N1] } :
N2 extends keyof Ctx ? N2 extends N0 ? Pick<Ctx, N0 extends keyof Ctx ? N0 : never> : { [K in N0]: Ctx[N2] } :
N3 extends keyof Ctx ? N3 extends N0 ? Pick<Ctx, N0 extends keyof Ctx ? N0 : never> : { [K in N0]: Ctx[N3] } :
N1 extends keyof LocalComponents ? N1 extends N0 ? Pick<LocalComponents, N0 extends keyof LocalComponents ? N0 : never> : { [K in N0]: LocalComponents[N1] } :
N2 extends keyof LocalComponents ? N2 extends N0 ? Pick<LocalComponents, N0 extends keyof LocalComponents ? N0 : never> : { [K in N0]: LocalComponents[N2] } :
N3 extends keyof LocalComponents ? N3 extends N0 ? Pick<LocalComponents, N0 extends keyof LocalComponents ? N0 : never> : { [K in N0]: LocalComponents[N3] } :
Expand All @@ -88,10 +91,10 @@ declare global {
: (_: {}${vueCompilerOptions.strictTemplates ? '' : ' & Record<string, unknown>'}, ctx?: any) => { __ctx?: { attrs?: any, expose?: any, slots?: any, emit?: any, props?: {}${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_pickFunctionalComponentCtx<T, K>(comp: T, compInstance: K): __VLS_PickNotAny<
function __VLS_pickFunctionalComponentCtx<T, K>(comp: T, compInstance: K): NonNullable<__VLS_PickNotAny<
'__ctx' extends keyof __VLS_PickNotAny<K, {}> ? K extends { __ctx?: infer Ctx } ? Ctx : never : any
, T extends (props: any, ctx: infer Ctx) => any ? Ctx : any
>;
>>;
type __VLS_FunctionalComponentProps<T, K> =
'__ctx' extends keyof __VLS_PickNotAny<K, {}> ? K extends { __ctx?: { props?: infer P } } ? NonNullable<P> : never
: T extends (props: infer P, ...args: any) => any ? P :
Expand Down
1 change: 1 addition & 0 deletions packages/language-core/lib/codegen/script/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export interface ScriptCodegenOptions {
scriptSetupRanges: ScriptSetupRanges | undefined;
templateCodegen: TemplateCodegenContext & { codes: Code[]; } | undefined;
globalTypes: boolean;
edited: boolean;
getGeneratedLength: () => number;
linkedCodeMappings: Mapping[];
}
Expand Down
2 changes: 1 addition & 1 deletion packages/language-core/lib/codegen/script/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export function* generateTemplate(
else {
yield `const __VLS_template = (() => {${newLine}`;
}
const templateCodegenCtx = createTemplateCodegenContext(new Set());
const templateCodegenCtx = createTemplateCodegenContext({ scriptSetupBindingNames: new Set(), edited: options.edited });
yield `const __VLS_template_return = () => {${newLine}`;
yield* generateCtx(options, isClassComponent);
yield* generateTemplateContext(options, templateCodegenCtx);
Expand Down
7 changes: 5 additions & 2 deletions packages/language-core/lib/codegen/template/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ const _codeFeatures = {

export type TemplateCodegenContext = ReturnType<typeof createTemplateCodegenContext>;

export function createTemplateCodegenContext(scriptSetupBindingNames: TemplateCodegenOptions['scriptSetupBindingNames']) {
export function createTemplateCodegenContext(options: Pick<TemplateCodegenOptions, 'scriptSetupBindingNames' | 'edited'>) {
let ignoredError = false;
let expectErrorToken: {
errors: number;
Expand Down Expand Up @@ -190,6 +190,9 @@ export function createTemplateCodegenContext(scriptSetupBindingNames: TemplateCo
}
},
generateAutoImportCompletion: function* (): Generator<Code> {
if (!options.edited) {
return;
}
const all = [...accessExternalVariables.entries()];
if (!all.some(([_, offsets]) => offsets.size)) {
return;
Expand All @@ -198,7 +201,7 @@ export function createTemplateCodegenContext(scriptSetupBindingNames: TemplateCo
yield `[`;
for (const [varName, offsets] of all) {
for (const offset of offsets) {
if (scriptSetupBindingNames.has(varName)) {
if (options.scriptSetupBindingNames.has(varName)) {
// #3409
yield [
varName,
Expand Down
139 changes: 52 additions & 87 deletions packages/language-core/lib/codegen/template/element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ export function* generateComponent(
options: TemplateCodegenOptions,
ctx: TemplateCodegenContext,
node: CompilerDOM.ElementNode,
currentComponent: CompilerDOM.ElementNode | undefined,
componentCtxVar: string | undefined
currentComponent: CompilerDOM.ElementNode | 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;
Expand Down Expand Up @@ -137,40 +136,26 @@ export function* generateComponent(
yield `)${endOfLine}`;
}
else if (!isComponentTag) {
yield `// @ts-ignore${newLine}`;
yield `const ${var_originalComponent} = ({} as `;
for (const componentName of possibleOriginalNames) {
yield `'${componentName}' extends keyof typeof __VLS_ctx ? { '${getCanonicalComponentName(node.tag)}': typeof __VLS_ctx`;
yield* generatePropertyAccess(options, ctx, componentName);
yield ` }: `;
}
yield `typeof __VLS_resolvedLocalAndGlobalComponents)${newLine}`;
yield* generatePropertyAccess(
options,
ctx,
getCanonicalComponentName(node.tag),
yield `const ${var_originalComponent} = __VLS_resolvedLocalAndGlobalComponents.`;
yield* generateCanonicalComponentName(
node.tag,
startTagOffset,
ctx.codeFeatures.verification
{
// with hover support
...ctx.codeFeatures.withoutHighlightAndCompletionAndNavigation,
...ctx.codeFeatures.verification,
}
);
yield endOfLine;
yield `${endOfLine}`;

// hover support
for (const offset of tagOffsets) {
yield `({} as { ${getCanonicalComponentName(node.tag)}: typeof ${var_originalComponent} }).`;
yield* generateCanonicalComponentName(
node.tag,
offset,
ctx.codeFeatures.withoutHighlightAndCompletionAndNavigation
);
yield endOfLine;
}
const camelizedTag = camelize(node.tag);
if (variableNameRegex.test(camelizedTag)) {
// renaming / find references support
yield `/** @type { [`;
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 `typeof __VLS_components.`;
yield* generateCamelized(
shouldCapitalize ? capitalize(node.tag) : node.tag,
tagOffset,
Expand All @@ -181,27 +166,25 @@ export function* generateComponent(
},
}
);
yield `;`;
yield `, `;
}
}
yield `${newLine}`;
yield `] } */${newLine}`;
// auto import support
yield `// @ts-ignore${newLine}`; // #2304
yield `[`;
for (const tagOffset of tagOffsets) {
if (options.edited) {
yield `// @ts-ignore${newLine}`; // #2304
yield* generateCamelized(
capitalize(node.tag),
tagOffset,
startTagOffset,
{
completion: {
isAdditional: true,
onlyImport: true,
},
}
);
yield `,`;
yield `${endOfLine}`;
}
yield `]${endOfLine}`;
}
}
else {
Expand All @@ -213,38 +196,17 @@ export function* generateComponent(
yield* generateElementProps(options, ctx, node, props, false);
yield `}))${endOfLine}`;

if (options.vueCompilerOptions.strictTemplates) {
// with strictTemplates, generate once for props type-checking + instance type
yield `const ${var_componentInstance} = ${var_functionalComponent}(`;
yield* wrapWith(
startTagOffset,
startTagOffset + node.tag.length,
ctx.codeFeatures.verification,
`{`,
...generateElementProps(options, ctx, node, props, true, propsFailedExps),
`}`
);
yield `, ...__VLS_functionalComponentArgsRest(${var_functionalComponent}))${endOfLine}`;
}
else {
// without strictTemplates, this only for instance type
yield `const ${var_componentInstance} = ${var_functionalComponent}({`;
yield* generateElementProps(options, ctx, node, props, false);
yield `}, ...__VLS_functionalComponentArgsRest(${var_functionalComponent}))${endOfLine}`;
// and this for props type-checking
yield `({} as (props: __VLS_FunctionalComponentProps<typeof ${var_originalComponent}, typeof ${var_componentInstance}> & Record<string, unknown>) => void)(`;
yield* wrapWith(
startTagOffset,
startTagOffset + node.tag.length,
ctx.codeFeatures.verification,
`{`,
...generateElementProps(options, ctx, node, props, true, propsFailedExps),
`}`
);
yield `)${endOfLine}`;
}
yield `const ${var_componentInstance} = ${var_functionalComponent}(`;
yield* wrapWith(
startTagOffset,
startTagOffset + node.tag.length,
ctx.codeFeatures.verification,
`{`,
...generateElementProps(options, ctx, node, props, true, propsFailedExps),
`}`
);
yield `, ...__VLS_functionalComponentArgsRest(${var_functionalComponent}))${endOfLine}`;

componentCtxVar = var_defineComponentCtx;
currentComponent = node;

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

const refName = yield* generateVScope(options, ctx, node, props);
if (refName) {
ctx.usedComponentCtxVars.add(var_defineComponentCtx);
}

ctx.usedComponentCtxVars.add(componentCtxVar);
const usedComponentEventsVar = yield* generateElementEvents(options, ctx, node, var_functionalComponent, var_componentInstance, var_componentEmit, var_componentEvents);

if (var_defineComponentCtx && ctx.usedComponentCtxVars.has(var_defineComponentCtx)) {
yield `const ${componentCtxVar} = __VLS_nonNullable(__VLS_pickFunctionalComponentCtx(${var_originalComponent}, ${var_componentInstance}))${endOfLine}`;
if (refName) {
yield `// @ts-ignore${newLine}`;
if (node.codegenNode?.type === CompilerDOM.NodeTypes.VNODE_CALL
&& node.codegenNode.props?.type === CompilerDOM.NodeTypes.JS_OBJECT_EXPRESSION
&& node.codegenNode.props.properties.find(({ key }) => key.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && key.content === 'ref_for')
) {
yield `(${refName} ??= []).push(${var_defineComponentCtx})`;
} else {
yield `${refName} = ${var_defineComponentCtx}`;
}

yield endOfLine;
}
}
if (usedComponentEventsVar) {
yield `let ${var_componentEmit}!: typeof ${componentCtxVar}.emit${endOfLine}`;
ctx.usedComponentCtxVars.add(var_defineComponentCtx);
yield `let ${var_componentEmit}!: typeof ${var_defineComponentCtx}.emit${endOfLine}`;
yield `let ${var_componentEvents}!: __VLS_NormalizeEmits<typeof ${var_componentEmit}>${endOfLine}`;
}

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

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

if (ctx.usedComponentCtxVars.has(var_defineComponentCtx)) {
yield `const ${var_defineComponentCtx} = __VLS_pickFunctionalComponentCtx(${var_originalComponent}, ${var_componentInstance})${endOfLine}`;
if (refName) {
yield `// @ts-ignore${newLine}`;
if (node.codegenNode?.type === CompilerDOM.NodeTypes.VNODE_CALL
&& node.codegenNode.props?.type === CompilerDOM.NodeTypes.JS_OBJECT_EXPRESSION
&& node.codegenNode.props.properties.find(({ key }) => key.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && key.content === 'ref_for')
) {
yield `(${refName} ??= []).push(${var_defineComponentCtx})`;
} else {
yield `${refName} = ${var_defineComponentCtx}`;
}

yield endOfLine;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export function* generateElementChildren(
&& node.tagType !== CompilerDOM.ElementTypes.ELEMENT
&& node.tagType !== CompilerDOM.ElementTypes.TEMPLATE
) {
ctx.usedComponentCtxVars.add(componentCtxVar);
yield `__VLS_nonNullable(${componentCtxVar}.slots).`;
yield* wrapWith(
node.children[0].loc.start.offset,
Expand Down
32 changes: 29 additions & 3 deletions packages/language-core/lib/codegen/template/elementProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export function* generateElementProps(
if (shouldSpread) {
yield `...{ `;
}
const codeInfo = ctx.codeFeatures.withoutHighlightAndCompletion;
const codes = wrapWith(
prop.loc.start.offset,
prop.loc.end.offset,
Expand All @@ -121,8 +122,20 @@ export function* generateElementProps(
propName,
prop.arg.loc.start.offset,
{
...ctx.codeFeatures.withoutHighlightAndCompletion,
navigation: ctx.codeFeatures.withoutHighlightAndCompletion.navigation
...codeInfo,
verification: options.vueCompilerOptions.strictTemplates
? codeInfo.verification
: {
shouldReport(_source, code) {
if (String(code) === '2353' || String(code) === '2561') {
return false;
}
return typeof codeInfo.verification === 'object'
? codeInfo.verification.shouldReport?.(_source, code) ?? true
: true;
},
},
navigation: codeInfo.navigation
? {
resolveRenameNewName: camelize,
resolveRenameEditText: shouldCamelize ? hyphenateAttr : undefined,
Expand Down Expand Up @@ -183,6 +196,7 @@ export function* generateElementProps(
if (shouldSpread) {
yield `...{ `;
}
const codeInfo = ctx.codeFeatures.withoutHighlightAndCompletion;
const codes = conditionWrapWith(
enableCodeFeatures,
prop.loc.start.offset,
Expand All @@ -195,7 +209,19 @@ export function* generateElementProps(
prop.loc.start.offset,
shouldCamelize
? {
...ctx.codeFeatures.withoutHighlightAndCompletion,
...codeInfo,
verification: options.vueCompilerOptions.strictTemplates
? codeInfo.verification
: {
shouldReport(_source, code) {
if (String(code) === '2353' || String(code) === '2561') {
return false;
}
return typeof codeInfo.verification === 'object'
? codeInfo.verification.shouldReport?.(_source, code) ?? true
: true;
},
},
navigation: ctx.codeFeatures.withoutHighlightAndCompletion.navigation
? {
resolveRenameNewName: camelize,
Expand Down
15 changes: 11 additions & 4 deletions packages/language-core/lib/codegen/template/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export interface TemplateCodegenOptions {
template: NonNullable<Sfc['template']>;
scriptSetupBindingNames: Set<string>;
scriptSetupImportComponentNames: Set<string>;
edited: boolean;
templateRefNames: Map<string, string>;
hasDefineSlots?: boolean;
slotsAssignName?: string;
Expand All @@ -23,7 +24,7 @@ export interface TemplateCodegenOptions {
}

export function* generateTemplate(options: TemplateCodegenOptions): Generator<Code, TemplateCodegenContext> {
const ctx = createTemplateCodegenContext(options.scriptSetupBindingNames);
const ctx = createTemplateCodegenContext(options);

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

function* generatePreResolveComponents(): Generator<Code> {
yield `let __VLS_resolvedLocalAndGlobalComponents!: {}`;
yield `let __VLS_resolvedLocalAndGlobalComponents!: Required<{}`;
if (options.template.ast) {
const components = new Set<string>();
for (const node of forEachElementNode(options.template.ast)) {
if (
node.tagType === CompilerDOM.ElementTypes.COMPONENT
&& node.tag.toLowerCase() !== 'component'
&& !node.tag.includes('.') // namespace tag
) {
yield ` & __VLS_WithComponent<'${getCanonicalComponentName(node.tag)}', typeof __VLS_localComponents, `;
if (components.has(node.tag)) {
continue;
}
components.add(node.tag);
yield newLine;
yield ` & __VLS_WithComponent<'${getCanonicalComponentName(node.tag)}', typeof __VLS_ctx, typeof __VLS_localComponents, `;
yield getPossibleOriginalComponentNames(node.tag, false)
.map(name => `"${name}"`)
.join(', ');
yield `>`;
}
}
}
yield endOfLine;
yield `>${endOfLine}`;
}
}

Expand Down
Loading

0 comments on commit 5936c82

Please sign in to comment.