mirror of https://github.com/vuejs/core.git
fix(compiler-sfc): handle prop keys that need escaping (#7803)
close #8291
This commit is contained in:
parent
aa1e77d532
commit
690ef29635
|
@ -97,6 +97,44 @@ return () => {}
|
|||
}"
|
||||
`;
|
||||
|
||||
exports[`sfc reactive props destructure > default values w/ runtime declaration & key is string 1`] = `
|
||||
"import { mergeDefaults as _mergeDefaults } from 'vue'
|
||||
|
||||
export default {
|
||||
props: _mergeDefaults(['foo', 'foo:bar'], {
|
||||
foo: 1,
|
||||
\\"foo:bar\\": 'foo-bar'
|
||||
}),
|
||||
setup(__props) {
|
||||
|
||||
|
||||
|
||||
return () => {}
|
||||
}
|
||||
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`sfc reactive props destructure > default values w/ type declaration & key is string 1`] = `
|
||||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
|
||||
export default /*#__PURE__*/_defineComponent({
|
||||
props: {
|
||||
foo: { type: Number, required: true, default: 1 },
|
||||
bar: { type: Number, required: true, default: 2 },
|
||||
\\"foo:bar\\": { type: String, required: true, default: 'foo-bar' },
|
||||
\\"onUpdate:modelValue\\": { type: Function, required: true }
|
||||
},
|
||||
setup(__props: any) {
|
||||
|
||||
|
||||
|
||||
return () => {}
|
||||
}
|
||||
|
||||
})"
|
||||
`;
|
||||
|
||||
exports[`sfc reactive props destructure > default values w/ type declaration 1`] = `
|
||||
"import { defineComponent as _defineComponent } from 'vue'
|
||||
|
||||
|
|
|
@ -106,6 +106,28 @@ describe('sfc reactive props destructure', () => {
|
|||
})`)
|
||||
assertCode(content)
|
||||
})
|
||||
test('default values w/ runtime declaration & key is string', () => {
|
||||
const { content, bindings } = compile(`
|
||||
<script setup>
|
||||
const { foo = 1, 'foo:bar': fooBar = 'foo-bar' } = defineProps(['foo', 'foo:bar'])
|
||||
</script>
|
||||
`)
|
||||
expect(bindings).toStrictEqual({
|
||||
__propsAliases: {
|
||||
fooBar: 'foo:bar'
|
||||
},
|
||||
foo: BindingTypes.PROPS,
|
||||
'foo:bar': BindingTypes.PROPS,
|
||||
fooBar: BindingTypes.PROPS_ALIASED
|
||||
})
|
||||
|
||||
expect(content).toMatch(`
|
||||
props: _mergeDefaults(['foo', 'foo:bar'], {
|
||||
foo: 1,
|
||||
"foo:bar": 'foo-bar'
|
||||
}),`)
|
||||
assertCode(content)
|
||||
})
|
||||
|
||||
test('default values w/ type declaration', () => {
|
||||
const { content } = compile(`
|
||||
|
@ -123,6 +145,37 @@ describe('sfc reactive props destructure', () => {
|
|||
assertCode(content)
|
||||
})
|
||||
|
||||
test('default values w/ type declaration & key is string', () => {
|
||||
const { content, bindings } = compile(`
|
||||
<script setup lang="ts">
|
||||
const { foo = 1, bar = 2, 'foo:bar': fooBar = 'foo-bar' } = defineProps<{
|
||||
"foo": number // double-quoted string
|
||||
'bar': number // single-quoted string
|
||||
'foo:bar': string // single-quoted string containing symbols
|
||||
"onUpdate:modelValue": (val: number) => void // double-quoted string containing symbols
|
||||
}>()
|
||||
</script>
|
||||
`)
|
||||
expect(bindings).toStrictEqual({
|
||||
__propsAliases: {
|
||||
fooBar: 'foo:bar'
|
||||
},
|
||||
foo: BindingTypes.PROPS,
|
||||
bar: BindingTypes.PROPS,
|
||||
'foo:bar': BindingTypes.PROPS,
|
||||
fooBar: BindingTypes.PROPS_ALIASED,
|
||||
'onUpdate:modelValue': BindingTypes.PROPS
|
||||
})
|
||||
expect(content).toMatch(`
|
||||
props: {
|
||||
foo: { type: Number, required: true, default: 1 },
|
||||
bar: { type: Number, required: true, default: 2 },
|
||||
"foo:bar": { type: String, required: true, default: 'foo-bar' },
|
||||
"onUpdate:modelValue": { type: Function, required: true }
|
||||
},`)
|
||||
assertCode(content)
|
||||
})
|
||||
|
||||
test('default values w/ type declaration, prod mode', () => {
|
||||
const { content } = compile(
|
||||
`
|
||||
|
|
|
@ -133,10 +133,11 @@ export function genRuntimeProps(ctx: ScriptCompileContext): string | undefined {
|
|||
const defaults: string[] = []
|
||||
for (const key in ctx.propsDestructuredBindings) {
|
||||
const d = genDestructuredDefaultValue(ctx, key)
|
||||
const finalKey = getEscapedKey(key)
|
||||
if (d)
|
||||
defaults.push(
|
||||
`${key}: ${d.valueString}${
|
||||
d.needSkipFactory ? `, __skip_${key}: true` : ``
|
||||
`${finalKey}: ${d.valueString}${
|
||||
d.needSkipFactory ? `, __skip_${finalKey}: true` : ``
|
||||
}`
|
||||
)
|
||||
}
|
||||
|
@ -248,8 +249,9 @@ function genRuntimePropFromType(
|
|||
}
|
||||
}
|
||||
|
||||
const finalKey = getEscapedKey(key)
|
||||
if (!ctx.options.isProd) {
|
||||
return `${key}: { ${concatStrings([
|
||||
return `${finalKey}: { ${concatStrings([
|
||||
`type: ${toRuntimeTypeString(type)}`,
|
||||
`required: ${required}`,
|
||||
skipCheck && 'skipCheck: true',
|
||||
|
@ -265,13 +267,13 @@ function genRuntimePropFromType(
|
|||
// #4783 for boolean, should keep the type
|
||||
// #7111 for function, if default value exists or it's not static, should keep it
|
||||
// in production
|
||||
return `${key}: { ${concatStrings([
|
||||
return `${finalKey}: { ${concatStrings([
|
||||
`type: ${toRuntimeTypeString(type)}`,
|
||||
defaultString
|
||||
])} }`
|
||||
} else {
|
||||
// production: checks are useless
|
||||
return `${key}: ${defaultString ? `{ ${defaultString} }` : `{}`}`
|
||||
return `${finalKey}: ${defaultString ? `{ ${defaultString} }` : `{}`}`
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -362,3 +364,14 @@ function inferValueType(node: Node): string | undefined {
|
|||
return 'Function'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* key may contain symbols
|
||||
* e.g. onUpdate:modelValue -> "onUpdate:modelValue"
|
||||
*/
|
||||
export const escapeSymbolsRE = /[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g
|
||||
function getEscapedKey(key: string) {
|
||||
return escapeSymbolsRE.test(key)
|
||||
? JSON.stringify(key)
|
||||
: key
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
BindingMetadata
|
||||
} from '@vue/compiler-dom'
|
||||
import { SFCDescriptor } from '../parse'
|
||||
import { escapeSymbolsRE } from '../script/defineProps'
|
||||
import { PluginCreator } from 'postcss'
|
||||
import hash from 'hash-sum'
|
||||
|
||||
|
@ -32,7 +33,7 @@ function genVarName(id: string, raw: string, isProd: boolean): string {
|
|||
} else {
|
||||
// escape ASCII Punctuation & Symbols
|
||||
return `${id}-${raw.replace(
|
||||
/[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g,
|
||||
escapeSymbolsRE,
|
||||
s => `\\${s}`
|
||||
)}`
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue