fix(compiler-core/compiler-sfc): handle destructure assignment expressions
This commit is contained in:
@@ -117,7 +117,7 @@ return { ref }
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> inlineTemplate mode avoid unref() when necessary 1`] = `
|
||||
"import { createVNode as _createVNode, unref as _unref, toDisplayString as _toDisplayString, Fragment as _Fragment, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\"
|
||||
"import { createVNode as _createVNode, toDisplayString as _toDisplayString, unref as _unref, Fragment as _Fragment, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\"
|
||||
|
||||
import { ref } from 'vue'
|
||||
import Foo from './Foo.vue'
|
||||
@@ -129,12 +129,14 @@ export default {
|
||||
|
||||
const count = ref(0)
|
||||
const constant = {}
|
||||
const maybe = foo()
|
||||
let lett = 1
|
||||
function fn() {}
|
||||
|
||||
return (_ctx, _cache) => {
|
||||
return (_openBlock(), _createBlock(_Fragment, null, [
|
||||
_createVNode(Foo),
|
||||
_createVNode(\\"div\\", { onClick: fn }, _toDisplayString(_unref(count)) + \\" \\" + _toDisplayString(constant) + \\" \\" + _toDisplayString(_unref(other)), 1 /* TEXT */)
|
||||
_createVNode(\\"div\\", { onClick: fn }, _toDisplayString(count.value) + \\" \\" + _toDisplayString(constant) + \\" \\" + _toDisplayString(_unref(maybe)) + \\" \\" + _toDisplayString(_unref(lett)) + \\" \\" + _toDisplayString(_unref(other)), 1 /* TEXT */)
|
||||
], 64 /* STABLE_FRAGMENT */))
|
||||
}
|
||||
}
|
||||
@@ -143,7 +145,7 @@ return (_ctx, _cache) => {
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> inlineTemplate mode should work 1`] = `
|
||||
"import { unref as _unref, toDisplayString as _toDisplayString, createVNode as _createVNode, Fragment as _Fragment, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\"
|
||||
"import { toDisplayString as _toDisplayString, createVNode as _createVNode, Fragment as _Fragment, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\"
|
||||
|
||||
const _hoisted_1 = /*#__PURE__*/_createVNode(\\"div\\", null, \\"static\\", -1 /* HOISTED */)
|
||||
|
||||
@@ -157,7 +159,7 @@ export default {
|
||||
|
||||
return (_ctx, _cache) => {
|
||||
return (_openBlock(), _createBlock(_Fragment, null, [
|
||||
_createVNode(\\"div\\", null, _toDisplayString(_unref(count)), 1 /* TEXT */),
|
||||
_createVNode(\\"div\\", null, _toDisplayString(count.value), 1 /* TEXT */),
|
||||
_hoisted_1
|
||||
], 64 /* STABLE_FRAGMENT */))
|
||||
}
|
||||
@@ -167,7 +169,7 @@ return (_ctx, _cache) => {
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> inlineTemplate mode template assignment expression codegen 1`] = `
|
||||
"import { createVNode as _createVNode, isRef as _isRef, unref as _unref, Fragment as _Fragment, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\"
|
||||
"import { createVNode as _createVNode, isRef as _isRef, Fragment as _Fragment, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\"
|
||||
|
||||
import { ref } from 'vue'
|
||||
|
||||
@@ -185,10 +187,42 @@ return (_ctx, _cache) => {
|
||||
onClick: _cache[1] || (_cache[1] = $event => (count.value = 1))
|
||||
}),
|
||||
_createVNode(\\"div\\", {
|
||||
onClick: _cache[2] || (_cache[2] = $event => (!_isRef(maybe) ? null : maybe.value = _unref(count)))
|
||||
onClick: _cache[2] || (_cache[2] = $event => (maybe.value = count.value))
|
||||
}),
|
||||
_createVNode(\\"div\\", {
|
||||
onClick: _cache[3] || (_cache[3] = $event => (_isRef(lett) ? lett.value = _unref(count) : lett = _unref(count)))
|
||||
onClick: _cache[3] || (_cache[3] = $event => (_isRef(lett) ? lett.value = count.value : lett = count.value))
|
||||
})
|
||||
], 64 /* STABLE_FRAGMENT */))
|
||||
}
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> inlineTemplate mode template destructure assignment codegen 1`] = `
|
||||
"import { createVNode as _createVNode, Fragment as _Fragment, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\"
|
||||
|
||||
import { ref } from 'vue'
|
||||
|
||||
export default {
|
||||
expose: [],
|
||||
setup(__props) {
|
||||
|
||||
const val = {}
|
||||
const count = ref(0)
|
||||
const maybe = foo()
|
||||
let lett = 1
|
||||
|
||||
return (_ctx, _cache) => {
|
||||
return (_openBlock(), _createBlock(_Fragment, null, [
|
||||
_createVNode(\\"div\\", {
|
||||
onClick: _cache[1] || (_cache[1] = $event => (({ count: count.value } = val)))
|
||||
}),
|
||||
_createVNode(\\"div\\", {
|
||||
onClick: _cache[2] || (_cache[2] = $event => ([maybe.value] = val))
|
||||
}),
|
||||
_createVNode(\\"div\\", {
|
||||
onClick: _cache[3] || (_cache[3] = $event => (({ lett: lett } = val)))
|
||||
})
|
||||
], 64 /* STABLE_FRAGMENT */))
|
||||
}
|
||||
@@ -219,10 +253,10 @@ return (_ctx, _cache) => {
|
||||
onClick: _cache[2] || (_cache[2] = $event => (--count.value))
|
||||
}),
|
||||
_createVNode(\\"div\\", {
|
||||
onClick: _cache[3] || (_cache[3] = $event => (!_isRef(maybe) ? null : maybe.value++))
|
||||
onClick: _cache[3] || (_cache[3] = $event => (maybe.value++))
|
||||
}),
|
||||
_createVNode(\\"div\\", {
|
||||
onClick: _cache[4] || (_cache[4] = $event => (!_isRef(maybe) ? null : --maybe.value))
|
||||
onClick: _cache[4] || (_cache[4] = $event => (--maybe.value))
|
||||
}),
|
||||
_createVNode(\\"div\\", {
|
||||
onClick: _cache[5] || (_cache[5] = $event => (_isRef(lett) ? lett.value++ : lett++))
|
||||
@@ -238,7 +272,7 @@ return (_ctx, _cache) => {
|
||||
`;
|
||||
|
||||
exports[`SFC compile <script setup> inlineTemplate mode v-model codegen 1`] = `
|
||||
"import { unref as _unref, vModelText as _vModelText, createVNode as _createVNode, withDirectives as _withDirectives, isRef as _isRef, Fragment as _Fragment, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\"
|
||||
"import { vModelText as _vModelText, createVNode as _createVNode, withDirectives as _withDirectives, unref as _unref, isRef as _isRef, Fragment as _Fragment, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\"
|
||||
|
||||
import { ref } from 'vue'
|
||||
|
||||
@@ -255,7 +289,7 @@ return (_ctx, _cache) => {
|
||||
_withDirectives(_createVNode(\\"input\\", {
|
||||
\\"onUpdate:modelValue\\": _cache[1] || (_cache[1] = $event => (count.value = $event))
|
||||
}, null, 512 /* NEED_PATCH */), [
|
||||
[_vModelText, _unref(count)]
|
||||
[_vModelText, count.value]
|
||||
]),
|
||||
_withDirectives(_createVNode(\\"input\\", {
|
||||
\\"onUpdate:modelValue\\": _cache[2] || (_cache[2] = $event => (_isRef(maybe) ? maybe.value = $event : null))
|
||||
@@ -364,6 +398,8 @@ export default {
|
||||
a.value = a.value + 1
|
||||
b.value.count++
|
||||
b.value.count = b.value.count + 1
|
||||
;({ a: a.value } = { a: 2 })
|
||||
;[a.value] = [1]
|
||||
}
|
||||
|
||||
return { a, b, inc }
|
||||
|
||||
@@ -128,28 +128,34 @@ const bar = 1
|
||||
import other from './util'
|
||||
const count = ref(0)
|
||||
const constant = {}
|
||||
const maybe = foo()
|
||||
let lett = 1
|
||||
function fn() {}
|
||||
</script>
|
||||
<template>
|
||||
<Foo/>
|
||||
<div @click="fn">{{ count }} {{ constant }} {{ other }}</div>
|
||||
<div @click="fn">{{ count }} {{ constant }} {{ maybe }} {{ lett }} {{ other }}</div>
|
||||
</template>
|
||||
`,
|
||||
{ inlineTemplate: true }
|
||||
)
|
||||
assertCode(content)
|
||||
// no need to unref vue component import
|
||||
expect(content).toMatch(`createVNode(Foo)`)
|
||||
// should unref other imports
|
||||
expect(content).toMatch(`unref(other)`)
|
||||
// no need to unref constant literals
|
||||
expect(content).not.toMatch(`unref(constant)`)
|
||||
// should unref const w/ call init (e.g. ref())
|
||||
expect(content).toMatch(`unref(count)`)
|
||||
// should directly use .value for known refs
|
||||
expect(content).toMatch(`count.value`)
|
||||
// should unref() on const bindings that may be refs
|
||||
expect(content).toMatch(`unref(maybe)`)
|
||||
// should unref() on let bindings
|
||||
expect(content).toMatch(`unref(lett)`)
|
||||
// no need to unref function declarations
|
||||
expect(content).toMatch(`{ onClick: fn }`)
|
||||
// no need to mark constant fns in patch flag
|
||||
expect(content).not.toMatch(`PROPS`)
|
||||
assertCode(content)
|
||||
})
|
||||
|
||||
test('v-model codegen', () => {
|
||||
@@ -170,8 +176,9 @@ const bar = 1
|
||||
)
|
||||
// known const ref: set value
|
||||
expect(content).toMatch(`count.value = $event`)
|
||||
// const but maybe ref: only assign after check
|
||||
expect(content).toMatch(`_isRef(maybe) ? maybe.value = $event : null`)
|
||||
// const but maybe ref: also assign .value directly since non-ref
|
||||
// won't work
|
||||
expect(content).toMatch(`maybe.value = $event`)
|
||||
// let: handle both cases
|
||||
expect(content).toMatch(
|
||||
`_isRef(lett) ? lett.value = $event : lett = $event`
|
||||
@@ -198,12 +205,10 @@ const bar = 1
|
||||
// known const ref: set value
|
||||
expect(content).toMatch(`count.value = 1`)
|
||||
// const but maybe ref: only assign after check
|
||||
expect(content).toMatch(
|
||||
`!_isRef(maybe) ? null : maybe.value = _unref(count)`
|
||||
)
|
||||
expect(content).toMatch(`maybe.value = count.value`)
|
||||
// let: handle both cases
|
||||
expect(content).toMatch(
|
||||
`_isRef(lett) ? lett.value = _unref(count) : lett = _unref(count)`
|
||||
`_isRef(lett) ? lett.value = count.value : lett = count.value`
|
||||
)
|
||||
assertCode(content)
|
||||
})
|
||||
@@ -230,14 +235,40 @@ const bar = 1
|
||||
// known const ref: set value
|
||||
expect(content).toMatch(`count.value++`)
|
||||
expect(content).toMatch(`--count.value`)
|
||||
// const but maybe ref: only assign after check
|
||||
expect(content).toMatch(`!_isRef(maybe) ? null : maybe.value++`)
|
||||
expect(content).toMatch(`!_isRef(maybe) ? null : --maybe.value`)
|
||||
// const but maybe ref (non-ref case ignored)
|
||||
expect(content).toMatch(`maybe.value++`)
|
||||
expect(content).toMatch(`--maybe.value`)
|
||||
// let: handle both cases
|
||||
expect(content).toMatch(`_isRef(lett) ? lett.value++ : lett++`)
|
||||
expect(content).toMatch(`_isRef(lett) ? --lett.value : --lett`)
|
||||
assertCode(content)
|
||||
})
|
||||
|
||||
test('template destructure assignment codegen', () => {
|
||||
const { content } = compile(
|
||||
`<script setup>
|
||||
import { ref } from 'vue'
|
||||
const val = {}
|
||||
const count = ref(0)
|
||||
const maybe = foo()
|
||||
let lett = 1
|
||||
</script>
|
||||
<template>
|
||||
<div @click="({ count } = val)"/>
|
||||
<div @click="[maybe] = val"/>
|
||||
<div @click="({ lett } = val)"/>
|
||||
</template>
|
||||
`,
|
||||
{ inlineTemplate: true }
|
||||
)
|
||||
// known const ref: set value
|
||||
expect(content).toMatch(`({ count: count.value } = val)`)
|
||||
// const but maybe ref (non-ref case ignored)
|
||||
expect(content).toMatch(`[maybe.value] = val`)
|
||||
// let: assumes non-ref
|
||||
expect(content).toMatch(`{ lett: lett } = val`)
|
||||
assertCode(content)
|
||||
})
|
||||
})
|
||||
|
||||
describe('with TypeScript', () => {
|
||||
@@ -524,12 +555,16 @@ const { props, emit } = defineOptions({
|
||||
a = a + 1
|
||||
b.count++
|
||||
b.count = b.count + 1
|
||||
;({ a } = { a: 2 })
|
||||
;[a] = [1]
|
||||
}
|
||||
</script>`)
|
||||
expect(content).toMatch(`a.value++`)
|
||||
expect(content).toMatch(`a.value = a.value + 1`)
|
||||
expect(content).toMatch(`b.value.count++`)
|
||||
expect(content).toMatch(`b.value.count = b.value.count + 1`)
|
||||
expect(content).toMatch(`;({ a: a.value } = { a: 2 })`)
|
||||
expect(content).toMatch(`;[a.value] = [1]`)
|
||||
assertCode(content)
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user