mirror of https://github.com/vuejs/core.git
fix(compiler-sfc): consistently escape type-only prop names (#8654)
close #8635 close #8910 close vitejs/vite-plugin-vue#184
This commit is contained in:
parent
9e1b74bcd5
commit
3e08d246df
|
@ -46,6 +46,51 @@ export default /*#__PURE__*/_defineComponent({
|
||||||
|
|
||||||
const { foo } = __props
|
const { foo } = __props
|
||||||
|
|
||||||
|
return { }
|
||||||
|
}
|
||||||
|
|
||||||
|
})"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`defineProps > should escape names w/ special symbols 1`] = `
|
||||||
|
"import { defineComponent as _defineComponent } from 'vue'
|
||||||
|
|
||||||
|
export default /*#__PURE__*/_defineComponent({
|
||||||
|
props: {
|
||||||
|
\\"spa ce\\": { type: null, required: true },
|
||||||
|
\\"exclamation!mark\\": { type: null, required: true },
|
||||||
|
\\"double\\\\\\"quote\\": { type: null, required: true },
|
||||||
|
\\"hash#tag\\": { type: null, required: true },
|
||||||
|
\\"dollar$sign\\": { type: null, required: true },
|
||||||
|
\\"percentage%sign\\": { type: null, required: true },
|
||||||
|
\\"amper&sand\\": { type: null, required: true },
|
||||||
|
\\"single'quote\\": { type: null, required: true },
|
||||||
|
\\"round(brack)ets\\": { type: null, required: true },
|
||||||
|
\\"aste*risk\\": { type: null, required: true },
|
||||||
|
\\"pl+us\\": { type: null, required: true },
|
||||||
|
\\"com,ma\\": { type: null, required: true },
|
||||||
|
\\"do.t\\": { type: null, required: true },
|
||||||
|
\\"sla/sh\\": { type: null, required: true },
|
||||||
|
\\"co:lon\\": { type: null, required: true },
|
||||||
|
\\"semi;colon\\": { type: null, required: true },
|
||||||
|
\\"angle<brack>ets\\": { type: null, required: true },
|
||||||
|
\\"equal=sign\\": { type: null, required: true },
|
||||||
|
\\"question?mark\\": { type: null, required: true },
|
||||||
|
\\"at@sign\\": { type: null, required: true },
|
||||||
|
\\"square[brack]ets\\": { type: null, required: true },
|
||||||
|
\\"back\\\\\\\\slash\\": { type: null, required: true },
|
||||||
|
\\"ca^ret\\": { type: null, required: true },
|
||||||
|
\\"back\`tick\\": { type: null, required: true },
|
||||||
|
\\"curly{bra}ces\\": { type: null, required: true },
|
||||||
|
\\"pi|pe\\": { type: null, required: true },
|
||||||
|
\\"til~de\\": { type: null, required: true },
|
||||||
|
\\"da-sh\\": { type: null, required: true }
|
||||||
|
},
|
||||||
|
setup(__props: any, { expose: __expose }) {
|
||||||
|
__expose();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return { }
|
return { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -611,4 +611,103 @@ const props = defineProps({ foo: String })
|
||||||
}).toThrow(`cannot accept both type and non-type arguments`)
|
}).toThrow(`cannot accept both type and non-type arguments`)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('should escape names w/ special symbols', () => {
|
||||||
|
const { content, bindings } = compile(`
|
||||||
|
<script setup lang="ts">
|
||||||
|
defineProps<{
|
||||||
|
'spa ce': unknown
|
||||||
|
'exclamation!mark': unknown
|
||||||
|
'double"quote': unknown
|
||||||
|
'hash#tag': unknown
|
||||||
|
'dollar$sign': unknown
|
||||||
|
'percentage%sign': unknown
|
||||||
|
'amper&sand': unknown
|
||||||
|
"single'quote": unknown
|
||||||
|
'round(brack)ets': unknown
|
||||||
|
'aste*risk': unknown
|
||||||
|
'pl+us': unknown
|
||||||
|
'com,ma': unknown
|
||||||
|
'do.t': unknown
|
||||||
|
'sla/sh': unknown
|
||||||
|
'co:lon': unknown
|
||||||
|
'semi;colon': unknown
|
||||||
|
'angle<brack>ets': unknown
|
||||||
|
'equal=sign': unknown
|
||||||
|
'question?mark': unknown
|
||||||
|
'at@sign': unknown
|
||||||
|
'square[brack]ets': unknown
|
||||||
|
'back\\\\slash': unknown
|
||||||
|
'ca^ret': unknown
|
||||||
|
'back\`tick': unknown
|
||||||
|
'curly{bra}ces': unknown
|
||||||
|
'pi|pe': unknown
|
||||||
|
'til~de': unknown
|
||||||
|
'da-sh': unknown
|
||||||
|
}>()
|
||||||
|
</script>`)
|
||||||
|
assertCode(content)
|
||||||
|
expect(content).toMatch(`"spa ce": { type: null, required: true }`)
|
||||||
|
expect(content).toMatch(
|
||||||
|
`"exclamation!mark": { type: null, required: true }`
|
||||||
|
)
|
||||||
|
expect(content).toMatch(`"double\\"quote": { type: null, required: true }`)
|
||||||
|
expect(content).toMatch(`"hash#tag": { type: null, required: true }`)
|
||||||
|
expect(content).toMatch(`"dollar$sign": { type: null, required: true }`)
|
||||||
|
expect(content).toMatch(`"percentage%sign": { type: null, required: true }`)
|
||||||
|
expect(content).toMatch(`"amper&sand": { type: null, required: true }`)
|
||||||
|
expect(content).toMatch(`"single'quote": { type: null, required: true }`)
|
||||||
|
expect(content).toMatch(`"round(brack)ets": { type: null, required: true }`)
|
||||||
|
expect(content).toMatch(`"aste*risk": { type: null, required: true }`)
|
||||||
|
expect(content).toMatch(`"pl+us": { type: null, required: true }`)
|
||||||
|
expect(content).toMatch(`"com,ma": { type: null, required: true }`)
|
||||||
|
expect(content).toMatch(`"do.t": { type: null, required: true }`)
|
||||||
|
expect(content).toMatch(`"sla/sh": { type: null, required: true }`)
|
||||||
|
expect(content).toMatch(`"co:lon": { type: null, required: true }`)
|
||||||
|
expect(content).toMatch(`"semi;colon": { type: null, required: true }`)
|
||||||
|
expect(content).toMatch(`"angle<brack>ets": { type: null, required: true }`)
|
||||||
|
expect(content).toMatch(`"equal=sign": { type: null, required: true }`)
|
||||||
|
expect(content).toMatch(`"question?mark": { type: null, required: true }`)
|
||||||
|
expect(content).toMatch(`"at@sign": { type: null, required: true }`)
|
||||||
|
expect(content).toMatch(
|
||||||
|
`"square[brack]ets": { type: null, required: true }`
|
||||||
|
)
|
||||||
|
expect(content).toMatch(`"back\\\\slash": { type: null, required: true }`)
|
||||||
|
expect(content).toMatch(`"ca^ret": { type: null, required: true }`)
|
||||||
|
expect(content).toMatch(`"back\`tick": { type: null, required: true }`)
|
||||||
|
expect(content).toMatch(`"curly{bra}ces": { type: null, required: true }`)
|
||||||
|
expect(content).toMatch(`"pi|pe": { type: null, required: true }`)
|
||||||
|
expect(content).toMatch(`"til~de": { type: null, required: true }`)
|
||||||
|
expect(content).toMatch(`"da-sh": { type: null, required: true }`)
|
||||||
|
expect(bindings).toStrictEqual({
|
||||||
|
'spa ce': BindingTypes.PROPS,
|
||||||
|
'exclamation!mark': BindingTypes.PROPS,
|
||||||
|
'double"quote': BindingTypes.PROPS,
|
||||||
|
'hash#tag': BindingTypes.PROPS,
|
||||||
|
dollar$sign: BindingTypes.PROPS,
|
||||||
|
'percentage%sign': BindingTypes.PROPS,
|
||||||
|
'amper&sand': BindingTypes.PROPS,
|
||||||
|
"single'quote": BindingTypes.PROPS,
|
||||||
|
'round(brack)ets': BindingTypes.PROPS,
|
||||||
|
'aste*risk': BindingTypes.PROPS,
|
||||||
|
'pl+us': BindingTypes.PROPS,
|
||||||
|
'com,ma': BindingTypes.PROPS,
|
||||||
|
'do.t': BindingTypes.PROPS,
|
||||||
|
'sla/sh': BindingTypes.PROPS,
|
||||||
|
'co:lon': BindingTypes.PROPS,
|
||||||
|
'semi;colon': BindingTypes.PROPS,
|
||||||
|
'angle<brack>ets': BindingTypes.PROPS,
|
||||||
|
'equal=sign': BindingTypes.PROPS,
|
||||||
|
'question?mark': BindingTypes.PROPS,
|
||||||
|
'at@sign': BindingTypes.PROPS,
|
||||||
|
'square[brack]ets': BindingTypes.PROPS,
|
||||||
|
'back\\slash': BindingTypes.PROPS,
|
||||||
|
'ca^ret': BindingTypes.PROPS,
|
||||||
|
'back`tick': BindingTypes.PROPS,
|
||||||
|
'curly{bra}ces': BindingTypes.PROPS,
|
||||||
|
'pi|pe': BindingTypes.PROPS,
|
||||||
|
'til~de': BindingTypes.PROPS,
|
||||||
|
'da-sh': BindingTypes.PROPS
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -17,7 +17,7 @@ import {
|
||||||
isCallOf,
|
isCallOf,
|
||||||
unwrapTSNode,
|
unwrapTSNode,
|
||||||
toRuntimeTypeString,
|
toRuntimeTypeString,
|
||||||
getEscapedKey
|
getEscapedPropName
|
||||||
} from './utils'
|
} from './utils'
|
||||||
import { genModelProps } from './defineModel'
|
import { genModelProps } from './defineModel'
|
||||||
import { getObjectOrArrayExpressionKeys } from './analyzeScriptBindings'
|
import { getObjectOrArrayExpressionKeys } from './analyzeScriptBindings'
|
||||||
|
@ -135,7 +135,7 @@ 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)
|
const finalKey = getEscapedPropName(key)
|
||||||
if (d)
|
if (d)
|
||||||
defaults.push(
|
defaults.push(
|
||||||
`${finalKey}: ${d.valueString}${
|
`${finalKey}: ${d.valueString}${
|
||||||
|
@ -251,7 +251,7 @@ function genRuntimePropFromType(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const finalKey = getEscapedKey(key)
|
const finalKey = getEscapedPropName(key)
|
||||||
if (!ctx.options.isProd) {
|
if (!ctx.options.isProd) {
|
||||||
return `${finalKey}: { ${concatStrings([
|
return `${finalKey}: { ${concatStrings([
|
||||||
`type: ${toRuntimeTypeString(type)}`,
|
`type: ${toRuntimeTypeString(type)}`,
|
||||||
|
|
|
@ -113,8 +113,14 @@ export const joinPaths = (path.posix || path).join
|
||||||
* key may contain symbols
|
* key may contain symbols
|
||||||
* e.g. onUpdate:modelValue -> "onUpdate:modelValue"
|
* e.g. onUpdate:modelValue -> "onUpdate:modelValue"
|
||||||
*/
|
*/
|
||||||
export const escapeSymbolsRE = /[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g
|
export const propNameEscapeSymbolsRE = /[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~\-]/
|
||||||
|
|
||||||
export function getEscapedKey(key: string) {
|
export function getEscapedPropName(key: string) {
|
||||||
return escapeSymbolsRE.test(key) ? JSON.stringify(key) : key
|
return propNameEscapeSymbolsRE.test(key) ? JSON.stringify(key) : key
|
||||||
|
}
|
||||||
|
|
||||||
|
export const cssVarNameEscapeSymbolsRE = /[ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~]/g
|
||||||
|
|
||||||
|
export function getEscapedCssVarName(key: string) {
|
||||||
|
return key.replace(cssVarNameEscapeSymbolsRE, s => `\\${s}`)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +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/utils'
|
import { getEscapedCssVarName } from '../script/utils'
|
||||||
import { PluginCreator } from 'postcss'
|
import { PluginCreator } from 'postcss'
|
||||||
import hash from 'hash-sum'
|
import hash from 'hash-sum'
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ function genVarName(id: string, raw: string, isProd: boolean): string {
|
||||||
return hash(id + raw)
|
return hash(id + raw)
|
||||||
} else {
|
} else {
|
||||||
// escape ASCII Punctuation & Symbols
|
// escape ASCII Punctuation & Symbols
|
||||||
return `${id}-${raw.replace(escapeSymbolsRE, s => `\\${s}`)}`
|
return `${id}-${getEscapedCssVarName(raw)}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue