diff --git a/packages/compiler-sfc/__tests__/__snapshots__/compileScriptPropsTransform.spec.ts.snap b/packages/compiler-sfc/__tests__/__snapshots__/compileScriptPropsTransform.spec.ts.snap index 093d2b9ddf7..dd107b3682b 100644 --- a/packages/compiler-sfc/__tests__/__snapshots__/compileScriptPropsTransform.spec.ts.snap +++ b/packages/compiler-sfc/__tests__/__snapshots__/compileScriptPropsTransform.spec.ts.snap @@ -10,9 +10,9 @@ const __props_bar = _toRef(__props, 'bar'); const __props_foo = _toRef(__props, 'foo'); - console.log((__props_foo)) - console.log((__props_bar)) - ({ foo: __props_foo, baz: __props_bar }) + console.log(__props_foo) + console.log(__props_bar) + console.log({ foo: __props_foo, baz: __props_bar }) return () => {} } diff --git a/packages/compiler-sfc/__tests__/__snapshots__/compileScriptRefTransform.spec.ts.snap b/packages/compiler-sfc/__tests__/__snapshots__/compileScriptRefTransform.spec.ts.snap index b7c81a08c86..442e170094b 100644 --- a/packages/compiler-sfc/__tests__/__snapshots__/compileScriptRefTransform.spec.ts.snap +++ b/packages/compiler-sfc/__tests__/__snapshots__/compileScriptRefTransform.spec.ts.snap @@ -7,11 +7,11 @@ export default { setup(__props, { expose }) { expose(); - let foo = (ref()) - let a = (ref(1)) - let b = (shallowRef({ + let foo = ref() + let a = ref(1) + let b = shallowRef({ count: 0 - })) + }) let c = () => {} let d @@ -65,7 +65,7 @@ exports[`sfc ref transform usage in normal `) expect(content).toMatch(`const __props_foo = _toRef(__props, 'foo')`) expect(content).toMatch(`const __props_bar = _toRef(__props, 'bar')`) - expect(content).toMatch(`console.log((__props_foo))`) - expect(content).toMatch(`console.log((__props_bar))`) - expect(content).toMatch(`({ foo: __props_foo, baz: __props_bar })`) + expect(content).toMatch(`console.log(__props_foo)`) + expect(content).toMatch(`console.log(__props_bar)`) + expect(content).toMatch(`{ foo: __props_foo, baz: __props_bar }`) assertCode(content) }) diff --git a/packages/compiler-sfc/__tests__/compileScriptRefTransform.spec.ts b/packages/compiler-sfc/__tests__/compileScriptRefTransform.spec.ts index 88d62f2b478..468c3f07b7e 100644 --- a/packages/compiler-sfc/__tests__/compileScriptRefTransform.spec.ts +++ b/packages/compiler-sfc/__tests__/compileScriptRefTransform.spec.ts @@ -22,12 +22,12 @@ describe('sfc ref transform', () => { expect(content).not.toMatch(`$(ref())`) expect(content).not.toMatch(`$(ref(1))`) expect(content).not.toMatch(`$(shallowRef({`) - expect(content).toMatch(`let foo = (ref())`) - expect(content).toMatch(`let a = (ref(1))`) + expect(content).toMatch(`let foo = ref()`) + expect(content).toMatch(`let a = ref(1)`) expect(content).toMatch(` - let b = (shallowRef({ + let b = shallowRef({ count: 0 - })) + }) `) // normal declarations left untouched expect(content).toMatch(`let c = () => {}`) @@ -95,7 +95,7 @@ describe('sfc ref transform', () => { expect(content).toMatch(`import { ref as _ref } from 'vue'`) expect(content).toMatch(`let count = _ref(0)`) expect(content).toMatch(`count.value++`) - expect(content).toMatch(`return ({ count })`) + expect(content).toMatch(`return { count }`) assertCode(content) }) diff --git a/packages/reactivity-transform/__tests__/__snapshots__/reactivityTransform.spec.ts.snap b/packages/reactivity-transform/__tests__/__snapshots__/reactivityTransform.spec.ts.snap index ea6641578b8..f28b06bf9cf 100644 --- a/packages/reactivity-transform/__tests__/__snapshots__/reactivityTransform.spec.ts.snap +++ b/packages/reactivity-transform/__tests__/__snapshots__/reactivityTransform.spec.ts.snap @@ -3,14 +3,14 @@ exports[`$ unwrapping 1`] = ` " import { ref, shallowRef } from 'vue' - let foo = (ref()) - export let a = (ref(1)) - let b = (shallowRef({ + let foo = ref() + export let a = ref(1) + let b = shallowRef({ count: 0 - })) + }) let c = () => {} let d - label: var e = (ref()) + label: var e = ref() " `; @@ -18,9 +18,38 @@ exports[`$$ 1`] = ` "import { ref as _ref } from 'vue' let a = _ref(1) - const b = (a) - const c = ({ a }) - callExternal((a)) + const b = a + const c = { a } + callExternal(a) + " +`; + +exports[`$$ with some edge cases 1`] = ` +"import { ref as _ref } from 'vue' + + /* 2 */ count /* 2 */ + ;( count /* 2 */, /**/ a ) + ; (count /* 2 */, /**/ a /**/ ) + { + a:(count,a) + } + ;((count) + 1) + ;([count]) + count + console.log(a) + ;(a,b) + ;(a++,b) + count = ( a++ ,b) + count = ()=>(a++,b) + let r1 = _ref(a, (a++,b)) + let r2 = { a:(a++,b),b:a } + switch(c){ + case d: + a + ;(h,f) + break + } + ((count++,count,(count,a))) " `; @@ -59,7 +88,7 @@ exports[`accessing ref binding 1`] = ` exports[`array destructure 1`] = ` "import { ref as _ref, toRef as _toRef } from 'vue' - let n = _ref(1), __$temp_1 = (useFoo()), + let n = _ref(1), __$temp_1 = useFoo(), a = _toRef(__$temp_1, 0), b = _toRef(__$temp_1, 1, 1); console.log(n.value, a.value, b.value) @@ -88,7 +117,7 @@ exports[`macro import alias and removal 1`] = ` let a = _ref(1) - const __$temp_1 = (useMouse()), + const __$temp_1 = useMouse(), x = _toRef(__$temp_1, 'x'), y = _toRef(__$temp_1, 'y'); " @@ -129,9 +158,9 @@ exports[`mutating ref binding 1`] = ` exports[`nested destructure 1`] = ` "import { toRef as _toRef } from 'vue' - let __$temp_1 = (useFoo()), + let __$temp_1 = useFoo(), b = _toRef(__$temp_1[0].a, 'b'); - let __$temp_2 = (useBar()), + let __$temp_2 = useBar(), d = _toRef(__$temp_2.c, 0), e = _toRef(__$temp_2.c, 1); console.log(b.value, d.value, e.value) @@ -170,7 +199,7 @@ exports[`nested scopes 1`] = ` a.value++ // if block a } - return ({ a, b, c, d }) + return { a, b, c, d } } " `; @@ -178,13 +207,13 @@ exports[`nested scopes 1`] = ` exports[`object destructure 1`] = ` "import { ref as _ref, toRef as _toRef } from 'vue' - let n = _ref(1), __$temp_1 = (useFoo()), + let n = _ref(1), __$temp_1 = useFoo(), a = _toRef(__$temp_1, 'a'), c = _toRef(__$temp_1, 'b'), d = _toRef(__$temp_1, 'd', 1), f = _toRef(__$temp_1, 'e', 2), h = _toRef(__$temp_1, g); - let __$temp_2 = (useSomething(() => 1)), + let __$temp_2 = useSomething(() => 1), foo = _toRef(__$temp_2, 'foo');; console.log(n.value, a.value, c.value, d.value, f.value, h.value, foo.value) " @@ -193,7 +222,7 @@ exports[`object destructure 1`] = ` exports[`object destructure w/ mid-path default values 1`] = ` "import { toRef as _toRef } from 'vue' - const __$temp_1 = (useFoo()), + const __$temp_1 = useFoo(), b = _toRef((__$temp_1.a || { b: 123 }), 'b'); console.log(b.value) " diff --git a/packages/reactivity-transform/__tests__/reactivityTransform.spec.ts b/packages/reactivity-transform/__tests__/reactivityTransform.spec.ts index f1c5d4eb0cb..2fa3af97f19 100644 --- a/packages/reactivity-transform/__tests__/reactivityTransform.spec.ts +++ b/packages/reactivity-transform/__tests__/reactivityTransform.spec.ts @@ -30,17 +30,17 @@ test('$ unwrapping', () => { expect(code).not.toMatch(`$(ref())`) expect(code).not.toMatch(`$(ref(1))`) expect(code).not.toMatch(`$(shallowRef({`) - expect(code).toMatch(`let foo = (ref())`) - expect(code).toMatch(`export let a = (ref(1))`) + expect(code).toMatch(`let foo = ref()`) + expect(code).toMatch(`export let a = ref(1)`) expect(code).toMatch(` - let b = (shallowRef({ + let b = shallowRef({ count: 0 - })) + }) `) // normal declarations left untouched expect(code).toMatch(`let c = () => {}`) expect(code).toMatch(`let d`) - expect(code).toMatch(`label: var e = (ref())`) + expect(code).toMatch(`label: var e = ref()`) expect(rootRefs).toStrictEqual(['foo', 'a', 'b', 'e']) assertCode(code) }) @@ -299,9 +299,53 @@ test('$$', () => { const c = $$({ a }) callExternal($$(a)) `) - expect(code).toMatch(`const b = (a)`) - expect(code).toMatch(`const c = ({ a })`) - expect(code).toMatch(`callExternal((a))`) + expect(code).toMatch(`const b = a`) + expect(code).toMatch(`const c = { a }`) + expect(code).toMatch(`callExternal(a)`) + assertCode(code) +}) + +test('$$ with some edge cases', () => { + const { code } = transform(` + $$( /* 2 */ count /* 2 */ ) + $$( count /* 2 */, /**/ a ) + $$( (count /* 2 */, /**/ a) /**/ ) + { + a:$$(count,a) + } + $$((count) + 1) + $$([count]) + $$ (count ) + console.log($$($$(a))) + $$(a,b) + $$($$((a++,b))) + count = $$( a++ ,b) + count = ()=>$$(a++,b) + let r1 = $ref(a, $$(a++,b)) + let r2 = { a:$$(a++,b),b:$$ (a) } + switch($$(c)){ + case d: + $$(a) + $$($$(h,f)) + break + } + ($$(count++,$$(count),$$(count,a))) + `) + expect(code).toMatch(`/* 2 */ count /* 2 */`) + expect(code).toMatch(`;( count /* 2 */, /**/ a )`) + expect(code).toMatch(`; (count /* 2 */, /**/ a /**/ )`) + expect(code).toMatch(`a:(count,a)`) + expect(code).toMatch(`;((count) + 1)`) + expect(code).toMatch(`;([count])`) + expect(code).toMatch(`;(a,b)`) + expect(code).toMatch(`log(a)`) + expect(code).toMatch(`count = ( a++ ,b)`) + expect(code).toMatch(`()=>(a++,b)`) + expect(code).toMatch(`_ref(a, (a++,b))`) + expect(code).toMatch(`{ a:(a++,b),b:a }`) + expect(code).toMatch(`switch(c)`) + expect(code).toMatch(`;(h,f)`) + expect(code).toMatch(`((count++,count,(count,a)))`) assertCode(code) }) @@ -358,7 +402,7 @@ test('nested scopes', () => { // inner bar shadowed by function declaration expect(code).toMatch(`bar() // inner bar`) - expect(code).toMatch(`return ({ a, b, c, d })`) + expect(code).toMatch(`return { a, b, c, d }`) assertCode(code) }) @@ -412,7 +456,7 @@ test('macro import alias and removal', () => { // should remove imports expect(code).not.toMatch(`from 'vue/macros'`) expect(code).toMatch(`let a = _ref(1)`) - expect(code).toMatch(`const __$temp_1 = (useMouse())`) + expect(code).toMatch(`const __$temp_1 = useMouse()`) assertCode(code) }) diff --git a/packages/reactivity-transform/src/reactivityTransform.ts b/packages/reactivity-transform/src/reactivityTransform.ts index f35be8b2e1d..ef4cb6a29e4 100644 --- a/packages/reactivity-transform/src/reactivityTransform.ts +++ b/packages/reactivity-transform/src/reactivityTransform.ts @@ -322,7 +322,7 @@ export function transformAST( if (method === convertSymbol) { // $ // remove macro - s.remove(call.callee.start! + offset, call.callee.end! + offset) + unwrapMacro(call) if (id.type === 'Identifier') { // single variable registerRefBinding(id) @@ -594,6 +594,79 @@ export function transformAST( } } + /** + * unwrap the code form the macro($、$$), fix #6312 and keep the desired behavior from RFC#369 + */ + function unwrapMacro(node: CallExpression) { + const bracketStart = node.callee.end! + offset + s.remove(node.callee.start! + offset, bracketStart) + + if (node.arguments.length === 1) { + const bracketEnd = node.arguments.at(-1)!.end! + offset + // remove brackets of macros + const firstArg = node.arguments[0] + const argsType = firstArg.type + if ( + argsType === 'CallExpression' || + argsType === 'Identifier' || + argsType === 'ObjectExpression' + ) { + const leading = firstArg.leadingComments?.at(0)!.start ?? Infinity + const trailing = firstArg.trailingComments?.at(-1)!.end ?? 0 + // fix space & comments place + s.remove( + node.start! + offset, + Math.min(firstArg.start!, leading) + offset + ) + s.remove(Math.max(bracketEnd, trailing + offset), node.end! + offset) + + // avoid traversal of like `$$(a)` + if (argsType === 'Identifier') { + return + } + } else if (argsType === 'SequenceExpression') { + // fix $$((a,b)) + s.remove(bracketStart, bracketStart + 1) + s.remove(bracketEnd, bracketEnd + 1) + } + } + + // avoid invalid traversal for $ + if (!escapeScope || node.extra) { + return + } + + // fix some edge cases for macro $$, eg. $$(a,b) + // resolve nested + if (isIsolateExpression(node)) { + // split the unwrapped code `()()` to `();()` + s.appendLeft(node.callee.start! + offset, ';') + } + } + + function isIsolateExpression(node: CallExpression) { + let i = parentStack.length - 1 + while (i >= 0) { + const curParent = parentStack[i] + if (curParent.type !== 'ExpressionStatement') { + i-- + continue + } + const expression = curParent.expression + if ( + expression.type === 'CallExpression' && + expression.callee.type === 'Identifier' && + expression.callee.name === escapeSymbol && + !expression.arguments.includes(node) && + (i < 1 || parentStack[i - 1].type !== 'LabeledStatement') + ) { + return true + } + i-- + } + return false + } + // check root scope first walkScope(ast, true) ;(walk as any)(ast, { @@ -671,8 +744,8 @@ export function transformAST( currentScope[escapeSymbol] === undefined && callee === escapeSymbol ) { - s.remove(node.callee.start! + offset, node.callee.end! + offset) escapeScope = node + unwrapMacro(node) } // TODO remove when out of experimental