8000 feat(language-core): typed directives in template (#4807) · vuejs/language-tools@410f929 · GitHub
[go: up one dir, main page]

Skip to content

Commit 410f929

Browse files
feat(language-core): typed directives in template (#4807)
* feat: typed directives in template * fix: compatibility * format * Update main.vue * Update globalTypes.ts * fixes --------- Co-authored-by: Johnson Chu <johnsoncodehk@gmail.com>
1 parent 408dca1 commit 410f929

File tree

7 files changed

+79
-11
lines changed

7 files changed

+79
-11
lines changed

packages/language-core/lib/codegen/globalTypes.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,16 @@ import { getSlotsPropertyName } from '../utils/shared';
22

33
export function generateGlobalTypes(lib: string, target: number, strictTemplates: boolean) {
44
const fnPropsType = `(K extends { $props: infer Props } ? Props : any)${strictTemplates ? '' : ' & Record<string, unknown>'}`;
5-
return `
5+
let text = ``;
6+
if (target < 3.5) {
7+
text += `
68
; declare module '${lib}' {
79
export interface GlobalComponents { }
8-
}
9-
declare global {
10+
export interface GlobalDirectives { }
11+
}`;
12+
}
13+
text += `
14+
; declare global {
1015
const __VLS_intrinsicElements: __VLS_IntrinsicElements;
1116
const __VLS_directiveBindingRestFields: { instance: null, oldValue: null, modifiers: any, dir: any };
1217
const __VLS_unref: typeof import('${lib}').unref;
@@ -28,9 +33,10 @@ declare global {
2833
)}
2934
type __VLS_GlobalComponents = ${(
3035
target >= 3.5
31-
? `import('${lib}').GlobalComponents`
36+
? `import('${lib}').GlobalComponents;`
3237
: `import('${lib}').GlobalComponents & Pick<typeof import('${lib}'), 'Transition' | 'TransitionGroup' | 'KeepAlive' | 'Suspense' | 'Teleport'>;`
3338
)}
39+
type __VLS_GlobalDirectives = import('${lib}').GlobalDirectives;
3440
type __VLS_IsAny<T> = 0 extends 1 & T ? true : false;
3541
type __VLS_PickNotAny<A, B> = __VLS_IsAny<A> extends true ? B : A;
3642
type __VLS_unknownDirective = (arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown) => void;
@@ -131,4 +137,5 @@ declare global {
131137
function __VLS_tryAsConstant<const T>(t: T): T;
132138
}
133139
`;
140+
return text;
134141
};

packages/language-core/lib/codegen/script/template.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,34 @@ function* generateTemplateComponents(options: ScriptCodegenOptions): Generator<C
8484
}
8585
yield `}${endOfLine}`;
8686

87-
yield `let __VLS_components: typeof __VLS_localComponents & __VLS_GlobalComponents${endOfLine}`;
87+
yield `let __VLS_components!: typeof __VLS_localComponents & __VLS_GlobalComponents${endOfLine}`;
88+
}
89+
90+
export function* generateTemplateDirectives(options: ScriptCodegenOptions): Generator<Code> {
91+
const exps: Code[] = [];
92+
93+
if (options.sfc.script && options.scriptRanges?.exportDefault?.directivesOption) {
94+
const { directivesOption } = options.scriptRanges.exportDefault;
95+
exps.push([
96+
options.sfc.script.content.substring(directivesOption.start, directivesOption.end),
97+
'script',
98+
directivesOption.start,
99+
codeFeatures.navigation,
100+
]);
101+
}
102+
103+
exps.push(`{} as NonNullable<typeof __VLS_self extends { directives: infer D } ? D : {}>`);
104+
exps.push(`__VLS_ctx`);
105+
106+
yield `const __VLS_localDirectives = {${newLine}`;
107+
for (const type of exps) {
108+
yield `...`;
109+
yield type;
110+
yield `,${newLine}`;
111+
}
112+
yield `}${endOfLine}`;
113+
114+
yield `let __VLS_directives!: typeof __VLS_localDirectives & __VLS_GlobalDirectives${endOfLine}`;
88115
}
89116

90117
export function* generateTemplate(
@@ -100,6 +127,7 @@ export function* generateTemplate(
100127
});
101128
yield* generateTemplateCtx(options, isClassComponent);
102129
yield* generateTemplateComponents(options);
130+
yield* generateTemplateDirectives(options);
103131
yield* generateTemplateBody(options, templateCodegenCtx);
104132
return templateCodegenCtx;
105133
}

packages/language-core/lib/codegen/template/elementDirectives.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export function* generateElementDirectives(
4343
prop.loc.start.offset,
4444
prop.loc.end.offset,
4545
ctx.codeFeatures.verification,
46-
`__VLS_directiveAsFunction(__VLS_ctx.`,
46+
`__VLS_directiveAsFunction(__VLS_directives.`,
4747
...generateCamelized(
4848
'v-' + prop.name,
4949
prop.loc.start.offset,

packages/language-core/lib/parsers/scriptRanges.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export function parseScriptRanges(ts: typeof import('typescript'), ast: ts.Sourc
1212
argsNode: ts.ObjectLiteralExpression | undefined,
1313
componentsOption: TextRange | undefined,
1414
componentsOptionNode: ts.ObjectLiteralExpression | undefined,
15+
directivesOption: TextRange | undefined,
1516
nameOption: TextRange | undefined,
1617
inheritAttrsOption: string | undefined,
1718
}) | undefined;
@@ -40,6 +41,7 @@ export function parseScriptRanges(ts: typeof import('typescript'), ast: ts.Sourc
4041
}
4142
if (obj) {
4243
let componentsOptionNode: ts.ObjectLiteralExpression | undefined;
44+
let directivesOptionNode: ts.ObjectLiteralExpression | undefined;
4345
let nameOptionNode: ts.Expression | undefined;
4446
let inheritAttrsOption: string | undefined;
4547
ts.forEachChild(obj, node => {
@@ -48,10 +50,13 @@ export function parseScriptRanges(ts: typeof import('typescript'), ast: ts.Sourc
4850
if (name === 'components' && ts.isObjectLiteralExpression(node.initializer)) {
4951
componentsOptionNode = node.initializer;
5052
}
51-
if (name === 'name') {
53+
else if (name === 'directives' && ts.isObjectLiteralExpression(node.initializer)) {
54+
directivesOptionNode = node.initializer;
55+
}
56+
else if (name === 'name') {
5257
nameOptionNode = node.initializer;
5358
}
54-
if (name === 'inheritAttrs') {
59+
else if (name === 'inheritAttrs') {
5560
inheritAttrsOption = getNodeText(ts, node.initializer, ast);
5661
}
5762
}
@@ -63,6 +68,7 @@ export function parseScriptRanges(ts: typeof import('typescript'), ast: ts.Sourc
6368
argsNode: withNode ? obj : undefined,
6469
componentsOption: componentsOptionNode ? _getStartEnd(componentsOptionNode) : undefined,
6570
componentsOptionNode: withNode ? componentsOptionNode : undefined,
71+
directivesOption: directivesOptionNode ? _getStartEnd(directivesOptionNode) : undefined,
6672
nameOption: nameOptionNode ? _getStartEnd(nameOptionNode) : undefined,
6773
inheritAttrsOption,
6874
};

test-workspace/tsc/passedFixtures/vue2/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"../vue3/defineEmits",
3030
"../vue3/defineModel",
3131
"../vue3/defineProp_B",
32+
"../vue3/directives/option.vue",
3233
"../vue3/events",
3334
"../vue3/no-script-block",
3435
"../vue3/slots",

test-workspace/tsc/passedFixtures/vue3/directives/main.vue

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,18 @@
22
import { FunctionDirective } from 'vue';
33
import { exactType } from '../../shared';
44
5-
let Foo: (_: { foo?: string; }) => void;
5+
declare module 'vue' {
6+
interface GlobalDirectives {
7+
vFoo: FunctionDirective<typeof Comp, (_: number) => void>;
8+
}
9+
}
610
7-
let vFoo: FunctionDirective<typeof Foo, (_: number) => void>;
11+
let Comp!: (_: { foo?: string; }) => void;
12+
13+
let vBar!: FunctionDirective<typeof Comp, (_: boolean) => void>;
814
</script>
915

1016
<template>
11-
<Foo v-foo="v => exactType(v, {} as number)"></Foo>
17+
<Comp v-foo="v => exactType(v, {} as number)" />
18+
<Comp v-bar="v => exactType(v, {} as boolean)" />
1219
</template>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<script lang="ts">
2+
import { FunctionDirective } from 'vue';
3+
import { exactType } from '../../shared';
4+
5+
let Comp!: (_: { foo?: string; }) => void;
6+
7+
export default {
8+
directives: {
9+
vBaz: {} as FunctionDirective<typeof Comp, (_: string) => void>
10+
}
11+
};
12+
</script>
13+
14+
<script setup lang="ts">
15+
</script>
16+
17+
<template>
18+
<Comp v-baz="v => exactType(v, {} as string)" />
19+
</template>

0 commit comments

Comments
 (0)
0