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`] = ` exports[`defineProps > withDefaults (reference) 1`] = `
"import { mergeDefaults as _mergeDefaults, defineComponent as _defineComponent } from 'vue' "import { mergeDefaults as _mergeDefaults, defineComponent as _defineComponent } from 'vue'
import { defaults } from './foo' 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 // #7111
test('withDefaults (dynamic) w/ production mode', () => { test('withDefaults (dynamic) w/ production mode', () => {
const { content } = compile( const { content } = compile(
@ -677,6 +697,20 @@ const props = defineProps({ foo: String })
</script>`) </script>`)
}).toThrow(`cannot accept both type and non-type arguments`) }).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', () => { test('should escape names w/ special symbols', () => {

View File

@ -198,6 +198,13 @@ export function compileScript(
// const ctx.bindingMetadata: BindingMetadata = {} // const ctx.bindingMetadata: BindingMetadata = {}
const scriptBindings: Record<string, BindingTypes> = Object.create(null) const scriptBindings: Record<string, BindingTypes> = Object.create(null)
const setupBindings: 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 defaultExport: Node | undefined
let hasAwait = false let hasAwait = false
@ -263,7 +270,11 @@ export function compileScript(
if (!node) return if (!node) return
walkIdentifiers(node, id => { walkIdentifiers(node, id => {
const binding = setupBindings[id.name] const binding = setupBindings[id.name]
if (binding && binding !== BindingTypes.LITERAL_CONST) { if (
binding &&
binding !== BindingTypes.LITERAL_CONST &&
!withDefaultsVariables[id.name].needHoist
) {
ctx.error( ctx.error(
`\`${method}()\` in <script setup> cannot reference locally ` + `\`${method}()\` in <script setup> cannot reference locally ` +
`declared variables because it will be hoisted outside of the ` + `declared variables because it will be hoisted outside of the ` +
@ -648,6 +659,30 @@ export function compileScript(
parent!.type === 'ExpressionStatement', 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) { exit(node: Node) {
if (node.type === 'BlockStatement') scope.pop() 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 // 3 props destructure transform
if (ctx.propsDestructureDecl) { if (ctx.propsDestructureDecl) {
transformDestructuredProps(ctx, vueImportAliases) transformDestructuredProps(ctx, vueImportAliases)