Skip to content

Commit c022037

Browse files
committed
fix(language-core): avoid early access to local types to skip unnecessary type generation
1 parent 3000a93 commit c022037

File tree

1 file changed

+69
-108
lines changed

1 file changed

+69
-108
lines changed

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

Lines changed: 69 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -80,56 +80,28 @@ export function* generateEmitsOption(
8080
options: ScriptCodegenOptions,
8181
scriptSetupRanges: ScriptSetupRanges,
8282
): Generator<Code> {
83-
const codes: {
84-
// undefined means the emit source cannot be explained by expression
85-
optionExp?: Code;
86-
// undefined means the emit source cannot be explained by type
87-
typeOptionType?: Code;
88-
}[] = [];
83+
const optionCodes: Code[] = [];
84+
const typeOptionCodes: Code[] = [];
85+
8986
if (scriptSetupRanges.defineModel.length) {
90-
codes.push({
91-
optionExp: `{} as __VLS_NormalizeEmits<typeof __VLS_modelEmit>`,
92-
typeOptionType: `__VLS_ModelEmit`,
93-
});
87+
optionCodes.push(`{} as __VLS_NormalizeEmits<typeof __VLS_modelEmit>`);
88+
typeOptionCodes.push(`__VLS_ModelEmit`);
9489
}
9590
if (scriptSetupRanges.defineEmits) {
9691
const { name, typeArg, hasUnionTypeArg } = scriptSetupRanges.defineEmits;
97-
codes.push({
98-
optionExp: `{} as __VLS_NormalizeEmits<typeof ${name ?? '__VLS_emit'}>`,
99-
typeOptionType: typeArg && !hasUnionTypeArg
100-
? `__VLS_Emit`
101-
: undefined,
102-
});
103-
}
104-
if (options.vueCompilerOptions.target >= 3.5 && codes.every(code => code.typeOptionType)) {
105-
if (codes.length === 1) {
106-
yield `__typeEmits: {} as `;
107-
yield codes[0].typeOptionType!;
108-
yield `,${newLine}`;
109-
} else if (codes.length >= 2) {
110-
yield `__typeEmits: {} as `;
111-
yield codes[0].typeOptionType!;
112-
for (let i = 1; i < codes.length; i++) {
113-
yield ` & `;
114-
yield codes[i].typeOptionType!;
115-
}
116-
yield `,${newLine}`;
117-
}
118-
} else if (codes.every(code => code.optionExp)) {
119-
if (codes.length === 1) {
120-
yield `emits: `;
121-
yield codes[0].optionExp!;
122-
yield `,${newLine}`;
123-
} else if (codes.length >= 2) {
124-
yield `emits: {${newLine}`;
125-
for (const code of codes) {
126-
yield `...`;
127-
yield code.optionExp!;
128-
yield `,${newLine}`;
129-
}
130-
yield `},${newLine}`;
92+
optionCodes.push(`{} as __VLS_NormalizeEmits<typeof ${name ?? '__VLS_emit'}>`);
93+
if (typeArg && !hasUnionTypeArg) {
94+
typeOptionCodes.push(`__VLS_Emit`);
95+
} else {
96+
typeOptionCodes.length = 0;
13197
}
13298
}
99+
100+
if (options.vueCompilerOptions.target >= 3.5 && typeOptionCodes.length) {
101+
yield* generateIntersectMerge('__typeEmits', typeOptionCodes);
102+
} else if (optionCodes.length) {
103+
yield* generateSpreadMerge('emits', optionCodes);
104+
}
133105
}
134106

135107
export function* generatePropsOption(
@@ -140,55 +112,42 @@ export function* generatePropsOption(
140112
hasEmitsOption: boolean,
141113
inheritAttrs: boolean,
142114
): Generator<Code> {
143-
const codes: {
144-
optionExp: Code;
145-
// undefined means the prop source cannot be explained by type
146-
typeOptionExp?: Code;
147-
}[] = [];
115+
const getOptionCodes: (() => Code)[] = [];
116+
const typeOptionCodes: Code[] = [];
148117

149-
if (ctx.generatedPropsType) {
150-
if (options.vueCompilerOptions.target >= 3.6) {
151-
codes.push({
152-
optionExp: '{}',
153-
typeOptionExp: `{} as __VLS_PublicProps`,
154-
});
155-
} else {
156-
codes.push({
157-
optionExp: [
158-
`{} as `,
159-
scriptSetupRanges.withDefaults?.arg ? `${ctx.localTypes.WithDefaults}<` : '',
160-
`${ctx.localTypes.TypePropsToOption}<__VLS_PublicProps>`,
161-
scriptSetupRanges.withDefaults?.arg ? `, typeof __VLS_withDefaultsArg>` : '',
162-
].join(''),
163-
typeOptionExp: `{} as __VLS_PublicProps`,
164-
});
165-
}
166-
}
167-
if (scriptSetupRanges.defineProps?.arg) {
168-
const { arg } = scriptSetupRanges.defineProps;
169-
codes.push({
170-
optionExp: generateSfcBlockSection(scriptSetup, arg.start, arg.end, codeFeatures.navigation),
171-
typeOptionExp: undefined,
172-
});
173-
}
174118
if (inheritAttrs && options.templateCodegen?.inheritedAttrVars.size) {
175119
let attrsType = `Partial<__VLS_InheritedAttrs>`;
176120
if (hasEmitsOption) {
177121
attrsType = `Omit<${attrsType}, \`on\${string}\`>`;
178122
}
179-
const propsType = `__VLS_PickNotAny<${ctx.localTypes.OmitIndexSignature}<${attrsType}>, {}>`;
180-
const optionType = `${ctx.localTypes.TypePropsToOption}<${propsType}>`;
181-
codes.unshift({
182-
optionExp: codes.length
183-
? `{} as ${optionType}`
184-
// workaround for https://github.com/vuejs/core/pull/7419
185-
: `{} as keyof ${propsType} extends never ? never: ${optionType}`,
186-
typeOptionExp: `{} as ${attrsType}`,
123+
getOptionCodes.push(() => {
124+
const propsType = `__VLS_PickNotAny<${ctx.localTypes.OmitIndexSignature}<${attrsType}>, {}>`;
125+
const optionType = `${ctx.localTypes.TypePropsToOption}<${propsType}>`;
126+
return `{} as ${optionType}`;
187127
});
128+
typeOptionCodes.push(`{} as ${attrsType}`);
129+
}
130+
if (ctx.generatedPropsType) {
131+
if (options.vueCompilerOptions.target < 3.6) {
132+
getOptionCodes.push(() => {
133+
const propsType = `${ctx.localTypes.TypePropsToOption}<__VLS_PublicProps>`;
134+
return `{} as ` + (
135+
scriptSetupRanges.withDefaults?.arg
136+
? `${ctx.localTypes.WithDefaults}<${propsType}, typeof __VLS_withDefaultsArg>`
137+
: propsType
138+
);
139+
});
140+
}
141+
typeOptionCodes.push(`{} as __VLS_PublicProps`);
142+
}
143+
if (scriptSetupRanges.defineProps?.arg) {
144+
const { arg } = scriptSetupRanges.defineProps;
145+
getOptionCodes.push(() => generateSfcBlockSection(scriptSetup, arg.start, arg.end, codeFeatures.navigation));
146+
typeOptionCodes.length = 0;
188147
}
189148

190-
const useTypeOption = options.vueCompilerOptions.target >= 3.5 && codes.every(code => code.typeOptionExp);
191-
const useOption = !useTypeOption || scriptSetupRanges.withDefaults;
149+
const useTypeOption = options.vueCompilerOptions.target >= 3.5 && typeOptionCodes.length;
150+
const useOption = (!useTypeOption || scriptSetupRanges.withDefaults) && getOptionCodes.length;
192151

193152
if (useTypeOption) {
194153
if (
@@ -197,33 +156,35 @@ export function* generatePropsOption(
197156
) {
198157
yield `__defaults: __VLS_withDefaultsArg,${newLine}`;
199158
}
200-
if (codes.length === 1) {
201-
yield `__typeProps: `;
202-
yield codes[0].typeOptionExp!;
203-
yield `,${newLine}`;
204-
} else if (codes.length >= 2) {
205-
yield `__typeProps: {${newLine}`;
206-
for (const { typeOptionExp } of codes) {
207-
yield `...`;
208-
yield typeOptionExp!;
209-
yield `,${newLine}`;
210-
}
211-
yield `},${newLine}`;
212-
}
159+
yield* generateSpreadMerge('__typeProps', typeOptionCodes);
213160
}
214161
if (useOption) {
215-
if (codes.length === 1) {
216-
yield `props: `;
217-
yield codes[0].optionExp;
162+
yield* generateSpreadMerge('props', getOptionCodes.map(fn => fn()));
163+
}
164+
}
165+
166+
function* generateIntersectMerge(key: string, codes: Code[]): Generator<Code> {
167+
yield `${key}: {} as `;
168+
yield codes[0];
169+
for (let i = 1; i < codes.length; i++) {
170+
yield ` & `;
171+
yield codes[i];
172+
}
173+
yield `,${newLine}`;
174+
}
175+
176+
function* generateSpreadMerge(key: string, codes: Code[]): Generator<Code> {
177+
yield `${key}: `;
178+
if (codes.length === 1) {
179+
yield codes[0];
180+
} else {
181+
yield `{${newLine}`;
182+
for (const code of codes) {
183+
yield `...`;
184+
yield code;
218185
yield `,${newLine}`;
219-
} else if (codes.length >= 2) {
220-
yield `props: {${newLine}`;
221-
for (const { optionExp } of codes) {
222-
yield `...`;
223-
yield optionExp;
224-
yield `,${newLine}`;
225-
}
226-
yield `},${newLine}`;
227186
}
187+
yield `}`;
228188
}
189+
yield `,${newLine}`;
229190
}

0 commit comments

Comments
 (0)