diff --git a/packages/language-core/lib/codegen/script/component.ts b/packages/language-core/lib/codegen/script/component.ts index 9c60e85014..0d2c89d20a 100644 --- a/packages/language-core/lib/codegen/script/component.ts +++ b/packages/language-core/lib/codegen/script/component.ts @@ -30,7 +30,7 @@ export function* generateComponent( yield `}${endOfLine}`; yield `},${newLine}`; if (!ctx.bypassDefineComponent) { - const emitOptionCodes = [...generateEmitsOption(options, scriptSetup, scriptSetupRanges)]; + const emitOptionCodes = [...generateEmitsOption(options, scriptSetupRanges)]; for (const code of emitOptionCodes) { yield code; } @@ -64,7 +64,6 @@ export function* generateComponentSetupReturns(scriptSetupRanges: ScriptSetupRan export function* generateEmitsOption( options: ScriptCodegenOptions, - scriptSetup: NonNullable, scriptSetupRanges: ScriptSetupRanges ): Generator { const codes: { @@ -75,8 +74,8 @@ export function* generateEmitsOption( }[] = []; if (scriptSetupRanges.defineProp.some(p => p.isModel)) { codes.push({ - optionExp: `{} as __VLS_NormalizeEmits<__VLS_ModelEmitsType>`, - typeOptionType: `__VLS_ModelEmitsType`, + optionExp: `{} as __VLS_NormalizeEmits`, + typeOptionType: `__VLS_ModelEmit`, }); } if (scriptSetupRanges.emits.define) { @@ -84,7 +83,7 @@ export function* generateEmitsOption( codes.push({ optionExp: `{} as __VLS_NormalizeEmits`, typeOptionType: typeArg && !hasUnionTypeArg - ? scriptSetup.content.slice(typeArg.start, typeArg.end) + ? `__VLS_Emit` : undefined, }); } diff --git a/packages/language-core/lib/codegen/script/componentSelf.ts b/packages/language-core/lib/codegen/script/componentSelf.ts index 061285e30f..e62212e150 100644 --- a/packages/language-core/lib/codegen/script/componentSelf.ts +++ b/packages/language-core/lib/codegen/script/componentSelf.ts @@ -48,7 +48,7 @@ export function* generateComponentSelf( yield `}${endOfLine}`; // return { yield `},${newLine}`; // setup() { if (options.sfc.scriptSetup && options.scriptSetupRanges && !ctx.bypassDefineComponent) { - const emitOptionCodes = [...generateEmitsOption(options, options.sfc.scriptSetup, options.scriptSetupRanges)]; + const emitOptionCodes = [...generateEmitsOption(options, options.scriptSetupRanges)]; for (const code of emitOptionCodes) { yield code; } diff --git a/packages/language-core/lib/codegen/script/scriptSetup.ts b/packages/language-core/lib/codegen/script/scriptSetup.ts index 19db16b49a..ffbd783794 100644 --- a/packages/language-core/lib/codegen/script/scriptSetup.ts +++ b/packages/language-core/lib/codegen/script/scriptSetup.ts @@ -64,7 +64,7 @@ export function* generateScriptSetup( emitTypes.push(`typeof ${scriptSetupRanges.emits.name ?? '__VLS_emit'}`); } if (scriptSetupRanges.defineProp.some(p => p.isModel)) { - emitTypes.push(`__VLS_ModelEmitsType`); + emitTypes.push(`typeof __VLS_modelEmit`); } yield ` return {} as {${newLine}` @@ -130,45 +130,15 @@ function* generateSetupFunction( ctx.scriptSetupGeneratedOffset = options.getGeneratedLength() - scriptSetupRanges.importSectionEndOffset; let setupCodeModifies: [Code[], number, number][] = []; - const propsRange = scriptSetupRanges.props.withDefaults ?? scriptSetupRanges.props.define; - if (propsRange && scriptSetupRanges.props.define) { - const statement = scriptSetupRanges.props.define.statement; - if (scriptSetupRanges.props.define.typeArg) { - setupCodeModifies.push([[ - `let __VLS_typeProps!: `, - generateSfcBlockSection(scriptSetup, scriptSetupRanges.props.define.typeArg.start, scriptSetupRanges.props.define.typeArg.end, codeFeatures.all), - endOfLine, - ], statement.start, statement.start]); - setupCodeModifies.push([[`typeof __VLS_typeProps`], scriptSetupRanges.props.define.typeArg.start, scriptSetupRanges.props.define.typeArg.end]); - } - if (!scriptSetupRanges.props.name) { - if (statement.start === propsRange.start && statement.end === propsRange.end) { - setupCodeModifies.push([[`const __VLS_props = `], propsRange.start, propsRange.start]); - } - else { - if (scriptSetupRanges.props.define.typeArg) { - setupCodeModifies.push([[ - `const __VLS_props = `, - generateSfcBlockSection(scriptSetup, propsRange.start, scriptSetupRanges.props.define.typeArg.start, codeFeatures.all), - ], statement.start, scriptSetupRanges.props.define.typeArg.start]); - setupCodeModifies.push([[ - generateSfcBlockSection(scriptSetup, scriptSetupRanges.props.define.typeArg.end, propsRange.end, codeFeatures.all), - `${endOfLine}`, - generateSfcBlockSection(scriptSetup, statement.start, propsRange.start, codeFeatures.all), - `__VLS_props`, - ], scriptSetupRanges.props.define.typeArg.end, propsRange.end]); - } - else { - setupCodeModifies.push([[ - `const __VLS_props = `, - generateSfcBlockSection(scriptSetup, propsRange.start, propsRange.end, codeFeatures.all), - `${endOfLine}`, - generateSfcBlockSection(scriptSetup, statement.start, propsRange.start, codeFeatures.all), - `__VLS_props`, - ], statement.start, propsRange.end]); - } - } - } + if (scriptSetupRanges.props.define) { + setupCodeModifies.push(...generateDefineWithType( + scriptSetup, + scriptSetupRanges.props.name, + scriptSetupRanges.props.define, + scriptSetupRanges.props.withDefaults ?? scriptSetupRanges.props.define, + '__VLS_props', + '__VLS_Props' + )); } if (scriptSetupRanges.slots.define) { if (scriptSetupRanges.slots.isObjectBindingPattern) { @@ -181,8 +151,15 @@ function* generateSetupFunction( setupCodeModifies.push([[`const __VLS_slots = `], scriptSetupRanges.slots.define.start, scriptSetupRanges.slots.define.start]); } } - if (scriptSetupRanges.emits.define && !scriptSetupRanges.emits.name) { - setupCodeModifies.push([[`const __VLS_emit = `], scriptSetupRanges.emits.define.start, scriptSetupRanges.emits.define.start]); + if (scriptSetupRanges.emits.define) { + setupCodeModifies.push(...generateDefineWithType( + scriptSetup, + scriptSetupRanges.emits.name, + scriptSetupRanges.emits.define, + scriptSetupRanges.emits.define, + '__VLS_emit', + '__VLS_Emit' + )); } if (scriptSetupRanges.expose.define) { if (scriptSetupRanges.expose.define?.typeArg) { @@ -217,30 +194,21 @@ function* generateSetupFunction( } if (scriptSetupRanges.cssModules.length) { for (const { exp, arg } of scriptSetupRanges.cssModules) { - if (arg) { - setupCodeModifies.push([ - [ - ` as Omit<__VLS_StyleModules, '$style'>[`, - generateSfcBlockSection(scriptSetup, arg.start, arg.end, codeFeatures.all), - `]` - ], - exp.end, - exp.end - ]); - } - else { - setupCodeModifies.push([ - [ - ` as __VLS_StyleModules[`, - ['', scriptSetup.name, exp.start, codeFeatures.verification], - `'$style'`, - ['', scriptSetup.name, exp.end, codeFeatures.verification], - `]` - ], - exp.end, - exp.end - ]); - } + setupCodeModifies.push([ + arg ? [ + ` as Omit<__VLS_StyleModules, '$style'>[`, + generateSfcBlockSection(scriptSetup, arg.start, arg.end, codeFeatures.all), + `]` + ] : [ + ` as __VLS_StyleModules[`, + ['', scriptSetup.name, exp.start, codeFeatures.verification], + `'$style'`, + ['', scriptSetup.name, exp.end, codeFeatures.verification], + `]` + ], + exp.end, + exp.end + ]); } } for (const { define } of scriptSetupRanges.templateRefs) { @@ -258,25 +226,15 @@ function* generateSetupFunction( } setupCodeModifies = setupCodeModifies.sort((a, b) => a[1] - b[1]); - if (setupCodeModifies.length) { - yield generateSfcBlockSection(scriptSetup, scriptSetupRanges.importSectionEndOffset, setupCodeModifies[0][1], codeFeatures.all); - while (setupCodeModifies.length) { - const [codes, _start, end] = setupCodeModifies.shift()!; - for (const code of codes) { - yield code; - } - if (setupCodeModifies.length) { - const nextStart = setupCodeModifies[0][1]; - yield generateSfcBlockSection(scriptSetup, end, nextStart, codeFeatures.all); - } - else { - yield generateSfcBlockSection(scriptSetup, end, scriptSetup.content.length, codeFeatures.all); - } + let nextStart = scriptSetupRanges.importSectionEndOffset; + for (const [codes, start, end] of setupCodeModifies) { + yield generateSfcBlockSection(scriptSetup, nextStart, start, codeFeatures.all); + for (const code of codes) { + yield code; } + nextStart = end; } - else { - yield generateSfcBlockSection(scriptSetup, scriptSetupRanges.importSectionEndOffset, scriptSetup.content.length, codeFeatures.all); - } + yield generateSfcBlockSection(scriptSetup, nextStart, scriptSetup.content.length, codeFeatures.all); yield* generateScriptSectionPartiallyEnding(scriptSetup.name, scriptSetup.content.length, '#3632/scriptSetup.vue'); @@ -288,7 +246,7 @@ function* generateSetupFunction( } yield* generateComponentProps(options, ctx, scriptSetup, scriptSetupRanges, definePropMirrors); - yield* generateModelEmits(options, scriptSetup, scriptSetupRanges); + yield* generateModelEmit(scriptSetup, scriptSetupRanges); yield `function __VLS_template() {${newLine}`; const templateCodegenCtx = yield* generateTemplate(options, ctx); yield `}${endOfLine}`; @@ -311,6 +269,54 @@ function* generateSetupFunction( } } +function* generateDefineWithType( + scriptSetup: NonNullable, + name: string | undefined, + define: { + statement: TextRange, + typeArg?: TextRange + }, + expression: TextRange, + defaultName: string, + typeName: string +): Generator<[Code[], number, number]> { + const { statement, typeArg } = define; + if (typeArg) { + yield [[ + `type ${typeName} = `, + generateSfcBlockSection(scriptSetup, typeArg.start, typeArg.end, codeFeatures.all), + endOfLine, + ], statement.start, statement.start]; + yield [[typeName], typeArg.start, typeArg.end]; + } + if (!name) { + if (statement.start === expression.start && statement.end === expression.end) { + yield [[`const ${defaultName} = `], expression.start, expression.start]; + } + else if (typeArg) { + yield [[ + `const ${defaultName} = `, + generateSfcBlockSection(scriptSetup, expression.start, typeArg.start, codeFeatures.all) + ], statement.start, typeArg.start]; + yield [[ + generateSfcBlockSection(scriptSetup, typeArg.end, expression.end, codeFeatures.all), + endOfLine, + generateSfcBlockSection(scriptSetup, statement.start, expression.start, codeFeatures.all), + defaultName + ], typeArg.end, expression.end]; + } + else { + yield [[ + `const ${defaultName} = `, + generateSfcBlockSection(scriptSetup, expression.start, expression.end, codeFeatures.all), + endOfLine, + generateSfcBlockSection(scriptSetup, statement.start, expression.start, codeFeatures.all), + defaultName + ], statement.start, expression.end]; + } + } +} + function* generateComponentProps( options: ScriptCodegenOptions, ctx: ScriptCodegenContext, @@ -326,7 +332,7 @@ function* generateComponentProps( yield `,${newLine}`; } - yield* generateEmitsOption(options, scriptSetup, scriptSetupRanges); + yield* generateEmitsOption(options, scriptSetupRanges); yield `})${endOfLine}`; @@ -422,7 +428,7 @@ function* generateComponentProps( yield ` & `; } ctx.generatedPropsType = true; - yield `typeof __VLS_typeProps`; + yield `__VLS_Props`; } if (!ctx.generatedPropsType) { yield `{}`; @@ -430,32 +436,21 @@ function* generateComponentProps( yield endOfLine; } -function* generateModelEmits( - options: ScriptCodegenOptions, +function* generateModelEmit( scriptSetup: NonNullable, scriptSetupRanges: ScriptSetupRanges ): Generator { const defineModels = scriptSetupRanges.defineProp.filter(p => p.isModel); if (defineModels.length) { - const generateDefineModels = function* () { - for (const defineModel of defineModels) { - const [propName, localName] = getPropAndLocalName(scriptSetup, defineModel); - yield `'update:${propName}': [${propName}:`; - yield* generateDefinePropType(scriptSetup, propName, localName, defineModel); - yield `]${endOfLine}`; - } - }; - if (options.vueCompilerOptions.target >= 3.5) { - yield `type __VLS_ModelEmitsType = {${newLine}`; - yield* generateDefineModels(); - yield `}${endOfLine}`; - } - else { - yield `const __VLS_modelEmitsType = (await import('${options.vueCompilerOptions.lib}')).defineEmits<{${newLine}`; - yield* generateDefineModels(); - yield `}>()${endOfLine}`; - yield `type __VLS_ModelEmitsType = typeof __VLS_modelEmitsType${endOfLine}`; + yield `type __VLS_ModelEmit = {${newLine}`; + for (const defineModel of defineModels) { + const [propName, localName] = getPropAndLocalName(scriptSetup, defineModel); + yield `'update:${propName}': [${propName}:`; + yield* generateDefinePropType(scriptSetup, propName, localName, defineModel); + yield `]${endOfLine}`; } + yield `}${endOfLine}`; + yield `const __VLS_modelEmit = defineEmits<__VLS_ModelEmit>()${endOfLine}`; } } diff --git a/packages/language-core/lib/parsers/scriptSetupRanges.ts b/packages/language-core/lib/parsers/scriptSetupRanges.ts index 4feccbde61..dfa5977085 100644 --- a/packages/language-core/lib/parsers/scriptSetupRanges.ts +++ b/packages/language-core/lib/parsers/scriptSetupRanges.ts @@ -32,6 +32,7 @@ export function parseScriptSetupRanges( const emits: { name?: string; define?: ReturnType & { + statement: TextRange; hasUnionTypeArg?: boolean; }; } = {}; @@ -290,7 +291,10 @@ export function parseScriptSetupRanges( } } else if (vueCompilerOptions.macros.defineEmits.includes(callText)) { - emits.define = parseDefineFunction(node); + emits.define = { + ...parseDefineFunction(node), + statement: getStatementRange(ts, parents, node, ast) + }; if (ts.isVariableDeclaration(parent)) { emits.name = getNodeText(ts, parent.name, ast); } @@ -326,25 +330,9 @@ export function parseScriptSetupRanges( } } - let statementRange: TextRange | undefined; - for (let i = parents.length - 1; i >= 0; i--) { - if (ts.isStatement(parents[i])) { - const statement = parents[i]; - ts.forEachChild(statement, child => { - const range = _getStartEnd(child); - statementRange ??= range; - statementRange.end = range.end; - }); - break; - } - } - if (!statementRange) { - statementRange = _getStartEnd(node); - } - props.define = { ...parseDefineFunction(node), - statement: statementRange, + statement: getStatementRange(ts, parents, node, ast), }; if (node.arguments.length) { @@ -518,3 +506,27 @@ export function getNodeText( const { start, end } = getStartEnd(ts, node, sourceFile); return sourceFile.text.substring(start, end); } + +function getStatementRange( + ts: typeof import('typescript'), + parents: ts.Node[], + node: ts.Node, + sourceFile: ts.SourceFile +) { + let statementRange: TextRange | undefined; + for (let i = parents.length - 1; i >= 0; i--) { + if (ts.isStatement(parents[i])) { + const statement = parents[i]; + ts.forEachChild(statement, child => { + const range = getStartEnd(ts, child, sourceFile); + statementRange ??= range; + statementRange.end = range.end; + }); + break; + } + } + if (!statementRange) { + statementRange = getStartEnd(ts, node, sourceFile); + } + return statementRange; +} \ No newline at end of file diff --git a/test-workspace/tsc/passedFixtures/vue2/tsconfig.json b/test-workspace/tsc/passedFixtures/vue2/tsconfig.json index 191e683f1e..2c97860f06 100644 --- a/test-workspace/tsc/passedFixtures/vue2/tsconfig.json +++ b/test-workspace/tsc/passedFixtures/vue2/tsconfig.json @@ -26,6 +26,7 @@ "../vue3/#4649", "../vue3/#4777", "../vue3/#4820", + "../vue3/#4822", "../vue3/components", "../vue3/defineEmits", "../vue3/defineModel", diff --git a/test-workspace/tsc/passedFixtures/vue3/#4822/comp.vue b/test-workspace/tsc/passedFixtures/vue3/#4822/comp.vue new file mode 100644 index 0000000000..096431e38e --- /dev/null +++ b/test-workspace/tsc/passedFixtures/vue3/#4822/comp.vue @@ -0,0 +1,6 @@ + diff --git a/test-workspace/tsc/passedFixtures/vue3/#4822/main.vue b/test-workspace/tsc/passedFixtures/vue3/#4822/main.vue new file mode 100644 index 0000000000..c6f1e69582 --- /dev/null +++ b/test-workspace/tsc/passedFixtures/vue3/#4822/main.vue @@ -0,0 +1,19 @@ + + + + +