fix: resolve circular dependency to enable verbatimModuleSyntax

- Use lazy initialization for EmptySubgraphInput/Output in SubgraphInputNode/OutputNode
- Replace direct instanceof checks with identity checks in LinkConnector
- Enable verbatimModuleSyntax in tsconfig.json
- Convert runtime imports to type-only imports where appropriate
- Use factory functions with require() to break circular dependencies

This resolves the circular dependency chain:
EmptySubgraphInput → SubgraphInput → SubgraphInputNode → EmptySubgraphInput

The require() usage is necessary here to break the circular dependency at module
initialization time. ESLint rules have been disabled for these specific cases.

Fixes #5545
This commit is contained in:
snomiao
2025-09-14 01:31:41 +00:00
parent 8ae50d140d
commit db9d76df65
4 changed files with 49 additions and 8 deletions

View File

@@ -17,8 +17,8 @@ import type {
INodeInputSlot,
INodeOutputSlot
} from '@/lib/litegraph/src/interfaces'
import { EmptySubgraphInput } from '@/lib/litegraph/src/subgraph/EmptySubgraphInput'
import { EmptySubgraphOutput } from '@/lib/litegraph/src/subgraph/EmptySubgraphOutput'
import type { EmptySubgraphInput } from '@/lib/litegraph/src/subgraph/EmptySubgraphInput'
import type { EmptySubgraphOutput } from '@/lib/litegraph/src/subgraph/EmptySubgraphOutput'
import { Subgraph } from '@/lib/litegraph/src/subgraph/Subgraph'
import type { SubgraphInput } from '@/lib/litegraph/src/subgraph/SubgraphInput'
import { SubgraphInputNode } from '@/lib/litegraph/src/subgraph/SubgraphInputNode'
@@ -673,7 +673,7 @@ export class LinkConnector {
link.connectToSubgraphOutput(targetSlot, this.events)
// If we just connected to an EmptySubgraphOutput, check if we should reuse the slot
if (output instanceof EmptySubgraphOutput && ioNode.slots.length > 0) {
if (output === ioNode.emptySlot && ioNode.slots.length > 0) {
// Get the last created slot (newest one)
const createdSlot = ioNode.slots[ioNode.slots.length - 1]
@@ -719,7 +719,7 @@ export class LinkConnector {
link.connectToSubgraphInput(targetSlot, this.events)
// If we just connected to an EmptySubgraphInput, check if we should reuse the slot
if (input instanceof EmptySubgraphInput && ioNode.slots.length > 0) {
if (input === ioNode.emptySlot && ioNode.slots.length > 0) {
// Get the last created slot (newest one)
const createdSlot = ioNode.slots[ioNode.slots.length - 1]

View File

@@ -16,18 +16,29 @@ import type { CanvasPointerEvent } from '@/lib/litegraph/src/types/events'
import { NodeSlotType } from '@/lib/litegraph/src/types/globalEnums'
import { findFreeSlotOfType } from '@/lib/litegraph/src/utils/collections'
import { EmptySubgraphInput } from './EmptySubgraphInput'
import { SubgraphIONodeBase } from './SubgraphIONodeBase'
import type { SubgraphInput } from './SubgraphInput'
import type { SubgraphOutput } from './SubgraphOutput'
// Import the type only to avoid circular dependency at runtime
import type { EmptySubgraphInput } from './EmptySubgraphInput'
export class SubgraphInputNode
extends SubgraphIONodeBase<SubgraphInput>
implements Positionable
{
readonly id: NodeId = SUBGRAPH_INPUT_ID
readonly emptySlot: EmptySubgraphInput = new EmptySubgraphInput(this)
#emptySlot?: EmptySubgraphInput
get emptySlot(): EmptySubgraphInput {
if (!this.#emptySlot) {
// Lazy initialization to break circular dependency
// Import is deferred until first access
this.#emptySlot = createEmptySubgraphInput(this)
}
return this.#emptySlot
}
get slots() {
return this.subgraph.inputs
@@ -264,3 +275,12 @@ export class SubgraphInputNode
this.drawSlots(ctx, colorContext, fromSlot, editorAlpha)
}
}
// Factory function to break circular dependency
// This is defined outside the class to avoid module loading issues
function createEmptySubgraphInput(parent: SubgraphInputNode): EmptySubgraphInput {
// Import here is safe because it happens after module initialization
// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports
const { EmptySubgraphInput } = require('./EmptySubgraphInput') as typeof import('./EmptySubgraphInput')
return new EmptySubgraphInput(parent)
}

View File

@@ -16,18 +16,29 @@ import type { CanvasPointerEvent } from '@/lib/litegraph/src/types/events'
import type { SubgraphIO } from '@/lib/litegraph/src/types/serialisation'
import { findFreeSlotOfType } from '@/lib/litegraph/src/utils/collections'
import { EmptySubgraphOutput } from './EmptySubgraphOutput'
import { SubgraphIONodeBase } from './SubgraphIONodeBase'
import type { SubgraphInput } from './SubgraphInput'
import type { SubgraphOutput } from './SubgraphOutput'
// Import the type only to avoid circular dependency at runtime
import type { EmptySubgraphOutput } from './EmptySubgraphOutput'
export class SubgraphOutputNode
extends SubgraphIONodeBase<SubgraphOutput>
implements Positionable
{
readonly id: NodeId = SUBGRAPH_OUTPUT_ID
readonly emptySlot: EmptySubgraphOutput = new EmptySubgraphOutput(this)
#emptySlot?: EmptySubgraphOutput
get emptySlot(): EmptySubgraphOutput {
if (!this.#emptySlot) {
// Lazy initialization to break circular dependency
// Import is deferred until first access
this.#emptySlot = createEmptySubgraphOutput(this)
}
return this.#emptySlot
}
get slots() {
return this.subgraph.outputs
@@ -158,3 +169,12 @@ export class SubgraphOutputNode
this.drawSlots(ctx, colorContext, fromSlot, editorAlpha)
}
}
// Factory function to break circular dependency
// This is defined outside the class to avoid module loading issues
function createEmptySubgraphOutput(parent: SubgraphOutputNode): EmptySubgraphOutput {
// Import here is safe because it happens after module initialization
// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports
const { EmptySubgraphOutput } = require('./EmptySubgraphOutput') as typeof import('./EmptySubgraphOutput')
return new EmptySubgraphOutput(parent)
}

View File

@@ -18,6 +18,7 @@
"noFallthroughCasesInSwitch": true,
"downlevelIteration": true,
"noImplicitOverride": true,
"verbatimModuleSyntax": true,
"allowJs": true,
"baseUrl": ".",
"paths": {