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

refactor(language-core): reduce virtual code generated by component tags #4714

Merged
merged 18 commits into from
Aug 27, 2024
Merged
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
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