fix(reactivity-transform): fix props access codegen for non-identifier prop names (#5436)
fix #5425
This commit is contained in:
		
							parent
							
								
									0c07f12541
								
							
						
					
					
						commit
						242914d938
					
				@ -24,7 +24,13 @@ import {
 | 
			
		||||
  walkIdentifiers
 | 
			
		||||
} from '../babelUtils'
 | 
			
		||||
import { advancePositionWithClone, isSimpleIdentifier } from '../utils'
 | 
			
		||||
import { isGloballyWhitelisted, makeMap, hasOwn, isString } from '@vue/shared'
 | 
			
		||||
import {
 | 
			
		||||
  isGloballyWhitelisted,
 | 
			
		||||
  makeMap,
 | 
			
		||||
  hasOwn,
 | 
			
		||||
  isString,
 | 
			
		||||
  genPropsAccessExp
 | 
			
		||||
} from '@vue/shared'
 | 
			
		||||
import { createCompilerError, ErrorCodes } from '../errors'
 | 
			
		||||
import {
 | 
			
		||||
  Node,
 | 
			
		||||
@ -185,17 +191,17 @@ export function processExpression(
 | 
			
		||||
      } else if (type === BindingTypes.PROPS) {
 | 
			
		||||
        // use __props which is generated by compileScript so in ts mode
 | 
			
		||||
        // it gets correct type
 | 
			
		||||
        return `__props.${raw}`
 | 
			
		||||
        return genPropsAccessExp(raw)
 | 
			
		||||
      } else if (type === BindingTypes.PROPS_ALIASED) {
 | 
			
		||||
        // prop with a different local alias (from defineProps() destructure)
 | 
			
		||||
        return `__props.${bindingMetadata.__propsAliases![raw]}`
 | 
			
		||||
        return genPropsAccessExp(bindingMetadata.__propsAliases![raw])
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      if (type && type.startsWith('setup')) {
 | 
			
		||||
        // setup bindings in non-inline mode
 | 
			
		||||
        return `$setup.${raw}`
 | 
			
		||||
      } else if (type === BindingTypes.PROPS_ALIASED) {
 | 
			
		||||
        return `$props.${bindingMetadata.__propsAliases![raw]}`
 | 
			
		||||
        return `$props['${bindingMetadata.__propsAliases![raw]}']`
 | 
			
		||||
      } else if (type) {
 | 
			
		||||
        return `$${type}.${raw}`
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
@ -134,6 +134,25 @@ return () => {}
 | 
			
		||||
}"
 | 
			
		||||
`;
 | 
			
		||||
 | 
			
		||||
exports[`sfc props transform non-identifier prop names 1`] = `
 | 
			
		||||
"import { toDisplayString as _toDisplayString } from \\"vue\\"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  props: { 'foo.bar': Function },
 | 
			
		||||
  setup(__props) {
 | 
			
		||||
 | 
			
		||||
      
 | 
			
		||||
      let x = __props[\\"foo.bar\\"]
 | 
			
		||||
      
 | 
			
		||||
return (_ctx, _cache) => {
 | 
			
		||||
  return _toDisplayString(__props[\\"foo.bar\\"])
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}"
 | 
			
		||||
`;
 | 
			
		||||
 | 
			
		||||
exports[`sfc props transform rest spread 1`] = `
 | 
			
		||||
"import { createPropsRestProxy as _createPropsRestProxy } from 'vue'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -127,6 +127,28 @@ describe('sfc props transform', () => {
 | 
			
		||||
    })
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  // #5425
 | 
			
		||||
  test('non-identifier prop names', () => {
 | 
			
		||||
    const { content, bindings } = compile(`
 | 
			
		||||
      <script setup>
 | 
			
		||||
      const { 'foo.bar': fooBar } = defineProps({ 'foo.bar': Function })
 | 
			
		||||
      let x = fooBar
 | 
			
		||||
      </script>
 | 
			
		||||
      <template>{{ fooBar }}</template>
 | 
			
		||||
    `)
 | 
			
		||||
    expect(content).toMatch(`x = __props["foo.bar"]`)
 | 
			
		||||
    expect(content).toMatch(`toDisplayString(__props["foo.bar"])`)
 | 
			
		||||
    assertCode(content)
 | 
			
		||||
    expect(bindings).toStrictEqual({
 | 
			
		||||
      x: BindingTypes.SETUP_LET,
 | 
			
		||||
      'foo.bar': BindingTypes.PROPS,
 | 
			
		||||
      fooBar: BindingTypes.PROPS_ALIASED,
 | 
			
		||||
      __propsAliases: {
 | 
			
		||||
        fooBar: 'foo.bar'
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  test('rest spread', () => {
 | 
			
		||||
    const { content, bindings } = compile(`
 | 
			
		||||
      <script setup>
 | 
			
		||||
 | 
			
		||||
@ -431,7 +431,11 @@ export function compileScript(
 | 
			
		||||
                prop.key
 | 
			
		||||
              )
 | 
			
		||||
            }
 | 
			
		||||
            const propKey = (prop.key as Identifier).name
 | 
			
		||||
            
 | 
			
		||||
            const propKey = prop.key.type === 'StringLiteral'
 | 
			
		||||
              ? prop.key.value
 | 
			
		||||
              : (prop.key as Identifier).name
 | 
			
		||||
            
 | 
			
		||||
            if (prop.value.type === 'AssignmentPattern') {
 | 
			
		||||
              // default value { foo = 123 }
 | 
			
		||||
              const { left, right } = prop.value
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,7 @@ import {
 | 
			
		||||
  walkFunctionParams
 | 
			
		||||
} from '@vue/compiler-core'
 | 
			
		||||
import { parse, ParserPlugin } from '@babel/parser'
 | 
			
		||||
import { hasOwn, isArray, isString } from '@vue/shared'
 | 
			
		||||
import { hasOwn, isArray, isString, genPropsAccessExp } from '@vue/shared'
 | 
			
		||||
 | 
			
		||||
const CONVERT_SYMBOL = '$'
 | 
			
		||||
const ESCAPE_SYMBOL = '$$'
 | 
			
		||||
@ -489,17 +489,17 @@ export function transformAST(
 | 
			
		||||
            if (isProp) {
 | 
			
		||||
              if (escapeScope) {
 | 
			
		||||
                // prop binding in $$()
 | 
			
		||||
                // { prop } -> { prop: __prop_prop }
 | 
			
		||||
                // { prop } -> { prop: __props_prop }
 | 
			
		||||
                registerEscapedPropBinding(id)
 | 
			
		||||
                s.appendLeft(
 | 
			
		||||
                  id.end! + offset,
 | 
			
		||||
                  `: __props_${propsLocalToPublicMap[id.name]}`
 | 
			
		||||
                )
 | 
			
		||||
              } else {
 | 
			
		||||
                // { prop } -> { prop: __prop.prop }
 | 
			
		||||
                // { prop } -> { prop: __props.prop }
 | 
			
		||||
                s.appendLeft(
 | 
			
		||||
                  id.end! + offset,
 | 
			
		||||
                  `: __props.${propsLocalToPublicMap[id.name]}`
 | 
			
		||||
                  `: ${genPropsAccessExp(propsLocalToPublicMap[id.name])}`
 | 
			
		||||
                )
 | 
			
		||||
              }
 | 
			
		||||
            } else {
 | 
			
		||||
@ -522,7 +522,7 @@ export function transformAST(
 | 
			
		||||
              s.overwrite(
 | 
			
		||||
                id.start! + offset,
 | 
			
		||||
                id.end! + offset,
 | 
			
		||||
                `__props.${propsLocalToPublicMap[id.name]}`
 | 
			
		||||
                genPropsAccessExp(propsLocalToPublicMap[id.name])
 | 
			
		||||
              )
 | 
			
		||||
            }
 | 
			
		||||
          } else {
 | 
			
		||||
 | 
			
		||||
@ -171,3 +171,11 @@ export const getGlobalThis = (): any => {
 | 
			
		||||
        : {})
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const identRE = /^[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*$/
 | 
			
		||||
 | 
			
		||||
export function genPropsAccessExp(name: string) {
 | 
			
		||||
  return identRE.test(name)
 | 
			
		||||
    ? `__props.${name}`
 | 
			
		||||
    : `__props[${JSON.stringify(name)}]`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user