From db9d76df65f04fbe72c56e324f14d6ce002e6345 Mon Sep 17 00:00:00 2001 From: snomiao Date: Sun, 14 Sep 2025 01:31:41 +0000 Subject: [PATCH] fix: resolve circular dependency to enable verbatimModuleSyntax MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- src/lib/litegraph/src/canvas/LinkConnector.ts | 8 +++---- .../src/subgraph/SubgraphInputNode.ts | 24 +++++++++++++++++-- .../src/subgraph/SubgraphOutputNode.ts | 24 +++++++++++++++++-- tsconfig.json | 1 + 4 files changed, 49 insertions(+), 8 deletions(-) diff --git a/src/lib/litegraph/src/canvas/LinkConnector.ts b/src/lib/litegraph/src/canvas/LinkConnector.ts index a70fd8752..85b7d6b58 100644 --- a/src/lib/litegraph/src/canvas/LinkConnector.ts +++ b/src/lib/litegraph/src/canvas/LinkConnector.ts @@ -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] diff --git a/src/lib/litegraph/src/subgraph/SubgraphInputNode.ts b/src/lib/litegraph/src/subgraph/SubgraphInputNode.ts index d46e94654..c302459c9 100644 --- a/src/lib/litegraph/src/subgraph/SubgraphInputNode.ts +++ b/src/lib/litegraph/src/subgraph/SubgraphInputNode.ts @@ -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 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) +} diff --git a/src/lib/litegraph/src/subgraph/SubgraphOutputNode.ts b/src/lib/litegraph/src/subgraph/SubgraphOutputNode.ts index 51fca97ef..c8fa18ee6 100644 --- a/src/lib/litegraph/src/subgraph/SubgraphOutputNode.ts +++ b/src/lib/litegraph/src/subgraph/SubgraphOutputNode.ts @@ -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 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) +} diff --git a/tsconfig.json b/tsconfig.json index e183ed3f2..3d971536e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,6 +18,7 @@ "noFallthroughCasesInSwitch": true, "downlevelIteration": true, "noImplicitOverride": true, + "verbatimModuleSyntax": true, "allowJs": true, "baseUrl": ".", "paths": {