Skip to content

Commit

Permalink
feat(language-core): add emit codegen for defineModel (#3895)
Browse files Browse the repository at this point in the history
* feat() add emit codegen for defineModel

* chore() add generated snapshot by test
  • Loading branch information
s-montigny-desautels committed Feb 29, 2024
1 parent 8869795 commit a9015fd
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 4 deletions.
18 changes: 18 additions & 0 deletions packages/component-meta/tests/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,24 @@ const worker = (checker: ComponentMetaChecker, withTsconfig: boolean) => describ
expect(meta.props.filter(prop => !prop.global)).toEqual([]);
});

test('reference-type-model', () => {
const componentPath = path.resolve(__dirname, '../../../test-workspace/component-meta/reference-type-model/component.vue');
const meta = checker.getComponentMeta(componentPath);

// expect(meta.type).toEqual(TypeMeta.Class);

const foo = meta.props.find(prop => prop.name === 'foo');
const onUpdateFoo = meta.events.find(event => event.name === 'update:foo')

const bar = meta.props.find(prop => prop.name === 'bar');
const onUpdateBar = meta.events.find(event => event.name === 'update:bar')

expect(foo).toBeDefined();
expect(bar).toBeDefined();
expect(onUpdateFoo).toBeDefined();
expect(onUpdateBar).toBeDefined();
})

