feat: compound expression for `v-on` (#60)
This commit is contained in:
parent
fb4d9a1443
commit
26fee414ff
|
@ -12,6 +12,18 @@ export function render(_ctx) {
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`v-on > complex member expression w/ prefixIdentifiers: true 1`] = `
|
||||||
|
"import { template as _template, children as _children, on as _on } from 'vue/vapor';
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const t0 = _template("<div></div>")
|
||||||
|
const n0 = t0()
|
||||||
|
const { 0: [n1],} = _children(n0)
|
||||||
|
_on(n1, "click", (...args) => (_ctx.a['b' + _ctx.c] && _ctx.a['b' + _ctx.c](...args)))
|
||||||
|
return n0
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`v-on > dynamic arg 1`] = `
|
exports[`v-on > dynamic arg 1`] = `
|
||||||
"import { template as _template, children as _children, renderEffect as _renderEffect, on as _on } from 'vue/vapor';
|
"import { template as _template, children as _children, renderEffect as _renderEffect, on as _on } from 'vue/vapor';
|
||||||
|
|
||||||
|
@ -26,6 +38,34 @@ export function render(_ctx) {
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`v-on > dynamic arg with complex exp prefixing 1`] = `
|
||||||
|
"import { template as _template, children as _children, renderEffect as _renderEffect, on as _on } from 'vue/vapor';
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const t0 = _template("<div></div>")
|
||||||
|
const n0 = t0()
|
||||||
|
const { 0: [n1],} = _children(n0)
|
||||||
|
_renderEffect(() => {
|
||||||
|
_on(n1, _ctx.event(_ctx.foo), (...args) => (_ctx.handler && _ctx.handler(...args)))
|
||||||
|
})
|
||||||
|
return n0
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`v-on > dynamic arg with prefixing 1`] = `
|
||||||
|
"import { template as _template, children as _children, renderEffect as _renderEffect, on as _on } from 'vue/vapor';
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const t0 = _template("<div></div>")
|
||||||
|
const n0 = t0()
|
||||||
|
const { 0: [n1],} = _children(n0)
|
||||||
|
_renderEffect(() => {
|
||||||
|
_on(n1, _ctx.event, (...args) => (_ctx.handler && _ctx.handler(...args)))
|
||||||
|
})
|
||||||
|
return n0
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`v-on > event modifier 1`] = `
|
exports[`v-on > event modifier 1`] = `
|
||||||
"import { template as _template, children as _children, on as _on, withModifiers as _withModifiers, withKeys as _withKeys } from 'vue/vapor';
|
"import { template as _template, children as _children, on as _on, withModifiers as _withModifiers, withKeys as _withKeys } from 'vue/vapor';
|
||||||
|
|
||||||
|
@ -59,6 +99,133 @@ export function render(_ctx) {
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`v-on > function expression w/ prefixIdentifiers: true 1`] = `
|
||||||
|
"import { template as _template, children as _children, on as _on } from 'vue/vapor';
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const t0 = _template("<div></div>")
|
||||||
|
const n0 = t0()
|
||||||
|
const { 0: [n1],} = _children(n0)
|
||||||
|
_on(n1, "click", e => _ctx.foo(e))
|
||||||
|
return n0
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`v-on > inline statement w/ prefixIdentifiers: true 1`] = `
|
||||||
|
"import { template as _template, children as _children, on as _on } from 'vue/vapor';
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const t0 = _template("<div></div>")
|
||||||
|
const n0 = t0()
|
||||||
|
const { 0: [n1],} = _children(n0)
|
||||||
|
_on(n1, "click", $event => (_ctx.foo($event)))
|
||||||
|
return n0
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`v-on > multiple inline statements w/ prefixIdentifiers: true 1`] = `
|
||||||
|
"import { template as _template, children as _children, on as _on } from 'vue/vapor';
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const t0 = _template("<div></div>")
|
||||||
|
const n0 = t0()
|
||||||
|
const { 0: [n1],} = _children(n0)
|
||||||
|
_on(n1, "click", $event => {_ctx.foo($event);_ctx.bar()})
|
||||||
|
return n0
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`v-on > should NOT add a prefix to $event if the expression is a function expression 1`] = `
|
||||||
|
"import { template as _template, children as _children, on as _on } from 'vue/vapor';
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const t0 = _template("<div></div>")
|
||||||
|
const n0 = t0()
|
||||||
|
const { 0: [n1],} = _children(n0)
|
||||||
|
_on(n1, "click", $event => {_ctx.i++;_ctx.foo($event)})
|
||||||
|
return n0
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`v-on > should NOT wrap as function if expression is already function expression (with Typescript) 1`] = `
|
||||||
|
"import { template as _template, children as _children, on as _on } from 'vue/vapor';
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const t0 = _template("<div></div>")
|
||||||
|
const n0 = t0()
|
||||||
|
const { 0: [n1],} = _children(n0)
|
||||||
|
_on(n1, "click", (e: any): any => _ctx.foo(e))
|
||||||
|
return n0
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`v-on > should NOT wrap as function if expression is already function expression (with newlines) 1`] = `
|
||||||
|
"import { template as _template, children as _children, on as _on } from 'vue/vapor';
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const t0 = _template("<div></div>")
|
||||||
|
const n0 = t0()
|
||||||
|
const { 0: [n1],} = _children(n0)
|
||||||
|
_on(n1, "click",
|
||||||
|
$event => {
|
||||||
|
_ctx.foo($event)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return n0
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`v-on > should NOT wrap as function if expression is already function expression 1`] = `
|
||||||
|
"import { template as _template, children as _children, on as _on } from 'vue/vapor';
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const t0 = _template("<div></div>")
|
||||||
|
const n0 = t0()
|
||||||
|
const { 0: [n1],} = _children(n0)
|
||||||
|
_on(n1, "click", $event => _ctx.foo($event))
|
||||||
|
return n0
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`v-on > should NOT wrap as function if expression is complex member expression 1`] = `
|
||||||
|
"import { template as _template, children as _children, on as _on } from 'vue/vapor';
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const t0 = _template("<div></div>")
|
||||||
|
const n0 = t0()
|
||||||
|
const { 0: [n1],} = _children(n0)
|
||||||
|
_on(n1, "click", (...args) => (_ctx.a['b' + _ctx.c] && _ctx.a['b' + _ctx.c](...args)))
|
||||||
|
return n0
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`v-on > should handle multi-line statement 1`] = `
|
||||||
|
"import { template as _template, children as _children, on as _on } from 'vue/vapor';
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const t0 = _template("<div></div>")
|
||||||
|
const n0 = t0()
|
||||||
|
const { 0: [n1],} = _children(n0)
|
||||||
|
_on(n1, "click", $event => {
|
||||||
|
_ctx.foo();
|
||||||
|
_ctx.bar()
|
||||||
|
})
|
||||||
|
return n0
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`v-on > should handle multiple inline statement 1`] = `
|
||||||
|
"import { template as _template, children as _children, on as _on } from 'vue/vapor';
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const t0 = _template("<div></div>")
|
||||||
|
const n0 = t0()
|
||||||
|
const { 0: [n1],} = _children(n0)
|
||||||
|
_on(n1, "click", $event => {_ctx.foo();_ctx.bar()})
|
||||||
|
return n0
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`v-on > should not wrap keys guard if no key modifier is present 1`] = `
|
exports[`v-on > should not wrap keys guard if no key modifier is present 1`] = `
|
||||||
"import { template as _template, children as _children, on as _on, withModifiers as _withModifiers } from 'vue/vapor';
|
"import { template as _template, children as _children, on as _on, withModifiers as _withModifiers } from 'vue/vapor';
|
||||||
|
|
||||||
|
@ -148,6 +315,32 @@ export function render(_ctx) {
|
||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`v-on > should wrap as function if expression is inline statement 1`] = `
|
||||||
|
"import { template as _template, children as _children, on as _on } from 'vue/vapor';
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const t0 = _template("<div></div>")
|
||||||
|
const n0 = t0()
|
||||||
|
const { 0: [n1],} = _children(n0)
|
||||||
|
_on(n1, "click", $event => (_ctx.i++))
|
||||||
|
return n0
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`v-on > should wrap both for dynamic key event w/ left/right modifiers 1`] = `
|
||||||
|
"import { template as _template, children as _children, renderEffect as _renderEffect, on as _on, withKeys as _withKeys, withModifiers as _withModifiers } from 'vue/vapor';
|
||||||
|
|
||||||
|
export function render(_ctx) {
|
||||||
|
const t0 = _template("<div></div>")
|
||||||
|
const n0 = t0()
|
||||||
|
const { 0: [n1],} = _children(n0)
|
||||||
|
_renderEffect(() => {
|
||||||
|
_on(n1, _ctx.e, _withKeys(_withModifiers((...args) => (_ctx.test && _ctx.test(...args)), ["left"]), ["left"]))
|
||||||
|
})
|
||||||
|
return n0
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`v-on > should wrap keys guard for keyboard events or dynamic events 1`] = `
|
exports[`v-on > should wrap keys guard for keyboard events or dynamic events 1`] = `
|
||||||
"import { template as _template, children as _children, on as _on, withKeys as _withKeys, withModifiers as _withModifiers } from 'vue/vapor';
|
"import { template as _template, children as _children, on as _on, withKeys as _withKeys, withModifiers as _withModifiers } from 'vue/vapor';
|
||||||
|
|
||||||
|
|
|
@ -124,30 +124,254 @@ describe('v-on', () => {
|
||||||
expect(code).matchSnapshot()
|
expect(code).matchSnapshot()
|
||||||
})
|
})
|
||||||
|
|
||||||
test.todo('dynamic arg with prefixing')
|
test('dynamic arg with prefixing', () => {
|
||||||
test.todo('dynamic arg with complex exp prefixing')
|
const { code } = compileWithVOn(`<div v-on:[event]="handler"/>`, {
|
||||||
test.todo('should wrap as function if expression is inline statement')
|
prefixIdentifiers: true,
|
||||||
test.todo('should handle multiple inline statement')
|
})
|
||||||
test.todo('should handle multi-line statement')
|
|
||||||
test.todo('inline statement w/ prefixIdentifiers: true')
|
expect(code).matchSnapshot()
|
||||||
test.todo('multiple inline statements w/ prefixIdentifiers: true')
|
})
|
||||||
test.todo(
|
|
||||||
'should NOT wrap as function if expression is already function expression',
|
test('dynamic arg with complex exp prefixing', () => {
|
||||||
)
|
const { ir, code } = compileWithVOn(`<div v-on:[event(foo)]="handler"/>`, {
|
||||||
test.todo(
|
prefixIdentifiers: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(ir.vaporHelpers).contains('on')
|
||||||
|
expect(ir.vaporHelpers).contains('renderEffect')
|
||||||
|
expect(ir.helpers.size).toBe(0)
|
||||||
|
expect(ir.operation).toEqual([])
|
||||||
|
|
||||||
|
expect(ir.effect[0].operations[0]).toMatchObject({
|
||||||
|
type: IRNodeTypes.SET_EVENT,
|
||||||
|
element: 1,
|
||||||
|
key: {
|
||||||
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
content: 'event(foo)',
|
||||||
|
isStatic: false,
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
content: 'handler',
|
||||||
|
isStatic: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(code).matchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should wrap as function if expression is inline statement', () => {
|
||||||
|
const { code, ir } = compileWithVOn(`<div @click="i++"/>`)
|
||||||
|
|
||||||
|
expect(ir.vaporHelpers).contains('on')
|
||||||
|
expect(ir.helpers.size).toBe(0)
|
||||||
|
expect(ir.effect).toEqual([])
|
||||||
|
|
||||||
|
expect(ir.operation).toMatchObject([
|
||||||
|
{
|
||||||
|
type: IRNodeTypes.SET_EVENT,
|
||||||
|
element: 1,
|
||||||
|
value: {
|
||||||
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
content: 'i++',
|
||||||
|
isStatic: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(code).matchSnapshot()
|
||||||
|
expect(code).contains('_on(n1, "click", $event => (_ctx.i++))')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should handle multiple inline statement', () => {
|
||||||
|
const { ir, code } = compileWithVOn(`<div @click="foo();bar()"/>`)
|
||||||
|
|
||||||
|
expect(ir.operation).toMatchObject([
|
||||||
|
{
|
||||||
|
type: IRNodeTypes.SET_EVENT,
|
||||||
|
value: { content: 'foo();bar()' },
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(code).matchSnapshot()
|
||||||
|
// should wrap with `{` for multiple statements
|
||||||
|
// in this case the return value is discarded and the behavior is
|
||||||
|
// consistent with 2.x
|
||||||
|
expect(code).contains('_on(n1, "click", $event => {_ctx.foo();_ctx.bar()})')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should handle multi-line statement', () => {
|
||||||
|
const { code, ir } = compileWithVOn(`<div @click="\nfoo();\nbar()\n"/>`)
|
||||||
|
|
||||||
|
expect(ir.operation).toMatchObject([
|
||||||
|
{
|
||||||
|
type: IRNodeTypes.SET_EVENT,
|
||||||
|
value: { content: '\nfoo();\nbar()\n' },
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(code).matchSnapshot()
|
||||||
|
// should wrap with `{` for multiple statements
|
||||||
|
// in this case the return value is discarded and the behavior is
|
||||||
|
// consistent with 2.x
|
||||||
|
expect(code).contains(
|
||||||
|
'_on(n1, "click", $event => {\n_ctx.foo();\n_ctx.bar()\n})',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('inline statement w/ prefixIdentifiers: true', () => {
|
||||||
|
const { code, ir } = compileWithVOn(`<div @click="foo($event)"/>`, {
|
||||||
|
prefixIdentifiers: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(ir.operation).toMatchObject([
|
||||||
|
{
|
||||||
|
type: IRNodeTypes.SET_EVENT,
|
||||||
|
value: { content: 'foo($event)' },
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(code).matchSnapshot()
|
||||||
|
// should NOT prefix $event
|
||||||
|
expect(code).contains('_on(n1, "click", $event => (_ctx.foo($event)))')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('multiple inline statements w/ prefixIdentifiers: true', () => {
|
||||||
|
const { ir, code } = compileWithVOn(`<div @click="foo($event);bar()"/>`, {
|
||||||
|
prefixIdentifiers: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(ir.operation).toMatchObject([
|
||||||
|
{
|
||||||
|
type: IRNodeTypes.SET_EVENT,
|
||||||
|
value: { content: 'foo($event);bar()' },
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(code).matchSnapshot()
|
||||||
|
// should NOT prefix $event
|
||||||
|
expect(code).contains(
|
||||||
|
'_on(n1, "click", $event => {_ctx.foo($event);_ctx.bar()})',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should NOT wrap as function if expression is already function expression', () => {
|
||||||
|
const { code, ir } = compileWithVOn(`<div @click="$event => foo($event)"/>`)
|
||||||
|
|
||||||
|
expect(ir.operation).toMatchObject([
|
||||||
|
{
|
||||||
|
type: IRNodeTypes.SET_EVENT,
|
||||||
|
value: { content: '$event => foo($event)' },
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(code).matchSnapshot()
|
||||||
|
expect(code).contains('_on(n1, "click", $event => _ctx.foo($event))')
|
||||||
|
})
|
||||||
|
|
||||||
|
test.fails(
|
||||||
'should NOT wrap as function if expression is already function expression (with Typescript)',
|
'should NOT wrap as function if expression is already function expression (with Typescript)',
|
||||||
|
() => {
|
||||||
|
const { ir, code } = compileWithVOn(
|
||||||
|
`<div @click="(e: any): any => foo(e)"/>`,
|
||||||
|
{ expressionPlugins: ['typescript'] },
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(ir.operation).toMatchObject([
|
||||||
|
{
|
||||||
|
type: IRNodeTypes.SET_EVENT,
|
||||||
|
value: { content: '(e: any): any => foo(e)' },
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(code).matchSnapshot()
|
||||||
|
expect(code).contains('_on(n1, "click", e => _ctx.foo(e))')
|
||||||
|
},
|
||||||
)
|
)
|
||||||
test.todo(
|
|
||||||
'should NOT wrap as function if expression is already function expression (with newlines)',
|
test('should NOT wrap as function if expression is already function expression (with newlines)', () => {
|
||||||
)
|
const { ir, code } = compileWithVOn(
|
||||||
test.todo(
|
`<div @click="
|
||||||
'should NOT wrap as function if expression is already function expression (with newlines + function keyword)',
|
$event => {
|
||||||
)
|
foo($event)
|
||||||
test.todo(
|
}
|
||||||
'should NOT wrap as function if expression is complex member expression',
|
"/>`,
|
||||||
)
|
)
|
||||||
test.todo('complex member expression w/ prefixIdentifiers: true')
|
|
||||||
test.todo('function expression w/ prefixIdentifiers: true')
|
expect(ir.operation).toMatchObject([
|
||||||
|
{
|
||||||
|
type: IRNodeTypes.SET_EVENT,
|
||||||
|
value: {
|
||||||
|
content: `
|
||||||
|
$event => {
|
||||||
|
foo($event)
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(code).matchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should NOT add a prefix to $event if the expression is a function expression', () => {
|
||||||
|
const { ir, code } = compileWithVOn(
|
||||||
|
`<div @click="$event => {i++;foo($event)}"></div>`,
|
||||||
|
{
|
||||||
|
prefixIdentifiers: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(ir.operation[0]).toMatchObject({
|
||||||
|
type: IRNodeTypes.SET_EVENT,
|
||||||
|
value: { content: '$event => {i++;foo($event)}' },
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(code).matchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should NOT wrap as function if expression is complex member expression', () => {
|
||||||
|
const { ir, code } = compileWithVOn(`<div @click="a['b' + c]"/>`)
|
||||||
|
expect(ir.operation).toMatchObject([
|
||||||
|
{
|
||||||
|
type: IRNodeTypes.SET_EVENT,
|
||||||
|
value: { content: `a['b' + c]` },
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(code).matchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('complex member expression w/ prefixIdentifiers: true', () => {
|
||||||
|
const { ir, code } = compileWithVOn(`<div @click="a['b' + c]"/>`)
|
||||||
|
expect(ir.operation).toMatchObject([
|
||||||
|
{
|
||||||
|
type: IRNodeTypes.SET_EVENT,
|
||||||
|
value: { content: `a['b' + c]` },
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(code).matchSnapshot()
|
||||||
|
expect(code).contains(
|
||||||
|
`_on(n1, "click", (...args) => (_ctx.a['b' + _ctx.c] && _ctx.a['b' + _ctx.c](...args)))`,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('function expression w/ prefixIdentifiers: true', () => {
|
||||||
|
const { code, ir } = compileWithVOn(`<div @click="e => foo(e)"/>`, {
|
||||||
|
prefixIdentifiers: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(ir.operation).toMatchObject([
|
||||||
|
{
|
||||||
|
type: IRNodeTypes.SET_EVENT,
|
||||||
|
value: { content: `e => foo(e)` },
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(code).matchSnapshot()
|
||||||
|
expect(code).contains('_on(n1, "click", e => _ctx.foo(e))')
|
||||||
|
})
|
||||||
|
|
||||||
test('should error if no expression AND no modifier', () => {
|
test('should error if no expression AND no modifier', () => {
|
||||||
const onError = vi.fn()
|
const onError = vi.fn()
|
||||||
|
@ -366,14 +590,40 @@ describe('v-on', () => {
|
||||||
expect(ir.operation).toMatchObject([
|
expect(ir.operation).toMatchObject([
|
||||||
{
|
{
|
||||||
type: IRNodeTypes.SET_EVENT,
|
type: IRNodeTypes.SET_EVENT,
|
||||||
modifiers: { keys: ['left'] },
|
modifiers: {
|
||||||
|
keys: ['left'],
|
||||||
|
nonKeys: [],
|
||||||
|
options: [],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
expect(code).matchSnapshot()
|
expect(code).matchSnapshot()
|
||||||
})
|
})
|
||||||
|
|
||||||
test.todo('should wrap both for dynamic key event w/ left/right modifiers')
|
test('should wrap both for dynamic key event w/ left/right modifiers', () => {
|
||||||
|
const { code, ir } = compileWithVOn(`<div @[e].left="test"/>`, {
|
||||||
|
prefixIdentifiers: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(ir.effect[0].operations).toMatchObject([
|
||||||
|
{
|
||||||
|
type: IRNodeTypes.SET_EVENT,
|
||||||
|
key: {
|
||||||
|
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||||
|
content: 'e',
|
||||||
|
isStatic: false,
|
||||||
|
},
|
||||||
|
modifiers: {
|
||||||
|
keys: ['left'],
|
||||||
|
nonKeys: ['left'],
|
||||||
|
options: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(code).matchSnapshot()
|
||||||
|
})
|
||||||
|
|
||||||
test('should transform click.right', () => {
|
test('should transform click.right', () => {
|
||||||
const { code, ir } = compileWithVOn(`<div @click.right="test"/>`)
|
const { code, ir } = compileWithVOn(`<div @click.right="test"/>`)
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {
|
||||||
advancePositionWithClone,
|
advancePositionWithClone,
|
||||||
advancePositionWithMutation,
|
advancePositionWithMutation,
|
||||||
createSimpleExpression,
|
createSimpleExpression,
|
||||||
|
isMemberExpression,
|
||||||
isSimpleIdentifier,
|
isSimpleIdentifier,
|
||||||
locStub,
|
locStub,
|
||||||
walkIdentifiers,
|
walkIdentifiers,
|
||||||
|
@ -33,6 +34,10 @@ import { SourceMapGenerator } from 'source-map-js'
|
||||||
import { camelize, isGloballyAllowed, isString, makeMap } from '@vue/shared'
|
import { camelize, isGloballyAllowed, isString, makeMap } from '@vue/shared'
|
||||||
import type { Identifier } from '@babel/types'
|
import type { Identifier } from '@babel/types'
|
||||||
|
|
||||||
|
// TODO: share this with compiler-core
|
||||||
|
const fnExpRE =
|
||||||
|
/^\s*([\w$_]+|(async\s*)?\([^)]*?\))\s*(:[^=]+)?=>|^\s*(async\s+)?function(?:\s+[\w$]+)?\s*\(/
|
||||||
|
|
||||||
// remove when stable
|
// remove when stable
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
function checkNever(x: never): never {}
|
function checkNever(x: never): never {}
|
||||||
|
@ -508,15 +513,7 @@ function genSetEvent(oper: SetEventIRNode, context: CodegenContext) {
|
||||||
|
|
||||||
;(keys.length ? pushWithKeys : pushNoop)(() =>
|
;(keys.length ? pushWithKeys : pushNoop)(() =>
|
||||||
(nonKeys.length ? pushWithModifiers : pushNoop)(() => {
|
(nonKeys.length ? pushWithModifiers : pushNoop)(() => {
|
||||||
if (oper.value && oper.value.content.trim()) {
|
genEventHandler()
|
||||||
push('(...args) => (')
|
|
||||||
genExpression(oper.value, context)
|
|
||||||
push(' && ')
|
|
||||||
genExpression(oper.value, context)
|
|
||||||
push('(...args))')
|
|
||||||
} else {
|
|
||||||
push('() => {}')
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -524,6 +521,37 @@ function genSetEvent(oper: SetEventIRNode, context: CodegenContext) {
|
||||||
!!options.length &&
|
!!options.length &&
|
||||||
(() => push(`{ ${options.map((v) => `${v}: true`).join(', ')} }`)),
|
(() => push(`{ ${options.map((v) => `${v}: true`).join(', ')} }`)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
function genEventHandler() {
|
||||||
|
const exp = oper.value
|
||||||
|
if (exp && exp.content.trim()) {
|
||||||
|
const isMemberExp = isMemberExpression(exp.content, {
|
||||||
|
// TODO: expression plugins
|
||||||
|
expressionPlugins: [],
|
||||||
|
})
|
||||||
|
const isInlineStatement = !(isMemberExp || fnExpRE.test(exp.content))
|
||||||
|
const hasMultipleStatements = exp.content.includes(`;`)
|
||||||
|
|
||||||
|
if (isInlineStatement) {
|
||||||
|
push('$event => ')
|
||||||
|
push(hasMultipleStatements ? '{' : '(')
|
||||||
|
const knownIds = Object.create(null)
|
||||||
|
knownIds['$event'] = 1
|
||||||
|
genExpression(exp, context, knownIds)
|
||||||
|
push(hasMultipleStatements ? '}' : ')')
|
||||||
|
} else if (isMemberExp) {
|
||||||
|
push('(...args) => (')
|
||||||
|
genExpression(exp, context)
|
||||||
|
push(' && ')
|
||||||
|
genExpression(exp, context)
|
||||||
|
push('(...args))')
|
||||||
|
} else {
|
||||||
|
genExpression(exp, context)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
push('() => {}')
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function genWithDirective(oper: WithDirectiveIRNode, context: CodegenContext) {
|
function genWithDirective(oper: WithDirectiveIRNode, context: CodegenContext) {
|
||||||
|
@ -588,7 +616,11 @@ function genArrayExpression(elements: string[]) {
|
||||||
|
|
||||||
const isLiteralWhitelisted = /*#__PURE__*/ makeMap('true,false,null,this')
|
const isLiteralWhitelisted = /*#__PURE__*/ makeMap('true,false,null,this')
|
||||||
|
|
||||||
function genExpression(node: IRExpression, context: CodegenContext): void {
|
function genExpression(
|
||||||
|
node: IRExpression,
|
||||||
|
context: CodegenContext,
|
||||||
|
knownIds: Record<string, number> = Object.create(null),
|
||||||
|
): void {
|
||||||
const { push } = context
|
const { push } = context
|
||||||
if (isString(node)) return push(node)
|
if (isString(node)) return push(node)
|
||||||
|
|
||||||
|
@ -616,10 +648,13 @@ function genExpression(node: IRExpression, context: CodegenContext): void {
|
||||||
const ids: Identifier[] = []
|
const ids: Identifier[] = []
|
||||||
walkIdentifiers(
|
walkIdentifiers(
|
||||||
ast!,
|
ast!,
|
||||||
(id) => {
|
(id, parent, parentStack, isReference, isLocal) => {
|
||||||
|
if (isLocal) return
|
||||||
ids.push(id)
|
ids.push(id)
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
|
[],
|
||||||
|
knownIds,
|
||||||
)
|
)
|
||||||
if (ids.length) {
|
if (ids.length) {
|
||||||
ids.sort((a, b) => a.start! - b.start!)
|
ids.sort((a, b) => a.start! - b.start!)
|
||||||
|
|
|
@ -71,6 +71,7 @@ export {
|
||||||
export { TrackOpTypes, TriggerOpTypes, ReactiveFlags } from './constants'
|
export { TrackOpTypes, TriggerOpTypes, ReactiveFlags } from './constants'
|
||||||
export {
|
export {
|
||||||
baseWatch,
|
baseWatch,
|
||||||
|
getCurrentEffect,
|
||||||
onEffectCleanup,
|
onEffectCleanup,
|
||||||
traverse,
|
traverse,
|
||||||
BaseWatchErrorCodes,
|
BaseWatchErrorCodes,
|
||||||
|
|
|
@ -29,6 +29,7 @@ export {
|
||||||
// effect
|
// effect
|
||||||
stop,
|
stop,
|
||||||
ReactiveEffect,
|
ReactiveEffect,
|
||||||
|
getCurrentEffect,
|
||||||
onEffectCleanup,
|
onEffectCleanup,
|
||||||
// effect scope
|
// effect scope
|
||||||
effectScope,
|
effectScope,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { getCurrentEffect, onEffectCleanup } from '@vue/reactivity'
|
||||||
|
|
||||||
export function on(
|
export function on(
|
||||||
el: HTMLElement,
|
el: HTMLElement,
|
||||||
event: string,
|
event: string,
|
||||||
|
@ -5,4 +7,7 @@ export function on(
|
||||||
options?: AddEventListenerOptions,
|
options?: AddEventListenerOptions,
|
||||||
) {
|
) {
|
||||||
el.addEventListener(event, handler, options)
|
el.addEventListener(event, handler, options)
|
||||||
|
if (getCurrentEffect()) {
|
||||||
|
onEffectCleanup(() => el.removeEventListener(event, handler, options))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue/vapor'
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
console.log('Hello, Vapor!')
|
console.log('Hello, Vapor!')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const i = ref(0)
|
||||||
|
const event = ref('click')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -12,4 +16,12 @@ const handleClick = () => {
|
||||||
<form>
|
<form>
|
||||||
<button @click.prevent="handleClick">no submit</button>
|
<button @click.prevent="handleClick">no submit</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{{ i }}
|
||||||
|
<button @[event].prevent="i++">Add by {{ event }}</button>
|
||||||
|
<button @click="event = event === 'click' ? 'contextmenu' : 'click'">
|
||||||
|
Change Event
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
Loading…
Reference in New Issue