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,
} 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}`
}
}"
`;
+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'
})
})
+ // #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>
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
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 = '$$'
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 {
s.overwrite(
id.start! + offset,
id.end! + offset,
- `__props.${propsLocalToPublicMap[id.name]}`
+ genPropsAccessExp(propsLocalToPublicMap[id.name])
)
}
} else {
: {})
)
}
+
+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)}]`
+}