]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
dx(defineModel): warn against reference of setup scope variables in defineModel options
authorEvan You <yyx990803@gmail.com>
Fri, 12 Jan 2024 14:06:46 +0000 (22:06 +0800)
committerEvan You <yyx990803@gmail.com>
Fri, 12 Jan 2024 14:07:16 +0000 (22:07 +0800)
close #10093

packages/compiler-core/src/babelUtils.ts
packages/compiler-sfc/__tests__/compileScript.spec.ts
packages/compiler-sfc/src/compileScript.ts
packages/compiler-sfc/src/script/defineModel.ts

index 0a7f48af9027d625b901dd6fe64b37a50d347b58..b12a6d9b47fb71e83fa597b021a2ffca906e3663 100644 (file)
@@ -50,7 +50,7 @@ export function walkIdentifiers(
         }
       } else if (
         node.type === 'ObjectProperty' &&
-        parent!.type === 'ObjectPattern'
+        parent?.type === 'ObjectPattern'
       ) {
         // mark property in destructure pattern
         ;(node as any).inPattern = true
index 7c21816eb7549104b04d2e6be66a6cf4eac820e8..2b9acbc7fd2a7ce2f70f57abf3b7f132665ef7be 100644 (file)
@@ -953,6 +953,38 @@ describe('SFC compile <script setup>', () => {
         </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`)
+    })
   })
 })
 
index b0b79f8c88d13bee0d8f162d627dc7a889950503..69beb2af79677a61211b9c50715444a55aaf3d36 100644 (file)
@@ -671,6 +671,11 @@ export function compileScript(
   checkInvalidScopeReference(ctx.propsDestructureDecl, DEFINE_PROPS)
   checkInvalidScopeReference(ctx.emitsRuntimeDecl, DEFINE_EMITS)
   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
   if (script) {
index b94b7994622eb49fb8d024f2489c9c86dd5d7cfe..24fd0780eaa6cea85f0fcc21a95558db56322131 100644 (file)
@@ -15,6 +15,7 @@ export interface ModelDecl {
   type: TSType | undefined
   options: string | undefined
   identifier: string | undefined
+  runtimeOptionNodes: Node[]
 }
 
 export function processDefineModel(
@@ -48,6 +49,7 @@ export function processDefineModel(
 
   let optionsString = options && ctx.getString(options)
   let optionsRemoved = !options
+  const runtimeOptionNodes: Node[] = []
 
   if (
     options &&
@@ -75,6 +77,8 @@ export function processDefineModel(
         // remove prop options from runtime options
         removed++
         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) {
@@ -89,6 +93,7 @@ export function processDefineModel(
   ctx.modelDecls[modelName] = {
     type,
     options: optionsString,
+    runtimeOptionNodes,
     identifier:
       declId && declId.type === 'Identifier' ? declId.name : undefined,
   }