From 7b32b1a68217cfa102717a68e1bcdad5ca9efe67 Mon Sep 17 00:00:00 2001 From: Zhilu Date: Thu, 3 Apr 2025 20:35:17 +0800 Subject: [PATCH 1/2] feat(compiler-core): support v-model shorthand for key and value with the same name Co-authored-by: KazariEX --- .../__snapshots__/vModel.spec.ts.snap | 15 ++++++ .../__tests__/transforms/vModel.spec.ts | 52 ++++++++++++++++++- packages/compiler-core/src/errors.ts | 6 ++- .../compiler-core/src/transforms/vModel.ts | 35 +++++++++++-- 4 files changed, 99 insertions(+), 9 deletions(-) diff --git a/packages/compiler-core/__tests__/transforms/__snapshots__/vModel.spec.ts.snap b/packages/compiler-core/__tests__/transforms/__snapshots__/vModel.spec.ts.snap index 17c4e80b160..b8ddb5812ba 100644 --- a/packages/compiler-core/__tests__/transforms/__snapshots__/vModel.spec.ts.snap +++ b/packages/compiler-core/__tests__/transforms/__snapshots__/vModel.spec.ts.snap @@ -26,6 +26,21 @@ return function render(_ctx, _cache) { }" `; +exports[`compiler: transform v-model > no expression 1`] = ` +"const _Vue = Vue + +return function render(_ctx, _cache) { + with (_ctx) { + const { openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue + + return (_openBlock(), _createElementBlock("input", { + "foo-value": fooValue, + "onUpdate:fooValue": $event => ((fooValue) = $event) + }, null, 40 /* PROPS, NEED_HYDRATION */, ["foo-value", "onUpdate:fooValue"])) + } +}" +`; + exports[`compiler: transform v-model > simple expression (with multilines) 1`] = ` "const _Vue = Vue diff --git a/packages/compiler-core/__tests__/transforms/vModel.spec.ts b/packages/compiler-core/__tests__/transforms/vModel.spec.ts index 82dd4909fd6..6915e7f499d 100644 --- a/packages/compiler-core/__tests__/transforms/vModel.spec.ts +++ b/packages/compiler-core/__tests__/transforms/vModel.spec.ts @@ -394,6 +394,42 @@ describe('compiler: transform v-model', () => { expect(generate(root, { mode: 'module' }).code).toMatchSnapshot() }) + test('no expression', () => { + const root = parseWithVModel('') + const node = root.children[0] as ElementNode + const props = ((node.codegenNode as VNodeCall).props as ObjectExpression) + .properties + expect(props[0]).toMatchObject({ + key: { + content: 'foo-value', + isStatic: true, + }, + value: { + content: 'fooValue', + isStatic: false, + }, + }) + + expect(props[1]).toMatchObject({ + key: { + content: 'onUpdate:fooValue', + isStatic: true, + }, + value: { + children: [ + '$event => ((', + { + content: 'fooValue', + isStatic: false, + }, + ') = $event)', + ], + }, + }) + + expect(generate(root).code).toMatchSnapshot() + }) + test('should cache update handler w/ cacheHandlers: true', () => { const root = parseWithVModel('', { prefixIdentifiers: true, @@ -508,14 +544,26 @@ describe('compiler: transform v-model', () => { }) describe('errors', () => { - test('missing expression', () => { + test('missing argument and expression', () => { const onError = vi.fn() parseWithVModel('', { onError }) expect(onError).toHaveBeenCalledTimes(1) expect(onError).toHaveBeenCalledWith( expect.objectContaining({ - code: ErrorCodes.X_V_MODEL_NO_EXPRESSION, + code: ErrorCodes.X_V_MODEL_NO_ARGUMENT_AND_EXPRESSION, + }), + ) + }) + + test('invalid argument for same-name shorthand', () => { + const onError = vi.fn() + parseWithVModel(`
`, { onError }) + + expect(onError).toHaveBeenCalledTimes(1) + expect(onError).toHaveBeenCalledWith( + expect.objectContaining({ + code: ErrorCodes.X_V_MODEL_INVALID_SAME_NAME_ARGUMENT, }), ) }) diff --git a/packages/compiler-core/src/errors.ts b/packages/compiler-core/src/errors.ts index 58e113ab19e..c980fbc7b38 100644 --- a/packages/compiler-core/src/errors.ts +++ b/packages/compiler-core/src/errors.ts @@ -84,7 +84,8 @@ export enum ErrorCodes { X_V_SLOT_DUPLICATE_SLOT_NAMES, X_V_SLOT_EXTRANEOUS_DEFAULT_SLOT_CHILDREN, X_V_SLOT_MISPLACED, - X_V_MODEL_NO_EXPRESSION, + X_V_MODEL_NO_ARGUMENT_AND_EXPRESSION, + X_V_MODEL_INVALID_SAME_NAME_ARGUMENT, X_V_MODEL_MALFORMED_EXPRESSION, X_V_MODEL_ON_SCOPE_VARIABLE, X_V_MODEL_ON_PROPS, @@ -172,7 +173,8 @@ export const errorMessages: Record = { `Extraneous children found when component already has explicitly named ` + `default slot. These children will be ignored.`, [ErrorCodes.X_V_SLOT_MISPLACED]: `v-slot can only be used on components or