dx(defineModel): warn against reference of setup scope variables in defineModel options

close #10093
This commit is contained in:
Evan You 2024-01-12 22:06:46 +08:00
parent d35b87725a
commit c60479146a
4 changed files with 43 additions and 1 deletions

View File

@ -50,7 +50,7 @@ export function walkIdentifiers(
} }
} else if ( } else if (
node.type === 'ObjectProperty' && node.type === 'ObjectProperty' &&
parent!.type === 'ObjectPattern' parent?.type === 'ObjectPattern'
) { ) {
// mark property in destructure pattern // mark property in destructure pattern
;(node as any).inPattern = true ;(node as any).inPattern = true

View File

@ -953,6 +953,38 @@ describe('SFC compile <script setup>', () => {
</script>`).content, </script>`).content,
) )
}) })
test('defineModel() referencing local var', () => {
expect(() =>
compile(`<script setup>
let bar = 1
defineModel({
default: () => bar
})
</script>`),
).toThrow(`cannot reference locally declared variables`)
// allow const
expect(() =>
compile(`<script setup>
const bar = 1
defineModel({
default: () => bar
})
</script>`),
).not.toThrow(`cannot reference locally declared variables`)
// allow in get/set
expect(() =>
compile(`<script setup>
let bar = 1
defineModel({
get: () => bar,
set: () => bar
})
</script>`),
).not.toThrow(`cannot reference locally declared variables`)
})
}) })
}) })

View File

@ -671,6 +671,11 @@ export function compileScript(
checkInvalidScopeReference(ctx.propsDestructureDecl, DEFINE_PROPS) checkInvalidScopeReference(ctx.propsDestructureDecl, DEFINE_PROPS)
checkInvalidScopeReference(ctx.emitsRuntimeDecl, DEFINE_EMITS) checkInvalidScopeReference(ctx.emitsRuntimeDecl, DEFINE_EMITS)
checkInvalidScopeReference(ctx.optionsRuntimeDecl, DEFINE_OPTIONS) checkInvalidScopeReference(ctx.optionsRuntimeDecl, DEFINE_OPTIONS)
for (const { runtimeOptionNodes } of Object.values(ctx.modelDecls)) {
for (const node of runtimeOptionNodes) {
checkInvalidScopeReference(node, DEFINE_MODEL)
}
}
// 5. remove non-script content // 5. remove non-script content
if (script) { if (script) {

View File

@ -15,6 +15,7 @@ export interface ModelDecl {
type: TSType | undefined type: TSType | undefined
options: string | undefined options: string | undefined
identifier: string | undefined identifier: string | undefined
runtimeOptionNodes: Node[]
} }
export function processDefineModel( export function processDefineModel(
@ -48,6 +49,7 @@ export function processDefineModel(
let optionsString = options && ctx.getString(options) let optionsString = options && ctx.getString(options)
let optionsRemoved = !options let optionsRemoved = !options
const runtimeOptionNodes: Node[] = []
if ( if (
options && options &&
@ -75,6 +77,8 @@ export function processDefineModel(
// remove prop options from runtime options // remove prop options from runtime options
removed++ removed++
ctx.s.remove(ctx.startOffset! + start, ctx.startOffset! + end) ctx.s.remove(ctx.startOffset! + start, ctx.startOffset! + end)
// record prop options for invalid scope var reference check
runtimeOptionNodes.push(p)
} }
} }
if (removed === options.properties.length) { if (removed === options.properties.length) {
@ -89,6 +93,7 @@ export function processDefineModel(
ctx.modelDecls[modelName] = { ctx.modelDecls[modelName] = {
type, type,
options: optionsString, options: optionsString,
runtimeOptionNodes,
identifier: identifier:
declId && declId.type === 'Identifier' ? declId.name : undefined, declId && declId.type === 'Identifier' ? declId.name : undefined,
} }