Skip to content

Commit

Permalink
fix(nested): Prevent infinite loops when resolving path
Browse files Browse the repository at this point in the history
  • Loading branch information
jsek committed Aug 25, 2024
1 parent 7e9a1b7 commit 34dda16
Show file tree
Hide file tree
Showing 6 changed files with 21 additions and 0 deletions.
2 changes: 2 additions & 0 deletions packages/vuetify/src/components/VTreeview/VTreeview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
filterTreeItems,
filterTreeItem,
} from './util/filterTreeItems'
import { preventLoops } from '@/util/nested'

type VTreeviewNodeInstance = InstanceType<typeof VTreeviewNode>

Expand Down Expand Up @@ -307,6 +308,7 @@ export default mixins(

const parents = []
while (parent !== null) {
preventLoops(parents, parent)
parents.push(parent)
parent = this.nodes[parent].parent
}
Expand Down
2 changes: 2 additions & 0 deletions packages/vuetify/src/composables/nested/nested.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
leafSingleSelectStrategy,
} from './selectStrategies'
import { getCurrentInstance, getUid, propsFactory } from '@/util'
import { preventLoops } from '@/util/nested'

// Types
import type { InjectionKey, PropType, Ref } from 'vue'
Expand Down Expand Up @@ -182,6 +183,7 @@ export const useNested = (props: NestedProps) => {
let parent: unknown = id

while (parent != null) {
preventLoops(path, parent)
path.unshift(parent)
parent = parents.value.get(parent)
}
Expand Down
4 changes: 4 additions & 0 deletions packages/vuetify/src/composables/nested/openStrategies.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Utilities
import { toRaw } from 'vue'
import { preventLoops } from '@/util/nested'

export type OpenStrategyFn = (data: {
id: unknown
Expand Down Expand Up @@ -34,6 +35,7 @@ export const singleOpenStrategy: OpenStrategy = {
let parent = parents.get(id)

while (parent != null) {
preventLoops(newOpened, parent)
newOpened.add(parent)
parent = parents.get(parent)
}
Expand All @@ -54,6 +56,7 @@ export const multipleOpenStrategy: OpenStrategy = {
opened.add(id)

while (parent != null && parent !== id) {
preventLoops(opened, parent)
opened.add(parent)
parent = toRaw(parents.get(parent))
}
Expand All @@ -77,6 +80,7 @@ export const listOpenStrategy: OpenStrategy = {
let parent = parents.get(id)

while (parent != null) {
preventLoops(path, parent)
path.push(parent)
parent = parents.get(parent)
}
Expand Down
2 changes: 2 additions & 0 deletions packages/vuetify/src/composables/nested/selectStrategies.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable sonarjs/no-identical-functions */
// Utilities
import { toRaw } from 'vue'
import { preventLoops } from '@/util/nested'

export type SelectStrategyFn = (data: {
id: unknown
Expand Down Expand Up @@ -163,6 +164,7 @@ export const classicSelectStrategy = (mandatory?: boolean): SelectStrategy => {
const everySelected = childrenIds.every(cid => selected.get(toRaw(cid)) === 'on')
const noneSelected = childrenIds.every(cid => !selected.has(toRaw(cid)) || selected.get(toRaw(cid)) === 'off')

preventLoops(selected, parent)
selected.set(parent, everySelected ? 'on' : noneSelected ? 'off' : 'indeterminate')

parent = toRaw(parents.get(parent))
Expand Down
2 changes: 2 additions & 0 deletions packages/vuetify/src/labs/VTreeview/VTreeview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { useProxiedModel } from '@/composables/proxiedModel'
// Utilities
import { computed, provide, ref, toRaw, toRef } from 'vue'
import { genericComponent, omit, propsFactory, useRender } from '@/util'
import { preventLoops } from '@/util/nested'

// Types
import { VTreeviewSymbol } from './shared'
Expand Down Expand Up @@ -98,6 +99,7 @@ export const VTreeview = genericComponent<new <T>(
const path: unknown[] = []
let parent: unknown = id
while (parent != null) {
preventLoops(path, parent)
path.unshift(parent)
parent = vListRef.value?.parents.get(parent)
}
Expand Down
9 changes: 9 additions & 0 deletions packages/vuetify/src/util/nested.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export function preventLoops<T> (path: T[] | Set<T> | Map<T, any>, itemToPush: T) {
if (
(Array.isArray(path) && path.includes(itemToPush)) ||
(path instanceof Set && path.has(itemToPush)) ||
(path instanceof Map && path.has(itemToPush))
) {
throw new Error('[Vuetify] Could not resolve nested path because of duplicated identifiers')
}
}

0 comments on commit 34dda16

Please sign in to comment.