fix: Prune invalid builder mappings on load (#9376)

## Summary

- extract resolveNode to reusable util
- remove mid builder pruning
- handle missing widgets with label

## Review Focus

`resolveNode` was simplified for subgraphs by calling getNodeById on
each of the subgraphs instead of searching their inner nodes manually.

## Screenshots (if applicable)

"Widget not visible"
<img width="657" height="822" alt="image"
src="https://github.com/user-attachments/assets/ab7d1e87-3210-4e54-876a-07881974b5c7"
/>
<img width="674" height="375" alt="image"
src="https://github.com/user-attachments/assets/c50ec871-d423-43d6-8e1e-7b1a362f621c"
/>

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-9376-fix-Prune-invalid-builder-mappings-on-load-3196d73d3650811280c2d459ed0271af)
by [Unito](https://www.unito.io)
This commit is contained in:
pythongosssss
2026-03-04 17:52:14 +00:00
committed by GitHub
parent 3e59f8e932
commit 194218a9d6
7 changed files with 223 additions and 36 deletions

View File

@@ -15,15 +15,21 @@ import {
createNode,
isAnimatedOutput,
isVideoOutput,
migrateWidgetsValues
migrateWidgetsValues,
resolveNode
} from '@/utils/litegraphUtil'
vi.mock('@/lib/litegraph/src/litegraph', () => ({
vi.mock('@/lib/litegraph/src/litegraph', async (importOriginal) => ({
...(await importOriginal()),
LiteGraph: {
createNode: vi.fn()
}
}))
vi.mock('@/scripts/app', () => ({
app: { rootGraph: null }
}))
vi.mock('@/platform/updates/common/toastStore', () => ({
useToastStore: vi.fn(() => ({
addAlert: vi.fn(),
@@ -384,3 +390,53 @@ describe('compressWidgetInputSlots', () => {
expect(graph.links).toEqual([])
})
})
describe('resolveNode', () => {
function mockGraph(
nodeList: Partial<LGraphNode>[],
subgraphs?: Map<string, LGraph>
) {
const nodesById: Record<string, LGraphNode> = {}
for (const n of nodeList) {
nodesById[String(n.id)] = n as LGraphNode
}
return {
nodes: nodeList as LGraphNode[],
getNodeById(id: unknown) {
return id != null ? (nodesById[String(id)] ?? null) : null
},
subgraphs: subgraphs ?? new Map()
} as unknown as LGraph
}
it('returns undefined when graph is nullish', () => {
expect(resolveNode(1, null)).toBeUndefined()
expect(resolveNode(1, undefined)).toBeUndefined()
})
it('finds a node in the main graph', () => {
const node = { id: 5 } as LGraphNode
const graph = mockGraph([node])
expect(resolveNode(5, graph)).toBe(node)
})
it('finds a node in a subgraph', () => {
const subNode = { id: 10 } as LGraphNode
const subgraph = mockGraph([subNode])
const graph = mockGraph([], new Map([['sg-1', subgraph]]))
expect(resolveNode(10, graph)).toBe(subNode)
})
it('returns undefined when node is not found anywhere', () => {
const graph = mockGraph([{ id: 1 } as LGraphNode])
expect(resolveNode(999, graph)).toBeUndefined()
})
it('prefers main graph over subgraph', () => {
const mainNode = { id: 1, title: 'main' } as LGraphNode
const subNode = { id: 1, title: 'sub' } as LGraphNode
const subgraph = mockGraph([subNode])
const graph = mockGraph([mainNode], new Map([['sg-1', subgraph]]))
expect(resolveNode(1, graph)).toBe(mainNode)
})
})