From 4ecc656b441a27ad967e5eafa5b4f7ffb05df382 Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Thu, 11 Apr 2024 18:12:03 +0800 Subject: [PATCH 1/9] feat(compiler-vapor, runtime-vapor): support listen sub components emit related #4 --- .../src/generators/component.ts | 6 ++--- packages/compiler-vapor/src/transform.ts | 1 + packages/compiler-vapor/src/transforms/vOn.ts | 23 ++++++++++++++++++- packages/runtime-vapor/src/componentEmits.ts | 15 ++++++++---- 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/packages/compiler-vapor/src/generators/component.ts b/packages/compiler-vapor/src/generators/component.ts index af7482db2..8f7113dad 100644 --- a/packages/compiler-vapor/src/generators/component.ts +++ b/packages/compiler-vapor/src/generators/component.ts @@ -63,9 +63,9 @@ export function genCreateComponent( ...props.map(prop => { return [ ...genPropKey(prop, context), - ': () => (', - ...genExpression(prop.values[0], context), - ')', + ...(prop.applyRawValue + ? [': ', prop.values[0].content, ','] + : [': () => (', ...genExpression(prop.values[0], context), ')']), ] }), ) diff --git a/packages/compiler-vapor/src/transform.ts b/packages/compiler-vapor/src/transform.ts index 7a693f785..ce7e93d59 100644 --- a/packages/compiler-vapor/src/transform.ts +++ b/packages/compiler-vapor/src/transform.ts @@ -41,6 +41,7 @@ export type DirectiveTransform = ( export interface DirectiveTransformResult { key: SimpleExpressionNode value: SimpleExpressionNode + applyRawValue?: boolean modifier?: '.' | '^' runtimeCamelize?: boolean } diff --git a/packages/compiler-vapor/src/transforms/vOn.ts b/packages/compiler-vapor/src/transforms/vOn.ts index d758754ed..e2d2ba70c 100644 --- a/packages/compiler-vapor/src/transforms/vOn.ts +++ b/packages/compiler-vapor/src/transforms/vOn.ts @@ -1,9 +1,15 @@ -import { ErrorCodes, createCompilerError } from '@vue/compiler-dom' +import { + ElementTypes, + ErrorCodes, + createCompilerError, + createSimpleExpression, +} from '@vue/compiler-dom' import type { DirectiveTransform } from '../transform' import { IRNodeTypes, type KeyOverride, type SetEventIRNode } from '../ir' import { resolveModifiers } from '@vue/compiler-dom' import { extend, makeMap } from '@vue/shared' import { resolveExpression } from '../utils' +import { EMPTY_EXPRESSION } from './utils' const delegatedEvents = /*#__PURE__*/ makeMap( 'beforeinput,click,dblclick,contextmenu,focusin,focusout,input,keydown,' + @@ -14,6 +20,8 @@ const delegatedEvents = /*#__PURE__*/ makeMap( export const transformVOn: DirectiveTransform = (dir, node, context) => { let { arg, exp, loc, modifiers } = dir + const isComponent = node.tagType === ElementTypes.COMPONENT + if (!exp && (!modifiers.length || !arg)) { context.options.onError( createCompilerError(ErrorCodes.X_V_ON_NO_EXPRESSION, loc), @@ -70,6 +78,19 @@ export const transformVOn: DirectiveTransform = (dir, node, context) => { } } + if (isComponent) { + const eventName = arg.content + const handler = exp || EMPTY_EXPRESSION + return { + key: createSimpleExpression(eventName, true, arg.loc), + value: extend(handler, { + isHandler: true, + loc: handler.loc, + }), + applyRawValue: true, + } + } + const operation: SetEventIRNode = { type: IRNodeTypes.SET_EVENT, element: context.reference(), diff --git a/packages/runtime-vapor/src/componentEmits.ts b/packages/runtime-vapor/src/componentEmits.ts index bbca2044c..002c5d78c 100644 --- a/packages/runtime-vapor/src/componentEmits.ts +++ b/packages/runtime-vapor/src/componentEmits.ts @@ -74,10 +74,17 @@ export function emit( // TODO: warn let handlerName - let handler = - rawProps[(handlerName = toHandlerKey(event))] || - // also try camelCase event handler (#2249) - rawProps[(handlerName = toHandlerKey(camelize(event)))] + let handler + for (let rawProp of rawProps) { + if ( + (handler = + rawProp[(handlerName = toHandlerKey(event))] || + // also try camelCase event handler (#2249) + rawProp[(handlerName = toHandlerKey(camelize(event)))]) + ) { + break + } + } // for v-model update:xxx events, also trigger kebab-case equivalent // for props passed via kebab-case if (!handler && isModelListener) { From 98bd432f8ab8e0ec317235a584750488a637b7c0 Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Thu, 11 Apr 2024 18:14:59 +0800 Subject: [PATCH 2/9] feat(compiler-vapor): remove unecessary code --- packages/compiler-vapor/src/transforms/vOn.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/compiler-vapor/src/transforms/vOn.ts b/packages/compiler-vapor/src/transforms/vOn.ts index e2d2ba70c..012ab1eba 100644 --- a/packages/compiler-vapor/src/transforms/vOn.ts +++ b/packages/compiler-vapor/src/transforms/vOn.ts @@ -83,10 +83,7 @@ export const transformVOn: DirectiveTransform = (dir, node, context) => { const handler = exp || EMPTY_EXPRESSION return { key: createSimpleExpression(eventName, true, arg.loc), - value: extend(handler, { - isHandler: true, - loc: handler.loc, - }), + value: handler, applyRawValue: true, } } From 56519b171168dfec8aae5d45f6299185dfc1cfb6 Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Thu, 11 Apr 2024 19:03:58 +0800 Subject: [PATCH 3/9] fix(compiler-core): tweak logic --- packages/compiler-vapor/src/generators/component.ts | 4 ++-- packages/compiler-vapor/src/transform.ts | 2 +- packages/compiler-vapor/src/transforms/vOn.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/compiler-vapor/src/generators/component.ts b/packages/compiler-vapor/src/generators/component.ts index 8f7113dad..9fad1cc65 100644 --- a/packages/compiler-vapor/src/generators/component.ts +++ b/packages/compiler-vapor/src/generators/component.ts @@ -63,8 +63,8 @@ export function genCreateComponent( ...props.map(prop => { return [ ...genPropKey(prop, context), - ...(prop.applyRawValue - ? [': ', prop.values[0].content, ','] + ...(prop.isHandler + ? [': ', ...genExpression(prop.values[0], context), ','] : [': () => (', ...genExpression(prop.values[0], context), ')']), ] }), diff --git a/packages/compiler-vapor/src/transform.ts b/packages/compiler-vapor/src/transform.ts index ce7e93d59..086a7b3ac 100644 --- a/packages/compiler-vapor/src/transform.ts +++ b/packages/compiler-vapor/src/transform.ts @@ -41,7 +41,7 @@ export type DirectiveTransform = ( export interface DirectiveTransformResult { key: SimpleExpressionNode value: SimpleExpressionNode - applyRawValue?: boolean + isHandler?: boolean modifier?: '.' | '^' runtimeCamelize?: boolean } diff --git a/packages/compiler-vapor/src/transforms/vOn.ts b/packages/compiler-vapor/src/transforms/vOn.ts index 012ab1eba..751e0b7e0 100644 --- a/packages/compiler-vapor/src/transforms/vOn.ts +++ b/packages/compiler-vapor/src/transforms/vOn.ts @@ -84,7 +84,7 @@ export const transformVOn: DirectiveTransform = (dir, node, context) => { return { key: createSimpleExpression(eventName, true, arg.loc), value: handler, - applyRawValue: true, + isHandler: true, } } From ec03d548d0c5e682c1265d9d4bab78d4757f78ae Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Fri, 12 Apr 2024 00:16:09 +0800 Subject: [PATCH 4/9] feat(compiler-core): tweak expression logic --- .../src/generators/expression.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/packages/compiler-vapor/src/generators/expression.ts b/packages/compiler-vapor/src/generators/expression.ts index a5c25adb1..7a1f9fdb1 100644 --- a/packages/compiler-vapor/src/generators/expression.ts +++ b/packages/compiler-vapor/src/generators/expression.ts @@ -13,6 +13,7 @@ import type { CodegenContext } from '../generate' import type { Node } from '@babel/types' import { isConstantExpression } from '../utils' import { type CodeFragment, buildCodeFragment } from './utils' +import { isGloballyAllowed } from '@vue/shared' export function genExpression( node: SimpleExpressionNode, @@ -47,8 +48,9 @@ export function genExpression( const parentStack: Node[] = [] walkIdentifiers( ast!, - id => { - ids.push(id) + (id, _, __, isReferenced) => { + const needPrefix = isReferenced && canPrefix(id) + if (needPrefix) ids.push(id) parentStackMap.set(id, parentStack.slice()) }, false, @@ -178,3 +180,15 @@ function genIdentifier( return `${vaporHelper('unref')}(${raw})` } } + +function canPrefix(id: Identifier) { + // skip whitelisted globals + if (isGloballyAllowed(id.name)) { + return false + } + // special case for webpack compilation + if (id.name === 'require') { + return false + } + return true +} From 11cc7a1b77417d9783b0d5e3e54842ea7469c8e9 Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Fri, 12 Apr 2024 00:25:55 +0800 Subject: [PATCH 5/9] Revert "feat(compiler-core): tweak expression logic" This reverts commit ec03d548d0c5e682c1265d9d4bab78d4757f78ae. --- .../src/generators/expression.ts | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/packages/compiler-vapor/src/generators/expression.ts b/packages/compiler-vapor/src/generators/expression.ts index 7a1f9fdb1..a5c25adb1 100644 --- a/packages/compiler-vapor/src/generators/expression.ts +++ b/packages/compiler-vapor/src/generators/expression.ts @@ -13,7 +13,6 @@ import type { CodegenContext } from '../generate' import type { Node } from '@babel/types' import { isConstantExpression } from '../utils' import { type CodeFragment, buildCodeFragment } from './utils' -import { isGloballyAllowed } from '@vue/shared' export function genExpression( node: SimpleExpressionNode, @@ -48,9 +47,8 @@ export function genExpression( const parentStack: Node[] = [] walkIdentifiers( ast!, - (id, _, __, isReferenced) => { - const needPrefix = isReferenced && canPrefix(id) - if (needPrefix) ids.push(id) + id => { + ids.push(id) parentStackMap.set(id, parentStack.slice()) }, false, @@ -180,15 +178,3 @@ function genIdentifier( return `${vaporHelper('unref')}(${raw})` } } - -function canPrefix(id: Identifier) { - // skip whitelisted globals - if (isGloballyAllowed(id.name)) { - return false - } - // special case for webpack compilation - if (id.name === 'require') { - return false - } - return true -} From fece8e6f5cec50a4655b94d58c514b4954421b56 Mon Sep 17 00:00:00 2001 From: Doctor Wu <44631608+Doctor-wu@users.noreply.github.com> Date: Sat, 13 Apr 2024 10:33:59 +0800 Subject: [PATCH 6/9] Update packages/compiler-vapor/src/transforms/vOn.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kevin Deng 三咲智子 --- packages/compiler-vapor/src/transforms/vOn.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/compiler-vapor/src/transforms/vOn.ts b/packages/compiler-vapor/src/transforms/vOn.ts index 751e0b7e0..18a78af56 100644 --- a/packages/compiler-vapor/src/transforms/vOn.ts +++ b/packages/compiler-vapor/src/transforms/vOn.ts @@ -82,7 +82,7 @@ export const transformVOn: DirectiveTransform = (dir, node, context) => { const eventName = arg.content const handler = exp || EMPTY_EXPRESSION return { - key: createSimpleExpression(eventName, true, arg.loc), + key: arg, value: handler, isHandler: true, } From 484b2ff525377f9044c3e5580aa3d6aaa0c24d7b Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Sat, 13 Apr 2024 11:06:08 +0800 Subject: [PATCH 7/9] style(lint): make lint happy --- packages/compiler-vapor/src/transforms/vOn.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/compiler-vapor/src/transforms/vOn.ts b/packages/compiler-vapor/src/transforms/vOn.ts index 18a78af56..884c0a1e5 100644 --- a/packages/compiler-vapor/src/transforms/vOn.ts +++ b/packages/compiler-vapor/src/transforms/vOn.ts @@ -2,7 +2,6 @@ import { ElementTypes, ErrorCodes, createCompilerError, - createSimpleExpression, } from '@vue/compiler-dom' import type { DirectiveTransform } from '../transform' import { IRNodeTypes, type KeyOverride, type SetEventIRNode } from '../ir' @@ -79,7 +78,6 @@ export const transformVOn: DirectiveTransform = (dir, node, context) => { } if (isComponent) { - const eventName = arg.content const handler = exp || EMPTY_EXPRESSION return { key: arg, From 460c970e383a5034572c151472f84405797ebb11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90=20Kevin=20Deng?= Date: Mon, 15 Apr 2024 02:36:34 +0800 Subject: [PATCH 8/9] refactor: toHandlerKey --- .../__snapshots__/component.spec.ts.snap | 15 +++++++++++++++ .../__tests__/generators/component.spec.ts | 8 ++++++++ .../compiler-vapor/src/generators/component.ts | 6 +++--- packages/compiler-vapor/src/generators/prop.ts | 17 +++++++++-------- packages/compiler-vapor/src/transform.ts | 2 +- packages/compiler-vapor/src/transforms/vOn.ts | 7 +++++-- 6 files changed, 41 insertions(+), 14 deletions(-) diff --git a/packages/compiler-vapor/__tests__/generators/__snapshots__/component.spec.ts.snap b/packages/compiler-vapor/__tests__/generators/__snapshots__/component.spec.ts.snap index 999526e70..5e4e289d4 100644 --- a/packages/compiler-vapor/__tests__/generators/__snapshots__/component.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/generators/__snapshots__/component.spec.ts.snap @@ -1,5 +1,20 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`generate component > generate component with emits 1`] = ` +"import { toHandlerKey as _toHandlerKey } from 'vue'; +import { resolveComponent as _resolveComponent, createComponent as _createComponent } from 'vue/vapor'; + +export function render(_ctx) { + const n0 = _createComponent(_resolveComponent("Comp"), [{ + onClick: () => (fn) + }]) + const n1 = _createComponent(_resolveComponent("Comp"), [{ + [_toHandlerKey(eventName)]: () => (fn) + }]) + return [n0, n1] +}" +`; + exports[`generate component > generate multi root component 1`] = ` "import { resolveComponent as _resolveComponent, createComponent as _createComponent, template as _template } from 'vue/vapor'; const t0 = _template("123") diff --git a/packages/compiler-vapor/__tests__/generators/component.spec.ts b/packages/compiler-vapor/__tests__/generators/component.spec.ts index 9d6ded902..28aaf91b0 100644 --- a/packages/compiler-vapor/__tests__/generators/component.spec.ts +++ b/packages/compiler-vapor/__tests__/generators/component.spec.ts @@ -20,4 +20,12 @@ describe('generate component', () => { const { code } = compile(`
`) expect(code).toMatchSnapshot() }) + + test('generate component with emits', () => { + const { code } = compile(` + + + `) + expect(code).toMatchSnapshot() + }) }) diff --git a/packages/compiler-vapor/src/generators/component.ts b/packages/compiler-vapor/src/generators/component.ts index 9fad1cc65..af7482db2 100644 --- a/packages/compiler-vapor/src/generators/component.ts +++ b/packages/compiler-vapor/src/generators/component.ts @@ -63,9 +63,9 @@ export function genCreateComponent( ...props.map(prop => { return [ ...genPropKey(prop, context), - ...(prop.isHandler - ? [': ', ...genExpression(prop.values[0], context), ','] - : [': () => (', ...genExpression(prop.values[0], context), ')']), + ': () => (', + ...genExpression(prop.values[0], context), + ')', ] }), ) diff --git a/packages/compiler-vapor/src/generators/prop.ts b/packages/compiler-vapor/src/generators/prop.ts index 8fcaf28bc..e2f1ab53e 100644 --- a/packages/compiler-vapor/src/generators/prop.ts +++ b/packages/compiler-vapor/src/generators/prop.ts @@ -86,7 +86,7 @@ function genLiteralObjectProps( } export function genPropKey( - { key: node, runtimeCamelize, modifier }: IRProp, + { key: node, modifier, runtimeCamelize, runtimeHandler }: IRProp, context: CodegenContext, ): CodeFragment[] { const { helper } = context @@ -104,13 +104,14 @@ export function genPropKey( ] } - const key = genExpression(node, context) - return [ - '[', - modifier && `${JSON.stringify(modifier)} + `, - ...(runtimeCamelize ? genCall(helper('camelize'), key) : key), - ']', - ] + let key = genExpression(node, context) + if (runtimeCamelize) { + key = genCall(helper('camelize'), key) + } + if (runtimeHandler) { + key = genCall(helper('toHandlerKey'), key) + } + return ['[', modifier && `${JSON.stringify(modifier)} + `, ...key, ']'] } function genPropValue(values: SimpleExpressionNode[], context: CodegenContext) { diff --git a/packages/compiler-vapor/src/transform.ts b/packages/compiler-vapor/src/transform.ts index 72e07d8f0..b88b01055 100644 --- a/packages/compiler-vapor/src/transform.ts +++ b/packages/compiler-vapor/src/transform.ts @@ -41,9 +41,9 @@ export type DirectiveTransform = ( export interface DirectiveTransformResult { key: SimpleExpressionNode value: SimpleExpressionNode - isHandler?: boolean modifier?: '.' | '^' runtimeCamelize?: boolean + runtimeHandler?: boolean } // A structural directive transform is technically also a NodeTransform; diff --git a/packages/compiler-vapor/src/transforms/vOn.ts b/packages/compiler-vapor/src/transforms/vOn.ts index 884c0a1e5..4032882e9 100644 --- a/packages/compiler-vapor/src/transforms/vOn.ts +++ b/packages/compiler-vapor/src/transforms/vOn.ts @@ -6,7 +6,7 @@ import { import type { DirectiveTransform } from '../transform' import { IRNodeTypes, type KeyOverride, type SetEventIRNode } from '../ir' import { resolveModifiers } from '@vue/compiler-dom' -import { extend, makeMap } from '@vue/shared' +import { extend, makeMap, toHandlerKey } from '@vue/shared' import { resolveExpression } from '../utils' import { EMPTY_EXPRESSION } from './utils' @@ -78,11 +78,14 @@ export const transformVOn: DirectiveTransform = (dir, node, context) => { } if (isComponent) { + if (arg.isStatic) { + arg = extend({}, arg, { content: toHandlerKey(arg.content) }) + } const handler = exp || EMPTY_EXPRESSION return { key: arg, value: handler, - isHandler: true, + runtimeHandler: !arg.isStatic, } } From 5e76c41c7bc1f755109ecb940a0360cc066008e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90=20Kevin=20Deng?= Date: Mon, 15 Apr 2024 02:39:16 +0800 Subject: [PATCH 9/9] revert runtime --- packages/runtime-vapor/src/componentEmits.ts | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/packages/runtime-vapor/src/componentEmits.ts b/packages/runtime-vapor/src/componentEmits.ts index 002c5d78c..bbca2044c 100644 --- a/packages/runtime-vapor/src/componentEmits.ts +++ b/packages/runtime-vapor/src/componentEmits.ts @@ -74,17 +74,10 @@ export function emit( // TODO: warn let handlerName - let handler - for (let rawProp of rawProps) { - if ( - (handler = - rawProp[(handlerName = toHandlerKey(event))] || - // also try camelCase event handler (#2249) - rawProp[(handlerName = toHandlerKey(camelize(event)))]) - ) { - break - } - } + let handler = + rawProps[(handlerName = toHandlerKey(event))] || + // also try camelCase event handler (#2249) + rawProps[(handlerName = toHandlerKey(camelize(event)))] // for v-model update:xxx events, also trigger kebab-case equivalent // for props passed via kebab-case if (!handler && isModelListener) {