Skip to content

Commit 85c5cf0

Browse files
fix(language-core): avoid generating component options within the setup scope (#5832)
1 parent fe687a6 commit 85c5cf0

File tree

6 files changed

+104
-93
lines changed

6 files changed

+104
-93
lines changed

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

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,7 @@ export function* generateComponent(
1313
scriptSetup: NonNullable<Sfc['scriptSetup']>,
1414
scriptSetupRanges: ScriptSetupRanges,
1515
): Generator<Code> {
16-
if (
17-
options.script
18-
&& options.scriptRanges?.componentOptions
19-
&& options.scriptRanges.componentOptions.expression.start !== options.scriptRanges.componentOptions.args.start
20-
) {
21-
// use defineComponent() from user space code if it exist
22-
yield* generateSfcBlockSection(
23-
options.script,
24-
options.scriptRanges.componentOptions.expression.start,
25-
options.scriptRanges.componentOptions.args.start,
26-
codeFeatures.all,
27-
);
28-
yield `{${newLine}`;
29-
}
30-
else {
31-
yield `(await import('${options.vueCompilerOptions.lib}')).defineComponent({${newLine}`;
32-
}
16+
yield `(await import('${options.vueCompilerOptions.lib}')).defineComponent({${newLine}`;
3317

3418
const returns: string[][] = [];
3519

@@ -60,10 +44,6 @@ export function* generateComponent(
6044
) {
6145
yield `__typeEl: {} as ${names.RootEl},${newLine}`;
6246
}
63-
if (options.script && options.scriptRanges?.componentOptions?.args) {
64-
const { args } = options.scriptRanges.componentOptions;
65-
yield* generateSfcBlockSection(options.script, args.start + 1, args.end - 1, codeFeatures.all);
66-
}
6747
yield `})`;
6848
}
6949

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

Lines changed: 83 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -55,20 +55,25 @@ function* generateWorker(
5555
// <script> + <script setup>
5656
if (script && scriptRanges && scriptSetup && scriptSetupRanges) {
5757
// <script>
58-
const { exportDefault, componentOptions } = scriptRanges;
58+
let selfType: string | undefined;
59+
const { exportDefault } = scriptRanges;
5960
if (exportDefault) {
60-
const { expression: options } = componentOptions ?? exportDefault;
61-
yield* generateSfcBlockSection(script, 0, options.start, codeFeatures.all);
62-
yield exportExpression;
63-
yield* generateSfcBlockSection(script, options.end, script.content.length, codeFeatures.all);
61+
yield* generateScriptWithExportDefault(
62+
ctx,
63+
script,
64+
scriptRanges,
65+
exportDefault,
66+
vueCompilerOptions,
67+
selfType = '__VLS_self',
68+
);
6469
}
6570
else {
6671
yield* generateSfcBlockSection(script, 0, script.content.length, codeFeatures.all);
6772
yield `export default ${exportExpression}${endOfLine}`;
6873
}
6974

7075
// <script setup>
71-
yield* generateExportDeclareEqual(scriptSetup);
76+
yield* generateExportDeclareEqual(scriptSetup, names._export);
7277
if (scriptSetup.generic) {
7378
yield* generateGeneric(
7479
options,
@@ -81,7 +86,7 @@ function* generateWorker(
8186
ctx,
8287
scriptSetup,
8388
scriptSetupRanges,
84-
generateTemplate(options, ctx),
89+
generateTemplate(options, ctx, selfType),
8590
),
8691
);
8792
}
@@ -92,7 +97,7 @@ function* generateWorker(
9297
ctx,
9398
scriptSetup,
9499
scriptSetupRanges,
95-
generateTemplate(options, ctx),
100+
generateTemplate(options, ctx, selfType),
96101
[`return `],
97102
);
98103
yield `})()${endOfLine}`;
@@ -101,7 +106,7 @@ function* generateWorker(
101106
// only <script setup>
102107
else if (scriptSetup && scriptSetupRanges) {
103108
if (scriptSetup.generic) {
104-
yield* generateExportDeclareEqual(scriptSetup);
109+
yield* generateExportDeclareEqual(scriptSetup, names._export);
105110
yield* generateGeneric(
106111
options,
107112
ctx,
@@ -125,7 +130,7 @@ function* generateWorker(
125130
scriptSetup,
126131
scriptSetupRanges,
127132
generateTemplate(options, ctx),
128-
generateExportDeclareEqual(scriptSetup),
133+
generateExportDeclareEqual(scriptSetup, names._export),
129134
);
130135
}
131136
yield `export default ${exportExpression}${endOfLine}`;
@@ -134,49 +139,19 @@ function* generateWorker(
134139
else if (script && scriptRanges) {
135140
const { exportDefault } = scriptRanges;
136141
if (exportDefault) {
137-
const { expression, isObjectLiteral } = exportDefault;
138-
139-
let wrapLeft: string | undefined;
140-
let wrapRight: string | undefined;
141-
if (
142-
isObjectLiteral
143-
&& vueCompilerOptions.optionsWrapper.length
144-
) {
145-
[wrapLeft, wrapRight] = vueCompilerOptions.optionsWrapper;
146-
ctx.inlayHints.push({
147-
blockName: script.name,
148-
offset: expression.start,
149-
setting: 'vue.inlayHints.optionsWrapper',
150-
label: wrapLeft || '[Missing optionsWrapper[0]]',
151-
tooltip: [
152-
'This is virtual code that is automatically wrapped for type support, it does not affect your runtime behavior, you can customize it via `vueCompilerOptions.optionsWrapper` option in tsconfig / jsconfig.',
153-
'To hide it, you can set `"vue.inlayHints.optionsWrapper": false` in IDE settings.',
154-
].join('\n\n'),
155-
}, {
156-
blockName: script.name,
157-
offset: expression.end,
158-
setting: 'vue.inlayHints.optionsWrapper',
159-
label: wrapRight || '[Missing optionsWrapper[1]]',
160-
});
161-
}
162-
163-
yield* generateSfcBlockSection(script, 0, expression.start, codeFeatures.all);
164-
yield `${exportExpression}${endOfLine}`;
165-
yield* generateTemplate(options, ctx, names._export);
166-
yield* generateExportDeclareEqual(script);
167-
if (wrapLeft && wrapRight) {
168-
yield wrapLeft;
169-
yield* generateSfcBlockSection(script, expression.start, expression.end, codeFeatures.all);
170-
yield wrapRight;
171-
yield* generateSfcBlockSection(script, expression.end, script.content.length, codeFeatures.all);
172-
}
173-
else {
174-
yield* generateSfcBlockSection(script, expression.start, script.content.length, codeFeatures.all);
175-
}
142+
yield* generateScriptWithExportDefault(
143+
ctx,
144+
script,
145+
scriptRanges,
146+
exportDefault,
147+
vueCompilerOptions,
148+
names._export,
149+
generateTemplate(options, ctx, names._export),
150+
);
176151
}
177152
else {
178153
yield* generateSfcBlockSection(script, 0, script.content.length, codeFeatures.all);
179-
yield* generateExportDeclareEqual(script);
154+
yield* generateExportDeclareEqual(script, names._export);
180155
yield `(await import('${vueCompilerOptions.lib}')).defineComponent({})${endOfLine}`;
181156
yield* generateTemplate(options, ctx, names._export);
182157
yield `export default ${exportExpression}${endOfLine}`;
@@ -186,6 +161,62 @@ function* generateWorker(
186161
yield* ctx.localTypes.generate();
187162
}
188163

164+
function* generateScriptWithExportDefault(
165+
ctx: ScriptCodegenContext,
166+
script: NonNullable<Sfc['script']>,
167+
scriptRanges: ScriptRanges,
168+
exportDefault: NonNullable<ScriptRanges['exportDefault']>,
169+
vueCompilerOptions: VueCompilerOptions,
170+
varName: string,
171+
templateGenerator?: Generator<Code>,
172+
): Generator<Code> {
173+
const { componentOptions } = scriptRanges;
174+
const { expression, isObjectLiteral } = componentOptions ?? exportDefault;
175+
176+
let wrapLeft: string | undefined;
177+
let wrapRight: string | undefined;
178+
if (
179+
isObjectLiteral
180+
&& vueCompilerOptions.optionsWrapper.length
181+
) {
182+
[wrapLeft, wrapRight] = vueCompilerOptions.optionsWrapper;
183+
ctx.inlayHints.push({
184+
blockName: script.name,
185+
offset: expression.start,
186+
setting: 'vue.inlayHints.optionsWrapper',
187+
label: wrapLeft || '[Missing optionsWrapper[0]]',
188+
tooltip: [
189+
'This is virtual code that is automatically wrapped for type support, it does not affect your runtime behavior, you can customize it via `vueCompilerOptions.optionsWrapper` option in tsconfig / jsconfig.',
190+
'To hide it, you can set `"vue.inlayHints.optionsWrapper": false` in IDE settings.',
191+
].join('\n\n'),
192+
}, {
193+
blockName: script.name,
194+
offset: expression.end,
195+
setting: 'vue.inlayHints.optionsWrapper',
196+
label: wrapRight || '[Missing optionsWrapper[1]]',
197+
});
198+
}
199+
200+
yield* generateSfcBlockSection(script, 0, expression.start, codeFeatures.all);
201+
yield exportExpression;
202+
yield* generateSfcBlockSection(script, expression.end, exportDefault.end, codeFeatures.all);
203+
yield endOfLine;
204+
if (templateGenerator) {
205+
yield* templateGenerator;
206+
}
207+
yield* generateExportDeclareEqual(script, varName);
208+
if (wrapLeft && wrapRight) {
209+
yield wrapLeft;
210+
yield* generateSfcBlockSection(script, expression.start, expression.end, codeFeatures.all);
211+
yield wrapRight;
212+
}
213+
else {
214+
yield* generateSfcBlockSection(script, expression.start, expression.end, codeFeatures.all);
215+
}
216+
yield endOfLine;
217+
yield* generateSfcBlockSection(script, exportDefault.end, script.content.length, codeFeatures.all);
218+
}
219+
189220
function* generateGlobalTypesReference(options: ScriptCodegenOptions): Generator<Code> {
190221
const globalTypesPath = options.vueCompilerOptions.globalTypesPath(options.fileName);
191222
if (!globalTypesPath) {
@@ -207,10 +238,10 @@ function* generateGlobalTypesReference(options: ScriptCodegenOptions): Generator
207238
}
208239
}
209240

210-
function* generateExportDeclareEqual(block: SfcBlock): Generator<Code> {
241+
function* generateExportDeclareEqual(block: SfcBlock, name: string): Generator<Code> {
211242
yield `const `;
212243
const token = yield* startBoundary(block.name, 0, codeFeatures.doNotReportTs6133);
213-
yield names._export;
244+
yield name;
214245
yield endBoundary(token, block.content.length);
215246
yield ` = `;
216247
}

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

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ export function* generateTemplate(
1212
ctx: ScriptCodegenContext,
1313
selfType?: string,
1414
): Generator<Code> {
15-
selfType ??= yield* generateSelf(options);
1615
yield* generateSetupExposed(options, ctx);
1716
yield* generateTemplateCtx(options, ctx, selfType);
1817
yield* generateTemplateComponents(options);
@@ -26,24 +25,6 @@ export function* generateTemplate(
2625
}
2726
}
2827

29-
function* generateSelf({ script, scriptRanges, vueCompilerOptions }: ScriptCodegenOptions): Generator<Code> {
30-
const varName = '__VLS_self';
31-
if (script && scriptRanges?.componentOptions) {
32-
yield `const ${varName} = (await import('${vueCompilerOptions.lib}')).defineComponent(`;
33-
const { args } = scriptRanges.componentOptions;
34-
yield* generateSfcBlockSection(script, args.start, args.end, codeFeatures.all);
35-
yield `)${endOfLine}`;
36-
return varName;
37-
}
38-
else if (script && scriptRanges?.exportDefault) {
39-
yield `const ${varName} = `;
40-
const { expression } = scriptRanges.exportDefault;
41-
yield* generateSfcBlockSection(script, expression.start, expression.end, codeFeatures.all);
42-
yield endOfLine;
43-
return varName;
44-
}
45-
}
46-
4728
function* generateTemplateCtx(
4829
{ vueCompilerOptions, script, styleCodegen, scriptSetupRanges, fileName }: ScriptCodegenOptions,
4930
ctx: ScriptCodegenContext,

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export function parseScriptRanges(
1818
| undefined;
1919
let componentOptions:
2020
| {
21+
isObjectLiteral: boolean;
2122
expression: TextRange;
2223
args: TextRange;
2324
argsNode: ts.ObjectLiteralExpression;
@@ -81,6 +82,7 @@ export function parseScriptRanges(
8182
}
8283
});
8384
componentOptions = {
85+
isObjectLiteral: ts.isObjectLiteralExpression(node.expression),
8486
expression: _getStartEnd(node.expression),
8587
args: _getStartEnd(obj),
8688
argsNode: obj,
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<script lang="ts">
2+
export default {
3+
beforeRouteEnter() {
4+
// @ts-expect-error
5+
console.log('counter', counter);
6+
},
7+
};
8+
</script>
9+
10+
<script setup lang="ts">
11+
const counter = 1;
12+
</script>
13+
14+
<template>
15+
{{ counter }}
16+
</template>

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import type { FunctionDirective } from 'vue';
33
import { exactType } from '../../shared';
44
5+
let Comp!: (_: { foo?: string; }) => void;
6+
57
export default {
68
directives: {
79
foo: {} as FunctionDirective<typeof Comp, (_: string) => void>
@@ -10,7 +12,6 @@ export default {
1012
</script>
1113

1214
<script setup lang="ts">
13-
let Comp!: (_: { foo?: string; }) => void;
1415
</script>
1516

1617
<template>

0 commit comments

Comments
 (0)