Skip to content

Commit

Permalink
Merge branch 'master' into simplify-virtual-code
Browse files Browse the repository at this point in the history
  • Loading branch information
johnsoncodehk committed Aug 27, 2024
2 parents 2fd7159 + 180af0b commit 2c3a134
Show file tree
Hide file tree
Showing 37 changed files with 407 additions and 162 deletions.
200 changes: 112 additions & 88 deletions packages/language-core/lib/codegen/script/component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { ScriptRanges } from '../../parsers/scriptRanges';
import type { ScriptSetupRanges } from '../../parsers/scriptSetupRanges';
import type { Code, Sfc } from '../../types';
import { endOfLine, generateSfcBlockSection, newLine } from '../common';
Expand Down Expand Up @@ -31,10 +30,15 @@ export function* generateComponent(
yield `}${endOfLine}`;
yield `},${newLine}`;
if (!ctx.bypassDefineComponent) {
yield* generateScriptSetupOptions(options, ctx, scriptSetup, scriptSetupRanges, true);
const emitOptionCodes = [...generateEmitsOption(options, scriptSetup, scriptSetupRanges)];
for (const code of emitOptionCodes) {
yield code;
}
yield* generatePropsOption(options, ctx, scriptSetup, scriptSetupRanges, !!emitOptionCodes.length, true);
}
if (options.sfc.script && options.scriptRanges) {
yield* generateScriptOptions(options.sfc.script, options.scriptRanges);
if (options.sfc.script && options.scriptRanges?.exportDefault?.args) {
const { args } = options.scriptRanges.exportDefault;
yield generateSfcBlockSection(options.sfc.script, args.start + 1, args.end - 1, codeFeatures.all);
}
if (options.vueCompilerOptions.target >= 3.5 && scriptSetupRanges.templateRefs.length) {
yield `__typeRefs: {} as __VLS_Refs,${newLine}`;
Expand All @@ -55,118 +59,138 @@ export function* generateComponentSetupReturns(scriptSetupRanges: ScriptSetupRan
}
}

export function* generateScriptOptions(
script: NonNullable<Sfc['script']>,
scriptRanges: ScriptRanges
): Generator<Code> {
if (scriptRanges.exportDefault?.args) {
yield generateSfcBlockSection(script, scriptRanges.exportDefault.args.start + 1, scriptRanges.exportDefault.args.end - 1, codeFeatures.all);
}
}

export function* generateScriptSetupOptions(
export function* generateEmitsOption(
options: ScriptCodegenOptions,
ctx: ScriptCodegenContext,
scriptSetup: NonNullable<Sfc['scriptSetup']>,
scriptSetupRanges: ScriptSetupRanges,
inheritAttrs: boolean
scriptSetupRanges: ScriptSetupRanges
): Generator<Code> {
yield* generatePropsOption(options, ctx, scriptSetup, scriptSetupRanges, inheritAttrs);
yield* generateEmitsOption(options, scriptSetup, scriptSetupRanges);
const codes: {
optionExp?: Code,
typeOptionType?: Code,
}[] = [];
if (scriptSetupRanges.defineProp.some(p => p.isModel)) {
codes.push({
optionExp: `{} as __VLS_NormalizeEmits<__VLS_ModelEmitsType>`,
typeOptionType: `__VLS_ModelEmitsType`,
});
}
if (scriptSetupRanges.emits.define) {
const { typeArg, hasUnionTypeArg } = scriptSetupRanges.emits.define;
codes.push({
optionExp: `{} as __VLS_NormalizeEmits<typeof ${scriptSetupRanges.emits.name ?? '__VLS_emit'}>`,
typeOptionType: typeArg && !hasUnionTypeArg
? scriptSetup.content.slice(typeArg.start, typeArg.end)
: undefined,
});
}
if (options.vueCompilerOptions.target >= 3.5 && codes.every(code => code.typeOptionType)) {
if (codes.length === 1) {
yield `__typeEmits: {} as `;
yield codes[0].typeOptionType!;
yield `,${newLine}`;
}
else if (codes.length >= 2) {
yield `__typeEmits: {} as `;
yield codes[0].typeOptionType!;
for (let i = 1; i < codes.length; i++) {
yield ` & `;
yield codes[i].typeOptionType!;
}
yield `,${newLine}`;
}
}
else if (codes.every(code => code.optionExp)) {
if (codes.length === 1) {
yield `emits: `;
yield codes[0].optionExp!;
yield `,${newLine}`;
}
else if (codes.length >= 2) {
yield `emits: {${newLine}`;
for (const code of codes) {
yield `...`;
yield code.optionExp!;
yield `,${newLine}`;
}
yield `},${newLine}`;
}
}
}

export function* generatePropsOption(
options: ScriptCodegenOptions,
ctx: ScriptCodegenContext,
scriptSetup: NonNullable<Sfc['scriptSetup']>,
scriptSetupRanges: ScriptSetupRanges,
hasEmitsOption: boolean,
inheritAttrs: boolean
) {
): Generator<Code> {
const optionExpCodes: Code[] = [];
const typeOptionExpCodes: Code[] = [];

if (options.vueCompilerOptions.target >= 3.5) {
const types = [];
if (inheritAttrs && options.templateCodegen?.inheritedAttrVars.size) {
types.push(`ReturnType<typeof __VLS_template>['attrs']`);
if (ctx.generatedPropsType) {
optionExpCodes.push([
`{} as `,
scriptSetupRanges.props.withDefaults?.arg ? `${ctx.helperTypes.WithDefaults.name}<` : '',
`${ctx.helperTypes.TypePropsToOption.name}<__VLS_PublicProps>`,
scriptSetupRanges.props.withDefaults?.arg ? `, typeof __VLS_withDefaultsArg>` : '',
].join(''));
typeOptionExpCodes.push(`{} as __VLS_PublicProps`);
}
if (scriptSetupRanges.props.define?.arg) {
const { arg } = scriptSetupRanges.props.define;
optionExpCodes.push(generateSfcBlockSection(scriptSetup, arg.start, arg.end, codeFeatures.navigation));
}
if (inheritAttrs && options.templateCodegen?.inheritedAttrVars.size) {
let attrsType = `ReturnType<typeof __VLS_template>['attrs']`;
if (hasEmitsOption) {
attrsType = `Omit<${attrsType}, \`on\${string}\`>`;
}
if (ctx.generatedPropsType) {
types.push(`{} as __VLS_PublicProps`);
const propsType = `__VLS_PickNotAny<${ctx.helperTypes.OmitIndexSignature.name}<${attrsType}>, {}>`;
const optionType = `${ctx.helperTypes.TypePropsToOption.name}<${propsType}>`;
if (optionExpCodes.length) {
optionExpCodes.unshift(`{} as ${optionType}`);
}
if (types.length) {
yield `__typeProps: ${types.join(' & ')},${newLine}`;
else {
// workaround for https://github.com/vuejs/core/pull/7419
optionExpCodes.unshift(`{} as keyof ${propsType} extends never ? never: ${optionType}`);
}
typeOptionExpCodes.unshift(`{} as ${attrsType}`);
}
if (options.vueCompilerOptions.target < 3.5 || !ctx.generatedPropsType || scriptSetupRanges.props.withDefaults) {
const codegens: (() => Generator<Code>)[] = [];

if (inheritAttrs && options.templateCodegen?.inheritedAttrVars.size) {
codegens.push(function* () {
yield `{} as ${ctx.helperTypes.TypePropsToOption.name}<__VLS_PickNotAny<${ctx.helperTypes.OmitIndexSignature.name}<ReturnType<typeof __VLS_template>['attrs']>, {}>>`;
});
}
const useTypeOption = options.vueCompilerOptions.target >= 3.5 && typeOptionExpCodes.length;
const useOption = (!useTypeOption || scriptSetupRanges.props.withDefaults) && optionExpCodes.length;

if (ctx.generatedPropsType) {
codegens.push(function* () {
yield `{} as `;
if (scriptSetupRanges.props.withDefaults?.arg) {
yield `${ctx.helperTypes.WithDefaults.name}<`;
}
yield `${ctx.helperTypes.TypePropsToOption.name}<`;
yield `__VLS_PublicProps>`;
if (scriptSetupRanges.props.withDefaults?.arg) {
yield `, typeof __VLS_withDefaultsArg>`;
}
});
if (useTypeOption) {
if (typeOptionExpCodes.length === 1) {
yield `__typeProps: `;
yield typeOptionExpCodes[0];
yield `,${newLine}`;
}
if (scriptSetupRanges.props.define?.arg) {
const { arg } = scriptSetupRanges.props.define;
codegens.push(function* () {
yield generateSfcBlockSection(scriptSetup, arg.start, arg.end, codeFeatures.navigation);
});
else if (typeOptionExpCodes.length >= 2) {
yield `__typeProps: {${newLine}`;
for (const code of typeOptionExpCodes) {
yield `...`;
yield code;
yield `,${newLine}`;
}
yield `},${newLine}`;
}

if (codegens.length === 1) {
}
if (useOption) {
if (optionExpCodes.length === 1) {
yield `props: `;
for (const generate of codegens) {
yield* generate();
}
yield optionExpCodes[0];
yield `,${newLine}`;
}
else if (codegens.length >= 2) {
else if (optionExpCodes.length >= 2) {
yield `props: {${newLine}`;
for (const generate of codegens) {
for (const code of optionExpCodes) {
yield `...`;
yield* generate();
yield code;
yield `,${newLine}`;
}
yield `},${newLine}`;
}
}
}

export function* generateEmitsOption(
options: ScriptCodegenOptions,
scriptSetup: NonNullable<Sfc['scriptSetup']>,
scriptSetupRanges: ScriptSetupRanges
): Generator<Code> {
if (!scriptSetupRanges.emits.define && !scriptSetupRanges.defineProp.some(p => p.isModel)) {
return;
}

if (options.vueCompilerOptions.target < 3.5 || scriptSetupRanges.emits.define?.arg || scriptSetupRanges.emits.define?.hasUnionTypeArg) {
yield `emits: ({} as __VLS_NormalizeEmits<__VLS_ModelEmitsType`;
if (scriptSetupRanges?.emits.define) {
yield ` & typeof `;
yield scriptSetupRanges.emits.name ?? '__VLS_emit';
}
yield `>),${newLine}`;
}
else {
yield `__typeEmits: {} as __VLS_ModelEmitsType`;
const typeArg = scriptSetupRanges.emits.define?.typeArg;
if (typeArg) {
yield ` & `;
yield scriptSetup.content.slice(typeArg.start, typeArg.end);
}
yield `,${newLine}`;
}
}
17 changes: 11 additions & 6 deletions packages/language-core/lib/codegen/script/internalComponent.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { Code } from '../../types';
import { endOfLine, newLine } from '../common';
import { endOfLine, generateSfcBlockSection, newLine } from '../common';
import type { TemplateCodegenContext } from '../template/context';
import { generateComponentSetupReturns, generateScriptOptions, generateScriptSetupOptions } from './component';
import { generateComponentSetupReturns, generateEmitsOption, generatePropsOption } from './component';
import type { ScriptCodegenContext } from './context';
import type { ScriptCodegenOptions } from './index';
import { codeFeatures, type ScriptCodegenOptions } from './index';
import { getTemplateUsageVars } from './template';

export function* generateInternalComponent(
Expand Down Expand Up @@ -52,10 +52,15 @@ export function* generateInternalComponent(
yield `__typeRefs: {} as __VLS_Refs,${newLine}`;
}
if (options.sfc.scriptSetup && options.scriptSetupRanges && !ctx.bypassDefineComponent) {
yield* generateScriptSetupOptions(options, ctx, options.sfc.scriptSetup, options.scriptSetupRanges, false);
const emitOptionCodes = [...generateEmitsOption(options, options.sfc.scriptSetup, options.scriptSetupRanges)];
for (const code of emitOptionCodes) {
yield code;
}
yield* generatePropsOption(options, ctx, options.sfc.scriptSetup, options.scriptSetupRanges, !!emitOptionCodes.length, false);
}
if (options.sfc.script && options.scriptRanges) {
yield* generateScriptOptions(options.sfc.script, options.scriptRanges);
if (options.sfc.script && options.scriptRanges?.exportDefault?.args) {
const { args } = options.scriptRanges.exportDefault;
yield generateSfcBlockSection(options.sfc.script, args.start + 1, args.end - 1, codeFeatures.all);
}
yield `})${endOfLine}`; // defineComponent {
}
Expand Down
57 changes: 29 additions & 28 deletions packages/language-core/lib/codegen/script/scriptSetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,21 @@ export function* generateScriptSetup(
+ ` __VLS_setup = (async () => {${newLine}`;
yield* generateSetupFunction(options, ctx, scriptSetup, scriptSetupRanges, undefined, definePropMirrors);

const emitTypes = ['__VLS_ModelEmitsType'];
const emitTypes: string[] = [];

if (scriptSetupRanges.emits.define) {
emitTypes.unshift(`typeof ${scriptSetupRanges.emits.name ?? '__VLS_emit'}`);
emitTypes.push(`typeof ${scriptSetupRanges.emits.name ?? '__VLS_emit'}`);
}
if (scriptSetupRanges.defineProp.some(p => p.isModel)) {
emitTypes.push(`__VLS_ModelEmitsType`);
}

yield ` return {} as {${newLine}`
+ ` props: ${ctx.helperTypes.Prettify.name}<typeof __VLS_functionalComponentProps & __VLS_PublicProps> & __VLS_BuiltInPublicProps,${newLine}`
+ ` expose(exposed: import('${options.vueCompilerOptions.lib}').ShallowUnwrapRef<${scriptSetupRanges.expose.define ? 'typeof __VLS_exposed' : '{}'}>): void,${newLine}`
+ ` attrs: any,${newLine}`
+ ` slots: __VLS_Slots,${newLine}`
+ ` emit: ${emitTypes.join(' & ')},${newLine}`
+ ` emit: ${emitTypes.length ? emitTypes.join(' & ') : `{}`},${newLine}`
+ ` }${endOfLine}`;
yield ` })(),${newLine}`; // __VLS_setup = (async () => {
yield `) => ({} as import('${options.vueCompilerOptions.lib}').VNode & { __ctx?: Awaited<typeof __VLS_setup> }))`;
Expand Down Expand Up @@ -303,14 +306,16 @@ function* generateComponentProps(
scriptSetupRanges: ScriptSetupRanges,
definePropMirrors: Map<string, number>
): Generator<Code> {
yield `const __VLS_fnComponent = `
+ `(await import('${options.vueCompilerOptions.lib}')).defineComponent({${newLine}`;
yield `const __VLS_fnComponent = (await import('${options.vueCompilerOptions.lib}')).defineComponent({${newLine}`;

if (scriptSetupRanges.props.define?.arg) {
yield ` props: `;
yield `props: `;
yield generateSfcBlockSection(scriptSetup, scriptSetupRanges.props.define.arg.start, scriptSetupRanges.props.define.arg.end, codeFeatures.navigation);
yield `,${newLine}`;
}

yield* generateEmitsOption(options, scriptSetup, scriptSetupRanges);

yield `})${endOfLine}`;

yield `type __VLS_BuiltInPublicProps = ${options.vueCompilerOptions.target >= 3.4
Expand Down Expand Up @@ -418,32 +423,28 @@ function* generateModelEmits(
scriptSetup: NonNullable<Sfc['scriptSetup']>,
scriptSetupRanges: ScriptSetupRanges
): Generator<Code> {
yield `type __VLS_ModelEmitsType = `;
if (scriptSetupRanges.defineProp.filter(p => p.isModel).length) {
if (options.vueCompilerOptions.target < 3.5) {
yield `typeof __VLS_modelEmitsType${endOfLine}`;
yield `const __VLS_modelEmitsType = (await import('${options.vueCompilerOptions.lib}')).defineEmits<`;
}
yield `{${newLine}`;
for (const defineProp of scriptSetupRanges.defineProp) {
if (!defineProp.isModel) {
continue;
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}`;
}

const [propName, localName] = getPropAndLocalName(scriptSetup, defineProp);

yield `'update:${propName}': [${propName}:`;
yield* generateDefinePropType(scriptSetup, propName, localName, defineProp);
yield `]${endOfLine}`;
};
if (options.vueCompilerOptions.target >= 3.5) {
yield `type __VLS_ModelEmitsType = {${newLine}`;
yield* generateDefineModels();
yield `}${endOfLine}`;
}
yield `}`;
if (options.vueCompilerOptions.target < 3.5) {
yield `>()`;
else {
yield `const __VLS_modelEmitsType = (await import('${options.vueCompilerOptions.lib}')).defineEmits<{${newLine}`;
yield* generateDefineModels();
yield `}>()${endOfLine}`;
yield `type __VLS_ModelEmitsType = typeof __VLS_modelEmitsType${endOfLine}`;
}
} else {
yield `{}`;
}
yield endOfLine;
}

function* generateStyleModules(
Expand Down
4 changes: 2 additions & 2 deletions packages/language-core/lib/codegen/script/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export function* generateTemplate(
const templateUsageVars = [...getTemplateUsageVars(options, ctx)];
yield `// @ts-ignore${newLine}`;
yield `[${templateUsageVars.join(', ')}]${newLine}`;
yield `return [{}, {}] as const${endOfLine}`;
yield `return { slots: {}, refs: {}, attrs: {} }${endOfLine}`;
yield `}${newLine}`;
}
}
Expand Down Expand Up @@ -158,7 +158,7 @@ function* generateTemplateContext(
yield `return {${newLine}`;
yield `slots: ${options.scriptSetupRanges?.slots.name ?? '__VLS_slots'},${newLine}`;
yield `refs: __VLS_refs as __VLS_PickRefsExpose<typeof __VLS_refs>,${newLine}`;
yield `attrs: __VLS_inheritedAttrs,${newLine}`;
yield `attrs: {} as Partial<typeof __VLS_inheritedAttrs>,${newLine}`;
yield `}${endOfLine}`;
}

Expand Down
Loading

0 comments on commit 2c3a134

Please sign in to comment.