Jelajahi Sumber

fix(runtime-vapor): avoid exposing built-in components internals via template ref (#14448)

edison 3 bulan lalu
induk
melakukan
3badf505ca

+ 1 - 1
package.json

@@ -20,7 +20,7 @@
     "test-unit": "vitest --project unit*",
     "test-unit": "vitest --project unit*",
     "test-e2e": "node scripts/build.js vue -f global+esm-browser-vapor -d && vitest --project e2e",
     "test-e2e": "node scripts/build.js vue -f global+esm-browser-vapor -d && vitest --project e2e",
     "test-e2e-vapor": "pnpm run prepare-e2e-vapor && vitest --project e2e-vapor",
     "test-e2e-vapor": "pnpm run prepare-e2e-vapor && vitest --project e2e-vapor",
-    "prepare-e2e-vapor": "node scripts/build.js -f cjs+esm-bundler+esm-bundler-runtime && pnpm run -C packages-private/vapor-e2e-test build",
+    "prepare-e2e-vapor": "__TEST__=true node scripts/build.js -f cjs+esm-bundler+esm-bundler-runtime && pnpm run -C packages-private/vapor-e2e-test build",
     "test-dts": "run-s build-dts test-dts-only",
     "test-dts": "run-s build-dts test-dts-only",
     "test-dts-only": "tsc -p packages-private/dts-built-test/tsconfig.json && tsc -p ./packages-private/dts-test/tsconfig.test.json && tsc -p ./packages-private/dts-test/vapor/tsconfig.json",
     "test-dts-only": "tsc -p packages-private/dts-built-test/tsconfig.json && tsc -p ./packages-private/dts-test/tsconfig.test.json && tsc -p ./packages-private/dts-test/vapor/tsconfig.json",
     "test-coverage": "vitest --ui --project unit* --coverage.enabled=true",
     "test-coverage": "vitest --ui --project unit* --coverage.enabled=true",

+ 1 - 1
packages-private/vapor-e2e-test/transition/App.vue

@@ -146,7 +146,7 @@ const UnmountBranch = defineVaporComponent({
 })
 })
 const keepAliveUnmountRef = ref(null)
 const keepAliveUnmountRef = ref(null)
 window.getKeepAliveUnmountStorageContainer = () =>
 window.getKeepAliveUnmountStorageContainer = () =>
-  keepAliveUnmountRef.value?.ctx?.getStorageContainer?.() ?? null
+  keepAliveUnmountRef.value.getStorageContainer()
 const unmountIncludeRef = ref(['UnmountBranch'])
 const unmountIncludeRef = ref(['UnmountBranch'])
 const unmountToggle = ref(true)
 const unmountToggle = ref(true)
 const unmountClick = () => {
 const unmountClick = () => {

+ 3 - 0
packages/runtime-vapor/src/apiTemplateRef.ts

@@ -25,6 +25,7 @@ import {
   isString,
   isString,
   remove,
   remove,
 } from '@vue/shared'
 } from '@vue/shared'
+import { isTeleportFragment } from './components/Teleport'
 import {
 import {
   type DynamicFragment,
   type DynamicFragment,
   type VaporFragment,
   type VaporFragment,
@@ -249,6 +250,8 @@ function setRef(
 const getRefValue = (el: RefEl) => {
 const getRefValue = (el: RefEl) => {
   if (isVaporComponent(el)) {
   if (isVaporComponent(el)) {
     return getExposed(el) || el
     return getExposed(el) || el
+  } else if (isTeleportFragment(el)) {
+    return null
   } else if (isDynamicFragment(el)) {
   } else if (isDynamicFragment(el)) {
     if (isArray(el.nodes)) return null
     if (isArray(el.nodes)) return null
     return getRefValue(el.nodes as RefEl)
     return getRefValue(el.nodes as RefEl)

+ 11 - 1
packages/runtime-vapor/src/components/KeepAlive.ts

@@ -117,7 +117,16 @@ const VaporKeepAliveImpl = defineVaporComponent({
     exclude: [String, RegExp, Array],
     exclude: [String, RegExp, Array],
     max: [String, Number],
     max: [String, Number],
   },
   },
-  setup(props: KeepAliveProps, { slots }) {
+  setup(props: KeepAliveProps, { slots, expose }) {
+    let exposed!: Record<string, any>
+    // for e2e test
+    if (__BROWSER__ && __TEST__) {
+      exposed = {
+        getStorageContainer: () => storageContainer,
+      }
+    }
+    expose(exposed)
+
     if (!slots.default) {
     if (!slots.default) {
       return undefined
       return undefined
     }
     }
@@ -141,6 +150,7 @@ const VaporKeepAliveImpl = defineVaporComponent({
     if (__DEV__) {
     if (__DEV__) {
       const rerender = keepAliveInstance.hmrRerender
       const rerender = keepAliveInstance.hmrRerender
       keepAliveInstance.hmrRerender = () => {
       keepAliveInstance.hmrRerender = () => {
+        keepAliveInstance.exposed = null
         cache.forEach(cached => resetCachedShapeFlag(cached))
         cache.forEach(cached => resetCachedShapeFlag(cached))
         cache.clear()
         cache.clear()
         keys.clear()
         keys.clear()

+ 4 - 1
packages/runtime-vapor/src/components/Transition.ts

@@ -88,7 +88,10 @@ const decorate = (t: typeof VaporTransition) => {
 }
 }
 
 
 export const VaporTransition: FunctionalVaporComponent<TransitionProps> =
 export const VaporTransition: FunctionalVaporComponent<TransitionProps> =
-  /*@__PURE__*/ decorate((props, { slots }) => {
+  /*@__PURE__*/ decorate((props, { slots, expose }) => {
+    // @ts-expect-error
+    expose()
+
     // Register transition hooks on first use
     // Register transition hooks on first use
     ensureTransitionHooksRegistered()
     ensureTransitionHooksRegistered()
 
 

+ 4 - 1
packages/runtime-vapor/src/components/TransitionGroup.ts

@@ -57,7 +57,10 @@ const VaporTransitionGroupImpl = defineVaporComponent({
     moveClass: String,
     moveClass: String,
   }),
   }),
 
 
-  setup(props: TransitionGroupProps, { slots }) {
+  setup(props: TransitionGroupProps, { slots, expose }) {
+    // @ts-expect-error
+    expose()
+
     // Register transition hooks on first use
     // Register transition hooks on first use
     ensureTransitionHooksRegistered()
     ensureTransitionHooksRegistered()