test('reference-type-props', () => {
const componentPath = path.resolve(__dirname, '../../../test-workspace/component-meta/reference-type-props/component.vue');
const meta = checker.getComponentMeta(componentPath);
Expand Down
41 changes: 38 additions & 3 deletions packages/language-core/src/generators/script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,37 @@ type __VLS_PrettifyGlobal<T> = { [K in keyof T]: T[K]; } & {};
enableAllFeatures({}),
]);
}
function* generateModelEmits(): Generator<CodeAndStack> {
if (!scriptSetup || !scriptSetupRanges) {
return;
}

yield _(`let __VLS_modelEmitsType!: {}`);

if (scriptSetupRanges.defineProp.length) {
yield _(` & ReturnType<typeof import('${vueCompilerOptions.lib}').defineEmits<{\n`);
for (const defineProp of scriptSetupRanges.defineProp) {
if (!defineProp.isModel)
continue;

let propName = 'modelValue';
if (defineProp.name) {
propName = scriptSetup.content.substring(defineProp.name.start, defineProp.name.end);
propName = propName.replace(/['"]+/g, '')
}
yield _(`'update:${propName}': [${propName}:`);
if (defineProp.type) {
yield _(scriptSetup.content.substring(defineProp.type.start, defineProp.type.end));
}
else {
yield _(`any`);
}
yield _(`];\n`);
}
yield _(`}>>`);
}
yield _(`;\n`);
}
function* generateScriptSetupAndTemplate(): Generator<CodeAndStack> {
if (!scriptSetup || !scriptSetupRanges)
return;
Expand Down Expand Up @@ -515,6 +546,8 @@ type __VLS_PrettifyGlobal<T> = { [K in keyof T]: T[K]; } & {};
}
yield _(`;\n`);

yield* generateModelEmits()

yield _(`let __VLS_fnPropsDefineComponent!: InstanceType<typeof __VLS_fnComponent>['$props'];\n`);
yield _(`let __VLS_fnPropsSlots!: `);
if (scriptSetupRanges.slots.define && vueCompilerOptions.jsxSlots) {
Expand All @@ -536,7 +569,7 @@ type __VLS_PrettifyGlobal<T> = { [K in keyof T]: T[K]; } & {};
+ ` expose(exposed: import('${vueCompilerOptions.lib}').ShallowUnwrapRef<${scriptSetupRanges.expose.define ? 'typeof __VLS_exposed' : '{}'}>): void,\n`
+ ` attrs: any,\n`
+ ` slots: ReturnType<typeof __VLS_template>,\n`
+ ` emit: typeof ${scriptSetupRanges.emits.name ?? '__VLS_emit'},\n`
+ ` emit: typeof ${scriptSetupRanges.emits.name ?? '__VLS_emit'} & typeof __VLS_modelEmitsType,\n`
+ ` };\n`);
yield _(` })(),\n`); // __VLS_setup = (async () => {
yield _(`) => ({} as import('${vueCompilerOptions.lib}').VNode & { __ctx?: Awaited<typeof __VLS_setup> }))`);
Expand Down Expand Up @@ -723,6 +756,7 @@ type __VLS_PrettifyGlobal<T> = { [K in keyof T]: T[K]; } & {};
yield _(`};\n`);
}

yield* generateModelEmits()
yield* generateTemplate(functional);

if (mode === 'return' || mode === 'export') {
Expand Down Expand Up @@ -823,11 +857,12 @@ type __VLS_PrettifyGlobal<T> = { [K in keyof T]: T[K]; } & {};
}
yield _(`},\n`);
}
yield _(`emits: ({} as __VLS_NormalizeEmits<typeof __VLS_modelEmitsType`)
if (ranges.emits.define) {
yield _(`emits: ({} as __VLS_NormalizeEmits<typeof `);
yield _(` & typeof `)
yield _(ranges.emits.name ?? '__VLS_emit');
yield _(`>),\n`);
}
yield _(`>),\n`);
}
if (script && scriptRanges?.exportDefault?.args) {
yield _(generateSourceCode(script, scriptRanges.exportDefault.args.start + 1, scriptRanges.exportDefault.args.end - 1));
Expand Down
2 changes: 2 additions & 0 deletions packages/language-core/src/parsers/scriptSetupRanges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export function parseScriptSetupRanges(
type: TextRange | undefined;
defaultValue: TextRange | undefined;
required: boolean;
isModel?: boolean;
}[] = [];
const bindings = parseBindingRanges(ts, ast);
const text = ast.text;
Expand Down Expand Up @@ -133,6 +134,7 @@ export function parseScriptSetupRanges(
type: node.typeArguments?.length ? _getStartEnd(node.typeArguments[0]) : undefined,
defaultValue: undefined,
required,
isModel: true,
});
}
else if (callText === 'defineProp') {
Expand Down
18 changes: 18 additions & 0 deletions packages/tsc/tests/__snapshots__/dts.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,24 @@ export default _default;
"
`;
exports[`vue-tsc-dts > Input: reference-type-model/component.vue, Output: reference-type-model/component.vue.d.ts 1`] = `
"declare const _default: import("vue").DefineComponent<{
foo: import("vue").PropType<number>;
bar: import("vue").PropType<string[]>;
}, {}, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
"update:foo": (foo: number) => void;
"update:bar": (bar: string[]) => void;
}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
foo: import("vue").PropType<number>;
bar: import("vue").PropType<string[]>;
}>> & {
"onUpdate:foo"?: (foo: number) => any;
"onUpdate:bar"?: (bar: string[]) => any;
}, {}, {}>;
export default _default;
"
`;
exports[`vue-tsc-dts > Input: reference-type-props/component.vue, Output: reference-type-props/component.vue.d.ts 1`] = `
"import { MyProps } from './my-props';
declare const _default: import("vue").DefineComponent<__VLS_WithDefaults<__VLS_TypePropsToOption<MyProps>, {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<script setup lang="ts">
const bar = defineModel<number>("foo")
const baz = defineModel<string[]>("bar")
</script>
1 change: 1 addition & 0 deletions test-workspace/tsc/vue2/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"../vue3/#3672",
"../vue3/components",
"../vue3/defineEmits",
"../vue3/defineModel",
"../vue3/defineProp_B",
"../vue3/events",
"../vue3/no-script-block",
Expand Down
23 changes: 22 additions & 1 deletion test-workspace/tsc/vue3/defineModel/main.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<script lang="ts">
<script setup lang="ts">
import { exactType } from '../../shared';
import { defineComponent, PropType } from 'vue';
import ScriptSetup from './script-setup.vue';
Expand All @@ -12,10 +12,31 @@ const ScriptSetupExact = defineComponent({
f: { type: PropType<string>, required: true };
g: PropType<string>;
},
emits: {} as {
"update:modelValue": (_:string) => void
"update:c": (_:number) => void
"update:d": (_:number) => void
"update:e": (_:string) => void
"update:f": (_:string) => void
"update:g": (_:string) => void
},
setup() {
return {};
},
});
exactType(ScriptSetup, ScriptSetupExact);
</script>

<template>
<ScriptSetup
:c="0"
f=""
@update:model-value="(x) => exactType(x, {} as string)"
@update:c="(x) => exactType(x, {} as number)"
@update:d="(x) => exactType(x, {} as number)"
@update:e="(x) => exactType(x, {} as string)"
@update:f="(x) => exactType(x, {} as string)"
@update:g="(x) => exactType(x, {} as string)"
/>
</template>

0 comments on commit a9015fd

Please sign in to comment.