]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(runtime-core): improve consistency of `PublicInstanceProxyHandlers.has` (#13507)
authorskirtle <65301168+skirtles-code@users.noreply.github.com>
Wed, 20 Aug 2025 13:05:26 +0000 (14:05 +0100)
committerGitHub <noreply@github.com>
Wed, 20 Aug 2025 13:05:26 +0000 (21:05 +0800)
packages/runtime-core/__tests__/componentPublicInstance.spec.ts
packages/runtime-core/src/componentPublicInstance.ts

index 37c228a5104ffb42dafebfa944c2029bdbe29f06..346d1d4e4d5dd9587023040fb870131d56383fd9 100644 (file)
@@ -167,13 +167,26 @@ describe('component: proxy', () => {
       data() {
         return {
           foo: 0,
+          $foo: 0,
         }
       },
+      computed: {
+        cmp: () => {
+          throw new Error('value of cmp should not be accessed')
+        },
+        $cmp: () => {
+          throw new Error('value of $cmp should not be read')
+        },
+      },
       setup() {
         return {
           bar: 1,
         }
       },
+      __cssModules: {
+        $style: {},
+        cssStyles: {},
+      },
       mounted() {
         instanceProxy = this
       },
@@ -181,6 +194,7 @@ describe('component: proxy', () => {
 
     const app = createApp(Comp, { msg: 'hello' })
     app.config.globalProperties.global = 1
+    app.config.globalProperties.$global = 1
 
     app.mount(nodeOps.createElement('div'))
 
@@ -188,12 +202,20 @@ describe('component: proxy', () => {
     expect('msg' in instanceProxy).toBe(true)
     // data
     expect('foo' in instanceProxy).toBe(true)
-    // ctx
+    expect('$foo' in instanceProxy).toBe(false)
+    // setupState
     expect('bar' in instanceProxy).toBe(true)
+    // ctx
+    expect('cmp' in instanceProxy).toBe(true)
+    expect('$cmp' in instanceProxy).toBe(true)
     // public properties
     expect('$el' in instanceProxy).toBe(true)
+    // CSS modules
+    expect('$style' in instanceProxy).toBe(true)
+    expect('cssStyles' in instanceProxy).toBe(true)
     // global properties
     expect('global' in instanceProxy).toBe(true)
+    expect('$global' in instanceProxy).toBe(true)
 
     // non-existent
     expect('$foobar' in instanceProxy).toBe(false)
@@ -202,11 +224,15 @@ describe('component: proxy', () => {
     // #4962 triggering getter should not cause non-existent property to
     // pass the has check
     instanceProxy.baz
+    instanceProxy.$baz
     expect('baz' in instanceProxy).toBe(false)
+    expect('$baz' in instanceProxy).toBe(false)
 
     // set non-existent (goes into proxyTarget sink)
     instanceProxy.baz = 1
     expect('baz' in instanceProxy).toBe(true)
+    instanceProxy.$baz = 1
+    expect('$baz' in instanceProxy).toBe(true)
 
     // dev mode ownKeys check for console inspection
     // should only expose own keys
@@ -214,7 +240,10 @@ describe('component: proxy', () => {
       'msg',
       'bar',
       'foo',
+      'cmp',
+      '$cmp',
       'baz',
+      '$baz',
     ])
   })
 
index e9e7770ebd988ac2c8ac2b43979d5c9351916cf9..58c18764ee1b6d15a3e4780878fba5729f76d497 100644 (file)
@@ -575,19 +575,20 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
 
   has(
     {
-      _: { data, setupState, accessCache, ctx, appContext, propsOptions },
+      _: { data, setupState, accessCache, ctx, appContext, propsOptions, type },
     }: ComponentRenderContext,
     key: string,
   ) {
-    let normalizedProps
-    return (
-      !!accessCache![key] ||
-      (data !== EMPTY_OBJ && hasOwn(data, key)) ||
+    let normalizedProps, cssModules
+    return !!(
+      accessCache![key] ||
+      (data !== EMPTY_OBJ && key[0] !== '$' && hasOwn(data, key)) ||
       hasSetupBinding(setupState, key) ||
       ((normalizedProps = propsOptions[0]) && hasOwn(normalizedProps, key)) ||
       hasOwn(ctx, key) ||
       hasOwn(publicPropertiesMap, key) ||
-      hasOwn(appContext.config.globalProperties, key)
+      hasOwn(appContext.config.globalProperties, key) ||
+      ((cssModules = type.__cssModules) && cssModules[key])
     )
   },