refactor(type)!: make AbstractGraph.getPlugin return T | undefined#842
refactor(type)!: make AbstractGraph.getPlugin return T | undefined#842
T | undefined#842Conversation
Explicitly state that a plugin may not be available. BREAKING CHANGE: The `AbstractGraph.getPlugin` method now explicitly returns `undefined` when a plugin is not found. TypeScript users must update their code to handle the `undefined` case when calling this method.
WalkthroughThis update refactors the internal plugin management of the graph system by changing plugin storage from an object to a Map and updating the Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant AbstractGraph
participant PluginMap
Client->>AbstractGraph: getPlugin<SomePlugin>('PluginId')
AbstractGraph->>PluginMap: Map.get('PluginId')
PluginMap-->>AbstractGraph: Plugin instance or undefined
AbstractGraph-->>Client: Plugin instance or undefined
alt Plugin is undefined
Client->>Client: Must handle undefined plugin
else Plugin is defined
Client->>Plugin: Use plugin methods/properties
end
Possibly related PRs
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms (4)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (3)
packages/core/__tests__/view/plugins/FitPlugin.test.ts (1)
56-56: Non-null assertions are safe in test context.The addition of non-null assertion operators (
!) is appropriate in this test environment since theFitPluginis explicitly added to the graph during initialization. However, consider using optional chaining or explicit null checks in production code to maintain type safety.For production code, consider this safer alternative:
-const scale = graph.getPlugin<FitPlugin>('fit')!.fitCenter(); +const fitPlugin = graph.getPlugin<FitPlugin>('fit'); +if (!fitPlugin) throw new Error('FitPlugin not available'); +const scale = fitPlugin.fitCenter();Also applies to: 72-72, 88-88, 104-104, 118-118, 133-133, 149-149, 167-167, 183-183
packages/core/src/view/other/DragSource.ts (1)
529-529: Excellent defensive programming approach.This implementation handles the potential undefined
selectionHandlergracefully by using optional chaining (?.) and providing a sensible fallback (?? []). This is more robust than the non-null assertions used elsewhere in this PR.However, this creates an inconsistency with the approach taken in the story files. Consider establishing a consistent pattern across the codebase for handling plugin retrieval.
packages/html/stories/ContextIcons.stories.ts (1)
121-122: Consider runtime safety of non-null assertions.The non-null assertions assume plugins will always be available. While this may be true for story demonstrations, consider whether defensive programming would be more appropriate to prevent potential runtime errors.
The approach in
DragSource.ts(line 529) demonstrates a more defensive pattern using optional chaining and nullish coalescing.Also applies to: 205-205
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (20)
CHANGELOG.md(1 hunks)packages/core/__tests__/view/plugins/FitPlugin.test.ts(9 hunks)packages/core/src/editor/Editor.ts(1 hunks)packages/core/src/view/AbstractGraph.ts(3 hunks)packages/core/src/view/other/DragSource.ts(1 hunks)packages/html/stories/AutoLayout.stories.ts(2 hunks)packages/html/stories/Codec.stories.ts(1 hunks)packages/html/stories/ContextIcons.stories.ts(2 hunks)packages/html/stories/CustomHandlesConfiguration.stories.ts(1 hunks)packages/html/stories/CustomStyleDefaults.stories.ts(1 hunks)packages/html/stories/DynamicToolbar.stories.ts(1 hunks)packages/html/stories/Events.stories.ts(2 hunks)packages/html/stories/FileIO.stories.ts(1 hunks)packages/html/stories/FixedPoints.stories.ts(1 hunks)packages/html/stories/Manhattan.stories.ts(1 hunks)packages/html/stories/Orthogonal.stories.ts(2 hunks)packages/html/stories/PageBreaks.stories.ts(1 hunks)packages/html/stories/ShowRegion.stories.ts(1 hunks)packages/html/stories/SwimLanes.stories.ts(1 hunks)packages/html/stories/Toolbar.stories.ts(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (5)
packages/html/stories/Events.stories.ts (1)
packages/html/stories/OrgChart.stories.js (1)
popupMenuHandler(167-167)
packages/core/__tests__/view/plugins/FitPlugin.test.ts (1)
packages/core/src/view/plugins/FitPlugin.ts (1)
FitPlugin(87-281)
packages/html/stories/AutoLayout.stories.ts (1)
packages/html/stories/OrgChart.stories.js (1)
popupMenuHandler(167-167)
packages/html/stories/PageBreaks.stories.ts (1)
packages/html/stories/Guides.stories.js (1)
selectionHandler(76-76)
packages/core/src/view/AbstractGraph.ts (1)
packages/core/src/types.ts (2)
GraphPlugin(1178-1180)PluginId(1169-1169)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: build (windows-2022)
🔇 Additional comments (24)
packages/core/src/view/AbstractGraph.ts (4)
113-113: Excellent choice to use Map for plugin storage.Using
Map<string, GraphPlugin>instead of a plain object provides better semantics for key-value storage and clearer intent for plugin management.
222-222: Correct JSDoc reference fix.Good correction from
mxConstants.PAGE_FORMAT_A4_PORTRAITtoPAGE_FORMAT_A4_PORTRAITto match the actual import and constant name.
479-479: Proper Map initialization for plugins.The change to use
Map.set()is consistent with the new Map-based plugin storage and maintains the same functionality.
485-486: Breaking change improves type safety and API clarity.The return type change from
TtoT | undefinedis a well-justified breaking change that:
- Makes the API more explicit about potential undefined returns
- Forces consumers to handle the case where plugins might not be available
- Aligns with the Map.get() behavior which returns undefined for missing keys
This change improves type safety and makes the plugin retrieval behavior more predictable.
packages/html/stories/CustomHandlesConfiguration.stories.ts (1)
108-108: Non-null assertion is appropriate for this story context.The
!assertion ongetPlugin<ConnectionHandler>('ConnectionHandler')is justified here because:
- The story setup uses default plugins which include ConnectionHandler
- This maintains the previous behavior while adapting to the new
T | undefinedreturn type- The story context guarantees plugin availability
packages/html/stories/CustomStyleDefaults.stories.ts (1)
82-82: Consistent and appropriate non-null assertion usage.The
!assertion follows the same pattern as other story files and is appropriate because:
- The story uses default plugins that include ConnectionHandler
- The assertion maintains existing functionality while satisfying new type requirements
- Consistent with the adaptation pattern across all story files
packages/html/stories/Codec.stories.ts (1)
114-114: Appropriate non-null assertion for PanningHandler.The
!assertion ongetPlugin<PanningHandler>('PanningHandler')is justified because:
- PanningHandler is part of the default plugin set
- The story context guarantees plugin availability
- Maintains consistency with the adaptation pattern used across all story files
This change successfully adapts to the breaking change while preserving existing functionality.
packages/html/stories/FileIO.stories.ts (1)
73-73: Non-null assertion applied correctly for plugin retrieval
The addition of!ongetPlugin<PanningHandler>('PanningHandler')properly reflects the updated return typeT | undefinedand avoids compile-time errors.packages/html/stories/Manhattan.stories.ts (1)
48-48: Non-null assertion applied correctly for plugin retrieval
Using!ongetPlugin<SelectionHandler>('SelectionHandler')aligns with the updated signature and ensuresguidesEnabledcan be accessed without TypeScript errors.packages/html/stories/Events.stories.ts (2)
101-101: Non-null assertion applied correctly for plugin retrieval
The!aftergetPlugin<ConnectionHandler>('ConnectionHandler')safely satisfies the newT | undefinedreturn type before settingconnectImage.
131-131: Non-null assertion applied correctly for plugin retrieval
Applying!ongetPlugin<PopupMenuHandler>('PopupMenuHandler')ensures the handler exists at runtime and removes TypeScript errors when assigningfactoryMethod.packages/html/stories/Orthogonal.stories.ts (2)
99-100: Non-null assertion applied correctly for plugin retrieval
The non-null assertion ongetPlugin<SelectionHandler>('SelectionHandler')correctly adapts to the updated signature and enablesguidesEnabled.
112-112: Non-null assertion applied correctly for plugin retrieval
Using!ongetPlugin<ConnectionHandler>('ConnectionHandler')properly handles theT | undefinedreturn and allows overridingcreateEdgeState.packages/html/stories/ShowRegion.stories.ts (1)
109-109: Non-null assertion applied correctly for plugin retrieval
The!ongetPlugin<PopupMenuHandler>('PopupMenuHandler')ensures the menu handler is defined before callingisMenuShowing(), satisfying the updated return type.packages/html/stories/FixedPoints.stories.ts (1)
148-151: LGTM: Non-null assertion properly handles the new getPlugin signature.The addition of the non-null assertion operator (
!) correctly addresses the breaking change wheregetPluginnow returnsT | undefined. This assumes theConnectionHandlerplugin is always available, which is reasonable for this story context.However, consider adding a defensive check if there's any possibility the plugin might not be registered:
override isConnectableCell(cell: Cell) { - return this.graph - .getPlugin<ConnectionHandler>('ConnectionHandler')! - .isConnectableCell(cell); + const connectionHandler = this.graph.getPlugin<ConnectionHandler>('ConnectionHandler'); + return connectionHandler?.isConnectableCell(cell) ?? false; }packages/html/stories/PageBreaks.stories.ts (1)
79-82: LGTM: Improved naming and proper handling of new getPlugin signature.The changes improve code clarity in two ways:
Better variable naming: Renaming
graphHandlertoselectionHandleris more descriptive and aligns with the actual plugin type being retrieved.Proper null handling: The non-null assertion (
!) correctly addresses the breaking change wheregetPluginnow returnsT | undefined.The variable name change is consistent with similar updates across other story files mentioned in the AI summary, promoting consistent naming conventions.
packages/core/src/editor/Editor.ts (1)
2392-2402: LGTM: Clean simplification with proper undefined handling.The removal of the explicit type annotation
: PanningHandleris a good simplification since TypeScript can infer the type from the generic parameter.Unlike other files in this PR that use non-null assertions, this implementation correctly handles the potential
undefinedreturn value using optional chaining (panningHandler &&) in the subsequent conditional statements (lines 2394, 2397, 2400). This is a more defensive approach that gracefully handles cases where thePanningHandlerplugin might not be available.CHANGELOG.md (1)
15-16: Clear documentation of breaking change.The changelog entry accurately describes the breaking change and its impact on TypeScript users. The explanation clearly communicates that
getPluginnow explicitly returnsundefinedwhen a plugin is not found, requiring developers to handle this case.packages/html/stories/DynamicToolbar.stories.ts (1)
91-91:Details
❓ Verification inconclusive
Verify ConnectionHandler is included in default plugins.
The non-null assertion is appropriate in this story context, but please ensure that
ConnectionHandleris actually included in the plugins returned bygetDefaultPlugins().
🏁 Script executed:
#!/bin/bash # Verify that ConnectionHandler is included in getDefaultPlugins ast-grep --pattern 'function getDefaultPlugins() { $$$ }'Length of output: 61
#!/bin/bash set -e # Locate definition or references of getDefaultPlugins to inspect its returned plugin list rg -n -C5 "getDefaultPlugins" . # Check if ConnectionHandler is instantiated in the default plugins array rg -n -C5 "new ConnectionHandler" .
Please confirm that
ConnectionHandleris registered ingetDefaultPlugins()The non-null assertion on line 91 of the story is fine in this controlled context, but you should verify that
ConnectionHandleris actually part of the default plugin set:
- File: packages/html/stories/DynamicToolbar.stories.ts (line 91)
- Inspect the
getDefaultPlugins()implementation (e.g. inpackages/html/src/graph/plugins/…) to ensure it returns an instance ofConnectionHandler.packages/html/stories/Toolbar.stories.ts (1)
101-101: Consistent with other story files.The non-null assertion follows the same pattern as other story files and is appropriate in this controlled environment. The change maintains consistency across the codebase.
packages/html/stories/SwimLanes.stories.ts (2)
84-85: Good variable renaming for clarity.The renaming from
graphHandlertoselectionHandlerimproves code readability by making the variable purpose more explicit.
84-84: Verify plugin availability assumptions with non-null assertions.The non-null assertions (
!) assume that bothSelectionHandlerandConnectionHandlerplugins will always be available. If these plugins are not registered or available, this will cause runtime errors.Consider whether these assumptions are safe in this context, or if defensive programming with optional chaining would be more appropriate.
Also applies to: 89-89
packages/html/stories/AutoLayout.stories.ts (1)
150-150:Details
✅ Verification successful
Verify plugin availability for non-null assertions.
The non-null assertions assume
ConnectionHandlerandPopupMenuHandlerplugins are always available. Ensure these plugins are guaranteed to be present in the graph configuration, or consider defensive programming approaches.Also applies to: 222-222
🏁 Script executed:
#!/bin/bash # Description: Check if these plugins are part of default plugins or explicitly added # Expected: Find evidence that these plugins are reliably available # Search for plugin registrations and default plugin configurations ast-grep --pattern $'getDefaultPlugins' echo "--- Searching for ConnectionHandler plugin references ---" rg -A 3 -B 3 "ConnectionHandler" --type ts echo "--- Searching for PopupMenuHandler plugin references ---" rg -A 3 -B 3 "PopupMenuHandler" --type tsLength of output: 54159
Non-null assertions are safe:
ConnectionHandler&PopupMenuHandlerare included ingetDefaultPlugins
- The default plugin list (
core/src/view/plugins/index.ts–getDefaultPlugins()) contains bothConnectionHandlerandPopupMenuHandler.- All of the stories under
packages/html/storiesusegetDefaultPlugins(), sograph.getPlugin<…>('…')!will never beundefinedin these contexts.No changes required.
packages/html/stories/ContextIcons.stories.ts (1)
121-121: Good variable renaming for clarity.The renaming from
graphHandlertoselectionHandlerimproves code readability and aligns with similar improvements in other story files.
|
ℹ️ SonarQube detects duplication but they are not introduced by this PR. |
|


Explicitly state that a plugin may not be available.
BREAKING CHANGE: The
AbstractGraph.getPluginmethod now explicitly returnsundefinedwhen a plugin is not found.TypeScript users must update their code to handle the
undefinedcase when calling this method.Summary by CodeRabbit