fix(compiler-sfc): resolve computed object key (#6963)

This commit is contained in:
三咲智子 Kevin Deng 2022-11-09 10:57:40 +08:00 committed by GitHub
parent 7663a79a29
commit 910fa7677f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 79 additions and 28 deletions

View File

@ -1743,12 +1743,13 @@ export default /*#__PURE__*/_defineComponent({
foo: { type: String, required: false, default: 'hi' },
bar: { type: Number, required: false },
baz: { type: Boolean, required: true },
qux: { type: Function, required: false, default() { return 1 } }
qux: { type: Function, required: false, default() { return 1 } },
quux: { type: Function, required: false, default() { } }
},
setup(__props: any, { expose }) {
expose();
const props = __props as { foo: string, bar?: number, baz: boolean, qux(): number };
const props = __props as { foo: string, bar?: number, baz: boolean, qux(): number, quux(): void };

View File

@ -59,6 +59,25 @@ return (_ctx, _cache) => {
}"
`;
exports[`sfc props transform computed static key 1`] = `
"import { toDisplayString as _toDisplayString } from \\"vue\\"
export default {
props: ['foo'],
setup(__props) {
console.log(__props.foo)
return (_ctx, _cache) => {
return _toDisplayString(__props.foo)
}
}
}"
`;
exports[`sfc props transform default values w/ runtime declaration 1`] = `
"import { mergeDefaults as _mergeDefaults } from 'vue'

View File

@ -1039,10 +1039,12 @@ const emit = defineEmits(['a', 'b'])
foo?: string
bar?: number;
baz: boolean;
qux?(): number
qux?(): number;
quux?(): void
}>(), {
foo: 'hi',
qux() { return 1 }
qux() { return 1 },
['quux']() { }
})
</script>
`)
@ -1056,7 +1058,10 @@ const emit = defineEmits(['a', 'b'])
`qux: { type: Function, required: false, default() { return 1 } }`
)
expect(content).toMatch(
`{ foo: string, bar?: number, baz: boolean, qux(): number }`
`quux: { type: Function, required: false, default() { } }`
)
expect(content).toMatch(
`{ foo: string, bar?: number, baz: boolean, qux(): number, quux(): void }`
)
expect(content).toMatch(`const props = __props`)
expect(bindings).toStrictEqual({
@ -1064,6 +1069,7 @@ const emit = defineEmits(['a', 'b'])
bar: BindingTypes.PROPS,
baz: BindingTypes.PROPS,
qux: BindingTypes.PROPS,
quux: BindingTypes.PROPS,
props: BindingTypes.SETUP_CONST
})
})

View File

@ -184,6 +184,24 @@ describe('sfc props transform', () => {
assertCode(content)
})
// #6960
test('computed static key', () => {
const { content, bindings } = compile(`
<script setup>
const { ['foo']: foo } = defineProps(['foo'])
console.log(foo)
</script>
<template>{{ foo }}</template>
`)
expect(content).not.toMatch(`const { foo } =`)
expect(content).toMatch(`console.log(__props.foo)`)
expect(content).toMatch(`_toDisplayString(__props.foo)`)
assertCode(content)
expect(bindings).toStrictEqual({
foo: BindingTypes.PROPS
})
})
describe('errors', () => {
test('should error on deep destructure', () => {
expect(() =>

View File

@ -447,18 +447,15 @@ export function compileScript(
// props destructure - handle compilation sugar
for (const prop of declId.properties) {
if (prop.type === 'ObjectProperty') {
if (prop.computed) {
const propKey = resolveObjectKey(prop.key, prop.computed)
if (!propKey) {
error(
`${DEFINE_PROPS}() destructure cannot use computed key.`,
prop.key
)
}
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
@ -774,7 +771,8 @@ export function compileScript(
propsRuntimeDefaults.type === 'ObjectExpression' &&
propsRuntimeDefaults.properties.every(
node =>
(node.type === 'ObjectProperty' && !node.computed) ||
(node.type === 'ObjectProperty' &&
(!node.computed || node.key.type.endsWith('Literal'))) ||
node.type === 'ObjectMethod'
)
)
@ -795,9 +793,10 @@ export function compileScript(
if (destructured) {
defaultString = `default: ${destructured}`
} else if (hasStaticDefaults) {
const prop = propsRuntimeDefaults!.properties.find(
(node: any) => node.key.name === key
) as ObjectProperty | ObjectMethod
const prop = propsRuntimeDefaults!.properties.find(node => {
if (node.type === 'SpreadElement') return false
return resolveObjectKey(node.key, node.computed) === key
}) as ObjectProperty | ObjectMethod
if (prop) {
if (prop.type === 'ObjectProperty') {
// prop has corresponding static default value
@ -874,9 +873,13 @@ export function compileScript(
m.key.type === 'Identifier'
) {
if (
propsRuntimeDefaults!.properties.some(
(p: any) => p.key.name === (m.key as Identifier).name
)
propsRuntimeDefaults!.properties.some(p => {
if (p.type === 'SpreadElement') return false
return (
resolveObjectKey(p.key, p.computed) ===
(m.key as Identifier).name
)
})
) {
res +=
m.key.name +
@ -2139,16 +2142,9 @@ function analyzeBindingsFromOptions(node: ObjectExpression): BindingMetadata {
function getObjectExpressionKeys(node: ObjectExpression): string[] {
const keys = []
for (const prop of node.properties) {
if (
(prop.type === 'ObjectProperty' || prop.type === 'ObjectMethod') &&
!prop.computed
) {
if (prop.key.type === 'Identifier') {
keys.push(prop.key.name)
} else if (prop.key.type === 'StringLiteral') {
keys.push(prop.key.value)
}
}
if (prop.type === 'SpreadElement') continue
const key = resolveObjectKey(prop.key, prop.computed)
if (key) keys.push(String(key))
}
return keys
}
@ -2297,3 +2293,14 @@ export function hmrShouldReload(
return false
}
export function resolveObjectKey(node: Node, computed: boolean) {
switch (node.type) {
case 'StringLiteral':
case 'NumericLiteral':
return node.value
case 'Identifier':
if (!computed) return node.name
}
return undefined
}