From bf1f926f7ec12f34552f2ab343e76017d49749ef Mon Sep 17 00:00:00 2001 From: KazariEX <1364035137@qq.com> Date: Tue, 17 Dec 2024 02:21:47 +0800 Subject: [PATCH 01/11] feat: navigation support for `$attrs`, `$slots`, `$refs` and `$el` in the template --- .../lib/codegen/script/context.ts | 1 + .../lib/codegen/script/template.ts | 4 +- .../lib/codegen/template/index.ts | 62 ++++++++++++++----- packages/language-core/lib/plugins/vue-tsx.ts | 27 ++++++-- 4 files changed, 72 insertions(+), 22 deletions(-) diff --git a/packages/language-core/lib/codegen/script/context.ts b/packages/language-core/lib/codegen/script/context.ts index 84434470fd..fbffe9ec1a 100644 --- a/packages/language-core/lib/codegen/script/context.ts +++ b/packages/language-core/lib/codegen/script/context.ts @@ -18,6 +18,7 @@ export function createScriptCodegenContext(options: ScriptCodegenOptions) { return { generatedTemplate: false, generatedPropsType: false, + templateGeneratedOffset: undefined as number | undefined, scriptSetupGeneratedOffset: undefined as number | undefined, bypassDefineComponent: options.lang === 'js' || options.lang === 'jsx', bindingNames: new Set([ diff --git a/packages/language-core/lib/codegen/script/template.ts b/packages/language-core/lib/codegen/script/template.ts index 348e1f0dd5..eefc1fd307 100644 --- a/packages/language-core/lib/codegen/script/template.ts +++ b/packages/language-core/lib/codegen/script/template.ts @@ -120,12 +120,13 @@ export function* generateTemplate( yield* generateTemplateCtx(options); yield* generateTemplateComponents(options); yield* generateTemplateDirectives(options); - yield* generateTemplateBody(options, templateCodegenCtx); + yield* generateTemplateBody(options, ctx, templateCodegenCtx); return templateCodegenCtx; } function* generateTemplateBody( options: ScriptCodegenOptions, + ctx: ScriptCodegenContext, templateCodegenCtx: TemplateCodegenContext ): Generator { const firstClasses = new Set(); @@ -159,6 +160,7 @@ function* generateTemplateBody( yield* generateCssVars(options, templateCodegenCtx); if (options.templateCodegen) { + ctx.templateGeneratedOffset = options.getGeneratedLength(); for (const code of options.templateCodegen.codes) { yield code; } diff --git a/packages/language-core/lib/codegen/template/index.ts b/packages/language-core/lib/codegen/template/index.ts index 4818b253e1..d1bdfd0029 100644 --- a/packages/language-core/lib/codegen/template/index.ts +++ b/packages/language-core/lib/codegen/template/index.ts @@ -1,3 +1,4 @@ +import type { Mapping } from '@volar/language-core'; import * as CompilerDOM from '@vue/compiler-dom'; import type * as ts from 'typescript'; import type { Code, Sfc, VueCompilerOptions } from '../../types'; @@ -24,6 +25,8 @@ export interface TemplateCodegenOptions { slotsAssignName?: string; propsAssignName?: string; inheritAttrs: boolean; + getGeneratedLength: () => number; + linkedCodeMappings: Mapping[]; } export function* generateTemplate(options: TemplateCodegenOptions): Generator { @@ -48,15 +51,18 @@ 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) { @@ -89,19 +95,25 @@ function* generateSlots(options: TemplateCodegenOptions, ctx: TemplateCodegenCon yield `}${endOfLine}`; } const name = getSlotsPropertyName(options.vueCompilerOptions.target); - yield `var ${name}!: typeof ${options.slotsAssignName ?? '__VLS_slots'}${endOfLine}`; + yield* generateContextVariable(options, name, options.slotsAssignName ?? `__VLS_slots`); } -function* generateInheritedAttrs(ctx: TemplateCodegenContext): Generator { +function* generateInheritedAttrs( + options: TemplateCodegenOptions, + ctx: TemplateCodegenContext +): Generator { yield 'let __VLS_inheritedAttrs!: {}'; for (const varName of ctx.inheritedAttrVars) { yield ` & typeof ${varName}`; } yield endOfLine; - yield `var $attrs!: Partial & Record${endOfLine}`; + yield* generateContextVariable(options, `$attrs`, `Partial & Record`); } -function* generateRefs(ctx: TemplateCodegenContext): Generator { +function* generateRefs( + options: TemplateCodegenOptions, + ctx: TemplateCodegenContext +): Generator { yield `const __VLS_refs = {${newLine}`; for (const [name, [varName, offset]] of ctx.templateRefs) { yield* generateStringLiteralKey( @@ -112,16 +124,34 @@ function* generateRefs(ctx: TemplateCodegenContext): Generator { yield `: ${varName},${newLine}`; } yield `}${endOfLine}`; - yield `var $refs!: typeof __VLS_refs${endOfLine}`; + yield* generateContextVariable(options, `$refs`, `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( + options: TemplateCodegenOptions, + ctx: TemplateCodegenContext +): Generator { + yield* generateContextVariable(options, `$el`, ctx.singleRootElType ?? `any`); +} + +function* generateContextVariable( + options: TemplateCodegenOptions, + varName: string, + typeExp: string +): Generator { + yield `/** @type { typeof __VLS_ctx.`; + const sourceOffset = options.getGeneratedLength(); + yield `${varName} } */${endOfLine}`; + yield `var `; + const generatedOffset = options.getGeneratedLength(); + yield `${varName}!: ${typeExp}${endOfLine}`; + + options.linkedCodeMappings.push({ + sourceOffsets: [sourceOffset], + generatedOffsets: [generatedOffset], + lengths: [varName.length], + data: undefined, + }); } function* generatePreResolveComponents(options: TemplateCodegenOptions): Generator { diff --git a/packages/language-core/lib/plugins/vue-tsx.ts b/packages/language-core/lib/plugins/vue-tsx.ts index 65ba00edc4..9c9cb32a91 100644 --- a/packages/language-core/lib/plugins/vue-tsx.ts +++ b/packages/language-core/lib/plugins/vue-tsx.ts @@ -37,13 +37,22 @@ const plugin: VueLanguagePlugin = ctx => { resolveEmbeddedCode(fileName, sfc, embeddedFile) { - const _tsx = useTsx(fileName, sfc); + const tsx = useTsx(fileName, sfc); if (/script_(js|jsx|ts|tsx)/.test(embeddedFile.id)) { - const tsx = _tsx.generatedScript.get(); - if (tsx) { - embeddedFile.content = [...tsx.codes]; - embeddedFile.linkedCodeMappings = [...tsx.linkedCodeMappings]; + const script = tsx.generatedScript.get(); + const template = tsx.generatedTemplate.get(); + if (script) { + const linkedCodeMappings = [ + ...script.linkedCodeMappings, + ...template?.linkedCodeMappings.map(mapping => ({ + ...mapping, + sourceOffsets: mapping.sourceOffsets.map(offset => offset + script.templateGeneratedOffset!), + generatedOffsets: mapping.generatedOffsets.map(offset => offset + script.templateGeneratedOffset!), + })) ?? [] + ]; + embeddedFile.content = [...script.codes]; + embeddedFile.linkedCodeMappings = linkedCodeMappings; } } }, @@ -139,6 +148,8 @@ function createTsx( } const codes: Code[] = []; + const linkedCodeMappings: Mapping[] = []; + let generatedLength = 0; const codegen = generateTemplate({ ts, compilerOptions: ctx.compilerOptions, @@ -153,6 +164,8 @@ function createTsx( slotsAssignName: slotsAssignName.get(), propsAssignName: propsAssignName.get(), inheritAttrs: inheritAttrs.get(), + getGeneratedLength: () => generatedLength, + linkedCodeMappings, }); let current = codegen.next(); @@ -160,12 +173,16 @@ function createTsx( while (!current.done) { const code = current.value; codes.push(code); + generatedLength += typeof code === 'string' + ? code.length + : code[0].length; current = codegen.next(); } return { ...current.value, codes: codes, + linkedCodeMappings, }; }); const generatedScript = computed(() => { From 18910d83b8e343d30f617a4f8f0e01681f8eabab Mon Sep 17 00:00:00 2001 From: KazariEX <1364035137@qq.com> Date: Tue, 17 Dec 2024 02:31:54 +0800 Subject: [PATCH 02/11] chore: lint --- packages/language-core/lib/plugins/vue-tsx.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/language-core/lib/plugins/vue-tsx.ts b/packages/language-core/lib/plugins/vue-tsx.ts index 9c9cb32a91..3a09e9a62d 100644 --- a/packages/language-core/lib/plugins/vue-tsx.ts +++ b/packages/language-core/lib/plugins/vue-tsx.ts @@ -181,7 +181,7 @@ function createTsx( return { ...current.value, - codes: codes, + codes, linkedCodeMappings, }; }); From f5f82b69f9e9722297b78efafd057215376c9a34 Mon Sep 17 00:00:00 2001 From: KazariEX <1364035137@qq.com> Date: Tue, 17 Dec 2024 17:22:50 +0800 Subject: [PATCH 03/11] fix: avoid circular reference of `$attrs` --- .../lib/codegen/template/elementProps.ts | 34 +++++---------- .../lib/codegen/template/interpolation.ts | 41 +++++++++---------- .../language-core/lib/codegen/utils/index.ts | 5 +-- 3 files changed, 31 insertions(+), 49 deletions(-) diff --git a/packages/language-core/lib/codegen/template/elementProps.ts b/packages/language-core/lib/codegen/template/elementProps.ts index 74512d60f8..c724d57e45 100644 --- a/packages/language-core/lib/codegen/template/elementProps.ts +++ b/packages/language-core/lib/codegen/template/elementProps.ts @@ -143,7 +143,6 @@ export function* generateElementProps( prop, prop.exp, ctx.codeFeatures.all, - prop.arg?.loc.start.offset === prop.exp?.loc.start.offset, enableCodeFeatures ), `)` @@ -181,7 +180,7 @@ export function* generateElementProps( yield `...{ `; } const codeInfo = getPropsCodeInfo(ctx, strictPropsCheck, true); - const codes = conditionWrapWith( + yield* conditionWrapWith( enableCodeFeatures, prop.loc.start.offset, prop.loc.end.offset, @@ -203,12 +202,6 @@ export function* generateElementProps( ), `)` ); - if (!enableCodeFeatures) { - yield toString([...codes]); - } - else { - yield* codes; - } if (shouldSpread) { yield ` }`; } @@ -220,30 +213,21 @@ export function* generateElementProps( && !prop.arg && prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION ) { - const codes = conditionWrapWith( + yield* conditionWrapWith( enableCodeFeatures, prop.exp.loc.start.offset, prop.exp.loc.end.offset, ctx.codeFeatures.verification, `...`, - ...generateInterpolation( + ...generatePropExp( options, ctx, - 'template', + prop, + prop.exp, ctx.codeFeatures.all, - prop.exp.content, - prop.exp.loc.start.offset, - prop.exp.loc, - '(', - ')' + enableCodeFeatures ) ); - if (!enableCodeFeatures) { - yield toString([...codes]); - } - else { - yield* codes; - } yield `, `; } } @@ -284,9 +268,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, @@ -304,7 +289,8 @@ function* generatePropExp( exp.loc.start.offset, exp.loc, '(', - ')' + ')', + true ); } else { const propVariableName = camelize(exp.loc.source); diff --git a/packages/language-core/lib/codegen/template/interpolation.ts b/packages/language-core/lib/codegen/template/interpolation.ts index f66fb18eea..01a60eff60 100644 --- a/packages/language-core/lib/codegen/template/interpolation.ts +++ b/packages/language-core/lib/codegen/template/interpolation.ts @@ -18,7 +18,8 @@ export function* generateInterpolation( start: number | undefined, astHolder: any = {}, prefix: string = '', - suffix: string = '' + suffix: string = '', + isPropExp: boolean = false ): Generator { const code = prefix + _code + suffix; const ast = createTsAst(options.ts, astHolder, code); @@ -29,7 +30,8 @@ export function* generateInterpolation( ctx, code, start !== undefined ? start - prefix.length : undefined, - ast + ast, + isPropExp )) { if (offset === undefined) { yield section; @@ -71,6 +73,12 @@ export function* generateInterpolation( } } +interface CtxVar { + text: string; + isShorthand: boolean; + offset: number; +}; + function* forEachInterpolationSegment( ts: typeof import('typescript'), destructuredPropNames: Set | undefined, @@ -78,22 +86,19 @@ function* forEachInterpolationSegment( ctx: TemplateCodegenContext, code: string, offset: number | undefined, - ast: ts.SourceFile + ast: ts.SourceFile, + isPropExp: boolean ): 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) && !(isPropExp && text === '$attrs') // 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)); } @@ -158,16 +163,8 @@ function* generateVar( code: string, 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 diff --git a/packages/language-core/lib/codegen/utils/index.ts b/packages/language-core/lib/codegen/utils/index.ts index e404e73c9a..fe97ebff4d 100644 --- a/packages/language-core/lib/codegen/utils/index.ts +++ b/packages/language-core/lib/codegen/utils/index.ts @@ -1,3 +1,4 @@ +import { toString } from 'muggle-string'; import type * as ts from 'typescript'; import { getNodeText } from '../../parsers/scriptSetupRanges'; import type { Code, SfcBlock, VueCodeInformation } from '../../types'; @@ -18,9 +19,7 @@ export function* conditionWrapWith( yield* wrapWith(startOffset, endOffset, features, ...wrapCodes); } else { - for (const wrapCode of wrapCodes) { - yield wrapCode; - } + yield toString([...wrapCodes]); } } From fbbf96377b26d6b9cad263c9ad30a6ccac39a16f Mon Sep 17 00:00:00 2001 From: KazariEX <1364035137@qq.com> Date: Tue, 17 Dec 2024 17:57:53 +0800 Subject: [PATCH 04/11] fix: set the variables should be externalized at the upper level --- .../lib/codegen/template/element.ts | 24 +++++------------ .../lib/codegen/template/elementProps.ts | 27 ++++++++++++++++--- .../lib/codegen/template/interpolation.ts | 8 +++--- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/packages/language-core/lib/codegen/template/element.ts b/packages/language-core/lib/codegen/template/element.ts index 9fdf951cd9..36d37c7a80 100644 --- a/packages/language-core/lib/codegen/template/element.ts +++ b/packages/language-core/lib/codegen/template/element.ts @@ -11,7 +11,7 @@ import type { TemplateCodegenContext } from './context'; import { generateElementChildren } from './elementChildren'; import { generateElementDirectives } from './elementDirectives'; import { generateElementEvents } from './elementEvents'; -import { type FailedPropExpression, generateElementProps } from './elementProps'; +import { type FailedPropExpression, generateElementProps, getHasVBindAttrs } from './elementProps'; import type { TemplateCodegenOptions } from './index'; import { generateInterpolation } from './interpolation'; import { generateObjectProperty } from './objectProperty'; @@ -252,16 +252,10 @@ 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 (getHasVBindAttrs(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; @@ -342,13 +336,7 @@ export function* generateElement( yield* generateElementChildren(options, ctx, node, currentComponent, componentCtxVar); } - 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 (getHasVBindAttrs(options, ctx, node)) { ctx.inheritedAttrVars.add(`__VLS_intrinsicElements.${node.tag}`); } } diff --git a/packages/language-core/lib/codegen/template/elementProps.ts b/packages/language-core/lib/codegen/template/elementProps.ts index c724d57e45..ef3167581c 100644 --- a/packages/language-core/lib/codegen/template/elementProps.ts +++ b/packages/language-core/lib/codegen/template/elementProps.ts @@ -30,6 +30,7 @@ export function* generateElementProps( failedPropExps?: FailedPropExpression[] ): Generator { const isComponent = node.tagType === CompilerDOM.ElementTypes.COMPONENT; + const hasVBindAttrs = getHasVBindAttrs(options, ctx, node); for (const prop of props) { if ( @@ -143,7 +144,8 @@ export function* generateElementProps( prop, prop.exp, ctx.codeFeatures.all, - enableCodeFeatures + enableCodeFeatures, + hasVBindAttrs ), `)` ); @@ -225,7 +227,8 @@ export function* generateElementProps( prop, prop.exp, ctx.codeFeatures.all, - enableCodeFeatures + enableCodeFeatures, + hasVBindAttrs ) ); yield `, `; @@ -233,6 +236,21 @@ export function* generateElementProps( } } +export function getHasVBindAttrs( + 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 getPropsCodeInfo( ctx: TemplateCodegenContext, strictPropsCheck: boolean, @@ -268,7 +286,8 @@ function* generatePropExp( prop: CompilerDOM.DirectiveNode, exp: CompilerDOM.SimpleExpressionNode | undefined, features: VueCodeInformation, - enableCodeFeatures: boolean + enableCodeFeatures: boolean, + hasVBindAttrs: boolean ): Generator { const isShorthand = prop.arg?.loc.start.offset === prop.exp?.loc.start.offset; @@ -290,7 +309,7 @@ function* generatePropExp( exp.loc, '(', ')', - true + hasVBindAttrs ? ['$attrs'] : undefined ); } else { const propVariableName = camelize(exp.loc.source); diff --git a/packages/language-core/lib/codegen/template/interpolation.ts b/packages/language-core/lib/codegen/template/interpolation.ts index 01a60eff60..78ac7721df 100644 --- a/packages/language-core/lib/codegen/template/interpolation.ts +++ b/packages/language-core/lib/codegen/template/interpolation.ts @@ -19,7 +19,7 @@ export function* generateInterpolation( astHolder: any = {}, prefix: string = '', suffix: string = '', - isPropExp: boolean = false + externalVars: string[] = [] ): Generator { const code = prefix + _code + suffix; const ast = createTsAst(options.ts, astHolder, code); @@ -31,7 +31,7 @@ export function* generateInterpolation( code, start !== undefined ? start - prefix.length : undefined, ast, - isPropExp + externalVars )) { if (offset === undefined) { yield section; @@ -87,14 +87,14 @@ function* forEachInterpolationSegment( code: string, offset: number | undefined, ast: ts.SourceFile, - isPropExp: boolean + externalVars: string[] ): Generator<[fragment: string, offset: number | undefined, type?: 'errorMappingOnly' | 'startText' | 'endText']> { let ctxVars: CtxVar[] = []; const varCb = (id: ts.Identifier, isShorthand: boolean) => { const text = getNodeText(ts, id, ast); if ( - ctx.hasLocalVariable(text) && !(isPropExp && text === '$attrs') + ctx.hasLocalVariable(text) && !externalVars.includes(text) // https://github.com/vuejs/core/blob/245230e135152900189f13a4281302de45fdcfaa/packages/compiler-core/src/transforms/transformExpression.ts#L342-L352 || isGloballyAllowed(text) || text === 'require' From 658d7528e93485f295e8b7265ab1f596a59396a8 Mon Sep 17 00:00:00 2001 From: KazariEX <1364035137@qq.com> Date: Tue, 17 Dec 2024 18:13:50 +0800 Subject: [PATCH 05/11] fix: temporarily remove external variables at the top level --- .../lib/codegen/template/interpolation.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/language-core/lib/codegen/template/interpolation.ts b/packages/language-core/lib/codegen/template/interpolation.ts index 78ac7721df..9e4865e0fc 100644 --- a/packages/language-core/lib/codegen/template/interpolation.ts +++ b/packages/language-core/lib/codegen/template/interpolation.ts @@ -21,6 +21,10 @@ export function* generateInterpolation( suffix: string = '', externalVars: string[] = [] ): Generator { + for (const name of externalVars) { + ctx.removeLocalVariable(name); + } + const code = prefix + _code + suffix; const ast = createTsAst(options.ts, astHolder, code); for (let [section, offset, type] of forEachInterpolationSegment( @@ -30,8 +34,7 @@ export function* generateInterpolation( ctx, code, start !== undefined ? start - prefix.length : undefined, - ast, - externalVars + ast )) { if (offset === undefined) { yield section; @@ -71,6 +74,10 @@ export function* generateInterpolation( yield addSuffix; } } + + for (const name of externalVars) { + ctx.addLocalVariable(name); + } } interface CtxVar { @@ -86,15 +93,14 @@ function* forEachInterpolationSegment( ctx: TemplateCodegenContext, code: string, offset: number | undefined, - ast: ts.SourceFile, - externalVars: string[] + ast: ts.SourceFile ): Generator<[fragment: string, offset: number | undefined, type?: 'errorMappingOnly' | 'startText' | 'endText']> { let ctxVars: CtxVar[] = []; const varCb = (id: ts.Identifier, isShorthand: boolean) => { const text = getNodeText(ts, id, ast); if ( - ctx.hasLocalVariable(text) && !externalVars.includes(text) + ctx.hasLocalVariable(text) // https://github.com/vuejs/core/blob/245230e135152900189f13a4281302de45fdcfaa/packages/compiler-core/src/transforms/transformExpression.ts#L342-L352 || isGloballyAllowed(text) || text === 'require' From 9c774065eaaf126799d9d74616a75ea9b7f85052 Mon Sep 17 00:00:00 2001 From: KazariEX <1364035137@qq.com> Date: Sat, 21 Dec 2024 13:24:17 +0800 Subject: [PATCH 06/11] fix: remove unnecessary handling for `v-bind="$attrs"` --- .../lib/codegen/template/element.ts | 23 ++++++++++++--- .../lib/codegen/template/elementProps.ts | 28 +++---------------- .../lib/codegen/template/interpolation.ts | 11 +------- 3 files changed, 24 insertions(+), 38 deletions(-) diff --git a/packages/language-core/lib/codegen/template/element.ts b/packages/language-core/lib/codegen/template/element.ts index f3e85b7f1b..b8f5c9e2d2 100644 --- a/packages/language-core/lib/codegen/template/element.ts +++ b/packages/language-core/lib/codegen/template/element.ts @@ -11,7 +11,7 @@ import type { TemplateCodegenContext } from './context'; import { generateElementChildren } from './elementChildren'; import { generateElementDirectives } from './elementDirectives'; import { generateElementEvents } from './elementEvents'; -import { type FailedPropExpression, generateElementProps, getHasVBindAttrs } from './elementProps'; +import { type FailedPropExpression, generateElementProps } from './elementProps'; import type { TemplateCodegenOptions } from './index'; import { generateInterpolation } from './interpolation'; import { generateObjectProperty } from './objectProperty'; @@ -252,7 +252,7 @@ export function* generateComponent( yield `let ${var_componentEvents}!: __VLS_NormalizeEmits${endOfLine}`; } - if (getHasVBindAttrs(options, ctx, node)) { + if (hasVBindAttrs(options, ctx, node)) { const attrsVar = ctx.getInternalVariable(); ctx.inheritedAttrVars.add(attrsVar); yield `let ${attrsVar}!: Parameters[0];\n`; @@ -336,7 +336,7 @@ export function* generateElement( yield* generateElementChildren(options, ctx, node, currentComponent, componentCtxVar); } - if (getHasVBindAttrs(options, ctx, node)) { + if (hasVBindAttrs(options, ctx, node)) { ctx.inheritedAttrVars.add(`__VLS_intrinsicElements.${node.tag}`); } } @@ -736,6 +736,21 @@ function* generateReferencesForScopedCssClasses( } } +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); } @@ -789,4 +804,4 @@ function collectClasses(content: string, startOffset = 0) { // isTemplateExpression is missing in tsc function isTemplateExpression(node: ts.Node): node is ts.TemplateExpression { return node.kind === 228 satisfies ts.SyntaxKind.TemplateExpression; -} \ No newline at end of file +} diff --git a/packages/language-core/lib/codegen/template/elementProps.ts b/packages/language-core/lib/codegen/template/elementProps.ts index 4fd0a1e5c4..48ed3d1b56 100644 --- a/packages/language-core/lib/codegen/template/elementProps.ts +++ b/packages/language-core/lib/codegen/template/elementProps.ts @@ -31,7 +31,6 @@ export function* generateElementProps( failedPropExps?: FailedPropExpression[] ): Generator { const isComponent = node.tagType === CompilerDOM.ElementTypes.COMPONENT; - const hasVBindAttrs = getHasVBindAttrs(options, ctx, node); for (const prop of props) { if ( @@ -146,8 +145,7 @@ export function* generateElementProps( prop, prop.exp, ctx.codeFeatures.all, - enableCodeFeatures, - hasVBindAttrs + enableCodeFeatures ), `)` ); @@ -252,8 +250,7 @@ export function* generateElementProps( prop, prop.exp, ctx.codeFeatures.all, - enableCodeFeatures, - hasVBindAttrs + enableCodeFeatures ) ); if (enableCodeFeatures) { @@ -273,8 +270,7 @@ function* generatePropExp( prop: CompilerDOM.DirectiveNode, exp: CompilerDOM.SimpleExpressionNode | undefined, features: VueCodeInformation, - enableCodeFeatures: boolean, - hasVBindAttrs: boolean + enableCodeFeatures: boolean ): Generator { const isShorthand = prop.arg?.loc.start.offset === prop.exp?.loc.start.offset; @@ -295,8 +291,7 @@ function* generatePropExp( exp.loc.start.offset, exp.loc, '(', - ')', - hasVBindAttrs ? ['$attrs'] : undefined + ')' ); } else { const propVariableName = camelize(exp.loc.source); @@ -384,21 +379,6 @@ function getPropsCodeInfo( }; } -export function getHasVBindAttrs( - 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 getModelPropName(node: CompilerDOM.ElementNode, vueCompilerOptions: VueCompilerOptions) { for (const modelName in vueCompilerOptions.experimentalModelPropName) { diff --git a/packages/language-core/lib/codegen/template/interpolation.ts b/packages/language-core/lib/codegen/template/interpolation.ts index 9e4865e0fc..daaf8a4f73 100644 --- a/packages/language-core/lib/codegen/template/interpolation.ts +++ b/packages/language-core/lib/codegen/template/interpolation.ts @@ -18,13 +18,8 @@ export function* generateInterpolation( start: number | undefined, astHolder: any = {}, prefix: string = '', - suffix: string = '', - externalVars: string[] = [] + suffix: string = '' ): Generator { - for (const name of externalVars) { - ctx.removeLocalVariable(name); - } - const code = prefix + _code + suffix; const ast = createTsAst(options.ts, astHolder, code); for (let [section, offset, type] of forEachInterpolationSegment( @@ -74,10 +69,6 @@ export function* generateInterpolation( yield addSuffix; } } - - for (const name of externalVars) { - ctx.addLocalVariable(name); - } } interface CtxVar { From 754cc63772453a64e8b343bb68aa4cf480ddaaf6 Mon Sep 17 00:00:00 2001 From: KazariEX <1364035137@qq.com> Date: Sun, 22 Dec 2024 00:16:32 +0800 Subject: [PATCH 07/11] refactor: consistency --- packages/language-core/lib/codegen/script/template.ts | 8 ++++---- packages/language-core/lib/codegen/template/index.ts | 5 ++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/language-core/lib/codegen/script/template.ts b/packages/language-core/lib/codegen/script/template.ts index 1f2da58326..94e8c322cf 100644 --- a/packages/language-core/lib/codegen/script/template.ts +++ b/packages/language-core/lib/codegen/script/template.ts @@ -147,15 +147,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/index.ts b/packages/language-core/lib/codegen/template/index.ts index 116a113529..c4bbae1fa2 100644 --- a/packages/language-core/lib/codegen/template/index.ts +++ b/packages/language-core/lib/codegen/template/index.ts @@ -142,7 +142,10 @@ function* generateRootEl( options: TemplateCodegenOptions, ctx: TemplateCodegenContext ): Generator { - yield* generateContextVariable(options, `$el`, ctx.singleRootElType ?? `any`); + yield `let __VLS_rootEl!: `; + yield ctx.singleRootElType ?? `any`; + yield endOfLine; + yield* generateContextVariable(options, `$el`, `typeof __VLS_rootEl`); } function* generateContextVariable( From a7a6dd8b3e0551c19952abd0249997ecfd337e49 Mon Sep 17 00:00:00 2001 From: KazariEX <1364035137@qq.com> Date: Sun, 22 Dec 2024 04:10:16 +0800 Subject: [PATCH 08/11] fix: typeof `__VLS_slots` --- packages/language-core/lib/codegen/template/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/language-core/lib/codegen/template/index.ts b/packages/language-core/lib/codegen/template/index.ts index c4bbae1fa2..32dda0943e 100644 --- a/packages/language-core/lib/codegen/template/index.ts +++ b/packages/language-core/lib/codegen/template/index.ts @@ -92,7 +92,7 @@ function* generateSlots( yield `}${endOfLine}`; } const name = getSlotsPropertyName(options.vueCompilerOptions.target); - yield* generateContextVariable(options, name, options.slotsAssignName ?? `__VLS_slots`); + yield* generateContextVariable(options, name, `typeof ${options.slotsAssignName ?? `__VLS_slots`}`); } function* generateInheritedAttrs( From 62bf2af164d33e7b83a2d397f64affb55cc1d044 Mon Sep 17 00:00:00 2001 From: KazariEX <1364035137@qq.com> Date: Tue, 31 Dec 2024 23:14:17 +0800 Subject: [PATCH 09/11] refactor: use `__VLS_special` --- .../lib/codegen/template/context.ts | 2 + .../lib/codegen/template/index.ts | 60 +++++++------------ .../lib/codegen/template/interpolation.ts | 10 +++- packages/language-core/lib/plugins/vue-tsx.ts | 27 ++------- 4 files changed, 35 insertions(+), 64 deletions(-) diff --git a/packages/language-core/lib/codegen/template/context.ts b/packages/language-core/lib/codegen/template/context.ts index 662ee6cff2..6996375ee6 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; @@ -132,6 +133,7 @@ export function createTemplateCodegenContext(options: Pick number; - linkedCodeMappings: Mapping[]; } export function* generateTemplate(options: TemplateCodegenOptions): Generator { @@ -38,20 +35,29 @@ export function* generateTemplate(options: TemplateCodegenOptions): Generator { yield 'let __VLS_inheritedAttrs!: {}'; @@ -105,7 +109,6 @@ function* generateInheritedAttrs( yield ` & typeof ${varName}`; } yield endOfLine; - yield* generateContextVariable(options, `$attrs`, `Partial & Record`); if (ctx.bindingAttrLocs.length) { yield `[`; @@ -120,10 +123,10 @@ function* generateInheritedAttrs( } yield `]${endOfLine}`; } + return `Partial & Record`; } function* generateRefs( - options: TemplateCodegenOptions, ctx: TemplateCodegenContext ): Generator { yield `const __VLS_refs = {${newLine}`; @@ -136,37 +139,16 @@ function* generateRefs( yield `: ${varName},${newLine}`; } yield `}${endOfLine}`; - yield* generateContextVariable(options, `$refs`, `typeof __VLS_refs`); + return `typeof __VLS_refs`; } function* generateRootEl( - options: TemplateCodegenOptions, ctx: TemplateCodegenContext ): Generator { yield `let __VLS_rootEl!: `; yield ctx.singleRootElType ?? `any`; yield endOfLine; - yield* generateContextVariable(options, `$el`, `typeof __VLS_rootEl`); -} - -function* generateContextVariable( - options: TemplateCodegenOptions, - varName: string, - typeExp: string -): Generator { - yield `/** @type { typeof __VLS_ctx.`; - const sourceOffset = options.getGeneratedLength(); - yield `${varName} } */${endOfLine}`; - yield `var `; - const generatedOffset = options.getGeneratedLength(); - yield `${varName}!: ${typeExp}${endOfLine}`; - - options.linkedCodeMappings.push({ - sourceOffsets: [sourceOffset], - generatedOffsets: [generatedOffset], - lengths: [varName.length], - data: undefined, - }); + 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 daaf8a4f73..366b606971 100644 --- a/packages/language-core/lib/codegen/template/interpolation.ts +++ b/packages/language-core/lib/codegen/template/interpolation.ts @@ -134,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]; @@ -146,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']; } @@ -158,6 +158,7 @@ function* forEachInterpolationSegment( function* generateVar( code: string, + specialVars: Set, destructuredPropNames: Set | undefined, templateRefNames: Set | undefined, curVar: CtxVar, @@ -175,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/plugins/vue-tsx.ts b/packages/language-core/lib/plugins/vue-tsx.ts index 0bb9f21601..48d33f31b6 100644 --- a/packages/language-core/lib/plugins/vue-tsx.ts +++ b/packages/language-core/lib/plugins/vue-tsx.ts @@ -41,22 +41,13 @@ const plugin: VueLanguagePlugin = ctx => { resolveEmbeddedCode(fileName, sfc, embeddedFile) { - const tsx = useTsx(fileName, sfc); + const _tsx = useTsx(fileName, sfc); if (/script_(js|jsx|ts|tsx)/.test(embeddedFile.id)) { - const script = tsx.generatedScript.get(); - const template = tsx.generatedTemplate.get(); - if (script) { - const linkedCodeMappings = [ - ...script.linkedCodeMappings, - ...template?.linkedCodeMappings.map(mapping => ({ - ...mapping, - sourceOffsets: mapping.sourceOffsets.map(offset => offset + script.templateGeneratedOffset!), - generatedOffsets: mapping.generatedOffsets.map(offset => offset + script.templateGeneratedOffset!), - })) ?? [] - ]; - embeddedFile.content = [...script.codes]; - embeddedFile.linkedCodeMappings = linkedCodeMappings; + const tsx = _tsx.generatedScript.get(); + if (tsx) { + embeddedFile.content = [...tsx.codes]; + embeddedFile.linkedCodeMappings = [...tsx.linkedCodeMappings]; } } }, @@ -184,8 +175,6 @@ function createTsx( } const codes: Code[] = []; - const linkedCodeMappings: Mapping[] = []; - let generatedLength = 0; const codegen = generateTemplate({ ts, compilerOptions: ctx.compilerOptions, @@ -201,8 +190,6 @@ function createTsx( propsAssignName: propsAssignName.get(), inheritAttrs: inheritAttrs.get(), selfComponentName: selfComponentName.get(), - getGeneratedLength: () => generatedLength, - linkedCodeMappings, }); let current = codegen.next(); @@ -210,16 +197,12 @@ function createTsx( while (!current.done) { const code = current.value; codes.push(code); - generatedLength += typeof code === 'string' - ? code.length - : code[0].length; current = codegen.next(); } return { ...current.value, codes, - linkedCodeMappings, }; }); const generatedScript = computed(() => { From 211c5d06bef959893f1e51d17b775f08d1aaa2a0 Mon Sep 17 00:00:00 2001 From: KazariEX <1364035137@qq.com> Date: Tue, 31 Dec 2024 23:18:01 +0800 Subject: [PATCH 10/11] fix: `$attrs` --- packages/language-core/lib/codegen/template/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/language-core/lib/codegen/template/index.ts b/packages/language-core/lib/codegen/template/index.ts index 8e94d5bade..565cb4f87a 100644 --- a/packages/language-core/lib/codegen/template/index.ts +++ b/packages/language-core/lib/codegen/template/index.ts @@ -113,6 +113,7 @@ function* generateInheritedAttrs( if (ctx.bindingAttrLocs.length) { yield `[`; for (const loc of ctx.bindingAttrLocs) { + yield `__VLS_special.`; yield [ loc.source, 'template', From d661954ec860ade86494496eaa80c79b0648e1f6 Mon Sep 17 00:00:00 2001 From: KazariEX <1364035137@qq.com> Date: Tue, 31 Dec 2024 23:19:32 +0800 Subject: [PATCH 11/11] fix: remove unnecessary code --- packages/language-core/lib/codegen/script/context.ts | 1 - packages/language-core/lib/codegen/script/template.ts | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/language-core/lib/codegen/script/context.ts b/packages/language-core/lib/codegen/script/context.ts index 2ad3aef75e..bb7fe16ded 100644 --- a/packages/language-core/lib/codegen/script/context.ts +++ b/packages/language-core/lib/codegen/script/context.ts @@ -18,7 +18,6 @@ export function createScriptCodegenContext(options: ScriptCodegenOptions) { return { generatedTemplate: false, generatedPropsType: false, - templateGeneratedOffset: undefined as number | undefined, scriptSetupGeneratedOffset: undefined as number | undefined, bypassDefineComponent: options.lang === 'js' || options.lang === 'jsx', bindingNames: new Set([ diff --git a/packages/language-core/lib/codegen/script/template.ts b/packages/language-core/lib/codegen/script/template.ts index eda21dc636..92a49bf7ac 100644 --- a/packages/language-core/lib/codegen/script/template.ts +++ b/packages/language-core/lib/codegen/script/template.ts @@ -20,7 +20,7 @@ export function* generateTemplate( yield* generateTemplateCtx(options); yield* generateTemplateComponents(options); yield* generateTemplateDirectives(options); - yield* generateTemplateBody(options, ctx, templateCodegenCtx); + yield* generateTemplateBody(options, templateCodegenCtx); return templateCodegenCtx; } @@ -110,7 +110,6 @@ export function* generateTemplateDirectives(options: ScriptCodegenOptions): Gene function* generateTemplateBody( options: ScriptCodegenOptions, - ctx: ScriptCodegenContext, templateCodegenCtx: TemplateCodegenContext ): Generator { yield* generateStyleScopedClasses(options, templateCodegenCtx); @@ -118,7 +117,6 @@ function* generateTemplateBody( yield* generateCssVars(options, templateCodegenCtx); if (options.templateCodegen) { - ctx.templateGeneratedOffset = options.getGeneratedLength(); for (const code of options.templateCodegen.codes) { yield code; }