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…
Reference in New Issue
Block a user