Skip to content

Commit

Permalink
fix(language-core): hoist $refs type (#4763)
Browse files Browse the repository at this point in the history
  • Loading branch information
johnsoncodehk authored Aug 30, 2024
1 parent 950c9e1 commit 306936b
Show file tree
Hide file tree
Showing 12 changed files with 81 additions and 95 deletions.
2 changes: 1 addition & 1 deletion packages/language-core/lib/codegen/globalTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ declare function __VLS_asFunctionalComponent<T, K = T extends new (...args: any)
: T extends (...args: any) => any ? T
: (_: {}${strictTemplates ? '' : ' & Record<string, unknown>'}, ctx?: any) => { __ctx?: { attrs?: any, expose?: any, slots?: any, emit?: any, props?: {}${strictTemplates ? '' : ' & Record<string, unknown>'} } };
declare function __VLS_elementAsFunction<T>(tag: T, endTag?: T): (_: T${strictTemplates ? '' : ' & Record<string, unknown>'}) => void;
declare function __VLS_functionalComponentArgsRest<T extends (...args: any) => any>(t: T): Parameters<T>['length'] extends 2 ? [any] : [];
declare function __VLS_functionalComponentArgsRest<T extends (...args: any) => any>(t: T): 2 extends Parameters<T>['length'] ? [any] : [];
declare 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
Expand Down
15 changes: 0 additions & 15 deletions packages/language-core/lib/codegen/localTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,19 +80,6 @@ type __VLS_TypePropsToOption<T> = {
`__VLS_OmitIndexSignature`,
() => `type __VLS_OmitIndexSignature<T> = { [K in keyof T as {} extends Record<K, unknown> ? never : K]: T[K]; }${endOfLine}`
);
const PickRefsExpose = defineHelper(
`__VLS_PickRefsExpose`,
() => `
type __VLS_PickRefsExpose<T> = T extends object
? { [K in keyof T]: (T[K] extends any[]
? Parameters<T[K][0]['expose']>[0][]
: T[K] extends { expose?: (exposed: infer E) => void }
? E
: T[K]) | null }
: never;
`.trimStart()
);

const helpers = {
[PrettifyLocal.name]: PrettifyLocal,
[OmitKeepDiscriminatedUnion.name]: OmitKeepDiscriminatedUnion,
Expand All @@ -101,7 +88,6 @@ type __VLS_PickRefsExpose<T> = T extends object
[PropsChildren.name]: PropsChildren,
[TypePropsToOption.name]: TypePropsToOption,
[OmitIndexSignature.name]: OmitIndexSignature,
[PickRefsExpose.name]: PickRefsExpose,
};
used.clear();

Expand All @@ -117,7 +103,6 @@ type __VLS_PickRefsExpose<T> = T extends object
get PropsChildren() { return PropsChildren.name; },
get TypePropsToOption() { return TypePropsToOption.name; },
get OmitIndexSignature() { return OmitIndexSignature.name; },
get PickRefsExpose() { return PickRefsExpose.name; },
};

