This commit is contained in:
zqran 2025-06-19 00:14:11 +08:00 committed by GitHub
commit f1a3a77a22
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 98 additions and 1 deletions

View File

@ -463,6 +463,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

@ -518,6 +518,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(
@ -677,6 +697,20 @@ const props = defineProps({ foo: String })
</script>`)
}).toThrow(`cannot accept both type and non-type arguments`)
})
test('withDefaults (locally variable)', () => {
expect(() => {
compile(`
<script setup lang="ts">
const defaults = { bar: 1 }
withDefaults(defineProps<{
bar?: number
}>(), defaults)
defaults.bar++
</script>
`)
}).toThrow(`cannot reference locally declared variables`)
})
})
test('should escape names w/ special symbols', () => {

View File

@ -198,6 +198,13 @@ 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,
{
node: Statement
needHoist?: boolean
}
> = {}
let defaultExport: Node | undefined
let hasAwait = false
@ -263,7 +270,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].needHoist
) {
ctx.error(
`\`${method}()\` in <script setup> cannot reference locally ` +
`declared variables because it will be hoisted outside of the ` +
@ -648,6 +659,30 @@ 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 variable = withDefaultsVariables[child.name]
if (variable.needHoist !== false) {
variable.needHoist = true
}
} else if (
parent!.type !== 'VariableDeclaration' &&
withDefaultsVariables[child.name]
) {
const variable = withDefaultsVariables[child.name]
variable.needHoist = false
}
}
},
exit(node: Node) {
if (node.type === 'BlockStatement') scope.pop()
@ -683,6 +718,13 @@ export function compileScript(
}
}
for (const key in withDefaultsVariables) {
const variable = withDefaultsVariables[key]
if (variable.needHoist) {
hoistNode(variable.node)
}
}
// 3 props destructure transform
if (ctx.propsDestructureDecl) {
transformDestructuredProps(ctx, vueImportAliases)