Skip to content

Commit

Permalink
feat(language-core): typed directives in template (#4807)
Browse files Browse the repository at this point in the history
* feat: typed directives in template

* fix: compatibility

* format

* Update main.vue

* Update globalTypes.ts

* fixes

---------

Co-authored-by: Johnson Chu <johnsoncodehk@gmail.com>
  • Loading branch information
KazariEX and johnsoncodehk authored Sep 4, 2024
1 parent 408dca1 commit 410f929
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 11 deletions.
15 changes: 11 additions & 4 deletions packages/language-core/lib/codegen/globalTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@ import { getSlotsPropertyName } from '../utils/shared';

export function generateGlobalTypes(lib: string, target: number, strictTemplates: boolean) {
const fnPropsType = `(K extends { $props: infer Props } ? Props : any)${strictTemplates ? '' : ' & Record<string, unknown>'}`;
return `
let text = ``;
if (target < 3.5) {
text += `
; declare module '${lib}' {
export interface GlobalComponents { }
}
declare global {
export interface GlobalDirectives { }
}`;
}
text += `
; declare global {
const __VLS_intrinsicElements: __VLS_IntrinsicElements;
const __VLS_directiveBindingRestFields: { instance: null, oldValue: null, modifiers: any, dir: any };
const __VLS_unref: typeof import('${lib}').unref;
Expand All @@ -28,9 +33,10 @@ declare global {
)}
type __VLS_GlobalComponents = ${(
target >= 3.5
? `import('${lib}').GlobalComponents`
? `import('${lib}').GlobalComponents;`
: `import('${lib}').GlobalComponents & Pick<typeof import('${lib}'), 'Transition' | 'TransitionGroup' | 'KeepAlive' | 'Suspense' | 'Teleport'>;`
)}
type __VLS_GlobalDirectives = import('${lib}').GlobalDirectives;
type __VLS_IsAny<T> = 0 extends 1 & T ? true : false;
type __VLS_PickNotAny<A, B> = __VLS_IsAny<A> extends true ? B : A;
type __VLS_unknownDirective = (arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown) => void;
Expand Down Expand Up @@ -131,4 +137,5 @@ declare global {
function __VLS_tryAsConstant<const T>(t: T): T;
}
`;
return text;
};
30 changes: 29 additions & 1 deletion packages/language-core/lib/codegen/script/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,34 @@ function* generateTemplateComponents(options: ScriptCodegenOptions): Generator<C
}
yield `}${endOfLine}`;

yield `let __VLS_components: typeof __VLS_localComponents & __VLS_GlobalComponents${endOfLine}`;
yield `let __VLS_components!: typeof __VLS_localComponents & __VLS_GlobalComponents${endOfLine}`;
}

export function* generateTemplateDirectives(options: ScriptCodegenOptions): Generator<Code> {
const exps: Code[] = [];

if (options.sfc.script && options.scriptRanges?.exportDefault?.directivesOption) {
const { directivesOption } = options.scriptRanges.exportDefault;
exps.push([
options.sfc.script.content.substring(directivesOption.start, directivesOption.end),
'script',
directivesOption.start,
codeFeatures.navigation,
]);
}

exps.push(`{} as NonNullable<typeof __VLS_self extends { directives: infer D } ? D : {}>`);
exps.push(`__VLS_ctx`);

yield `const __VLS_localDirectives = {${newLine}`;
for (const type of exps) {
yield `...`;
yield type;
yield `,${newLine}`;
}
yield `}${endOfLine}`;

yield `let __VLS_directives!: typeof __VLS_localDirectives & __VLS_GlobalDirectives${endOfLine}`;
}

export function* generateTemplate(
Expand All @@ -100,6 +127,7 @@ export function* generateTemplate(
});
yield* generateTemplateCtx(options, isClassComponent);
yield* generateTemplateComponents(options);
yield* generateTemplateDirectives(options);
yield* generateTemplateBody(options, templateCodegenCtx);
return templateCodegenCtx;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export function* generateElementDirectives(
prop.loc.start.offset,
prop.loc.end.offset,
ctx.codeFeatures.verification,
`__VLS_directiveAsFunction(__VLS_ctx.`,
`__VLS_directiveAsFunction(__VLS_directives.`,
...generateCamelized(
'v-' + prop.name,
prop.loc.start.offset,
Expand Down
10 changes: 8 additions & 2 deletions packages/language-core/lib/parsers/scriptRanges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export function parseScriptRanges(ts: typeof import('typescript'), ast: ts.Sourc
argsNode: ts.ObjectLiteralExpression | undefined,
componentsOption: TextRange | undefined,
componentsOptionNode: ts.ObjectLiteralExpression | undefined,
directivesOption: TextRange | undefined,
nameOption: TextRange | undefined,
inheritAttrsOption: string | undefined,
}) | undefined;
Expand Down Expand Up @@ -40,6 +41,7 @@ export function parseScriptRanges(ts: typeof import('typescript'), ast: ts.Sourc
}
if (obj) {
let componentsOptionNode: ts.ObjectLiteralExpression | undefined;
let directivesOptionNode: ts.ObjectLiteralExpression | undefined;
let nameOptionNode: ts.Expression | undefined;
let inheritAttrsOption: string | undefined;
ts.forEachChild(obj, node => {
Expand All @@ -48,10 +50,13 @@ export function parseScriptRanges(ts: typeof import('typescript'), ast: ts.Sourc
if (name === 'components' && ts.isObjectLiteralExpression(node.initializer)) {
componentsOptionNode = node.initializer;
}
if (name === 'name') {
else if (name === 'directives' && ts.isObjectLiteralExpression(node.initializer)) {
directivesOptionNode = node.initializer;
}
else if (name === 'name') {
nameOptionNode = node.initializer;
}
if (name === 'inheritAttrs') {
else if (name === 'inheritAttrs') {
inheritAttrsOption = getNodeText(ts, node.initializer, ast);
}
}
Expand All @@ -63,6 +68,7 @@ export function parseScriptRanges(ts: typeof import('typescript'), ast: ts.Sourc
argsNode: withNode ? obj : undefined,
componentsOption: componentsOptionNode ? _getStartEnd(componentsOptionNode) : undefined,
componentsOptionNode: withNode ? componentsOptionNode : undefined,
directivesOption: directivesOptionNode ? _getStartEnd(directivesOptionNode) : undefined,
nameOption: nameOptionNode ? _getStartEnd(nameOptionNode) : undefined,
inheritAttrsOption,
};
Expand Down
1 change: 1 addition & 0 deletions test-workspace/tsc/passedFixtures/vue2/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"../vue3/defineEmits",
"../vue3/defineModel",
"../vue3/defineProp_B",
"../vue3/directives/option.vue",
"../vue3/events",
"../vue3/no-script-block",
"../vue3/slots",
Expand Down
13 changes: 10 additions & 3 deletions test-workspace/tsc/passedFixtures/vue3/directives/main.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,18 @@
import { FunctionDirective } from 'vue';
import { exactType } from '../../shared';
let Foo: (_: { foo?: string; }) => void;
declare module 'vue' {
interface GlobalDirectives {
vFoo: FunctionDirective<typeof Comp, (_: number) => void>;
}
}
let vFoo: FunctionDirective<typeof Foo, (_: number) => void>;
let Comp!: (_: { foo?: string; }) => void;
let vBar!: FunctionDirective<typeof Comp, (_: boolean) => void>;
</script>

<template>
<Foo v-foo="v => exactType(v, {} as number)"></Foo>
<Comp v-foo="v => exactType(v, {} as number)" />
<Comp v-bar="v => exactType(v, {} as boolean)" />
</template>
19 changes: 19 additions & 0 deletions test-workspace/tsc/passedFixtures/vue3/directives/option.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<script lang="ts">
import { FunctionDirective } from 'vue';
import { exactType } from '../../shared';
let Comp!: (_: { foo?: string; }) => void;
export default {
directives: {
vBaz: {} as FunctionDirective<typeof Comp, (_: string) => void>
}
};
</script>

<script setup lang="ts">
</script>

<template>
<Comp v-baz="v => exactType(v, {} as string)" />
</template>

0 comments on commit 410f929

Please sign in to comment.