diff --git a/packages/language-core/lib/codegen/globalTypes.ts b/packages/language-core/lib/codegen/globalTypes.ts index ac0a213671..8e7b3c65f6 100644 --- a/packages/language-core/lib/codegen/globalTypes.ts +++ b/packages/language-core/lib/codegen/globalTypes.ts @@ -41,10 +41,11 @@ export function generateGlobalTypes(lib: string, target: number, strictTemplates 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; - type __VLS_WithComponent = + type __VLS_WithComponent = N1 extends keyof LocalComponents ? N1 extends N0 ? Pick : { [K in N0]: LocalComponents[N1] } : N2 extends keyof LocalComponents ? N2 extends N0 ? Pick : { [K in N0]: LocalComponents[N2] } : N3 extends keyof LocalComponents ? N3 extends N0 ? Pick : { [K in N0]: LocalComponents[N3] } : + Self extends object ? { [K in N0]: Self } : N1 extends keyof __VLS_GlobalComponents ? N1 extends N0 ? Pick<__VLS_GlobalComponents, N0 extends keyof __VLS_GlobalComponents ? N0 : never> : { [K in N0]: __VLS_GlobalComponents[N1] } : N2 extends keyof __VLS_GlobalComponents ? N2 extends N0 ? Pick<__VLS_GlobalComponents, N0 extends keyof __VLS_GlobalComponents ? N0 : never> : { [K in N0]: __VLS_GlobalComponents[N2] } : N3 extends keyof __VLS_GlobalComponents ? N3 extends N0 ? Pick<__VLS_GlobalComponents, N0 extends keyof __VLS_GlobalComponents ? N0 : never> : { [K in N0]: __VLS_GlobalComponents[N3] } : @@ -143,7 +144,7 @@ export function generateGlobalTypes(lib: string, target: number, strictTemplates : T extends () => any ? (props: {}, ctx?: any) => ReturnType : T extends (...args: any) => any ? T : (_: {}${strictTemplates ? '' : ' & Record'}, ctx?: any) => { __ctx?: { attrs?: any, expose?: any, slots?: any, emit?: any, props?: {}${strictTemplates ? '' : ' & Record'} } }; - function __VLS_elementAsFunction(tag: T, endTag?: T): (_: T${strictTemplates ? '' : ' & Record'}) => void; + function __VLS_asFunctionalElement(tag: T, endTag?: T): (_: T${strictTemplates ? '' : ' & Record'}) => void; function __VLS_functionalComponentArgsRest any>(t: T): 2 extends Parameters['length'] ? [any] : []; function __VLS_normalizeSlot(s: S): S extends () => infer R ? (props: {}) => R : S; function __VLS_tryAsConstant(t: T): T; diff --git a/packages/language-core/lib/codegen/script/scriptSetup.ts b/packages/language-core/lib/codegen/script/scriptSetup.ts index 8f357a734b..a7b6a588df 100644 --- a/packages/language-core/lib/codegen/script/scriptSetup.ts +++ b/packages/language-core/lib/codegen/script/scriptSetup.ts @@ -321,6 +321,7 @@ function* generateMacros( ctx: ScriptCodegenContext ): Generator { if (options.vueCompilerOptions.target >= 3.3) { + yield `// @ts-ignore${newLine}`; yield `declare const { `; for (const macro of Object.keys(options.vueCompilerOptions.macros)) { if (!ctx.bindingNames.has(macro)) { diff --git a/packages/language-core/lib/codegen/script/template.ts b/packages/language-core/lib/codegen/script/template.ts index 982e37a909..92a49bf7ac 100644 --- a/packages/language-core/lib/codegen/script/template.ts +++ b/packages/language-core/lib/codegen/script/template.ts @@ -1,6 +1,5 @@ -import * as path from 'path-browserify'; import type { Code } from '../../types'; -import { getSlotsPropertyName, hyphenateTag } from '../../utils/shared'; +import { hyphenateTag } from '../../utils/shared'; import { TemplateCodegenContext, createTemplateCodegenContext } from '../template/context'; import { generateInterpolation } from '../template/interpolation'; import { generateStyleScopedClassReferences } from '../template/styleScopedClasses'; @@ -69,23 +68,6 @@ function* generateTemplateComponents(options: ScriptCodegenOptions): Generator { ` - + getSlotsPropertyName(options.vueCompilerOptions.target) - + `: typeof ${options.scriptSetupRanges?.defineSlots?.name ?? `__VLS_slots`} }) }` - ); - } - types.push(`typeof __VLS_ctx`); yield `type __VLS_LocalComponents =`; @@ -145,15 +127,15 @@ function* generateTemplateBody( yield `const __VLS_slots = {}${endOfLine}`; } yield `const __VLS_inheritedAttrs = {}${endOfLine}`; - yield `const $refs = {}${endOfLine}`; - yield `const $el = {} as any${endOfLine}`; + yield `const __VLS_refs = {}${endOfLine}`; + yield `const __VLS_rootEl = {} as any${endOfLine}`; } yield `return {${newLine}`; yield ` attrs: {} as Partial,${newLine}`; yield ` slots: ${options.scriptSetupRanges?.defineSlots?.name ?? '__VLS_slots'},${newLine}`; - yield ` refs: $refs,${newLine}`; - yield ` rootEl: $el,${newLine}`; + yield ` refs: __VLS_refs,${newLine}`; + yield ` rootEl: __VLS_rootEl,${newLine}`; yield `}${endOfLine}`; } diff --git a/packages/language-core/lib/codegen/template/context.ts b/packages/language-core/lib/codegen/template/context.ts index 662ee6cff2..c435b70f44 100644 --- a/packages/language-core/lib/codegen/template/context.ts +++ b/packages/language-core/lib/codegen/template/context.ts @@ -103,6 +103,7 @@ export function createTemplateCodegenContext(options: Pick(); + const specialVars = new Set(); const accessExternalVariables = new Map>(); const slots: { name: string; @@ -115,7 +116,6 @@ export function createTemplateCodegenContext(options: Pick();; const blockConditions: string[] = []; const scopedClasses: { source: string; @@ -132,9 +132,9 @@ export function createTemplateCodegenContext(options: Pick { ` + + getSlotsPropertyName(options.vueCompilerOptions.target) + + `: typeof ${options.slotsAssignName ?? `__VLS_slots`} }), `; + } + else { + yield `void, `; + } yield getPossibleOriginalComponentNames(node.tag, false) .map(name => `'${name}'`) .join(`, `); @@ -269,24 +275,18 @@ export function* generateComponent( yield `let ${var_componentEvents}!: __VLS_NormalizeEmits${endOfLine}`; } - if ( - options.vueCompilerOptions.fallthroughAttributes - && ( - node.props.some(prop => prop.type === CompilerDOM.NodeTypes.DIRECTIVE && prop.name === 'bind' && prop.exp?.loc.source === '$attrs') - || node === ctx.singleRootNode - ) - ) { - const varAttrs = ctx.getInternalVariable(); - ctx.inheritedAttrVars.add(varAttrs); - yield `var ${varAttrs}!: Parameters[0];\n`; + if (hasVBindAttrs(options, ctx, node)) { + const attrsVar = ctx.getInternalVariable(); + ctx.inheritedAttrVars.add(attrsVar); + yield `let ${attrsVar}!: Parameters[0];\n`; } 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); + yield* generateVSlot(options, ctx, node, slotDir); } else { - yield* generateElementChildren(options, ctx, node); + yield* generateElementChildren(options, ctx, node, true); } if (ctx.currentComponent.used) { @@ -306,7 +306,7 @@ export function* generateElement( : undefined; const failedPropExps: FailedPropExpression[] = []; - yield `__VLS_elementAsFunction(__VLS_intrinsicElements`; + yield `__VLS_asFunctionalElement(__VLS_intrinsicElements`; yield* generatePropertyAccess( options, ctx, @@ -349,23 +349,11 @@ export function* generateElement( ctx.singleRootElType = `typeof __VLS_nativeElements['${node.tag}']`; } - const slotDir = node.props.find(p => p.type === CompilerDOM.NodeTypes.DIRECTIVE && p.name === 'slot') as CompilerDOM.DirectiveNode; - if (slotDir && ctx.currentComponent) { - yield* generateComponentSlot(options, ctx, node, slotDir); - } - else { - yield* generateElementChildren(options, ctx, node); - } - - if ( - options.vueCompilerOptions.fallthroughAttributes - && ( - node.props.some(prop => prop.type === CompilerDOM.NodeTypes.DIRECTIVE && prop.name === 'bind' && prop.exp?.loc.source === '$attrs') - || node === ctx.singleRootNode - ) - ) { + if (hasVBindAttrs(options, ctx, node)) { ctx.inheritedAttrVars.add(`__VLS_intrinsicElements.${node.tag}`); } + + yield* generateElementChildren(options, ctx, node); } function* generateFailedPropExps( @@ -483,105 +471,6 @@ function* generateComponentGeneric( ctx.lastGenericComment = undefined; } -function* generateComponentSlot( - options: TemplateCodegenOptions, - ctx: TemplateCodegenContext, - node: CompilerDOM.ElementNode, - slotDir: CompilerDOM.DirectiveNode -): Generator { - yield `{${newLine}`; - if (ctx.currentComponent) { - ctx.currentComponent.used = true; - ctx.hasSlotElements.add(ctx.currentComponent.node); - } - const slotBlockVars: string[] = []; - yield `const {`; - if (slotDir?.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && slotDir.arg.content) { - yield* generateObjectProperty( - options, - ctx, - slotDir.arg.loc.source, - slotDir.arg.loc.start.offset, - slotDir.arg.isStatic ? ctx.codeFeatures.withoutHighlight : ctx.codeFeatures.all, - slotDir.arg.loc, - false, - true - ); - } - else { - yield* wrapWith( - slotDir.loc.start.offset, - slotDir.loc.start.offset + (slotDir.rawName?.length ?? 0), - ctx.codeFeatures.withoutHighlightAndCompletion, - `default` - ); - } - yield `: __VLS_thisSlot } = ${ctx.currentComponent!.ctxVar}.slots!${endOfLine}`; - - if (slotDir?.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) { - const slotAst = createTsAst(options.ts, slotDir, `(${slotDir.exp.content}) => {}`); - collectVars(options.ts, slotAst, slotAst, slotBlockVars); - if (!slotDir.exp.content.includes(':')) { - yield `const [`; - yield [ - slotDir.exp.content, - 'template', - slotDir.exp.loc.start.offset, - ctx.codeFeatures.all, - ]; - yield `] = __VLS_getSlotParams(__VLS_thisSlot)${endOfLine}`; - } - else { - yield `const `; - yield [ - slotDir.exp.content, - 'template', - slotDir.exp.loc.start.offset, - ctx.codeFeatures.all, - ]; - yield ` = __VLS_getSlotParam(__VLS_thisSlot)${endOfLine}`; - } - } - - for (const varName of slotBlockVars) { - ctx.addLocalVariable(varName); - } - - yield* ctx.resetDirectiveComments('end of slot children start'); - - let prev: CompilerDOM.TemplateChildNode | undefined; - for (const childNode of node.children) { - yield* generateTemplateChild(options, ctx, childNode, prev); - prev = childNode; - } - - for (const varName of slotBlockVars) { - ctx.removeLocalVariable(varName); - } - let isStatic = true; - if (slotDir?.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) { - isStatic = slotDir.arg.isStatic; - } - if (isStatic && slotDir && !slotDir.arg) { - yield `${ctx.currentComponent!.ctxVar}.slots!['`; - yield [ - '', - 'template', - slotDir.loc.start.offset + ( - slotDir.loc.source.startsWith('#') - ? '#'.length : slotDir.loc.source.startsWith('v-slot:') - ? 'v-slot:'.length - : 0 - ), - ctx.codeFeatures.completion, - ]; - yield `'/* empty slot name completion */]${newLine}`; - } - - yield* ctx.generateAutoImportCompletion(); - yield `}${newLine}`; -} - function* generateReferencesForElements( options: TemplateCodegenOptions, ctx: TemplateCodegenContext, @@ -617,6 +506,21 @@ function* generateReferencesForElements( return []; } +function hasVBindAttrs( + options: TemplateCodegenOptions, + ctx: TemplateCodegenContext, + node: CompilerDOM.ElementNode +) { + return options.vueCompilerOptions.fallthroughAttributes && ( + node === ctx.singleRootNode || + node.props.some(prop => + prop.type === CompilerDOM.NodeTypes.DIRECTIVE + && prop.name === 'bind' + && prop.exp?.loc.source === '$attrs' + ) + ); +} + function camelizeComponentName(newName: string) { return camelize('-' + newName); } diff --git a/packages/language-core/lib/codegen/template/elementChildren.ts b/packages/language-core/lib/codegen/template/elementChildren.ts index 410df8ffae..f49265e5c7 100644 --- a/packages/language-core/lib/codegen/template/elementChildren.ts +++ b/packages/language-core/lib/codegen/template/elementChildren.ts @@ -8,7 +8,8 @@ import { generateTemplateChild } from './templateChild'; export function* generateElementChildren( options: TemplateCodegenOptions, ctx: TemplateCodegenContext, - node: CompilerDOM.ElementNode + node: CompilerDOM.ElementNode, + isDefaultSlot: boolean = false ): Generator { yield* ctx.resetDirectiveComments('end of element children start'); let prev: CompilerDOM.TemplateChildNode | undefined; @@ -21,10 +22,9 @@ export function* generateElementChildren( // fix https://github.com/vuejs/language-tools/issues/932 if ( ctx.currentComponent - && !ctx.hasSlotElements.has(node) + && isDefaultSlot && node.children.length - && node.tagType !== CompilerDOM.ElementTypes.ELEMENT - && node.tagType !== CompilerDOM.ElementTypes.TEMPLATE + && node.tagType === CompilerDOM.ElementTypes.COMPONENT ) { ctx.currentComponent.used = true; yield `${ctx.currentComponent.ctxVar}.slots!.`; diff --git a/packages/language-core/lib/codegen/template/elementProps.ts b/packages/language-core/lib/codegen/template/elementProps.ts index c55084e82b..95a1b64ecb 100644 --- a/packages/language-core/lib/codegen/template/elementProps.ts +++ b/packages/language-core/lib/codegen/template/elementProps.ts @@ -145,7 +145,6 @@ export function* generateElementProps( prop, prop.exp, ctx.codeFeatures.all, - prop.arg?.loc.start.offset === prop.exp?.loc.start.offset, enableCodeFeatures ), `)` @@ -257,7 +256,6 @@ export function* generateElementProps( prop, prop.exp, ctx.codeFeatures.all, - false, enableCodeFeatures ) ); @@ -279,9 +277,10 @@ function* generatePropExp( prop: CompilerDOM.DirectiveNode, exp: CompilerDOM.SimpleExpressionNode | undefined, features: VueCodeInformation, - isShorthand: boolean, enableCodeFeatures: boolean ): Generator { + const isShorthand = prop.arg?.loc.start.offset === prop.exp?.loc.start.offset; + if (isShorthand && features.completion) { features = { ...features, diff --git a/packages/language-core/lib/codegen/template/index.ts b/packages/language-core/lib/codegen/template/index.ts index 63bcdfa5d6..565cb4f87a 100644 --- a/packages/language-core/lib/codegen/template/index.ts +++ b/packages/language-core/lib/codegen/template/index.ts @@ -23,6 +23,7 @@ export interface TemplateCodegenOptions { slotsAssignName?: string; propsAssignName?: string; inheritAttrs: boolean; + selfComponentName?: string; } export function* generateTemplate(options: TemplateCodegenOptions): Generator { @@ -34,26 +35,38 @@ export function* generateTemplate(options: TemplateCodegenOptions): Generator { +function* generateSlots( + options: TemplateCodegenOptions, + ctx: TemplateCodegenContext +): Generator { if (!options.hasDefineSlots) { yield `var __VLS_slots!: `; for (const { expVar, varName } of ctx.dynamicSlots) { @@ -85,21 +98,22 @@ function* generateSlots(options: TemplateCodegenOptions, ctx: TemplateCodegenCon } yield `}${endOfLine}`; } - const name = getSlotsPropertyName(options.vueCompilerOptions.target); - yield `var ${name}!: typeof ${options.slotsAssignName ?? '__VLS_slots'}${endOfLine}`; + return `typeof ${options.slotsAssignName ?? `__VLS_slots`}`; } -function* generateInheritedAttrs(ctx: TemplateCodegenContext): Generator { +function* generateInheritedAttrs( + ctx: TemplateCodegenContext +): Generator { yield 'let __VLS_inheritedAttrs!: {}'; for (const varName of ctx.inheritedAttrVars) { yield ` & typeof ${varName}`; } yield endOfLine; - yield `var $attrs!: Partial & Record${endOfLine}`; if (ctx.bindingAttrLocs.length) { yield `[`; for (const loc of ctx.bindingAttrLocs) { + yield `__VLS_special.`; yield [ loc.source, 'template', @@ -110,9 +124,12 @@ function* generateInheritedAttrs(ctx: TemplateCodegenContext): Generator { } yield `]${endOfLine}`; } + return `Partial & Record`; } -function* generateRefs(ctx: TemplateCodegenContext): Generator { +function* generateRefs( + ctx: TemplateCodegenContext +): Generator { yield `const __VLS_refs = {${newLine}`; for (const [name, [varName, offset]] of ctx.templateRefs) { yield* generateStringLiteralKey( @@ -123,16 +140,16 @@ function* generateRefs(ctx: TemplateCodegenContext): Generator { yield `: ${varName},${newLine}`; } yield `}${endOfLine}`; - yield `var $refs!: typeof __VLS_refs${endOfLine}`; + return `typeof __VLS_refs`; } -function* generateRootEl(ctx: TemplateCodegenContext): Generator { - if (ctx.singleRootElType) { - yield `var $el!: ${ctx.singleRootElType}${endOfLine}`; - } - else { - yield `var $el!: any${endOfLine}`; - } +function* generateRootEl( + ctx: TemplateCodegenContext +): Generator { + yield `let __VLS_rootEl!: `; + yield ctx.singleRootElType ?? `any`; + yield endOfLine; + return `typeof __VLS_rootEl`; } export function* forEachElementNode(node: CompilerDOM.RootNode | CompilerDOM.TemplateChildNode): Generator { diff --git a/packages/language-core/lib/codegen/template/interpolation.ts b/packages/language-core/lib/codegen/template/interpolation.ts index f66fb18eea..366b606971 100644 --- a/packages/language-core/lib/codegen/template/interpolation.ts +++ b/packages/language-core/lib/codegen/template/interpolation.ts @@ -71,6 +71,12 @@ export function* generateInterpolation( } } +interface CtxVar { + text: string; + isShorthand: boolean; + offset: number; +}; + function* forEachInterpolationSegment( ts: typeof import('typescript'), destructuredPropNames: Set | undefined, @@ -80,20 +86,16 @@ function* forEachInterpolationSegment( offset: number | undefined, ast: ts.SourceFile ): Generator<[fragment: string, offset: number | undefined, type?: 'errorMappingOnly' | 'startText' | 'endText']> { - let ctxVars: { - text: string, - isShorthand: boolean, - offset: number, - }[] = []; + let ctxVars: CtxVar[] = []; const varCb = (id: ts.Identifier, isShorthand: boolean) => { const text = getNodeText(ts, id, ast); if ( - ctx.hasLocalVariable(text) || + ctx.hasLocalVariable(text) // https://github.com/vuejs/core/blob/245230e135152900189f13a4281302de45fdcfaa/packages/compiler-core/src/transforms/transformExpression.ts#L342-L352 - isGloballyAllowed(text) || - text === 'require' || - text.startsWith('__VLS_') + || isGloballyAllowed(text) + || text === 'require' + || text.startsWith('__VLS_') ) { // localVarOffsets.push(localVar.getStart(ast)); } @@ -132,7 +134,7 @@ function* forEachInterpolationSegment( const curVar = ctxVars[i]; const nextVar = ctxVars[i + 1]; - yield* generateVar(code, destructuredPropNames, templateRefNames, curVar, nextVar); + yield* generateVar(code, ctx.specialVars, destructuredPropNames, templateRefNames, curVar, nextVar); if (nextVar.isShorthand) { yield [code.slice(curVar.offset + curVar.text.length, nextVar.offset + nextVar.text.length), curVar.offset + curVar.text.length]; @@ -144,7 +146,7 @@ function* forEachInterpolationSegment( } const lastVar = ctxVars.at(-1)!; - yield* generateVar(code, destructuredPropNames, templateRefNames, lastVar); + yield* generateVar(code, ctx.specialVars, destructuredPropNames, templateRefNames, lastVar); if (lastVar.offset + lastVar.text.length < code.length) { yield [code.slice(lastVar.offset + lastVar.text.length), lastVar.offset + lastVar.text.length, 'endText']; } @@ -156,18 +158,11 @@ function* forEachInterpolationSegment( function* generateVar( code: string, + specialVars: Set, destructuredPropNames: Set | undefined, templateRefNames: Set | undefined, - curVar: { - text: string, - isShorthand: boolean, - offset: number, - }, - nextVar: { - text: string, - isShorthand: boolean, - offset: number, - } = curVar + curVar: CtxVar, + nextVar: CtxVar = curVar ): Generator<[fragment: string, offset: number | undefined, type?: 'errorMappingOnly']> { // fix https://github.com/vuejs/language-tools/issues/1205 // fix https://github.com/vuejs/language-tools/issues/1264 @@ -181,7 +176,10 @@ function* generateVar( yield [`)`, undefined]; } else { - if (!isDestructuredProp) { + if (specialVars.has(curVar.text)) { + yield [`__VLS_special.`, undefined]; + } + else if (!isDestructuredProp) { yield [`__VLS_ctx.`, undefined]; } yield [code.slice(curVar.offset, curVar.offset + curVar.text.length), curVar.offset]; diff --git a/packages/language-core/lib/codegen/template/templateChild.ts b/packages/language-core/lib/codegen/template/templateChild.ts index 90b810dee9..922c33e6cc 100644 --- a/packages/language-core/lib/codegen/template/templateChild.ts +++ b/packages/language-core/lib/codegen/template/templateChild.ts @@ -8,6 +8,7 @@ import { generateInterpolation } from './interpolation'; import { generateSlotOutlet } from './slotOutlet'; import { generateVFor } from './vFor'; import { generateVIf } from './vIf'; +import { generateVSlot } from './vSlot'; // @ts-ignore const transformContext: CompilerDOM.TransformContext = { @@ -83,9 +84,17 @@ export function* generateTemplateChild( else if (vIfNode) { yield* generateVIf(options, ctx, vIfNode); } + else if (node.tagType === CompilerDOM.ElementTypes.SLOT) { + yield* generateSlotOutlet(options, ctx, node); + } else { - if (node.tagType === CompilerDOM.ElementTypes.SLOT) { - yield* generateSlotOutlet(options, ctx, node); + const slotDir = node.props.find(p => p.type === CompilerDOM.NodeTypes.DIRECTIVE && p.name === 'slot') as CompilerDOM.DirectiveNode; + if ( + node.tagType === CompilerDOM.ElementTypes.TEMPLATE + && ctx.currentComponent + && slotDir + ) { + yield* generateVSlot(options, ctx, node, slotDir); } else if ( node.tagType === CompilerDOM.ElementTypes.ELEMENT diff --git a/packages/language-core/lib/codegen/template/vSlot.ts b/packages/language-core/lib/codegen/template/vSlot.ts new file mode 100644 index 0000000000..8be7bb9c66 --- /dev/null +++ b/packages/language-core/lib/codegen/template/vSlot.ts @@ -0,0 +1,109 @@ +import * as CompilerDOM from '@vue/compiler-dom'; +import type { Code } from '../../types'; +import { collectVars, createTsAst, endOfLine, newLine, wrapWith } from '../utils'; +import type { TemplateCodegenContext } from './context'; +import type { TemplateCodegenOptions } from './index'; +import { generateObjectProperty } from './objectProperty'; +import { generateTemplateChild } from './templateChild'; + +export function* generateVSlot( + options: TemplateCodegenOptions, + ctx: TemplateCodegenContext, + node: CompilerDOM.ElementNode, + slotDir: CompilerDOM.DirectiveNode +): Generator { + if (!ctx.currentComponent) { + return; + } + ctx.currentComponent.used = true; + const slotBlockVars: string[] = []; + yield `{${newLine}`; + + yield `const { `; + if (slotDir.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && slotDir.arg.content) { + yield* generateObjectProperty( + options, + ctx, + slotDir.arg.loc.source, + slotDir.arg.loc.start.offset, + slotDir.arg.isStatic ? ctx.codeFeatures.withoutHighlight : ctx.codeFeatures.all, + slotDir.arg.loc, + false, + true + ); + } + else { + yield* wrapWith( + slotDir.loc.start.offset, + slotDir.loc.start.offset + (slotDir.rawName?.length ?? 0), + ctx.codeFeatures.withoutHighlightAndCompletion, + `default` + ); + } + yield `: __VLS_thisSlot } = ${ctx.currentComponent.ctxVar}.slots!${endOfLine}`; + + if (slotDir.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) { + const slotAst = createTsAst(options.ts, slotDir, `(${slotDir.exp.content}) => {}`); + collectVars(options.ts, slotAst, slotAst, slotBlockVars); + if (!slotDir.exp.content.includes(':')) { + yield `const [`; + yield [ + slotDir.exp.content, + 'template', + slotDir.exp.loc.start.offset, + ctx.codeFeatures.all, + ]; + yield `] = __VLS_getSlotParams(__VLS_thisSlot)${endOfLine}`; + } + else { + yield `const `; + yield [ + slotDir.exp.content, + 'template', + slotDir.exp.loc.start.offset, + ctx.codeFeatures.all, + ]; + yield ` = __VLS_getSlotParam(__VLS_thisSlot)${endOfLine}`; + } + } + + for (const varName of slotBlockVars) { + ctx.addLocalVariable(varName); + } + + yield* ctx.resetDirectiveComments('end of slot children start'); + + let prev: CompilerDOM.TemplateChildNode | undefined; + for (const childNode of node.children) { + yield* generateTemplateChild(options, ctx, childNode, prev); + prev = childNode; + } + + for (const varName of slotBlockVars) { + ctx.removeLocalVariable(varName); + } + + let isStatic = true; + if (slotDir.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) { + isStatic = slotDir.arg.isStatic; + } + if (isStatic && !slotDir.arg) { + yield `${ctx.currentComponent.ctxVar}.slots!['`; + yield [ + '', + 'template', + slotDir.loc.start.offset + ( + slotDir.loc.source.startsWith('#') + ? '#'.length + : slotDir.loc.source.startsWith('v-slot:') + ? 'v-slot:'.length + : 0 + ), + ctx.codeFeatures.completion, + ]; + yield `'/* empty slot name completion */]${endOfLine}`; + } + + yield* ctx.generateAutoImportCompletion(); + yield `}${newLine}`; +} diff --git a/packages/language-core/lib/plugins/vue-tsx.ts b/packages/language-core/lib/plugins/vue-tsx.ts index a378b240e8..48d33f31b6 100644 --- a/packages/language-core/lib/plugins/vue-tsx.ts +++ b/packages/language-core/lib/plugins/vue-tsx.ts @@ -1,5 +1,7 @@ import type { Mapping } from '@volar/language-core'; +import { camelize, capitalize } from '@vue/shared'; import { computed, unstable } from 'alien-signals'; +import * as path from 'path-browserify'; import { generateScript } from '../codegen/script'; import { generateTemplate } from '../codegen/template'; import { parseScriptRanges } from '../parsers/scriptRanges'; @@ -153,6 +155,19 @@ function createTsx( const value = scriptSetupRanges.get()?.defineOptions?.inheritAttrs ?? scriptRanges.get()?.exportDefault?.inheritAttrsOption; return value !== 'false'; }); + const selfComponentName = computed(() => { + const { exportDefault } = scriptRanges.get() ?? {}; + if (_sfc.script && exportDefault?.nameOption) { + const { nameOption } = exportDefault; + return _sfc.script.content.slice(nameOption.start + 1, nameOption.end - 1); + } + const { defineOptions } = scriptSetupRanges.get() ?? {}; + if (_sfc.scriptSetup && defineOptions?.name) { + return defineOptions.name; + } + const baseName = path.basename(fileName); + return capitalize(camelize(baseName.slice(0, baseName.lastIndexOf('.')))); + }); const generatedTemplate = computed(() => { if (vueCompilerOptions.get().skipTemplateCodegen || !_sfc.template) { @@ -174,6 +189,7 @@ function createTsx( slotsAssignName: slotsAssignName.get(), propsAssignName: propsAssignName.get(), inheritAttrs: inheritAttrs.get(), + selfComponentName: selfComponentName.get(), }); let current = codegen.next(); @@ -186,7 +202,7 @@ function createTsx( return { ...current.value, - codes: codes, + codes, }; }); const generatedScript = computed(() => { diff --git a/packages/language-server/tests/completions.spec.ts b/packages/language-server/tests/completions.spec.ts index dcc67b3a06..1e56bf6307 100644 --- a/packages/language-server/tests/completions.spec.ts +++ b/packages/language-server/tests/completions.spec.ts @@ -187,8 +187,8 @@ describe('Completions', async () => { "component", "slot", "template", - "fixture", "BaseTransition", + "Fixture", ] `); }); diff --git a/packages/language-service/data/language-blocks/zh-cn.json b/packages/language-service/data/language-blocks/zh-cn.json index 8176b91713..a6a2fee6e0 100644 --- a/packages/language-service/data/language-blocks/zh-cn.json +++ b/packages/language-service/data/language-blocks/zh-cn.json @@ -8,7 +8,7 @@ "name": "src", "description": { "kind": "markdown", - "value": "如果你更喜欢将 `*.vue` 组件分散到多个文件中,可以为一个语块使用 `src` 这个 attribute 来导入一个外部文件:\n\n```vue\n\n\n\n```\n\n请注意 `src` 导入和 JS 模块导入遵循相同的路径解析规则,这意味着:\n\n- 相对路径需要以 `./` 开头\n- 你也可以从 npm 依赖中导入资源\n\n```vue\n\n\n\n```\n\n请注意 `src` 导入和 JS 模块导入遵循相同的路径解析规则,这意味着:\n\n- 相对路径需要以 `./` 开头\n- 你也可以从 npm 依赖中导入资源\n\n```vue\n\n\n\n```\n\n请注意 `src` 导入和 JS 模块导入遵循相同的路径解析规则,这意味着:\n\n- 相对路径需要以 `./` 开头\n- 你也可以从 npm 依赖中导入资源\n\n```vue\n\n\n\n```\n\n请注意 `src` 导入和 JS 模块导入遵循相同的路径解析规则,这意味着:\n\n- 相对路径需要以 `./` 开头\n- 你也可以从 npm 依赖中导入资源\n\n```vue\n\n\n\n```\n\n请注意 `src` 导入和 JS 模块导入遵循相同的路径解析规则,这意味着:\n\n- 相对路径需要以 `./` 开头\n- 你也可以从 npm 依赖中导入资源\n\n```vue\n\n\n\n```\n\n请注意 `src` 导入和 JS 模块导入遵循相同的路径解析规则,这意味着:\n\n- 相对路径需要以 `./` 开头\n- 你也可以从 npm 依赖中导入资源\n\n```vue\n\n\n```\n\n得出的 class 将被哈希化以避免冲突,实现了同样的将 CSS 仅作用于当前组件的效果。\n\n参考 [CSS Modules spec](https://github.com/css-modules/css-modules) 以查看更多详情,例如 [global exceptions](https://github.com/css-modules/css-modules/blob/master/docs/composition.md#exceptions) 和 [composition](https://github.com/css-modules/css-modules/blob/master/docs/composition.md#composition)。\n\n### 自定义注入名称 \n\n你可以通过给 `module` attribute 一个值来自定义注入 class 对象的属性名:\n\n```vue\n\n\n\n```\n\n### 与组合式 API 一同使用 \n\n可以通过 `useCssModule` API 在 `setup()` 和 `\n\n\n\n\n```" }, "references": "api/sfc-css-features.html#css-modules" } @@ -167,7 +167,7 @@ "name": "src", "description": { "kind": "markdown", - "value": "如果你更喜欢将 `*.vue` 组件分散到多个文件中,可以为一个语块使用 `src` 这个 attribute 来导入一个外部文件:\n\n```vue\n\n\n\n```\n\n请注意 `src` 导入和 JS 模块导入遵循相同的路径解析规则,这意味着:\n\n- 相对路径需要以 `./` 开头\n- 你也可以从 npm 依赖中导入资源\n\n```vue\n\n\n\n```\n\n请注意 `src` 导入和 JS 模块导入遵循相同的路径解析规则,这意味着:\n\n- 相对路径需要以 `./` 开头\n- 你也可以从 npm 依赖中导入资源\n\n```vue\n\n\n\n```\n\n请注意 `src` 导入和 JS 模块导入遵循相同的路径解析规则,这意味着:\n\n- 相对路径需要以 `./` 开头\n- 你也可以从 npm 依赖中导入资源\n\n```vue\n\n\n\n```\n\n请注意 `src` 导入和 JS 模块导入遵循相同的路径解析规则,这意味着:\n\n- 相对路径需要以 `./` 开头\n- 你也可以从 npm 依赖中导入资源\n\n```vue\n\n