Skip to content

Conversation

@nawabfurquan
Copy link
Collaborator

@nawabfurquan nawabfurquan commented Nov 6, 2025

  • Upgraded React and it's type versions from 18 to 19
  • Removed wdyr and visx due to incompatibility with React 19
  • Upgraded mobx, mobx-react-lite and lucide-react dependencies

Summary by CodeRabbit

  • Updates
    • Upgraded React to v19 and updated related type packages; updated several UI and state-management dependencies.
  • Chores
    • Removed a runtime rendering-debugging tool and a charting dependency.
  • Bug Fixes / Behavior
    • Chart axis rendering remains disabled and chart visuals are degraded as a result.
  • Compatibility
    • Various component refs and dialog typings adjusted for safer nullability handling.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 6, 2025

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This PR removes the visx visualization dependency and why-did-you-render integration, upgrades React and its types to v19, updates several dependencies (lucide-react, mobx, mobx-react-lite), widens multiple internal ref/nullability types, and comments out visx-based axis rendering with warning logs.

Changes

Cohort / File(s) Summary
Dependencies
package.json
Removed @visx/visx and @welldone-software/why-did-you-render; upgraded react / react-dom to ^19.2.0 and @types/react / @types/react-dom to ^19.2.0; updated lucide-react, mobx, and mobx-react-lite.
WDYR integration
src/modules/wdyr.ts
Fully commented out dynamic import and initialization of why-did-you-render; preserved explanatory comments about React 19 incompatibility.
Axis / visx-related charting
src/react/components/AxisComponent.tsx, src/react/components/DeckScatterComponent.tsx, src/react/components/VivScatterComponent.tsx
Removed/commented visx axis/scale imports and rendering; scales disabled; added a useEffect warning about missing visx; adjusted ref initializations to nullable in scatter components.
Ref nullability / typing tweaks
src/charts/dialogs/ChatDialog.tsx, src/react/components/ColumnDropdownComponent.tsx, src/react/components/PopoutWindow.tsx, src/react/components/SelectionDialogComponent.tsx, src/react/components/SettingsDialogComponent.tsx, src/react/hooks.ts, src/react/components/TextFieldExtended.tsx, src/react/components/BaseReactChart.tsx, src/react/react_utils.tsx, src/react/screen_state.tsx
Widened/refined types to allow null for various useRef usages and RefObject parameters; switched several JSX.Element types to React.JSX.Element; updated hook signatures to accept nullable refs; OuterContainerProvider props now use PropsWithChildren.

Sequence Diagram(s)

sequenceDiagram
  participant App as App / Entry
  participant WDYR_old as dynamic import('why-did-you-render')
  participant WDYR_new as (commented out)

  rect rgb(220,235,255)
    note right of App: Previous runtime flow
    App->>WDYR_old: dynamic import & init on URL param
    WDYR_old-->>App: whyDidYouRender enabled
  end

  rect rgb(255,235,220)
    note right of App: New runtime flow (this PR)
    App->>WDYR_new: (no-op, code commented out)
    App-->>App: console.warn('WDYR disabled / React 19 incompatible')
  end
Loading
sequenceDiagram
  participant Chart as Chart components
  participant visx_old as visx Axis/Scale
  participant visx_new as (removed)

  rect rgb(220,235,255)
    note right of Chart: Previous: axis rendered
    Chart->>visx_old: create scales & AxisBottom/AxisLeft
    visx_old-->>Chart: axis elements
  end

  rect rgb(255,235,220)
    note right of Chart: New: axis disabled
    Chart->>visx_new: (imports and axis calls commented)
    Chart-->>Chart: console.warn('visx removed - axis disabled')
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Areas to focus:
    • React 19 upgrade surface changes (types and semantics) across components and providers.
    • visx removal: ensure dependent features (charts, deck components) handle missing axis/scales gracefully.
    • Hook and ref signature changes (nullable refs) — check call sites for corresponding null-safety handling.

Possibly related PRs

Suggested reviewers

  • martinSergeant

Poem

🐰
I nudged the axes out of sight,
and shelved WDYR for the night.
React nineteen hops in with grace,
types adjusted, refs embrace.
A little warning, a gentle thump—charts rest while I munch a clump.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Upgrade frontend deps' is concise and directly describes the main change—upgrading frontend dependencies including React 19, mobx, lucide-react, and removing incompatible packages.

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@nawabfurquan
Copy link
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 6, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/react/components/AxisComponent.tsx (1)

122-159: Axis rendering completely disabled.

All axis rendering is commented out, leaving scatter plots without axes. This is related to the visx removal issue flagged in package.json.

This should be tracked as part of the visx migration effort. The component needs a complete reimplementation of axis rendering using an alternative approach.

🧹 Nitpick comments (1)
src/react/components/AxisComponent.tsx (1)

17-28: Remove or document unused helper function.

The getLabelProps function is defined but never called since all axis rendering is commented out. Consider either removing it or adding a TODO comment indicating it will be needed when axis rendering is restored.

Apply this diff to add context:

 //! todo - get label options working... for now, we ignore rotate_labels
 // not generally relevant to scatterplot, but will be for other charts
+//! Currently unused due to visx removal - will be needed when axis rendering is restored
 function getLabelProps(axisConfig?: AxisConfig) {
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aaf2922 and ecf9598.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (3)
  • package.json (2 hunks)
  • src/modules/wdyr.ts (1 hunks)
  • src/react/components/AxisComponent.tsx (4 hunks)
🧰 Additional context used
🧠 Learnings (15)
📓 Common learnings
Learnt from: xinaesthete
Repo: Taylor-CCB-Group/MDV PR: 0
File: :0-0
Timestamp: 2025-04-09T21:07:38.683Z
Learning: When building interactive data visualizations in React that require D3.js functionality (like brushing/zooming), using a dedicated bridging library like visx (visx/brush) is preferable to manual DOM manipulation. It eliminates circular update issues and complex state management by providing React components that abstract D3's imperative approach.
Learnt from: xinaesthete
Repo: Taylor-CCB-Group/MDV PR: 0
File: :0-0
Timestamp: 2025-04-09T20:29:56.245Z
Learning: When working with D3.js in React components, it's appropriate to intentionally exclude certain dependencies from useEffect dependency arrays to prevent unnecessary recreation of D3 elements. This approach should be documented with explanatory comments and ESLint suppression comments like `// eslint-disable-next-line react-hooks/exhaustive-deps` to clarify the intentional exclusion.
Learnt from: xinaesthete
Repo: Taylor-CCB-Group/MDV PR: 0
File: :0-0
Timestamp: 2025-03-17T15:08:26.492Z
Learning: The project uses MobX for state management in chart configurations, with `mobxAutorun` and reaction disposers to manage reactive updates based on changes to linked data sources.
Learnt from: xinaesthete
Repo: Taylor-CCB-Group/MDV PR: 162
File: src/react/components/AxisComponent.tsx:58-67
Timestamp: 2025-04-23T09:25:51.968Z
Learning: When implementing deck.gl components in React, axis scales can appear to be a frame behind due to synchronization issues between deck.gl's render cycle and React's component updates.
Learnt from: xinaesthete
Repo: Taylor-CCB-Group/MDV PR: 162
File: src/react/components/AxisComponent.tsx:46-46
Timestamp: 2025-04-23T09:26:52.252Z
Learning: In React dependency arrays, conditional expressions (e.g., `is2d && config.axis.x.size`) can be necessary for type safety when some properties might not exist on all possible types. In these cases, extracting safe values before useMemo or adding a lint exception with explanation is preferred.
📚 Learning: 2025-04-09T20:29:56.245Z
Learnt from: xinaesthete
Repo: Taylor-CCB-Group/MDV PR: 0
File: :0-0
Timestamp: 2025-04-09T20:29:56.245Z
Learning: When working with D3.js in React components, it's appropriate to intentionally exclude certain dependencies from useEffect dependency arrays to prevent unnecessary recreation of D3 elements. This approach should be documented with explanatory comments and ESLint suppression comments like `// eslint-disable-next-line react-hooks/exhaustive-deps` to clarify the intentional exclusion.

Applied to files:

  • src/modules/wdyr.ts
  • src/react/components/AxisComponent.tsx
📚 Learning: 2025-04-09T20:54:14.593Z
Learnt from: xinaesthete
Repo: Taylor-CCB-Group/MDV PR: 0
File: :0-0
Timestamp: 2025-04-09T20:54:14.593Z
Learning: In React components that use D3.js for visualization, preventing infinite update loops requires strategic flags (like updatingProgrammatically.current) to track the source of updates, minimal debouncing for store updates, and setTimeout(0) to reset flags asynchronously after programmatic UI changes.

Applied to files:

  • src/modules/wdyr.ts
  • src/react/components/AxisComponent.tsx
📚 Learning: 2025-05-30T11:08:52.145Z
Learnt from: nawabfurquan
Repo: Taylor-CCB-Group/MDV PR: 208
File: src/react/components/PopoutWindow.tsx:11-11
Timestamp: 2025-05-30T11:08:52.145Z
Learning: For PopoutWindow components in React with MUI, use async/await with Promise.all to fetch and inline CSS content from link tags in parallel, combined with a loading state that hides content until all CSS operations complete, to avoid blank screens and provide better user experience.

Applied to files:

  • src/modules/wdyr.ts
📚 Learning: 2025-04-23T09:26:52.252Z
Learnt from: xinaesthete
Repo: Taylor-CCB-Group/MDV PR: 162
File: src/react/components/AxisComponent.tsx:46-46
Timestamp: 2025-04-23T09:26:52.252Z
Learning: In React dependency arrays, conditional expressions (e.g., `is2d && config.axis.x.size`) can be necessary for type safety when some properties might not exist on all possible types. In these cases, extracting safe values before useMemo or adding a lint exception with explanation is preferred.

Applied to files:

  • src/modules/wdyr.ts
  • src/react/components/AxisComponent.tsx
📚 Learning: 2025-05-30T10:51:04.022Z
Learnt from: nawabfurquan
Repo: Taylor-CCB-Group/MDV PR: 208
File: src/react/components/PopoutWindow.tsx:11-11
Timestamp: 2025-05-30T10:51:04.022Z
Learning: For PopoutWindow components in React with MUI, avoid async CSS fetching operations as they cause blank screens. Instead, copy link tags directly and rely on Emotion CacheProvider for CSS-in-JS styling to work properly in popout windows.

Applied to files:

  • src/modules/wdyr.ts
📚 Learning: 2025-04-09T21:07:38.683Z
Learnt from: xinaesthete
Repo: Taylor-CCB-Group/MDV PR: 0
File: :0-0
Timestamp: 2025-04-09T21:07:38.683Z
Learning: When building interactive data visualizations in React that require D3.js functionality (like brushing/zooming), using a dedicated bridging library like visx (visx/brush) is preferable to manual DOM manipulation. It eliminates circular update issues and complex state management by providing React components that abstract D3's imperative approach.

Applied to files:

  • package.json
  • src/react/components/AxisComponent.tsx
📚 Learning: 2025-04-23T09:25:51.968Z
Learnt from: xinaesthete
Repo: Taylor-CCB-Group/MDV PR: 162
File: src/react/components/AxisComponent.tsx:58-67
Timestamp: 2025-04-23T09:25:51.968Z
Learning: When implementing deck.gl components in React, axis scales can appear to be a frame behind due to synchronization issues between deck.gl's render cycle and React's component updates.

Applied to files:

  • src/react/components/AxisComponent.tsx
📚 Learning: 2025-05-22T11:21:43.866Z
Learnt from: nawabfurquan
Repo: Taylor-CCB-Group/MDV PR: 202
File: src/charts/TableChart.js:18-19
Timestamp: 2025-05-22T11:21:43.866Z
Learning: In the TableChart component, the condition for including the index column was changed from `config.include_index !== false` to `config?.include_index`, making it opt-in rather than opt-out. This means the index column is only included when explicitly enabled rather than being included by default.

Applied to files:

  • src/react/components/AxisComponent.tsx
📚 Learning: 2025-03-17T15:08:26.492Z
Learnt from: xinaesthete
Repo: Taylor-CCB-Group/MDV PR: 0
File: :0-0
Timestamp: 2025-03-17T15:08:26.492Z
Learning: The project uses MobX for state management in chart configurations, with `mobxAutorun` and reaction disposers to manage reactive updates based on changes to linked data sources.

Applied to files:

  • src/react/components/AxisComponent.tsx
📚 Learning: 2025-06-04T10:03:08.161Z
Learnt from: xinaesthete
Repo: Taylor-CCB-Group/MDV PR: 220
File: src/charts/CategoryChart.js:22-22
Timestamp: 2025-06-04T10:03:08.161Z
Learning: In the MDV project, React hooks provide a more natural and simpler pattern for MobX integration compared to class-based charts, as hooks automatically handle lifecycle management and cleanup in a way that aligns better with reactive programming patterns.

Applied to files:

  • src/react/components/AxisComponent.tsx
📚 Learning: 2025-04-23T08:48:20.916Z
Learnt from: xinaesthete
Repo: Taylor-CCB-Group/MDV PR: 162
File: src/react/components/DeckScatterReactWrapper.tsx:42-44
Timestamp: 2025-04-23T08:48:20.916Z
Learning: In DeckScatterReactWrapper.tsx, properties like maxZoom and minZoom in viewState are currently treated as constants rather than user-configurable settings, and are not preserved during serialization in getConfig().

Applied to files:

  • src/react/components/AxisComponent.tsx
📚 Learning: 2025-04-23T15:23:31.175Z
Learnt from: xinaesthete
Repo: Taylor-CCB-Group/MDV PR: 162
File: src/react/contour_state.ts:173-175
Timestamp: 2025-04-23T15:23:31.175Z
Learning: The `useContour` hook in `src/react/contour_state.ts` doesn't handle undefined parameters gracefully, which can cause issues when used with conditional returns in parent hooks.

Applied to files:

  • src/react/components/AxisComponent.tsx
📚 Learning: 2025-06-04T10:19:55.042Z
Learnt from: xinaesthete
Repo: Taylor-CCB-Group/MDV PR: 220
File: src/charts/CategoryChart.js:0-0
Timestamp: 2025-06-04T10:19:55.042Z
Learning: In the MDV codebase, BaseChart.ts provides automatic disposer management through the `mobxAutorun()` method which tracks disposers in a `reactionDisposers` array and automatically disposes them in the `remove()` method. When using direct `reaction()` calls instead of `mobxAutorun()`, manual disposer management is required.

Applied to files:

  • src/react/components/AxisComponent.tsx
📚 Learning: 2025-06-04T09:41:18.914Z
Learnt from: xinaesthete
Repo: Taylor-CCB-Group/MDV PR: 220
File: src/charts/CategoryChart.js:17-17
Timestamp: 2025-06-04T09:41:18.914Z
Learning: In the MDV project, BaseChart.mobxAutorun automatically stores the disposer and runs it when the chart is destroyed, eliminating the need for manual cleanup of MobX autorun reactions in derived chart classes.

Applied to files:

  • src/react/components/AxisComponent.tsx
🔇 Additional comments (3)
src/modules/wdyr.ts (1)

5-22: Appropriate approach for dev-only tool.

Disabling Why Did You Render due to React 19 incompatibility is reasonable since it's a development-only debugging tool that doesn't affect production functionality.

package.json (2)

95-96: Critical: visx removal breaks axis rendering with no alternative provided.

The upgrade to React 19 requires removing visx, but this breaks core visualization functionality in AxisComponent. According to project learnings, visx was the preferred solution for bridging React and D3.js for interactive visualizations. Based on learnings.

This PR leaves charts without functional axes and provides no migration path or alternative implementation.

Possible solutions:

  1. Wait for visx to release React 19 compatible version
  2. Implement custom D3-based axis rendering (would require careful handling of React/D3 integration patterns)
  3. Explore alternative React-compatible visualization libraries (recharts, nivo, victory)

Would you like me to:

  • Search for React 19 compatible visualization libraries?
  • Generate a custom D3 axis implementation following React best practices?
  • Check visx's roadmap for React 19 support?

Also applies to: 135-136

⛔ Skipped due to learnings
Learnt from: xinaesthete
Repo: Taylor-CCB-Group/MDV PR: 0
File: :0-0
Timestamp: 2025-04-09T21:07:38.683Z
Learning: When building interactive data visualizations in React that require D3.js functionality (like brushing/zooming), using a dedicated bridging library like visx (visx/brush) is preferable to manual DOM manipulation. It eliminates circular update issues and complex state management by providing React components that abstract D3's imperative approach.
Learnt from: xinaesthete
Repo: Taylor-CCB-Group/MDV PR: 162
File: src/react/components/AxisComponent.tsx:58-67
Timestamp: 2025-04-23T09:25:51.968Z
Learning: When implementing deck.gl components in React, axis scales can appear to be a frame behind due to synchronization issues between deck.gl's render cycle and React's component updates.
Learnt from: xinaesthete
Repo: Taylor-CCB-Group/MDV PR: 0
File: :0-0
Timestamp: 2025-04-03T10:26:40.958Z
Learning: In the MDV repository, there's a cross-browser issue where react-based viv charts don't have proper mouse events attached when combining popout and fullscreen functionality, even though they work correctly with either popout or fullscreen individually.
Learnt from: xinaesthete
Repo: Taylor-CCB-Group/MDV PR: 0
File: :0-0
Timestamp: 2025-04-09T20:29:56.245Z
Learning: When working with D3.js in React components, it's appropriate to intentionally exclude certain dependencies from useEffect dependency arrays to prevent unnecessary recreation of D3 elements. This approach should be documented with explanatory comments and ESLint suppression comments like `// eslint-disable-next-line react-hooks/exhaustive-deps` to clarify the intentional exclusion.
Learnt from: xinaesthete
Repo: Taylor-CCB-Group/MDV PR: 0
File: :0-0
Timestamp: 2025-04-03T10:26:40.958Z
Learning: In the MDV repository, there's a Safari-specific bug when popping out vanilla-js charts to a separate window and then making them fullscreen. The axes often only occupy the width of the popped-out window, while GL content is sometimes clipped to that width.

87-87: Lucide-react may lack official React 19 support; verify compatibility in your environment.

Current versions: lucide-react ^0.552.0, mobx ^6.13.7, mobx-react-lite ^4.1.1

The web search indicates lucide-react has no released version officially declaring React 19 support, though maintainers are tracking it. The mobx (6.13.7) and mobx-react-lite (4.1.1) versions align with the latest stable releases compatible with React 19.

Test lucide-react components thoroughly in your React 19 setup to ensure they function as expected before merging.

// import * as Axis from "@visx/axis";
// import * as Scale from "@visx/scale";

//! This component is broken as the visx dependency is uninstalled due to incompatibility with React 19 version
Copy link
Contributor

@coderabbitai coderabbitai bot Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Track the broken component with a GitHub issue.

The comment indicates this component is broken, but there's no tracking issue linked. This critical functionality loss should be tracked and prioritized.

Would you like me to create a GitHub issue to track the restoration of axis rendering functionality?

🤖 Prompt for AI Agents
In src/react/components/AxisComponent.tsx around line 9 the file contains a note
that the component is broken because the visx dependency was removed for React
19; create a GitHub issue in the repo to track restoring axis rendering (title,
description with reproduction steps, affected versions, and suggested options:
reintroduce compatible visx, find alternative lib, or implement custom axis),
then add a one-line TODO comment in this file referencing the new issue number
(e.g. "TODO: track restore of AxisComponent - GH#123") and update the PR
description to link the new issue so the loss of functionality is triaged and
prioritized.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Create GitHub issue to track AxisComponent restoration

gh issue create \
  --repo Taylor-CCB-Group/MDV \
  --title "AxisComponent broken after visx removal for React 19 compatibility" \
  --body "## Problem

The \`AxisComponent\` in \`src/react/components/AxisComponent.tsx\` is currently non-functional after the visx dependency was removed during the React 19 upgrade (PR #278).

## Context

- **Affected PR**: #278
- **Cause**: visx library is incompatible with React 19
- **Impact**: Axis rendering (x-axis and y-axis) is completely disabled in scatter plots

## Current State

All axis rendering functionality is commented out:
- \`@visx/axis\` imports are commented
- \`@visx/scale\` imports are commented  
- Scale creation (\`scaleX\`, \`scaleY\`) is disabled
- Axis components (\`AxisBottom\`, \`AxisLeft\`) are not rendered
- A warning is logged: \"This is a broken chart, visx dependency has been removed\"

## Reproduction

1. Open any 2D scatter plot in the application
2. Observe that axis labels, tick marks, and scales are not rendered
3. Check browser console for the warning message

## Suggested Solutions

1. **Wait for visx React 19 compatibility**: Monitor visx repository for React 19 support and reintroduce when available
2. **Alternative library**: Evaluate other axis/scale libraries compatible with React 19 (e.g., recharts, victory, custom D3.js wrapper)
3. **Custom implementation**: Implement custom axis rendering using SVG elements and D3.js scales directly

## Files Affected

- \`src/react/components/AxisComponent.tsx\`

## Related

- PR: https://github.com/Taylor-CCB-Group/MDV/pull/278
- Comment: https://github.com/Taylor-CCB-Group/MDV/pull/278#discussion_r2499948979" \
  --assignee nawabfurquan

Length of output: 50


@nawabfurquan, I've created GitHub issue #280 to track the broken AxisComponent functionality:

🔗 #280

The issue includes:

  • Detailed description of the problem (axis rendering disabled due to visx removal)
  • Context about React 19 incompatibility
  • Reproduction steps
  • Three suggested solutions:
    1. Wait for visx React 19 compatibility
    2. Evaluate alternative libraries (recharts, victory, custom D3.js wrapper)
    3. Implement custom axis rendering
  • Links back to PR Upgrade frontend deps #278 and this comment thread

The issue has been assigned to you for tracking and prioritization.

/)/) 

(^.^)
/>📋 Issue created!


🧬 Code Graph Analysis Results

[src/react/scatter_state.ts] (lines 77-85)

export type ScatterPlotConfig2D = ScatterPlotConfig & {
    dimension: "2d";
    axis: AxisConfig2D;
    viewState: OrthographicViewState;
};

[src/react/scatter_state.ts] (lines 82-85)

export type ScatterPlotConfig3D = ScatterPlotConfig & {
    dimension: "3d";
    viewState: OrbitViewState;
};

[src/react/hooks.ts] (lines 100-126)

export function useParamColumns(): LoadedDataColumn<DataType>[] {
    const chart = useChart();
    const { columnIndex } = chart.dataStore;
    const columns = useMemo(() => {
        const param = chart.config.param;
        if (!param) return [];
        if (typeof param === "string")
            return [columnIndex[param]];
        // we could make sure they are loaded as well...
        if (!isArray(param)) {
            //must be a query object...
            //!no - in that case, it should be a one-element array with a query object
            // return param.columns;
            throw new Error("config.param should always be an array");
        }
        // const param = chart.config.param as FieldName[]; // up for review with query objects etc.
        //@ts-expect-error non-string 'name' as index; if we had 'concrete fieldName' version of config.param?
        return param.map((name) => columnIndex[name]);
    }, [chart.config.param, columnIndex]) as DataColumn<DataType>[];
    // note that columns is 'any' here as of this writing
    // - so this isn't an exhaustive check and ts will have limited capacity to help us.
    // but we should be fairly safe to assume that once we get past here, we have `LoadedDataColumn`s
    if (!allColumnsLoaded(columns)) {
        throw new Error("we always expect that param columns are loaded by the time we try to use them... this shouldn't happen");
    }
    return columns;
}

[src/react/hooks.ts] (lines 36-50)

export function useChartSize() {
    const chart = useChart();
    // return chart.config.size; // not so well behaved?
    const div = chart.contentDiv;
    const [size, setSize] = useState([div.clientWidth, div.clientHeight]);
    useLayoutEffect(() => {
        const resize = () => {
            setSize([div.clientWidth, div.clientHeight]);
        };
        const observer = new ResizeObserver(resize);
        observer.observe(div);
        return () => observer.unobserve(div);
    }, [div]);
    return size;
}

[src/charts/charts.d.ts] (lines 68-116)

export type DataColumn<T extends DataType> = {
    /** human-readable column name, to be displayed in GUI etc */
    name: ColumnName; //nb - we should check use of 'name' vs 'field'
    /** id of the column, used internally */
    field: FieldName;
    /** the datatype- can be one of
     *   - `"double"` - any floating point data
     *   - `"integer"` - any integer data
     *   - `"text"` - data containing strings but with no more than 256 categories
     *   - `"text16"` - data containing strings with up to 65536 categories
     *   - `"unique"` - data containing strings but with many categories
     *   - `"multitext"` -
     */
    datatype: T;
    /** whether the column's data can be changed */
    editable?: boolean;
    /**In the case of a double/integer (number) column, the array
     * buffer should be the appropriate size to contain float32s. For text it should be Uint8
     * and contain numbers corresponding to the indexes in the values parameter. For a column of
     * type unique it should be a JavaScript array. This parameter is optional as the data can
     * be added later see {@link DataStore#setColumnData}.
     * A {@link LoadedDataColumn<T>} can be used to represent a column that is known to have loaded data.
     */
    data?: DataStructureTypes[T];
    values: T extends CategoricalDataType ? string[] : never; //probably wrong for 'unique'
    /** An array of rgb hex colors. In the case of a `"text"` column the `colors` should match the `values`. For number columns, the list represents
     * colors that will be interpolated. If not specified, default color pallettes will be supplied.
     */
    colors?: Colors;
    /** if `true` then the colors will be displayed on a log scale- useful if the dataset contains outliers.
     * Because a symlog scale is used the data can contain 0 and negative values */
    colorLogScale?: boolean;
    /** the column's values will be displayed as links (text and unique columns only).
     * not sure if this is strictly boolean or can be undefined */
    is_url?: T extends CategoricalDataType ? boolean : never;
    /** the min max values in the column's values (integer/double only) */
    minMax: T extends NumberDataType ? [number, number] : never;
    /** an object describing the 0.05,0.01 and 0,001 qunatile ranges (integer/double only) */
    quantiles: T extends NumberDataType ? Quantiles : never;
    /** if `true` then the store will keep a record that this column has been added and is not permanently stored in the backend */
    dirty?: boolean;
    /** return the value corresponding to a given row index `i`. If the data is categorical, this will be the appropriate value from `values` */
    getValue: (i: number) => T extends CategoricalDataType ? string : number;
    stringLength: T extends "unique" ? number : never;
    delimiter?: T extends "multitext" ? string : never;
    subgroup?: SubgroupName; //not attempting to descriminate the other sg properties being related to this for now
    sgindex?: number;
    sgtype?: "dense" | "sparse"; //?? any other options?
};

🧠 Learnings used
Learnt from: xinaesthete
Repo: Taylor-CCB-Group/MDV PR: 162
File: src/react/components/AxisComponent.tsx:58-67
Timestamp: 2025-04-23T09:25:51.968Z
Learning: When implementing deck.gl components in React, axis scales can appear to be a frame behind due to synchronization issues between deck.gl's render cycle and React's component updates.

Learnt from: xinaesthete
Repo: Taylor-CCB-Group/MDV PR: 162
File: src/react/components/AxisComponent.tsx:46-46
Timestamp: 2025-04-23T09:26:52.252Z
Learning: In React dependency arrays, conditional expressions (e.g., `is2d && config.axis.x.size`) can be necessary for type safety when some properties might not exist on all possible types. In these cases, extracting safe values before useMemo or adding a lint exception with explanation is preferred.

Learnt from: xinaesthete
Repo: Taylor-CCB-Group/MDV PR: 0
File: :0-0
Timestamp: 2025-03-17T15:08:26.492Z
Learning: The project uses MobX for state management in chart configurations, with `mobxAutorun` and reaction disposers to manage reactive updates based on changes to linked data sources.

Learnt from: xinaesthete
Repo: Taylor-CCB-Group/MDV PR: 0
File: :0-0
Timestamp: 2025-04-09T20:29:56.245Z
Learning: When working with D3.js in React components, it's appropriate to intentionally exclude certain dependencies from useEffect dependency arrays to prevent unnecessary recreation of D3 elements. This approach should be documented with explanatory comments and ESLint suppression comments like `// eslint-disable-next-line react-hooks/exhaustive-deps` to clarify the intentional exclusion.

Learnt from: xinaesthete
Repo: Taylor-CCB-Group/MDV PR: 220
File: src/charts/CategoryChart.js:22-22
Timestamp: 2025-06-04T10:03:08.161Z
Learning: In the MDV project, React hooks provide a more natural and simpler pattern for MobX integration compared to class-based charts, as hooks automatically handle lifecycle management and cleanup in a way that aligns better with reactive programming patterns.

Learnt from: xinaesthete
Repo: Taylor-CCB-Group/MDV PR: 0
File: :0-0
Timestamp: 2025-04-09T14:32:53.708Z
Learning: When implementing React components, the MobX `observer()` wrapper is only necessary for components that directly access MobX observables or stores. Components that exclusively use props and local React state (useState, useRef, etc.) don't need to be wrapped with observer() as they will re-render appropriately when props change through normal React mechanisms.

Learnt from: xinaesthete
Repo: Taylor-CCB-Group/MDV PR: 0
File: :0-0
Timestamp: 2025-04-09T14:32:53.708Z
Learning: In React components that don't directly access MobX observables or stores, the `observer()` wrapper is unnecessary. Components that only receive data via props and use standard React hooks for internal state don't need to be MobX observers, as they will re-render appropriately when props change.

Learnt from: xinaesthete
Repo: Taylor-CCB-Group/MDV PR: 0
File: :0-0
Timestamp: 2025-04-09T21:07:38.683Z
Learning: When building interactive data visualizations in React that require D3.js functionality (like brushing/zooming), using a dedicated bridging library like visx (visx/brush) is preferable to manual DOM manipulation. It eliminates circular update issues and complex state management by providing React components that abstract D3's imperative approach.

Learnt from: xinaesthete
Repo: Taylor-CCB-Group/MDV PR: 220
File: src/charts/CategoryChart.js:0-0
Timestamp: 2025-06-04T10:19:55.042Z
Learning: In the MDV codebase, BaseChart.ts provides automatic disposer management through the `mobxAutorun()` method which tracks disposers in a `reactionDisposers` array and automatically disposes them in the `remove()` method. When using direct `reaction()` calls instead of `mobxAutorun()`, manual disposer management is required.

Learnt from: xinaesthete
Repo: Taylor-CCB-Group/MDV PR: 115
File: src/react/components/ColumnSelectionComponent.tsx:165-165
Timestamp: 2025-03-03T15:53:04.505Z
Learning: In complex MobX-React components like those in the MDV codebase, creating intermediate objects (like `props2 = ...`) without making them observable can lead to read-only props in child components. This can be intentional to control the flow of state updates and avoid circular reactions.

Learnt from: nawabfurquan
Repo: Taylor-CCB-Group/MDV PR: 202
File: src/charts/TableChart.js:18-19
Timestamp: 2025-05-22T11:21:43.866Z
Learning: In the TableChart component, the condition for including the index column was changed from `config.include_index !== false` to `config?.include_index`, making it opt-in rather than opt-out. This means the index column is only included when explicitly enabled rather than being included by default.

If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

Comment on lines +115 to +117
useEffect(() => {
console.warn("This is a broken chart, visx dependency has been removed");
}, []);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Display visible warning to users, not just console.

A console warning is insufficient for a broken chart. Users won't see it, leading to confusion about missing axes. Render a visible overlay or error message in the UI.

Apply this diff to show a user-visible warning:

     } as const), [chartWidth, chartHeight, margin.top, margin.left]);
-    // useEffect(() => {
-    //     if (is2d && (config.axis.x.rotate_labels || config.axis.y.rotate_labels)) {
-    //         console.warn("Axis rotation not implemented for react charts");
-    //     }
-    // }, [is2d]);
-    useEffect(() => {
-        console.warn("This is a broken chart, visx dependency has been removed");
-    }, []);
+    
     return (
         <>
+            {is2d && (
+                <div style={{
+                    position: "absolute",
+                    top: 0,
+                    left: 0,
+                    right: 0,
+                    padding: "8px",
+                    backgroundColor: "rgba(255, 165, 0, 0.9)",
+                    color: "black",
+                    fontSize: "12px",
+                    zIndex: 1000,
+                    textAlign: "center"
+                }}>
+                    ⚠️ Axes temporarily disabled due to React 19 upgrade (visx incompatibility)
+                </div>
+            )}
             <div style={deckStyle}>{children}</div>

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/react/components/AxisComponent.tsx around lines 115 to 117, replace the
console.warn-only effect with rendering a visible user-facing warning: add
component state (e.g., hasBrokenChartWarning) or a prop-controlled flag and
render an overlay/banner inside the component DOM when true that clearly informs
users the chart axes are unavailable and the visx dependency was removed; ensure
the warning is styled to be prominent, accessible (role="alert"), and
dismissible if appropriate, and remove or keep the console.warn for developer
logs as secondary output.

@nawabfurquan
Copy link
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 7, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@xinaesthete
Copy link
Collaborator

At some point we accidentally indirectly installed another version of @luma.gl/core here. Looking at npm why @luma.gl/core, this part is relevant

@luma.gl/core@9.2.4
node_modules/@deck.gl-community/layers/node_modules/@luma.gl/core
  @luma.gl/core@"^9.0.11" from @deck.gl-community/layers@9.0.3

We should only have one version, which should currently be 9.11.9. It appears that this change happened with 00ffb68 when we installed React 19, not sure why yet.

@xinaesthete
Copy link
Collaborator

@coderabbitai can you see why 00ffb68 caused another version of @luma.gl/core to be installed when the version of @deck.gl-community/layers and other deck/luma related dependencies haven't changed?

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 8, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash

echo "=== Examining commit 00ffb68 ==="
git show 00ffb68 --stat

echo -e "\n=== Checking package.json changes in commit 00ffb68 ==="
git show 00ffb68:package.json > /tmp/package-new.json
git show 00ffb68^:package.json > /tmp/package-old.json
echo "--- Changes to package.json ---"
diff -u /tmp/package-old.json /tmp/package-new.json || true

echo -e "\n=== Checking @deck.gl-community/layers in current package.json ==="
cat package.json | jq '.dependencies["@deck.gl-community/layers"]'

echo -e "\n=== Checking @luma.gl/core references in package-lock.json ==="
cat package-lock.json | jq '[.. | objects | select(has("node_modules/@luma.gl/core"))] | length'

echo -e "\n=== Checking all @luma.gl/core entries in package-lock.json ==="
cat package-lock.json | jq -r '
  .. | 
  objects | 
  select(.name == "@luma.gl/core") | 
  "Version: \(.version), from: \(input_filename)"
' 2>/dev/null || cat package-lock.json | grep -A 5 '"@luma.gl/core"' | head -40

echo -e "\n=== Checking @deck.gl-community/layers peer dependencies ==="
npm view @deck.gl-community/layers@9.0.3 peerDependencies --json

Length of output: 1529


🏁 Script executed:

#!/bin/bash

echo "=== Detailed @luma.gl/core analysis in package-lock.json ==="
# Get all luma.gl/core entries from the lock file
node -e '
const fs = require("fs");
const lock = JSON.parse(fs.readFileSync("package-lock.json", "utf8"));

function findLumaCore(obj, path = []) {
  let results = [];
  
  if (obj && typeof obj === "object") {
    for (let key in obj) {
      if (key === "@luma.gl/core" || (obj.name === "@luma.gl/core")) {
        results.push({
          path: path.join(" > "),
          version: obj.version || obj[key]?.version,
          resolved: obj.resolved || obj[key]?.resolved
        });
      }
      results = results.concat(findLumaCore(obj[key], [...path, key]));
    }
  }
  
  return results;
}

const results = findLumaCore(lock);
console.log(JSON.stringify(results, null, 2));
'

echo -e "\n=== Checking npm version ==="
npm --version

echo -e "\n=== Checking if overrides or resolutions are set ==="
cat package.json | jq '.overrides // .resolutions // "none"'

Length of output: 2733


🏁 Script executed:

#!/bin/bash

echo "=== Extracting @luma.gl/core version details from package-lock.json ==="
node -e '
const fs = require("fs");
const lock = JSON.parse(fs.readFileSync("package-lock.json", "utf8"));

// Get top-level @luma.gl/core
if (lock.packages && lock.packages["node_modules/@luma.gl/core"]) {
  console.log("Top-level @luma.gl/core:");
  console.log("  Version:", lock.packages["node_modules/@luma.gl/core"].version);
  console.log("");
}

// Get nested @luma.gl/core under @deck.gl-community/layers
const nestedPath = "node_modules/@deck.gl-community/layers/node_modules/@luma.gl/core";
if (lock.packages && lock.packages[nestedPath]) {
  console.log("Nested @luma.gl/core (under @deck.gl-community/layers):");
  console.log("  Version:", lock.packages[nestedPath].version);
  console.log("");
}

// Check @deck.gl-community/layers dependencies
const layersPath = "node_modules/@deck.gl-community/layers";
if (lock.packages && lock.packages[layersPath]) {
  console.log("@deck.gl-community/layers@9.0.3 info:");
  if (lock.packages[layersPath].dependencies) {
    console.log("  Dependencies:");
    for (const [dep, ver] of Object.entries(lock.packages[layersPath].dependencies)) {
      if (dep.includes("luma") || dep.includes("deck.gl")) {
        console.log("    " + dep + ": " + ver);
      }
    }
  }
  if (lock.packages[layersPath].peerDependencies) {
    console.log("  Peer Dependencies:");
    for (const [dep, ver] of Object.entries(lock.packages[layersPath].peerDependencies)) {
      if (dep.includes("luma") || dep.includes("deck.gl") || dep.includes("react")) {
        console.log("    " + dep + ": " + ver);
      }
    }
  }
}
'

echo -e "\n=== Checking React peer dependencies in @deck.gl-community/layers ==="
npm view @deck.gl-community/layers@9.0.3 peerDependencies

echo -e "\n=== Checking if nested deck.gl/core exists ==="
node -e '
const fs = require("fs");
const lock = JSON.parse(fs.readFileSync("package-lock.json", "utf8"));

const nestedDeckCorePath = "node_modules/@deck.gl-community/layers/node_modules/@deck.gl/core";
if (lock.packages && lock.packages[nestedDeckCorePath]) {
  console.log("Nested @deck.gl/core found under @deck.gl-community/layers:");
  console.log("  Version:", lock.packages[nestedDeckCorePath].version);
  console.log("  Dependencies:");
  if (lock.packages[nestedDeckCorePath].dependencies) {
    for (const [dep, ver] of Object.entries(lock.packages[nestedDeckCorePath].dependencies)) {
      if (dep.includes("luma")) {
        console.log("    " + dep + ": " + ver);
      }
    }
  }
} else {
  console.log("No nested @deck.gl/core found");
}
'

Length of output: 871


🏁 Script executed:

#!/bin/bash

echo "=== Checking package-lock.json changes in commit 00ffb68 for @luma.gl/core ==="
# Compare before and after the commit
git show 00ffb68^:package-lock.json > /tmp/lock-before.json
git show 00ffb68:package-lock.json > /tmp/lock-after.json

echo "Checking @luma.gl/core in lock file BEFORE React 19:"
node -e '
const fs = require("fs");
const lock = JSON.parse(fs.readFileSync("/tmp/lock-before.json", "utf8"));

const nestedPath = "node_modules/@deck.gl-community/layers/node_modules/@luma.gl/core";
if (lock.packages && lock.packages[nestedPath]) {
  console.log("  NESTED @luma.gl/core existed: version", lock.packages[nestedPath].version);
} else {
  console.log("  NESTED @luma.gl/core did NOT exist");
}

if (lock.packages && lock.packages["node_modules/@luma.gl/core"]) {
  console.log("  TOP-LEVEL @luma.gl/core: version", lock.packages["node_modules/@luma.gl/core"].version);
}
'

echo -e "\nChecking @luma.gl/core in lock file AFTER React 19:"
node -e '
const fs = require("fs");
const lock = JSON.parse(fs.readFileSync("/tmp/lock-after.json", "utf8"));

const nestedPath = "node_modules/@deck.gl-community/layers/node_modules/@luma.gl/core";
if (lock.packages && lock.packages[nestedPath]) {
  console.log("  NESTED @luma.gl/core exists: version", lock.packages[nestedPath].version);
} else {
  console.log("  NESTED @luma.gl/core does NOT exist");
}

if (lock.packages && lock.packages["node_modules/@luma.gl/core"]) {
  console.log("  TOP-LEVEL @luma.gl/core: version", lock.packages["node_modules/@luma.gl/core"].version);
}
'

Length of output: 348


🏁 Script executed:

#!/bin/bash

echo "=== Checking top-level @deck.gl/core version ==="
node -e '
const fs = require("fs");
const lock = JSON.parse(fs.readFileSync("package-lock.json", "utf8"));

if (lock.packages && lock.packages["node_modules/@deck.gl/core"]) {
  console.log("Top-level @deck.gl/core:");
  console.log("  Version:", lock.packages["node_modules/@deck.gl/core"].version);
  if (lock.packages["node_modules/@deck.gl/core"].peerDependencies) {
    console.log("  Peer Dependencies:");
    for (const [dep, ver] of Object.entries(lock.packages["node_modules/@deck.gl/core"].peerDependencies)) {
      console.log("    " + dep + ": " + ver);
    }
  }
}
'

echo -e "\n=== Checking React peer dependencies in deck.gl packages ==="
npm view @deck.gl/core@9.1.9 peerDependencies
echo ""
npm view @deck.gl/core@9.2.2 peerDependencies

echo -e "\n=== Checking if @deck.gl-community/layers has React peer deps ==="
npm view @deck.gl-community/layers@9.0.3 peerDependencies

echo -e "\n=== Understanding why npm created nested installations ==="
# Check if the nested deck.gl/core satisfies the dependency requirement
echo "@deck.gl-community/layers@9.0.3 requires: @deck.gl/core ^9.0.12"
echo "Top-level @deck.gl/core version is 9.1.9"
echo "But npm installed nested @deck.gl/core 9.2.2"
echo ""
echo "This suggests npm's peer dependency resolution changed behavior."
echo "Let's check the full dependency chain:"

node -e '
const fs = require("fs");
const lock = JSON.parse(fs.readFileSync("package-lock.json", "utf8"));

// Check what depends on @deck.gl/core at top level
console.log("\nPackages that depend on @deck.gl/core:");
for (const [path, pkg] of Object.entries(lock.packages || {})) {
  if (pkg.dependencies && pkg.dependencies["@deck.gl/core"]) {
    const name = path.split("/").pop() || path;
    console.log("  " + name + " requires: " + pkg.dependencies["@deck.gl/core"]);
  }
}
'

Length of output: 630


🏁 Script executed:

#!/bin/bash

echo "=== Checking npm lockfile version and algorithm ==="
node -e '
const fs = require("fs");
const lock = JSON.parse(fs.readFileSync("package-lock.json", "utf8"));
console.log("Lockfile version:", lock.lockfileVersion);
'

echo -e "\n=== Simulating dependency resolution explanation ==="
echo "The issue occurs because of how npm v7+ handles peer dependencies."
echo ""
echo "When React was upgraded from 18 to 19:"
echo "1. npm re-evaluated the entire dependency tree"
echo "2. Some deck.gl packages may have React as a peer dependency"
echo "3. npm v7+ automatically installs peer dependencies"
echo "4. If peer dependency versions conflict, npm creates nested installations"
echo ""
echo "Let's verify if deck.gl packages have React peer dependencies:"

npm view @deck.gl/react@9.0.12 peerDependencies 2>/dev/null || echo "Could not fetch @deck.gl/react peer deps"

Length of output: 610


🏁 Script executed:

#!/bin/bash

echo "=== Checking what pulled in the newer deck.gl versions ==="
node -e '
const fs = require("fs");
const lock = JSON.parse(fs.readFileSync("package-lock.json", "utf8"));

const nestedDeckCore = lock.packages["node_modules/@deck.gl-community/layers/node_modules/@deck.gl/core"];
console.log("Nested @deck.gl/core@9.2.2:");
console.log("  Resolved from:", nestedDeckCore.resolved);

// Check what packages depend on this nested version
console.log("\n@deck.gl-community/layers dependencies that might have pulled newer versions:");
const layersPath = "node_modules/@deck.gl-community/layers";
if (lock.packages[layersPath] && lock.packages[layersPath].dependencies) {
  for (const [dep, ver] of Object.entries(lock.packages[layersPath].dependencies)) {
    if (dep.includes("@deck.gl") || dep.includes("@luma.gl")) {
      console.log("  " + dep + ": " + ver);
    }
  }
}
'

echo -e "\n=== Checking what version ranges allow for the mismatch ==="
echo "The root cause analysis:"
echo "1. Top-level deck.gl: 9.1.11 (explicitly specified in package.json)"
echo "2. @deck.gl-community/layers@9.0.3 requires @deck.gl/core ^9.0.12"
echo "3. The ^9.0.12 range allows any 9.x version"
echo ""
echo "Let's check if React 19 upgrade triggered re-evaluation of peer dependencies:"

# Check deck.gl version in package.json
cat package.json | jq -r '.dependencies["deck.gl"]'

echo -e "\n=== Checking deck.gl package.json to see what it specifies ==="
npm view deck.gl@9.1.11 dependencies | grep -E "@deck.gl|@luma.gl" || echo "Could not fetch"

Length of output: 1396


🏁 Script executed:

#!/bin/bash

echo "=== Root cause investigation ==="
echo "The issue is related to npm's peer dependency resolution algorithm in npm v7+"
echo ""
echo "Here's what happened:"
echo ""
echo "BEFORE React 19 (with React 18.3):"
echo "  - npm could hoist all deck.gl and luma.gl packages to the top level"
echo "  - All peer dependencies were satisfied"
echo "  - Single version of @luma.gl/core@9.1.9"
echo ""
echo "AFTER React 19 (with React 19.2.0):"
echo "  - npm re-evaluated the entire dependency tree"
echo "  - npm's algorithm determined that it needed to create nested installations"
echo "  - This might be due to version conflicts or peer dependency chains"
echo ""
echo "The most likely cause:"
echo "  When React 19 was installed, npm updated the resolution algorithm"
echo "  and found that some packages under @deck.gl-community/layers"
echo "  needed different versions than what was at the top level."
echo ""

echo "Let's verify the deck.gl dependency structure:"
npm ls @luma.gl/core 2>&1 | head -30

Length of output: 858


@xinaesthete, I've investigated the issue. Here's what's happening:

Root Cause

When React 19 was installed, npm v7+'s automatic peer dependency resolution re-evaluated the entire dependency tree, and this caused a change in how dependencies were resolved.

Before React 19 (commit 00ffb68^):

  • Single top-level @luma.gl/core@9.1.9
  • All packages were hoisted to the top level
  • No nested installations

After React 19 (commit 00ffb68):

  • Top-level @luma.gl/core@9.1.9 remains
  • New nested @luma.gl/core@9.2.4 under @deck.gl-community/layers
  • New nested @deck.gl/core@9.2.2 under @deck.gl-community/layers

Why This Happened

The issue stems from version range conflicts:

  1. Top-level deck.gl@~9.1.11 specifies:

    • @deck.gl/core: 9.1.11 (exact)
    • @luma.gl/core: ^9.1.5 (caret range)
  2. @deck.gl-community/layers@9.0.3 specifies:

    • @deck.gl/core: ^9.0.12 (caret range - allows 9.x)
    • @deck.gl/react: ^9.0.12 (which has React peer dependencies)
    • @luma.gl/core: ^9.0.11 (caret range)

When React was upgraded, npm's resolution algorithm re-evaluated peer dependencies throughout the tree. Since @deck.gl-community/layers depends on @deck.gl/react@^9.0.12 (which declares React as a peer dependency), the algorithm decided to create separate installations to satisfy all constraints.

npm favors updates by default, even when doing so causes duplication, and the peer dependency chain with React 19 triggered this behavior.

Solution

Use npm's overrides field to force a single version of @luma.gl/core:

{
  "overrides": {
    "@luma.gl/core": "9.1.9"
  }
}

Add this to your package.json, then run:

rm -rf node_modules package-lock.json
npm install

This will force all packages to use the same version of @luma.gl/core, regardless of their version ranges.

Would you like me to create a script to verify this fix resolves the duplicate installation issue?

@xinaesthete
Copy link
Collaborator

@coderabbitai lots of other issues reported by npm i when I try that:

npm warn ERESOLVE overriding peer dependency
npm warn While resolving: @deck.gl/widgets@9.2.2
npm warn Found: @luma.gl/core@9.2.4
npm warn node_modules/@deck.gl-community/layers/node_modules/@luma.gl/core
npm warn
npm warn Could not resolve dependency:
npm warn peer overridden @luma.gl/core@"9.1.9" (was "~9.2.2") from @deck.gl/widgets@9.2.2
npm warn node_modules/@deck.gl-community/layers/node_modules/@deck.gl/widgets
npm warn   peer @deck.gl/widgets@"~9.2.0" from @deck.gl/react@9.2.2
npm warn   node_modules/@deck.gl-community/layers/node_modules/@deck.gl/react
npm warn ERESOLVE overriding peer dependency
npm warn While resolving: @luma.gl/engine@9.2.4
npm warn Found: @luma.gl/core@9.2.4
npm warn node_modules/@deck.gl-community/layers/node_modules/@luma.gl/core
npm warn
npm warn Could not resolve dependency:
npm warn peer overridden @luma.gl/core@"9.1.9" (was "~9.2.0") from @luma.gl/engine@9.2.4
npm warn node_modules/@deck.gl-community/layers/node_modules/@luma.gl/engine
npm warn   @luma.gl/engine@"^9.0.11" from @deck.gl-community/layers@9.0.3
npm warn   node_modules/@deck.gl-community/layers
npm warn   1 more (@deck.gl/core)
npm warn ERESOLVE overriding peer dependency
npm warn While resolving: @luma.gl/shadertools@9.2.4
npm warn Found: @luma.gl/core@9.2.4
npm warn node_modules/@deck.gl-community/layers/node_modules/@luma.gl/core
npm warn
npm warn Could not resolve dependency:
npm warn peer overridden @luma.gl/core@"9.1.9" (was "~9.2.0") from @luma.gl/shadertools@9.2.4
npm warn node_modules/@deck.gl-community/layers/node_modules/@luma.gl/shadertools
npm warn   @luma.gl/shadertools@"^9.2.2" from @deck.gl/core@9.2.2
npm warn   node_modules/@deck.gl-community/layers/node_modules/@deck.gl/core
npm warn   1 more (@luma.gl/engine)
npm warn ERESOLVE overriding peer dependency
npm warn While resolving: @luma.gl/webgl@9.2.4
npm warn Found: @luma.gl/core@9.2.4
npm warn node_modules/@deck.gl-community/layers/node_modules/@luma.gl/core
npm warn
npm warn Could not resolve dependency:
npm warn peer overridden @luma.gl/core@"9.1.9" (was "~9.2.0") from @luma.gl/webgl@9.2.4
npm warn node_modules/@deck.gl-community/layers/node_modules/@luma.gl/webgl
npm warn   @luma.gl/webgl@"^9.2.2" from @deck.gl/core@9.2.2
npm warn   node_modules/@deck.gl-community/layers/node_modules/@deck.gl/core

I also have another pnpm based repo with similar dependencies -including react-19, but not @deck.gl-community/layers. I added the community layers as a dependency there and it doesn't seem like it had any issues.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 8, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash

echo "=== Checking current deck.gl version in package.json ==="
cat package.json | jq -r '.dependencies["deck.gl"]'

echo -e "\n=== Checking what @deck.gl-community/layers@9.0.3 specifies ==="
npm view @deck.gl-community/layers@9.0.3 dependencies --json | jq -r 'to_entries[] | select(.key | contains("deck.gl") or contains("luma.gl")) | "\(.key): \(.value)"'

echo -e "\n=== Understanding why 9.2.x was installed under @deck.gl-community/layers ==="
node -e '
const fs = require("fs");
const lock = JSON.parse(fs.readFileSync("package-lock.json", "utf8"));

console.log("Nested @deck.gl packages under @deck.gl-community/layers:");
const layersBase = "node_modules/@deck.gl-community/layers/node_modules/";
for (const [path, pkg] of Object.entries(lock.packages || {})) {
  if (path.startsWith(layersBase) && path.includes("@deck.gl")) {
    const name = path.replace(layersBase, "").split("/node_modules/")[0];
    console.log("  " + name + "@" + pkg.version);
  }
}
'

echo -e "\n=== Checking top-level deck.gl dependencies ==="
npm view deck.gl@9.1.11 dependencies --json | jq -r 'to_entries[] | select(.key | contains("deck.gl") or contains("luma.gl")) | "\(.key): \(.value)"'

echo -e "\n=== Checking if upgrading deck.gl would help ==="
echo "Latest deck.gl versions:"
npm view deck.gl versions --json | jq -r '.[-10:][]'

echo -e "\n=== Checking what version satisfies both requirements ==="
echo "Top-level requires: ~9.1.11 (allows 9.1.x)"
echo "@deck.gl-community/layers requires: ^9.0.12 (allows 9.x)"
echo "Latest compatible would be latest 9.x that works with both"

Length of output: 1534


🏁 Script executed:

#!/bin/bash

echo "=== Checking if there are any React peer dependencies in deck.gl packages ==="
npm view @deck.gl/react@9.1.11 peerDependencies --json
echo ""
npm view @deck.gl/react@9.2.2 peerDependencies --json

echo -e "\n=== Checking compatibility of community layers with deck.gl 9.2.x ==="
echo "Does @deck.gl-community/layers@9.0.3 work with deck.gl 9.2.x?"
echo "Checking its peer dependencies:"
npm view @deck.gl-community/layers@9.0.3 peerDependencies --json

echo -e "\n=== Solution: Upgrade deck.gl to 9.2.2 ==="
echo "This would:"
echo "1. Align top-level deck.gl with the nested installations"
echo "2. Remove the version conflict"
echo "3. Potentially use React 19 compatible versions"
echo ""
echo "To upgrade, change package.json:"
echo '  "deck.gl": "~9.2.2"'
echo ""
echo "Then run:"
echo "  rm -rf node_modules package-lock.json"
echo "  npm install"

Length of output: 784


@xinaesthete, I've analyzed the issue. The overrides approach won't work cleanly because it's forcing an older version on packages that expect newer ones. Here's what's happening:

The Problem

When React 19 was installed, npm's resolution algorithm decided to install deck.gl 9.2.x packages under @deck.gl-community/layers because:

  1. Your top-level deck.gl@~9.1.11 restricts to 9.1.x versions
  2. @deck.gl-community/layers@9.0.3 uses ^9.0.12 (caret ranges), which allows any 9.x version
  3. npm chose to install the latest compatible versions (9.2.2 for deck.gl, 9.2.4 for luma.gl) to satisfy the community layers
  4. This creates version conflicts - @deck.gl/react@9.2.2 has peer dependencies expecting @deck.gl/core@~9.2.0, but the override forces 9.1.9

Why pnpm Works Better

pnpm uses a different peer dependency resolution strategy:

  • Stricter hoisting rules
  • Better deduplication algorithm
  • More predictable peer dependency resolution

This is why your pnpm repo doesn't have this issue.

Solutions

Option 1: Upgrade deck.gl to 9.2.2 (Recommended)

This aligns your top-level version with what npm wants to install:

{
  "dependencies": {
    "deck.gl": "~9.2.2"
  }
}

Then:

rm -rf node_modules package-lock.json
npm install

Both @deck.gl/react@9.1.11 and @deck.gl/react@9.2.2 support react >=16.3.0, so React 19 should work fine.

Option 2: Switch to pnpm

Since you've verified it works in another repo:

npm install -g pnpm
rm -rf node_modules package-lock.json
pnpm install

Update your scripts to use pnpm instead of npm.

Option 3: Accept the Duplication

If the application runs without issues, you could keep the duplicate versions. Multiple @luma.gl/core versions may cause bundle size bloat but might not break functionality.

Which approach would you prefer? I'd recommend upgrading to deck.gl 9.2.2 as the cleanest solution.

@xinaesthete
Copy link
Collaborator

@coderabbitai I will update the deck.gl version, but this is a much more involved process of making a PR for viv and publishing a new version. I intend to do that soon anyway, but it is out-of-scope here. There may be other benefits of switching to pnpm anyway as it seems generally better at handling monorepos, and it may be a good idea for us to move more towards that kind of structure in future.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 8, 2025

Tip

For best results, initiate chat on the files or code changes.

@xinaesthete, that sounds like a solid plan! Upgrading deck.gl when it involves coordinating with viv definitely makes sense as a separate effort.

For this PR, the duplicate @luma.gl/core versions should be acceptable if the application is functioning correctly. It will cause some bundle size bloat, but shouldn't break functionality.

Regarding the pnpm migration - you're right that it handles monorepos much better. A few considerations when you decide to make that switch:

  1. Workspace support: pnpm has excellent built-in workspace support with pnpm-workspace.yaml
  2. Migration: The transition is usually straightforward - just install pnpm globally, remove node_modules and package-lock.json, then run pnpm install
  3. CI/CD: You'll need to update any CI scripts to use pnpm instead of npm
  4. Hoisting: pnpm uses strict hoisting by default, which may surface some hidden dependency issues (usually a good thing)

Good luck with the viv PR! Let me know if you need any help when you tackle the deck.gl upgrade.

@xinaesthete
Copy link
Collaborator

@coderabbitai luma.gl is designed to detect if there are multiple versions installed, throw a runtime error and absolutely will not work without this being resolved.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 8, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

@xinaesthete, you're absolutely right - I apologize for the incorrect suggestion. If luma.gl throws runtime errors on version conflicts, this must be resolved.

Let me investigate solutions that work within your constraints (no deck.gl upgrade yet, no pnpm migration yet):


🏁 Script executed:

#!/bin/bash

echo "=== Solution 1: Check if explicitly adding @luma.gl/core as a dependency helps ==="
echo "Current @luma.gl/core in package.json:"
cat package.json | jq '.dependencies["@luma.gl/core"] // "not present"'

echo -e "\n=== Solution 2: Try comprehensive overrides for all deck.gl/luma.gl packages ==="
echo "We can override ALL the nested packages to force 9.1.x versions"
echo ""
echo "Checking what nested packages need overriding:"
node -e '
const fs = require("fs");
const lock = JSON.parse(fs.readFileSync("package-lock.json", "utf8"));

const nestedBase = "node_modules/@deck.gl-community/layers/node_modules/";
const nestedPackages = new Set();

for (const [path, pkg] of Object.entries(lock.packages || {})) {
  if (path.startsWith(nestedBase)) {
    const pkgName = path.replace(nestedBase, "").split("/node_modules/")[0];
    if (pkgName.startsWith("@deck.gl/") || pkgName.startsWith("@luma.gl/")) {
      nestedPackages.add(pkgName + "@" + pkg.version);
    }
  }
}

console.log("Nested packages that need overriding:");
Array.from(nestedPackages).sort().forEach(p => console.log("  " + p));
'

echo -e "\n=== Solution 3: Check what versions deck.gl@9.1.11 uses ==="
npm view deck.gl@9.1.11 dependencies --json | jq -r 'to_entries[] | select(.key | startswith("@deck.gl/") or startswith("@luma.gl/")) | "\(.key): \(.value)"'

echo -e "\n=== Recommended override configuration ==="
echo "Add this to package.json to force all packages to use 9.1.x versions:"
echo '{'
echo '  "overrides": {'
echo '    "@deck.gl/core": "9.1.11",'
echo '    "@deck.gl/layers": "9.1.11",'
echo '    "@deck.gl/geo-layers": "9.1.11",'
echo '    "@deck.gl/react": "9.1.11",'
echo '    "@deck.gl/widgets": "9.1.11",'
echo '    "@luma.gl/core": "9.1.9",'
echo '    "@luma.gl/engine": "9.1.9",'
echo '    "@luma.gl/shadertools": "9.1.9",'
echo '    "@luma.gl/webgl": "9.1.9"'
echo '  }'
echo '}'

Length of output: 1436


@xinaesthete, now I understand the severity. Here's the solution using comprehensive npm overrides to force all packages to 9.1.x versions and eliminate the duplicate installations:

Solution: Comprehensive npm Overrides

Add this overrides section to your package.json:

{
  "overrides": {
    "@deck.gl/core": "9.1.11",
    "@deck.gl/layers": "9.1.11",
    "@deck.gl/geo-layers": "9.1.11",
    "@deck.gl/react": "9.1.11",
    "@deck.gl/widgets": "9.1.11",
    "@luma.gl/core": "9.1.9",
    "@luma.gl/engine": "9.1.9",
    "@luma.gl/shadertools": "9.1.9",
    "@luma.gl/webgl": "9.1.9",
    "@luma.gl/constants": "9.1.9"
  }
}

Then run:

rm -rf node_modules package-lock.json
npm install

What This Does

This forces npm to use only the 9.1.x versions throughout the entire dependency tree, preventing the 9.2.x versions from being installed under @deck.gl-community/layers. You'll see some peer dependency warnings during npm install, but those are expected and won't cause runtime issues - the important thing is that there will be only one version of @luma.gl/core.

Verification

After running the install, verify with:

npm why @luma.gl/core

You should see only the 9.1.9 version listed.

This is a temporary solution until you can upgrade deck.gl to 9.2.x in the separate viv PR. The overrides can be removed once deck.gl is upgraded.

@xinaesthete
Copy link
Collaborator

@coderabbitai with the more extensive overrides and making all deck/luma related packages use explicit versions rather than ranges, it does npm i without ending up with multiple luma.gl/core versions, but there are masses of warnings in the process. I haven't checked build or runtime behaviour. I did try installing react-19 on top of that version - which is what originally caused the multiple versions to be installed - and that is no longer the case.

As for whether we move to pnpm... another consideration is that any users installing outside of docker would be required to have this in their system.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 8, 2025

Tip

For best results, initiate chat on the files or code changes.

@xinaesthete, excellent progress! The override solution is working as intended. Here's what's happening:

The Warnings Are Expected and Safe

You can ignore these warnings as npm is overriding the peer dependency. The "masses of warnings" you're seeing are npm informing you that it's forcing version resolutions that don't match what some packages declared in their peer dependencies. This is exactly what the overrides field is designed to do - it's a "sledgehammer" approach that prioritizes your explicit version choices over package semver requirements.

Since you've verified that:

  1. ✅ No duplicate @luma.gl/core versions are installed
  2. ✅ React 19 can be installed without causing duplicates to reappear

The approach is working correctly. The warnings won't cause runtime issues - they're just npm's way of saying "heads up, we're using version X even though package Y requested version Z."

Next Steps

Critical: Test the build and runtime behavior, as this is what matters. luma.gl will throw runtime errors if multiple versions exist, so if the application runs without luma.gl version conflicts, you're good to go. Run:

npm run vite-build
# Then test the application runtime

Regarding pnpm

You're right that pnpm requires users to have it installed. A few options:

  • Document both: Provide instructions for both npm and pnpm workflows
  • Docker-first: If most development happens in Docker, the local install burden may be minimal
  • Defer: Since the npm override solution works, you can postpone the pnpm migration until the deck.gl 9.2.x upgrade

The override approach is a solid interim solution until you can upgrade deck.gl and remove the overrides entirely.

Would you like me to help verify anything else about the setup, or shall we consider this issue resolved pending your build/runtime tests?

@xinaesthete
Copy link
Collaborator

@coderabbitai when I try applying the change to add those overrides on top of the latest commit here, I still end up with multiple luma versions. I can go back to the pre-react-19 revision, add the overrides and then install the updated react version. May be simpler to branch from there and apply changes, or maybe I'll get on with updating deck.gl and see how how smoothly that goes.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 8, 2025

Tip

For best results, initiate chat on the files or code changes.

@xinaesthete, you've identified a key issue - npm's dependency resolution is influenced by the existing lockfile state, and simply adding overrides after React 19 is already installed with the problematic nested structure may not fully re-resolve the tree.

Quick Fix to Try

Before branching or upgrading deck.gl, try this complete reset procedure on the current branch:

# Ensure overrides are in package.json (with React 19 still present)
# Then completely reset:
rm -rf node_modules package-lock.json
npm cache clean --force
npm install

The npm cache clean --force is the key addition - it may help with stubborn resolution issues.

If That Doesn't Work

You have two solid options:

Option 1: Rebase approach

  • Branch from pre-React-19 commit
  • Add overrides + explicit versions for deck/luma packages
  • Then install React 19
  • Reapply any other changes from this PR

Option 2: Upgrade deck.gl now (Recommended if feasible)

  • This is the proper long-term solution anyway
  • Removes the need for overrides workaround
  • Aligns everything to 9.2.x which plays better with React 19

Since you mentioned you were planning to upgrade deck.gl soon anyway, it might be worth just doing that now rather than fighting with npm's resolution algorithm. The override approach is a workaround - upgrading is the actual fix.

Which path would you prefer? I'm happy to help with either approach - debugging the override application, helping with the rebase, or assisting with the deck.gl upgrade preparation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants