From df20826cbc1cfc9fd95f7e6ac48fe97f7febc992 Mon Sep 17 00:00:00 2001 From: mathuo Date: Thu, 16 Jul 2026 07:28:57 +0000 Subject: [PATCH] Fix safe SonarCloud issues (imports, assertions, optional chaining) Address the low-risk, mechanical SonarCloud findings surfaced on the code touched by the comment-cleanup pass. Behaviour is unchanged. - S3863 (x49): merge the multiple `from 'dockview'` imports in each enterprise service into a single import statement (same specifiers, no reordering of other imports). - S5906 (x10): use `toHaveLength(n)` instead of `expect(x.length).toBe(n)` in tests for clearer failure messages. - S6582 (x3): use optional chaining where it is exactly equivalent (`!service?.includes(...)`, `existing?.tagName`, `!parsed?.validUntil`). - S3358 (x2): extract a nested ternary into an if/else in the keyboard-nav focus-wrap calc. - S7762 (x1): `firstChild.remove()` instead of `removeChild(firstChild)`. - S6606 (x1): nullish coalescing (`visible ?? true`) instead of an `!== undefined` ternary. Deliberately left alone (documented so they are not mistaken for oversights): - S7747 (x4): the `for..of [...set]` spreads are defensive copies; the loop body mutates the collection (`_endSession`/`detach`/delete), so removing the copy would iterate over a mutating set. - S7758/S6035 (x3): `charCodeAt`/regex in the license checksum are byte-compatible with the key issuer; changing them risks validation drift. - S3735 (x2): the `void` operators mark intentionally-ignored promises. - S3776 (x9): cognitive-complexity refactors carry behavioural risk and are out of scope for a nit pass. - S2301, S4144, css S4666, S1135, and 3 further S6582: intentional design (boolean-flag method, typed never-fire event fallbacks, a deliberately split CSS ruleset, a real TODO) or would break type narrowing. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01UZrBEk48dxvwBR6VkskSxw --- .../dnd/pointer/pointerDropTarget.spec.ts | 4 +-- .../titlebar/advancedOverflowSeam.spec.ts | 2 +- .../dockview/dockviewComponent.spec.ts | 2 +- .../components/titlebar/tabGroupIndicator.ts | 2 +- .../src/dockview/dockviewComponent.ts | 2 +- .../dockview-core/src/gridview/branchNode.ts | 5 +--- .../src/__tests__/advancedOverflow.spec.ts | 2 +- .../src/__tests__/dropGuide.spec.ts | 2 +- .../src/__tests__/keyboardDocking.spec.ts | 10 +++---- .../src/advancedOverflowService.ts | 2 +- .../src/autoEdgeGroupService.ts | 8 +++--- .../src/autoHideEdgeGroupService.ts | 16 +++++++----- .../dockview-enterprise/src/contextMenu.ts | 15 ++++++----- .../src/dropGuideService.ts | 9 +++---- .../src/keyboardDockingService.ts | 17 ++++++------ .../src/keyboardNavigationService.ts | 26 +++++++++++-------- .../src/layoutHistoryService.ts | 6 +---- .../src/licenseValidator.ts | 2 +- .../src/multiRowTabsService.ts | 3 ++- .../src/smartGuidesService.ts | 8 +++--- 20 files changed, 73 insertions(+), 70 deletions(-) diff --git a/packages/dockview-core/src/__tests__/dnd/pointer/pointerDropTarget.spec.ts b/packages/dockview-core/src/__tests__/dnd/pointer/pointerDropTarget.spec.ts index 27fc21582..b2cb29a14 100644 --- a/packages/dockview-core/src/__tests__/dnd/pointer/pointerDropTarget.spec.ts +++ b/packages/dockview-core/src/__tests__/dnd/pointer/pointerDropTarget.spec.ts @@ -259,8 +259,8 @@ describe('PointerDropTarget', () => { (target as any)._onDragOver(makeDragEvent(10, 20)); expect( - element.getElementsByClassName('dv-drop-target-dropzone').length - ).toBe(0); + element.getElementsByClassName('dv-drop-target-dropzone') + ).toHaveLength(0); expect(target.state).toBeUndefined(); target.dispose(); diff --git a/packages/dockview-core/src/__tests__/dockview/components/titlebar/advancedOverflowSeam.spec.ts b/packages/dockview-core/src/__tests__/dockview/components/titlebar/advancedOverflowSeam.spec.ts index 8ff962e38..4d41c0f5e 100644 --- a/packages/dockview-core/src/__tests__/dockview/components/titlebar/advancedOverflowSeam.spec.ts +++ b/packages/dockview-core/src/__tests__/dockview/components/titlebar/advancedOverflowSeam.spec.ts @@ -86,7 +86,7 @@ describe('advanced overflow seam: free fallback (no module)', () => { // No search input in the free path. expect(body!.querySelector('.dv-tabs-overflow-search')).toBeNull(); // The clipped tabs render as rows. - expect(body!.querySelectorAll('.dv-tab').length).toBe(2); + expect(body!.querySelectorAll('.dv-tab')).toHaveLength(2); dockview.dispose(); }); diff --git a/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts b/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts index 687f775e4..37f7b0675 100644 --- a/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts +++ b/packages/dockview-core/src/__tests__/dockview/dockviewComponent.spec.ts @@ -319,7 +319,7 @@ describe('dockviewComponent', () => { const tabEls = Array.from( group.element.querySelectorAll('.dv-tab') ) as HTMLElement[]; - expect(tabEls.length).toBe(2); + expect(tabEls).toHaveLength(2); // each tab element resolves to a distinct panel const r1 = group.model.getPanelForTab(tabEls[0]); diff --git a/packages/dockview-core/src/dockview/components/titlebar/tabGroupIndicator.ts b/packages/dockview-core/src/dockview/components/titlebar/tabGroupIndicator.ts index f23ea7c91..18a9fd3b1 100644 --- a/packages/dockview-core/src/dockview/components/titlebar/tabGroupIndicator.ts +++ b/packages/dockview-core/src/dockview/components/titlebar/tabGroupIndicator.ts @@ -609,7 +609,7 @@ abstract class BaseTabGroupIndicator implements ITabGroupIndicator { path: SVGPathElement; } { const existing = underline.firstElementChild as SVGSVGElement | null; - if (existing && existing.tagName === 'svg') { + if (existing?.tagName === 'svg') { return { svg: existing, path: existing.firstElementChild as SVGPathElement, diff --git a/packages/dockview-core/src/dockview/dockviewComponent.ts b/packages/dockview-core/src/dockview/dockviewComponent.ts index 03c0c5868..f6c3ccece 100644 --- a/packages/dockview-core/src/dockview/dockviewComponent.ts +++ b/packages/dockview-core/src/dockview/dockviewComponent.ts @@ -3172,7 +3172,7 @@ export class DockviewComponent value: boolean | undefined ): void { const service = this._edgeGroupService; - if (!service || !service.includes(group)) { + if (!service?.includes(group)) { return; } service.setAutoHide(group, value); diff --git a/packages/dockview-core/src/gridview/branchNode.ts b/packages/dockview-core/src/gridview/branchNode.ts index 0612be321..c0d53cc84 100644 --- a/packages/dockview-core/src/gridview/branchNode.ts +++ b/packages/dockview-core/src/gridview/branchNode.ts @@ -179,10 +179,7 @@ export class BranchNode extends CompositeDisposable implements IView { // Honour an explicit `visible` flag for branch children // too (not just leaves), so a hidden sub-grid restores // hidden with its cached size rather than visible at 0. - visible: - childDescriptor.visible !== undefined - ? childDescriptor.visible - : true, + visible: childDescriptor.visible ?? true, }; }), size: this.orthogonalSize, diff --git a/packages/dockview-enterprise/src/__tests__/advancedOverflow.spec.ts b/packages/dockview-enterprise/src/__tests__/advancedOverflow.spec.ts index f8aa36f5a..c3cf46208 100644 --- a/packages/dockview-enterprise/src/__tests__/advancedOverflow.spec.ts +++ b/packages/dockview-enterprise/src/__tests__/advancedOverflow.spec.ts @@ -459,7 +459,7 @@ describe('advanced overflow: full component integration', () => { expect(body).toBeTruthy(); expect(body!.querySelector('.dv-tabs-overflow-search')).toBeTruthy(); // scope 'group' (search: true) => every tab is reachable. - expect(body!.querySelectorAll('[role="option"]').length).toBe(4); + expect(body!.querySelectorAll('[role="option"]')).toHaveLength(4); dockview.dispose(); }); diff --git a/packages/dockview-enterprise/src/__tests__/dropGuide.spec.ts b/packages/dockview-enterprise/src/__tests__/dropGuide.spec.ts index 2403deaf0..df9e8ee6b 100644 --- a/packages/dockview-enterprise/src/__tests__/dropGuide.spec.ts +++ b/packages/dockview-enterprise/src/__tests__/dropGuide.spec.ts @@ -322,7 +322,7 @@ describe('drop guide', () => { // 9 cells but inner+outer of a direction share a position, so the veto // (which can fire onUnhandledDragOver) must run at most once per position. - expect(gateCalls.length).toBe(new Set(gateCalls).size); + expect(gateCalls).toHaveLength(new Set(gateCalls).size); expect(gateCalls.length).toBeLessThanOrEqual(5); }); diff --git a/packages/dockview-enterprise/src/__tests__/keyboardDocking.spec.ts b/packages/dockview-enterprise/src/__tests__/keyboardDocking.spec.ts index 594c16870..2279d1046 100644 --- a/packages/dockview-enterprise/src/__tests__/keyboardDocking.spec.ts +++ b/packages/dockview-enterprise/src/__tests__/keyboardDocking.spec.ts @@ -216,8 +216,8 @@ describe('accessibility: keyboard docking', () => { make(true); dockview.addPanel({ id: 'p1', component: 'default', title: 'P1' }); dockview.addPanel({ id: 'p2', component: 'default', title: 'P2' }); - expect(dockview.groups.length).toBe(1); // p1, p2 tabs in one group - expect(dockview.floatingGroups.length).toBe(0); + expect(dockview.groups).toHaveLength(1); // p1, p2 tabs in one group + expect(dockview.floatingGroups).toHaveLength(0); fireEvent.keyDown(dockview.element, { key: 'm', ctrlKey: true }); expect(region().textContent).toContain('Moving P2'); @@ -230,7 +230,7 @@ describe('accessibility: keyboard docking', () => { }); expect(region().textContent).toBe('P2 floated.'); - expect(dockview.floatingGroups.length).toBe(1); + expect(dockview.floatingGroups).toHaveLength(1); }); test('float is rebindable', () => { @@ -245,11 +245,11 @@ describe('accessibility: keyboard docking', () => { ctrlKey: true, shiftKey: true, }); - expect(dockview.floatingGroups.length).toBe(0); + expect(dockview.floatingGroups).toHaveLength(0); // the rebound key does fireEvent.keyDown(dockview.element, { key: 'f', altKey: true }); - expect(dockview.floatingGroups.length).toBe(1); + expect(dockview.floatingGroups).toHaveLength(1); }); test('does nothing when keyboardNavigation is off (default)', () => { diff --git a/packages/dockview-enterprise/src/advancedOverflowService.ts b/packages/dockview-enterprise/src/advancedOverflowService.ts index be8dcdf5f..ae01f4880 100644 --- a/packages/dockview-enterprise/src/advancedOverflowService.ts +++ b/packages/dockview-enterprise/src/advancedOverflowService.ts @@ -247,7 +247,7 @@ export class OverflowListView extends CompositeDisposable { const { context } = this.params; while (this._list.firstChild) { - this._list.removeChild(this._list.firstChild); + this._list.firstChild.remove(); } this._rows = []; diff --git a/packages/dockview-enterprise/src/autoEdgeGroupService.ts b/packages/dockview-enterprise/src/autoEdgeGroupService.ts index 824a18b60..f2cafa15f 100644 --- a/packages/dockview-enterprise/src/autoEdgeGroupService.ts +++ b/packages/dockview-enterprise/src/autoEdgeGroupService.ts @@ -1,5 +1,5 @@ -import { DockviewCompositeDisposable as CompositeDisposable } from 'dockview'; import { + DockviewCompositeDisposable as CompositeDisposable, DockviewWillDropEvent, DockviewWillShowOverlayLocationEvent, EdgeGroupPosition, @@ -9,9 +9,11 @@ import { PositionResolver, PositionResolverArgs, PositionResolverResult, + defineModule, + EdgeGroupModule, + IAutoEdgeGroupHost, + IAutoEdgeGroupService, } from 'dockview'; -import { defineModule, EdgeGroupModule } from 'dockview'; -import { IAutoEdgeGroupHost, IAutoEdgeGroupService } from 'dockview'; /** * Distance (px) from the content-area edge within which a drop docks as an diff --git a/packages/dockview-enterprise/src/autoHideEdgeGroupService.ts b/packages/dockview-enterprise/src/autoHideEdgeGroupService.ts index 4ccb47df0..577cbb30c 100644 --- a/packages/dockview-enterprise/src/autoHideEdgeGroupService.ts +++ b/packages/dockview-enterprise/src/autoHideEdgeGroupService.ts @@ -1,17 +1,19 @@ -import { DockviewCompositeDisposable as CompositeDisposable } from 'dockview'; -import { DockviewGroupPanel } from 'dockview'; -import { IDockviewPanel } from 'dockview'; -import { EdgeGroupPosition } from 'dockview'; -import { EdgeGroupPeekOptions } from 'dockview'; -import { defineModule, EdgeGroupModule } from 'dockview'; import { + DockviewCompositeDisposable as CompositeDisposable, + DockviewGroupPanel, + IDockviewPanel, + EdgeGroupPosition, + EdgeGroupPeekOptions, + defineModule, + EdgeGroupModule, createCloseButton, createDismissableLayer, createPinButton, prefersReducedMotion, resolveOpaqueBackground, + IAutoHideEdgeGroupHost, + IAutoHideEdgeGroupService, } from 'dockview'; -import { IAutoHideEdgeGroupHost, IAutoHideEdgeGroupService } from 'dockview'; /** Height (px) of the title bar; the content/`always` overlay is inset below it * so nothing paints under the bar. The title bar's own height is set inline to diff --git a/packages/dockview-enterprise/src/contextMenu.ts b/packages/dockview-enterprise/src/contextMenu.ts index 4731ce9b6..48cb51aba 100644 --- a/packages/dockview-enterprise/src/contextMenu.ts +++ b/packages/dockview-enterprise/src/contextMenu.ts @@ -1,15 +1,16 @@ -import { findRelativeZIndexParent } from 'dockview'; -import { DockviewGroupPanel } from 'dockview'; -import { IDockviewPanel } from 'dockview'; import { + findRelativeZIndexParent, + DockviewGroupPanel, + IDockviewPanel, BuiltInChipContextMenuItem, ContextMenuItemConfig, ContextMenuItem, + ITabGroup, + TabGroupColorPalette, + defineModule, + IContextMenuHost, + IContextMenuService, } from 'dockview'; -import { ITabGroup } from 'dockview'; -import { TabGroupColorPalette } from 'dockview'; -import { defineModule } from 'dockview'; -import { IContextMenuHost, IContextMenuService } from 'dockview'; function popoverZIndexFor(target: EventTarget | null): string | undefined { if (!(target instanceof HTMLElement)) { diff --git a/packages/dockview-enterprise/src/dropGuideService.ts b/packages/dockview-enterprise/src/dropGuideService.ts index 49ff21c06..4f4470529 100644 --- a/packages/dockview-enterprise/src/dropGuideService.ts +++ b/packages/dockview-enterprise/src/dropGuideService.ts @@ -1,18 +1,17 @@ import { DockviewCompositeDisposable as CompositeDisposable, DockviewIDisposable as IDisposable, -} from 'dockview'; -import { DockviewGroupPanel, DockviewWillShowOverlayLocationEvent, Position, PositionResolver, PositionResolverArgs, PositionResolverResult, + defineModule, + IDropGuideHost, + IDropGuideService, + AdvancedDnDModule, } from 'dockview'; -import { defineModule } from 'dockview'; -import { IDropGuideHost, IDropGuideService } from 'dockview'; -import { AdvancedDnDModule } from 'dockview'; /** Size (px) of each compass cell + the gap between them. */ const CELL = 38; diff --git a/packages/dockview-enterprise/src/keyboardDockingService.ts b/packages/dockview-enterprise/src/keyboardDockingService.ts index f9ab6bc36..9586e0011 100644 --- a/packages/dockview-enterprise/src/keyboardDockingService.ts +++ b/packages/dockview-enterprise/src/keyboardDockingService.ts @@ -1,15 +1,16 @@ import { DockviewCompositeDisposable as CompositeDisposable, DockviewIDisposable as IDisposable, + Position, + DockviewGroupPanel, + IDockviewPanel, + resolveMessages, + defineModule, + AdvancedDnDModule, + LiveRegionModule, + IKeyboardNavigationHost, + IKeyboardDockingService, } from 'dockview'; -import { Position } from 'dockview'; -import { DockviewGroupPanel } from 'dockview'; -import { IDockviewPanel } from 'dockview'; -import { resolveMessages } from 'dockview'; -import { defineModule } from 'dockview'; -import { AdvancedDnDModule } from 'dockview'; -import { LiveRegionModule } from 'dockview'; -import { IKeyboardNavigationHost, IKeyboardDockingService } from 'dockview'; import { bindDocumentListeners, KEYBOARD_MOVE_ATTRIBUTE, diff --git a/packages/dockview-enterprise/src/keyboardNavigationService.ts b/packages/dockview-enterprise/src/keyboardNavigationService.ts index 6d3e32646..9aeb6f803 100644 --- a/packages/dockview-enterprise/src/keyboardNavigationService.ts +++ b/packages/dockview-enterprise/src/keyboardNavigationService.ts @@ -1,8 +1,12 @@ -import { DockviewCompositeDisposable as CompositeDisposable } from 'dockview'; -import { DockviewGroupPanel } from 'dockview'; -import { DockviewKeybindings, KeyboardNavigationOptions } from 'dockview'; -import { defineModule } from 'dockview'; -import { IKeyboardNavigationHost, IKeyboardNavigationService } from 'dockview'; +import { + DockviewCompositeDisposable as CompositeDisposable, + DockviewGroupPanel, + DockviewKeybindings, + KeyboardNavigationOptions, + defineModule, + IKeyboardNavigationHost, + IKeyboardNavigationService, +} from 'dockview'; import { bindDocumentListeners, KEYBOARD_MOVE_ATTRIBUTE, @@ -205,12 +209,12 @@ export class KeyboardNavigationService const index = active instanceof HTMLElement ? tabbables.indexOf(active) : -1; const n = tabbables.length; - const next = - index === -1 - ? e.shiftKey - ? n - 1 - : 0 - : (index + (e.shiftKey ? -1 : 1) + n) % n; + let next: number; + if (index === -1) { + next = e.shiftKey ? n - 1 : 0; + } else { + next = (index + (e.shiftKey ? -1 : 1) + n) % n; + } tabbables[next].focus(); return true; } diff --git a/packages/dockview-enterprise/src/layoutHistoryService.ts b/packages/dockview-enterprise/src/layoutHistoryService.ts index 0df49aa5e..7731d5c18 100644 --- a/packages/dockview-enterprise/src/layoutHistoryService.ts +++ b/packages/dockview-enterprise/src/layoutHistoryService.ts @@ -2,15 +2,11 @@ import { DockviewCompositeDisposable as CompositeDisposable, DockviewEmitter as Emitter, DockviewEvent as Event, -} from 'dockview'; -import { DockviewComponentOptions, DockviewLayoutMutationEvent, DockviewLayoutMutationKind, SerializedDockview, -} from 'dockview'; -import { defineModule } from 'dockview'; -import { + defineModule, ILayoutHistoryHost, ILayoutHistoryService, LayoutHistoryChangeEvent, diff --git a/packages/dockview-enterprise/src/licenseValidator.ts b/packages/dockview-enterprise/src/licenseValidator.ts index 1764ee914..91bf50660 100644 --- a/packages/dockview-enterprise/src/licenseValidator.ts +++ b/packages/dockview-enterprise/src/licenseValidator.ts @@ -194,7 +194,7 @@ export function validateLicense( return 'missing'; } const parsed = parseLicenseKey(key); - if (!parsed || !parsed.validUntil) { + if (!parsed?.validUntil) { return 'invalid'; } // Compare at UTC-date granularity (keys carry dates, not times): a build diff --git a/packages/dockview-enterprise/src/multiRowTabsService.ts b/packages/dockview-enterprise/src/multiRowTabsService.ts index 2c0ecf7fa..462065a43 100644 --- a/packages/dockview-enterprise/src/multiRowTabsService.ts +++ b/packages/dockview-enterprise/src/multiRowTabsService.ts @@ -7,8 +7,9 @@ import { OVERFLOW_MAX_TAB_ROWS_VARIABLE as MAX_ROWS_VAR, OVERFLOW_WRAP_TABS_VERTICAL_TAB_HEIGHT_VARIABLE as VERTICAL_TAB_HEIGHT_VAR, defineModule, + IMultiRowTabsHost, + IMultiRowTabsService, } from 'dockview'; -import { IMultiRowTabsHost, IMultiRowTabsService } from 'dockview'; function isWrapMode(overflow: DockviewOverflowOptions | undefined): boolean { return typeof overflow === 'object' && overflow?.mode === 'wrap'; diff --git a/packages/dockview-enterprise/src/smartGuidesService.ts b/packages/dockview-enterprise/src/smartGuidesService.ts index 0fa4ac9c7..850474cd1 100644 --- a/packages/dockview-enterprise/src/smartGuidesService.ts +++ b/packages/dockview-enterprise/src/smartGuidesService.ts @@ -2,8 +2,6 @@ import { DockviewCompositeDisposable as CompositeDisposable, DockviewEmitter as Emitter, DockviewEvent as Event, -} from 'dockview'; -import { Box, DockviewGroupPanel, DragModifiers, @@ -13,9 +11,11 @@ import { SmartGuidesSnapPosition, SmartGuidesSnapTogetherEvent, SnapModifier, + defineModule, + FloatingGroupModule, + ISmartGuidesHost, + ISmartGuidesService, } from 'dockview'; -import { defineModule, FloatingGroupModule } from 'dockview'; -import { ISmartGuidesHost, ISmartGuidesService } from 'dockview'; /** Fraction of the perpendicular extents that must overlap for an edge to read * as an adjacency (dock-beside) rather than a glancing touch. */