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
 | 
					  walkIdentifiers
 | 
				
			||||||
} from '../babelUtils'
 | 
					} from '../babelUtils'
 | 
				
			||||||
import { advancePositionWithClone, isSimpleIdentifier } from '../utils'
 | 
					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 { createCompilerError, ErrorCodes } from '../errors'
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  Node,
 | 
					  Node,
 | 
				
			||||||
@ -185,17 +191,17 @@ export function processExpression(
 | 
				
			|||||||
      } else if (type === BindingTypes.PROPS) {
 | 
					      } else if (type === BindingTypes.PROPS) {
 | 
				
			||||||
        // use __props which is generated by compileScript so in ts mode
 | 
					        // use __props which is generated by compileScript so in ts mode
 | 
				
			||||||
        // it gets correct type
 | 
					        // it gets correct type
 | 
				
			||||||
        return `__props.${raw}`
 | 
					        return genPropsAccessExp(raw)
 | 
				
			||||||
      } else if (type === BindingTypes.PROPS_ALIASED) {
 | 
					      } else if (type === BindingTypes.PROPS_ALIASED) {
 | 
				
			||||||
        // prop with a different local alias (from defineProps() destructure)
 | 
					        // prop with a different local alias (from defineProps() destructure)
 | 
				
			||||||
        return `__props.${bindingMetadata.__propsAliases![raw]}`
 | 
					        return genPropsAccessExp(bindingMetadata.__propsAliases![raw])
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      if (type && type.startsWith('setup')) {
 | 
					      if (type && type.startsWith('setup')) {
 | 
				
			||||||
        // setup bindings in non-inline mode
 | 
					        // setup bindings in non-inline mode
 | 
				
			||||||
        return `$setup.${raw}`
 | 
					        return `$setup.${raw}`
 | 
				
			||||||
      } else if (type === BindingTypes.PROPS_ALIASED) {
 | 
					      } else if (type === BindingTypes.PROPS_ALIASED) {
 | 
				
			||||||
        return `$props.${bindingMetadata.__propsAliases![raw]}`
 | 
					        return `$props['${bindingMetadata.__propsAliases![raw]}']`
 | 
				
			||||||
      } else if (type) {
 | 
					      } else if (type) {
 | 
				
			||||||
        return `$${type}.${raw}`
 | 
					        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`] = `
 | 
					exports[`sfc props transform rest spread 1`] = `
 | 
				
			||||||
"import { createPropsRestProxy as _createPropsRestProxy } from 'vue'
 | 
					"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', () => {
 | 
					  test('rest spread', () => {
 | 
				
			||||||
    const { content, bindings } = compile(`
 | 
					    const { content, bindings } = compile(`
 | 
				
			||||||
      <script setup>
 | 
					      <script setup>
 | 
				
			||||||
 | 
				
			|||||||
@ -431,7 +431,11 @@ export function compileScript(
 | 
				
			|||||||
                prop.key
 | 
					                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') {
 | 
					            if (prop.value.type === 'AssignmentPattern') {
 | 
				
			||||||
              // default value { foo = 123 }
 | 
					              // default value { foo = 123 }
 | 
				
			||||||
              const { left, right } = prop.value
 | 
					              const { left, right } = prop.value
 | 
				
			||||||
 | 
				
			|||||||
@ -21,7 +21,7 @@ import {
 | 
				
			|||||||
  walkFunctionParams
 | 
					  walkFunctionParams
 | 
				
			||||||
} from '@vue/compiler-core'
 | 
					} from '@vue/compiler-core'
 | 
				
			||||||
import { parse, ParserPlugin } from '@babel/parser'
 | 
					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 CONVERT_SYMBOL = '$'
 | 
				
			||||||
const ESCAPE_SYMBOL = '$$'
 | 
					const ESCAPE_SYMBOL = '$$'
 | 
				
			||||||
@ -489,17 +489,17 @@ export function transformAST(
 | 
				
			|||||||
            if (isProp) {
 | 
					            if (isProp) {
 | 
				
			||||||
              if (escapeScope) {
 | 
					              if (escapeScope) {
 | 
				
			||||||
                // prop binding in $$()
 | 
					                // prop binding in $$()
 | 
				
			||||||
                // { prop } -> { prop: __prop_prop }
 | 
					                // { prop } -> { prop: __props_prop }
 | 
				
			||||||
                registerEscapedPropBinding(id)
 | 
					                registerEscapedPropBinding(id)
 | 
				
			||||||
                s.appendLeft(
 | 
					                s.appendLeft(
 | 
				
			||||||
                  id.end! + offset,
 | 
					                  id.end! + offset,
 | 
				
			||||||
                  `: __props_${propsLocalToPublicMap[id.name]}`
 | 
					                  `: __props_${propsLocalToPublicMap[id.name]}`
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
              } else {
 | 
					              } else {
 | 
				
			||||||
                // { prop } -> { prop: __prop.prop }
 | 
					                // { prop } -> { prop: __props.prop }
 | 
				
			||||||
                s.appendLeft(
 | 
					                s.appendLeft(
 | 
				
			||||||
                  id.end! + offset,
 | 
					                  id.end! + offset,
 | 
				
			||||||
                  `: __props.${propsLocalToPublicMap[id.name]}`
 | 
					                  `: ${genPropsAccessExp(propsLocalToPublicMap[id.name])}`
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
@ -522,7 +522,7 @@ export function transformAST(
 | 
				
			|||||||
              s.overwrite(
 | 
					              s.overwrite(
 | 
				
			||||||
                id.start! + offset,
 | 
					                id.start! + offset,
 | 
				
			||||||
                id.end! + offset,
 | 
					                id.end! + offset,
 | 
				
			||||||
                `__props.${propsLocalToPublicMap[id.name]}`
 | 
					                genPropsAccessExp(propsLocalToPublicMap[id.name])
 | 
				
			||||||
              )
 | 
					              )
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          } else {
 | 
					          } 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