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`] = `
|
exports[`sfc reactive props destructure > default values w/ type declaration 1`] = `
|
||||||
"import { defineComponent as _defineComponent } from 'vue'
|
"import { defineComponent as _defineComponent } from 'vue'
|
||||||
|
|
||||||
|
|
|
@ -106,6 +106,28 @@ describe('sfc reactive props destructure', () => {
|
||||||
})`)
|
})`)
|
||||||
assertCode(content)
|
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', () => {
|
test('default values w/ type declaration', () => {
|
||||||
const { content } = compile(`
|
const { content } = compile(`
|
||||||
|
@ -123,6 +145,37 @@ describe('sfc reactive props destructure', () => {
|
||||||
assertCode(content)
|
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', () => {
|
test('default values w/ type declaration, prod mode', () => {
|
||||||
const { content } = compile(
|
const { content } = compile(
|
||||||
`
|
`
|
||||||
|
|
|
@ -133,10 +133,11 @@ export function genRuntimeProps(ctx: ScriptCompileContext): string | undefined {
|
||||||
const defaults: string[] = []
|
const defaults: string[] = []
|
||||||
for (const key in ctx.propsDestructuredBindings) {
|
for (const key in ctx.propsDestructuredBindings) {
|
||||||
const d = genDestructuredDefaultValue(ctx, key)
|
const d = genDestructuredDefaultValue(ctx, key)
|
||||||
|
const finalKey = getEscapedKey(key)
|
||||||
if (d)
|
if (d)
|
||||||
defaults.push(
|
defaults.push(
|
||||||
`${key}: ${d.valueString}${
|
`${finalKey}: ${d.valueString}${
|
||||||
d.needSkipFactory ? `, __skip_${key}: true` : ``
|
d.needSkipFactory ? `, __skip_${finalKey}: true` : ``
|
||||||
}`
|
}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -248,8 +249,9 @@ function genRuntimePropFromType(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const finalKey = getEscapedKey(key)
|
||||||
if (!ctx.options.isProd) {
|
if (!ctx.options.isProd) {
|
||||||
return `${key}: { ${concatStrings([
|
return `${finalKey}: { ${concatStrings([
|
||||||
`type: ${toRuntimeTypeString(type)}`,
|
`type: ${toRuntimeTypeString(type)}`,
|
||||||
`required: ${required}`,
|
`required: ${required}`,
|
||||||
skipCheck && 'skipCheck: true',
|
skipCheck && 'skipCheck: true',
|
||||||
|
@ -265,13 +267,13 @@ function genRuntimePropFromType(
|
||||||
// #4783 for boolean, should keep the type
|
// #4783 for boolean, should keep the type
|
||||||
// #7111 for function, if default value exists or it's not static, should keep it
|
// #7111 for function, if default value exists or it's not static, should keep it
|
||||||
// in production
|
// in production
|
||||||
return `${key}: { ${concatStrings([
|
return `${finalKey}: { ${concatStrings([
|
||||||
`type: ${toRuntimeTypeString(type)}`,
|
`type: ${toRuntimeTypeString(type)}`,
|
||||||
defaultString
|
defaultString
|
||||||
])} }`
|
])} }`
|
||||||
} else {
|
} else {
|
||||||
// production: checks are useless
|
// production: checks are useless
|
||||||
return `${key}: ${defaultString ? `{ ${defaultString} }` : `{}`}`
|
return `${finalKey}: ${defaultString ? `{ ${defaultString} }` : `{}`}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -362,3 +364,14 @@ function inferValueType(node: Node): string | undefined {
|
||||||
return 'Function'
|
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
|
BindingMetadata
|
||||||
} from '@vue/compiler-dom'
|
} from '@vue/compiler-dom'
|
||||||
import { SFCDescriptor } from '../parse'
|
import { SFCDescriptor } from '../parse'
|
||||||
|
import { escapeSymbolsRE } from '../script/defineProps'
|
||||||
import { PluginCreator } from 'postcss'
|
import { PluginCreator } from 'postcss'
|
||||||
import hash from 'hash-sum'
|
import hash from 'hash-sum'
|
||||||
|
|
||||||
|
@ -32,7 +33,7 @@ function genVarName(id: string, raw: string, isProd: boolean): string {
|
||||||
} else {
|
} else {
|
||||||
// escape ASCII Punctuation & Symbols
|
// escape ASCII Punctuation & Symbols
|
||||||
return `${id}-${raw.replace(
|
return `${id}-${raw.replace(
|
||||||
/[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g,
|
escapeSymbolsRE,
|
||||||
s => `\\${s}`
|
s => `\\${s}`
|
||||||
)}`
|
)}`
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue