diff --git a/packages/compiler-sfc/__tests__/__snapshots__/compileScriptPropsTransform.spec.ts.snap b/packages/compiler-sfc/__tests__/__snapshots__/compileScriptPropsTransform.spec.ts.snap index 31378a26535..7105dfe3864 100644 --- a/packages/compiler-sfc/__tests__/__snapshots__/compileScriptPropsTransform.spec.ts.snap +++ b/packages/compiler-sfc/__tests__/__snapshots__/compileScriptPropsTransform.spec.ts.snap @@ -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\\" diff --git a/packages/compiler-sfc/__tests__/compileScript.spec.ts b/packages/compiler-sfc/__tests__/compileScript.spec.ts index 85b9f9a41ae..df2d99348ce 100644 --- a/packages/compiler-sfc/__tests__/compileScript.spec.ts +++ b/packages/compiler-sfc/__tests__/compileScript.spec.ts @@ -36,7 +36,7 @@ describe('SFC compile + `) + 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(() => diff --git a/packages/ref-transform/src/refTransform.ts b/packages/ref-transform/src/refTransform.ts index a9972a2a2e9..1acdca74d1c 100644 --- a/packages/ref-transform/src/refTransform.ts +++ b/packages/ref-transform/src/refTransform.ts @@ -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 = {} + 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 + } } })