feat(compiler-sfc): `withDefaults` supports the use of local variables

This commit is contained in:
zqran 2023-06-17 13:19:02 +08:00
parent a95e612b25
commit 43e0e26dd4
3 changed files with 61 additions and 1 deletions

View File

@ -338,6 +338,27 @@ return { props, get defaults() { return defaults } }
})"
`;
exports[`defineProps > withDefaults (locally variable) 1`] = `
"import { mergeDefaults as _mergeDefaults, defineComponent as _defineComponent } from 'vue'
const defaults = { baz: false }
export default /*#__PURE__*/_defineComponent({
props: _mergeDefaults({
baz: { type: Boolean, required: true }
}, defaults),
setup(__props: any, { expose: __expose }) {
__expose();
const props = __props;
return { defaults, props }
}
})"
`;
exports[`defineProps > withDefaults (reference) 1`] = `
"import { mergeDefaults as _mergeDefaults, defineComponent as _defineComponent } from 'vue'
import { defaults } from './foo'

View File

@ -470,6 +470,26 @@ const props = defineProps({ foo: String })
)
})
test('withDefaults (locally variable)', () => {
const { content } = compile(`
<script setup lang="ts">
const defaults = { baz: false }
const props = withDefaults(defineProps<{
baz: boolean
}>(), defaults)
</script>
`)
assertCode(content)
expect(content).toMatch(`import { mergeDefaults as _mergeDefaults`)
expect(content).toMatch(
`
_mergeDefaults({
baz: { type: Boolean, required: true }
}, defaults)`.trim()
)
})
// #7111
test('withDefaults (dynamic) w/ production mode', () => {
const { content } = compile(

View File

@ -193,6 +193,7 @@ export function compileScript(
// const ctx.bindingMetadata: BindingMetadata = {}
const scriptBindings: Record<string, BindingTypes> = Object.create(null)
const setupBindings: Record<string, BindingTypes> = Object.create(null)
const withDefaultsVariables: Record<string, Statement> = {}
let defaultExport: Node | undefined
let hasAwait = false
@ -258,7 +259,11 @@ export function compileScript(
if (!node) return
walkIdentifiers(node, id => {
const binding = setupBindings[id.name]
if (binding && binding !== BindingTypes.LITERAL_CONST) {
if (
binding &&
binding !== BindingTypes.LITERAL_CONST &&
!withDefaultsVariables[id.name]
) {
ctx.error(
`\`${method}()\` in <script setup> cannot reference locally ` +
`declared variables because it will be hoisted outside of the ` +
@ -636,6 +641,20 @@ export function compileScript(
parent.type === 'ExpressionStatement'
)
}
if (child.type === 'Identifier') {
if (parent.type === 'VariableDeclarator') {
withDefaultsVariables[child.name] = node
} else if (
parent.type === 'CallExpression' &&
parent.callee.type === 'Identifier' &&
parent.callee.name === WITH_DEFAULTS &&
withDefaultsVariables[child.name]
) {
const node = withDefaultsVariables[child.name]
hoistNode(node)
}
}
},
exit(node: Node) {
if (node.type === 'BlockStatement') scope.pop()