]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(reactivity-transform): $$() escape for destructured prop bindings
authorEvan You <yyx990803@gmail.com>
Sat, 11 Dec 2021 09:50:09 +0000 (17:50 +0800)
committerEvan You <yyx990803@gmail.com>
Sat, 11 Dec 2021 09:50:09 +0000 (17:50 +0800)
packages/compiler-sfc/__tests__/__snapshots__/compileScriptPropsTransform.spec.ts.snap
packages/compiler-sfc/__tests__/compileScript.spec.ts
packages/compiler-sfc/__tests__/compileScriptPropsTransform.spec.ts
packages/ref-transform/src/refTransform.ts

index 31378a265356771b99081d20784040f6f2120071..7105dfe38643b52acd9d9213b4c3ff03f05ced96 100644 (file)
@@ -1,5 +1,25 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
+exports[`sfc props transform $$() escape 1`] = `
+"import { toRef as _toRef } from 'vue'
+
+export default {
+  props: ['foo'],
+  setup(__props) {
+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 })
+      
+return () => {}
+}
+
+}"
+`;
+
 exports[`sfc props transform aliasing 1`] = `
 "import { toDisplayString as _toDisplayString } from \\"vue\\"
 
index 85b9f9a41ae3eb1e10c232abbbd1f5d1ba7d675c..df2d99348ce6c1089ce0b04102aac629467b2012 100644 (file)
@@ -36,7 +36,7 @@ describe('SFC compile <script setup>', () => {
     assertCode(content)
   })
 
-  test('binding analysis for destructur', () => {
+  test('binding analysis for destructure', () => {
     const { content, bindings } = compile(`
       <script setup>
       const { foo, b: bar, ['x' + 'y']: baz, x: { y, zz: { z }}} = {}
index b71495d726c2fc4ecb29b790ba81bfac812aaf9f..08a97316dcfe452285bc5a089ea74e75f20d4d94 100644 (file)
@@ -145,6 +145,23 @@ describe('sfc props transform', () => {
     })
   })
 
+  test('$$() escape', () => {
+    const { content } = compile(`
+      <script setup>
+      const { foo, bar: baz } = defineProps(['foo'])
+      console.log($$(foo))
+      console.log($$(baz))
+      $$({ foo, baz })
+      </script>
+    `)
+    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 })`)
+    assertCode(content)
+  })
+
   describe('errors', () => {
     test('should error on deep destructure', () => {
       expect(() =>
index a9972a2a2e9863f09fd1d777ee67aa5e385f5b61..1acdca74d1ca97c2da36bb62c6444ec08ae73a0a 100644 (file)
@@ -23,7 +23,7 @@ import { parse, ParserPlugin } from '@babel/parser'
 import { hasOwn, isArray, isString } from '@vue/shared'
 
 const TO_VAR_SYMBOL = '$'
-const TO_REF_SYMBOL = '$$'
+const ESCAPE_SYMBOL = '$$'
 const shorthands = ['ref', 'computed', 'shallowRef', 'toRef', 'customRef']
 const transformCheckRE = /[^\w]\$(?:\$|ref|computed|shallowRef)?\s*(\(|\<)/
 
@@ -420,30 +420,52 @@ export function transformAST(
         const isProp = bindingType === 'prop'
         if (isStaticProperty(parent) && parent.shorthand) {
           // let binding used in a property shorthand
-          // { foo } -> { foo: foo.value }
-          // { prop } -> { prop: __prop.prop }
           // skip for destructure patterns
           if (
             !(parent as any).inPattern ||
             isInDestructureAssignment(parent, parentStack)
           ) {
             if (isProp) {
-              s.appendLeft(
-                id.end! + offset,
-                `: __props.${propsLocalToPublicMap[id.name]}`
-              )
+              if (escapeScope) {
+                // prop binding in $$()
+                // { prop } -> { prop: __prop_prop }
+                registerEscapedPropBinding(id)
+                s.appendLeft(
+                  id.end! + offset,
+                  `: __props_${propsLocalToPublicMap[id.name]}`
+                )
+              } else {
+                // { prop } -> { prop: __prop.prop }
+                s.appendLeft(
+                  id.end! + offset,
+                  `: __props.${propsLocalToPublicMap[id.name]}`
+                )
+              }
             } else {
+              // { foo } -> { foo: foo.value }
               s.appendLeft(id.end! + offset, `: ${id.name}.value`)
             }
           }
         } else {
           if (isProp) {
-            s.overwrite(
-              id.start! + offset,
-              id.end! + offset,
-              `__props.${propsLocalToPublicMap[id.name]}`
-            )
+            if (escapeScope) {
+              // x --> __props_x
+              registerEscapedPropBinding(id)
+              s.overwrite(
+                id.start! + offset,
+                id.end! + offset,
+                `__props_${propsLocalToPublicMap[id.name]}`
+              )
+            } else {
+              // x --> __props.x
+              s.overwrite(
+                id.start! + offset,
+                id.end! + offset,
+                `__props.${propsLocalToPublicMap[id.name]}`
+              )
+            }
           } else {
+            // x --> x.value
             s.appendLeft(id.end! + offset, '.value')
           }
         }
@@ -453,8 +475,25 @@ export function transformAST(
     return false
   }
 
+  const propBindingRefs: Record<string, true> = {}
+  function registerEscapedPropBinding(id: Identifier) {
+    if (!propBindingRefs.hasOwnProperty(id.name)) {
+      propBindingRefs[id.name] = true
+      const publicKey = propsLocalToPublicMap[id.name]
+      s.prependRight(
+        offset,
+        `const __props_${publicKey} = ${helper(
+          `toRef`
+        )}(__props, '${publicKey}')\n`
+      )
+    }
+  }
+
   // check root scope first
   walkScope(ast, true)
+
+  // inside $$()
+  let escapeScope: CallExpression | undefined
   ;(walk as any)(ast, {
     enter(node: Node, parent?: Node) {
       parent && parentStack.push(parent)
@@ -488,6 +527,8 @@ export function transformAST(
 
       if (
         node.type === 'Identifier' &&
+        // if inside $$(), skip unless this is a destructured prop binding
+        !(escapeScope && rootScope[node.name] !== 'prop') &&
         isReferencedIdentifier(node, parent!, parentStack) &&
         !excludedIds.has(node)
       ) {
@@ -512,9 +553,9 @@ export function transformAST(
           )
         }
 
-        if (callee === TO_REF_SYMBOL) {
+        if (callee === ESCAPE_SYMBOL) {
           s.remove(node.callee.start! + offset, node.callee.end! + offset)
-          return this.skip()
+          escapeScope = node
         }
 
         // TODO remove when out of experimental
@@ -543,6 +584,9 @@ export function transformAST(
         scopeStack.pop()
         currentScope = scopeStack[scopeStack.length - 1] || null
       }
+      if (node === escapeScope) {
+        escapeScope = undefined
+      }
     }
   })