diff --git a/packages/language-core/lib/codegen/globalTypes.ts b/packages/language-core/lib/codegen/globalTypes.ts index 28dab72d37..0c704a71d3 100644 --- a/packages/language-core/lib/codegen/globalTypes.ts +++ b/packages/language-core/lib/codegen/globalTypes.ts @@ -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'}`; - 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; @@ -28,9 +33,10 @@ declare global { )} type __VLS_GlobalComponents = ${( target >= 3.5 - ? `import('${lib}').GlobalComponents` + ? `import('${lib}').GlobalComponents;` : `import('${lib}').GlobalComponents & Pick;` )} + type __VLS_GlobalDirectives = import('${lib}').GlobalDirectives; type __VLS_IsAny = 0 extends 1 & T ? true : false; type __VLS_PickNotAny = __VLS_IsAny extends true ? B : A; type __VLS_unknownDirective = (arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown) => void; @@ -131,4 +137,5 @@ declare global { function __VLS_tryAsConstant(t: T): T; } `; + return text; }; diff --git a/packages/language-core/lib/codegen/script/template.ts b/packages/language-core/lib/codegen/script/template.ts index 43b95da7c9..44dbbc59ce 100644 --- a/packages/language-core/lib/codegen/script/template.ts +++ b/packages/language-core/lib/codegen/script/template.ts @@ -84,7 +84,34 @@ function* generateTemplateComponents(options: ScriptCodegenOptions): Generator { + 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`); + 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( @@ -100,6 +127,7 @@ export function* generateTemplate( }); yield* generateTemplateCtx(options, isClassComponent); yield* generateTemplateComponents(options); + yield* generateTemplateDirectives(options); yield* generateTemplateBody(options, templateCodegenCtx); return templateCodegenCtx; } diff --git a/packages/language-core/lib/codegen/template/elementDirectives.ts b/packages/language-core/lib/codegen/template/elementDirectives.ts index edf88ab15b..06869d0392 100644 --- a/packages/language-core/lib/codegen/template/elementDirectives.ts +++ b/packages/language-core/lib/codegen/template/elementDirectives.ts @@ -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, diff --git a/packages/language-core/lib/parsers/scriptRanges.ts b/packages/language-core/lib/parsers/scriptRanges.ts index 01e5101e81..394eb16dce 100644 --- a/packages/language-core/lib/parsers/scriptRanges.ts +++ b/packages/language-core/lib/parsers/scriptRanges.ts @@ -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; @@ -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 => { @@ -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); } } @@ -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, }; diff --git a/test-workspace/tsc/passedFixtures/vue2/tsconfig.json b/test-workspace/tsc/passedFixtures/vue2/tsconfig.json index 13e6c0f8f6..1ff36d1175 100644 --- a/test-workspace/tsc/passedFixtures/vue2/tsconfig.json +++ b/test-workspace/tsc/passedFixtures/vue2/tsconfig.json @@ -29,6 +29,7 @@ "../vue3/defineEmits", "../vue3/defineModel", "../vue3/defineProp_B", + "../vue3/directives/option.vue", "../vue3/events", "../vue3/no-script-block", "../vue3/slots", diff --git a/test-workspace/tsc/passedFixtures/vue3/directives/main.vue b/test-workspace/tsc/passedFixtures/vue3/directives/main.vue index 006440002f..05bf31f895 100644 --- a/test-workspace/tsc/passedFixtures/vue3/directives/main.vue +++ b/test-workspace/tsc/passedFixtures/vue3/directives/main.vue @@ -2,11 +2,18 @@ import { FunctionDirective } from 'vue'; import { exactType } from '../../shared'; -let Foo: (_: { foo?: string; }) => void; +declare module 'vue' { + interface GlobalDirectives { + vFoo: FunctionDirective void>; + } +} -let vFoo: FunctionDirective void>; +let Comp!: (_: { foo?: string; }) => void; + +let vBar!: FunctionDirective void>; diff --git a/test-workspace/tsc/passedFixtures/vue3/directives/option.vue b/test-workspace/tsc/passedFixtures/vue3/directives/option.vue new file mode 100644 index 0000000000..10878f25f9 --- /dev/null +++ b/test-workspace/tsc/passedFixtures/vue3/directives/option.vue @@ -0,0 +1,19 @@ + + + + +