function* generate(names: string[]) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,6 @@ export function* generateInternalComponent(
}
yield `}${endOfLine}`; // return {
yield `},${newLine}`; // setup() {
if (options.vueCompilerOptions.target >= 3.5) {
yield `__typeRefs: {} as __VLS_Refs,${newLine}`;
}
if (options.sfc.scriptSetup && options.scriptSetupRanges && !ctx.bypassDefineComponent) {
const emitOptionCodes = [...generateEmitsOption(options, options.sfc.scriptSetup, options.scriptSetupRanges)];
for (const code of emitOptionCodes) {
Expand Down
33 changes: 23 additions & 10 deletions packages/language-core/lib/codegen/script/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,34 @@ export function* generateTemplateCtx(
options: ScriptCodegenOptions,
isClassComponent: boolean
): Generator<Code> {
const types = [];
const exps = [];
if (isClassComponent) {
types.push(`typeof this`);
exps.push(`this`);
}
else {
types.push(`InstanceType<__VLS_PickNotAny<typeof __VLS_internalComponent, new () => {}>>`);
exps.push(`{} as InstanceType<__VLS_PickNotAny<typeof __VLS_internalComponent, new () => {}>>`);
}
if (options.vueCompilerOptions.petiteVueExtensions.some(ext => options.fileBaseName.endsWith(ext))) {
types.push(`typeof globalThis`);
exps.push(`globalThis`);
}
if (options.sfc.styles.some(style => style.module)) {
types.push(`__VLS_StyleModules`);
exps.push(`{} as __VLS_StyleModules`);
}

yield `const __VLS_ctx = `;
if (exps.length === 1) {
yield exps[0];
yield endOfLine;
}
else {
yield `{${newLine}`;
for (const exp of exps) {
yield `...`;
yield exp;
yield `,${newLine}`;
}
yield `}${endOfLine}`;
}
yield `let __VLS_ctx!: ${types.join(' & ')}${endOfLine}`;
}

export function* generateTemplateComponents(options: ScriptCodegenOptions): Generator<Code> {
Expand Down Expand Up @@ -87,7 +101,7 @@ export function* generateTemplate(
});
yield* generateTemplateCtx(options, isClassComponent);
yield* generateTemplateComponents(options);
yield* generateTemplateBody(options, ctx, templateCodegenCtx);
yield* generateTemplateBody(options, templateCodegenCtx);
yield* generateInternalComponent(options, ctx, templateCodegenCtx);
}
else {
Expand All @@ -100,7 +114,6 @@ export function* generateTemplate(

function* generateTemplateBody(
options: ScriptCodegenOptions,
ctx: ScriptCodegenContext,
templateCodegenCtx: TemplateCodegenContext
): Generator<Code> {
const firstClasses = new Set<string>();
Expand Down Expand Up @@ -142,14 +155,14 @@ function* generateTemplateBody(
yield `// no template${newLine}`;
if (!options.scriptSetupRanges?.slots.define) {
yield `const __VLS_slots = {}${endOfLine}`;
yield `const __VLS_refs = {}${endOfLine}`;
yield `const $refs = {}${endOfLine}`;
yield `const __VLS_inheritedAttrs = {}${endOfLine}`;
}
}

yield `const __VLS_templateResult = {`;
yield `slots: ${options.scriptSetupRanges?.slots.name ?? '__VLS_slots'},${newLine}`;
yield `refs: __VLS_refs as ${ctx.localTypes.PickRefsExpose}<typeof __VLS_refs>,${newLine}`;
yield `refs: $refs,${newLine}`;
yield `attrs: {} as Partial<typeof __VLS_inheritedAttrs>,${newLine}`;
yield `}${endOfLine}`;
}
Expand Down
38 changes: 14 additions & 24 deletions packages/language-core/lib/codegen/template/element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,19 @@ export function* generateComponent(

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

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 `var ${varName} = [{} as Parameters<typeof ${var_defineComponentCtx}['expose']>[0]]${endOfLine}`;
} else {
yield `var ${varName} = {} as Parameters<typeof ${var_defineComponentCtx}['expose']>[0]${endOfLine}`;
}
}

const usedComponentEventsVar = yield* generateElementEvents(options, ctx, node, var_functionalComponent, var_componentInstance, var_componentEmit, var_componentEvents);
Expand Down Expand Up @@ -257,19 +269,6 @@ export function* generateComponent(

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 Expand Up @@ -335,14 +334,7 @@ export function* generateElement(

const refName = yield* generateVScope(options, ctx, node, node.props);
if (refName) {
yield `// @ts-ignore${newLine}`;
yield `${refName} = __VLS_intrinsicElements`;
yield* generatePropertyAccess(
options,
ctx,
node.tag
);
yield endOfLine;
options.templateRefNames.set(refName, `__VLS_intrinsicElements['${node.tag}']`);
}

const slotDir = node.props.find(p => p.type === CompilerDOM.NodeTypes.DIRECTIVE && p.name === 'slot') as CompilerDOM.DirectiveNode;
Expand Down Expand Up @@ -574,9 +566,7 @@ function* generateReferencesForElements(
ctx.accessExternalVariable(content, startOffset);
}

const refName = CompilerDOM.toValidAssetId(prop.value.content, '_VLS_refs' as any);
options.templateRefNames.set(prop.value.content, refName);
return refName;
return prop.value.content;
}
}
}
Expand Down
11 changes: 5 additions & 6 deletions packages/language-core/lib/codegen/template/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export function* generateTemplate(options: TemplateCodegenOptions): Generator<Co
if (options.propsAssignName) {
ctx.addLocalVariable(options.propsAssignName);
}
ctx.addLocalVariable('$refs');

yield* generatePreResolveComponents();

Expand All @@ -51,19 +52,17 @@ export function* generateTemplate(options: TemplateCodegenOptions): Generator<Co

yield* ctx.generateAutoImportCompletion();

yield* generateRefs()
yield* generateRefs();

return ctx;

function* generateRefs(): Generator<Code> {
for (const [, validId] of options.templateRefNames) {
yield `let ${validId}${newLine}`;
}
yield `const __VLS_refs = {${newLine}`;
for (const [name, validId] of options.templateRefNames) {
yield `'${name}': ${validId}!,${newLine}`;
for (const [name, varName] of options.templateRefNames) {
yield `'${name}': ${varName}!,${newLine}`;
}
yield `}${endOfLine}`;
yield `declare var $refs: typeof __VLS_refs${endOfLine}`;
}

function* generateSlotsType(): Generator<Code> {
Expand Down
10 changes: 8 additions & 2 deletions packages/language-core/lib/parsers/scriptSetupRanges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ export function parseScriptSetupRanges(
name?: string;
define?: ReturnType<typeof parseDefineFunction>;
}[] = [];

const definePropProposalA = vueCompilerOptions.experimentalDefinePropProposal === 'kevinEdition' || ast.text.trimStart().startsWith('// @experimentalDefinePropProposal=kevinEdition');
const definePropProposalB = vueCompilerOptions.experimentalDefinePropProposal === 'johnsonEdition' || ast.text.trimStart().startsWith('// @experimentalDefinePropProposal=johnsonEdition');
const defineProp: {
Expand All @@ -63,11 +62,12 @@ export function parseScriptSetupRanges(
required: boolean;
isModel?: boolean;
}[] = [];
const bindings = parseBindingRanges(ts, ast);
const text = ast.text;
const leadingCommentEndOffset = ts.getLeadingCommentRanges(text, 0)?.reverse()[0].end ?? 0;
const importComponentNames = new Set<string>();

let bindings = parseBindingRanges(ts, ast);

ts.forEachChild(ast, node => {
const isTypeExport = (ts.isTypeAliasDeclaration(node) || ts.isInterfaceDeclaration(node)) && node.modifiers?.some(mod => mod.kind === ts.SyntaxKind.ExportKeyword);
if (
Expand Down Expand Up @@ -102,6 +102,12 @@ export function parseScriptSetupRanges(
});
ts.forEachChild(ast, child => visitNode(child, [ast]));

const templateRefNames = new Set(templateRefs.map(ref => ref.name));
bindings = bindings.filter(range => {
const name = text.substring(range.start, range.end);
return !templateRefNames.has(name);
});

return {
leadingCommentEndOffset,
importSectionEndOffset,
Expand Down
32 changes: 4 additions & 28 deletions packages/tsc/tests/__snapshots__/dts.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -645,26 +645,20 @@ declare var __VLS_3: {
str: string;
};
declare var __VLS_inheritedAttrs: {};
declare const __VLS_refs: {};
declare const __VLS_templateResult: {
slots: {
"no-bind"?(_: typeof __VLS_0): any;
default?(_: typeof __VLS_1): any;
"named-slot"?(_: typeof __VLS_2): any;
vbind?(_: typeof __VLS_3): any;
};
refs: __VLS_PickRefsExpose<typeof __VLS_refs>;
refs: {};
attrs: Partial<typeof __VLS_inheritedAttrs>;
};
type __VLS_Slots = typeof __VLS_templateResult['slots'];
declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}, {}>;
declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, __VLS_Slots>;
export default _default;
type __VLS_PickRefsExpose<T> = T extends object ? {
[K in keyof T]: (T[K] extends any[] ? Parameters<T[K][0]['expose']>[0][] : T[K] extends {
expose?: (exposed: infer E) => void;
} ? E : T[K]) | null;
} : never;
type __VLS_WithTemplateSlots<T, S> = T & {
new (): {
$slots: S;
Expand All @@ -676,7 +670,6 @@ type __VLS_WithTemplateSlots<T, S> = T & {
exports[`vue-tsc-dts > Input: template-slots/component-define-slots.vue, Output: template-slots/component-define-slots.vue.d.ts 1`] = `
"import { VNode } from 'vue';
declare var __VLS_inheritedAttrs: {};
declare const __VLS_refs: {};
declare const __VLS_templateResult: {
slots: Readonly<{
default: (props: {
Expand All @@ -703,18 +696,13 @@ declare const __VLS_templateResult: {
}) => VNode[];
'no-bind': () => VNode[];
};
refs: __VLS_PickRefsExpose<typeof __VLS_refs>;
refs: {};
attrs: Partial<typeof __VLS_inheritedAttrs>;
};
type __VLS_Slots = typeof __VLS_templateResult['slots'];
declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}, {}>;
declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, __VLS_Slots>;
export default _default;
type __VLS_PickRefsExpose<T> = T extends object ? {
[K in keyof T]: (T[K] extends any[] ? Parameters<T[K][0]['expose']>[0][] : T[K] extends {
expose?: (exposed: infer E) => void;
} ? E : T[K]) | null;
} : never;
type __VLS_WithTemplateSlots<T, S> = T & {
new (): {
$slots: S;
Expand All @@ -725,7 +713,6 @@ type __VLS_WithTemplateSlots<T, S> = T & {
exports[`vue-tsc-dts > Input: template-slots/component-destructuring.vue, Output: template-slots/component-destructuring.vue.d.ts 1`] = `
"declare var __VLS_inheritedAttrs: {};
declare const __VLS_refs: {};
declare const __VLS_templateResult: {
slots: Readonly<{
bottom: (props: {
Expand All @@ -736,18 +723,13 @@ declare const __VLS_templateResult: {
num: number;
}) => any[];
};
refs: __VLS_PickRefsExpose<typeof __VLS_refs>;
refs: {};
attrs: Partial<typeof __VLS_inheritedAttrs>;
};
type __VLS_Slots = typeof __VLS_templateResult['slots'];
declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}, {}>;
declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, __VLS_Slots>;
export default _default;
type __VLS_PickRefsExpose<T> = T extends object ? {
[K in keyof T]: (T[K] extends any[] ? Parameters<T[K][0]['expose']>[0][] : T[K] extends {
expose?: (exposed: infer E) => void;
} ? E : T[K]) | null;
} : never;
type __VLS_WithTemplateSlots<T, S> = T & {
new (): {
$slots: S;
Expand All @@ -769,26 +751,20 @@ declare var __VLS_3: {
str: string;
};
declare var __VLS_inheritedAttrs: {};
declare const __VLS_refs: {};
declare const __VLS_templateResult: {
slots: {
"no-bind"?(_: typeof __VLS_0): any;
default?(_: typeof __VLS_1): any;
"named-slot"?(_: typeof __VLS_2): any;
vbind?(_: typeof __VLS_3): any;
};
refs: __VLS_PickRefsExpose<typeof __VLS_refs>;
refs: {};
attrs: Partial<typeof __VLS_inheritedAttrs>;
};
type __VLS_Slots = typeof __VLS_templateResult['slots'];
declare const __VLS_component: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}, {}>;
declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, __VLS_Slots>;
export default _default;
type __VLS_PickRefsExpose<T> = T extends object ? {
[K in keyof T]: (T[K] extends any[] ? Parameters<T[K][0]['expose']>[0][] : T[K] extends {
expose?: (exposed: infer E) => void;
} ? E : T[K]) | null;
} : never;
type __VLS_WithTemplateSlots<T, S> = T & {
new (): {
$slots: S;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
declare module 'vue3.5' {
export interface GlobalComponents {
Generic: typeof import('./generic.vue')['default'];
}
}

export { };
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ import { exactType } from '../../shared';
<template>
<TemplateRef ref="templateRef" />

{{ exactType($refs.templateRef?.$refs.generic?.foo, {} as 1 | undefined) }}
{{ exactType($refs.templateRef.$refs.generic.foo, {} as 1) }}
</template>
Loading

0 comments on commit 306936b

Please sign in to comment.