Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
f0a654e
refactor(app): remove LegacyWorkList and converge on automatic JSX ru…
sedghi Jul 17, 2026
1d798d1
feat(react): upgrade to React 19.2.7 across the monorepo
sedghi Jul 18, 2026
6623a5d
feat(app): move the production build from rspack to rsbuild
sedghi Jul 18, 2026
a38d3e5
feat(compiler): enable React Compiler across both build pipelines
sedghi Jul 18, 2026
be6371d
refactor(ui-next): adopt React 19 idioms, drop compiler-redundant mem…
sedghi Jul 18, 2026
14ad35f
refactor(extensions,app,core): wave-2 compiler-era cleanup
sedghi Jul 18, 2026
457dbf7
fix(compiler): repair two latent mutation/timing bugs the compiler su…
sedghi Jul 18, 2026
619de59
fix(cornerstone): restore useViewportHover memoization, narrow useCus…
sedghi Jul 18, 2026
f8ece65
fix(build): apply React Compiler in the coverage/e2e build
sedghi Jul 18, 2026
a750314
chore(audit): accept the ambient adm-zip advisory in the audit gate
sedghi Jul 18, 2026
8c0a405
fix(compiler): exclude cornerstone viewport components from React Com…
sedghi Jul 18, 2026
1ed1682
fix(app): render the data source configuration customization as a com…
sedghi Jul 19, 2026
6ee3d00
fix(cornerstone): memoize the slice-scrollbar image id lookups
sedghi Jul 19, 2026
ee890c9
chore(build): bump rsbuild to 2.x and serve the dev app at the root URL
sedghi Jul 19, 2026
a4d5c43
Merge remote-tracking branch 'origin/master' into ohifReact
wayfarer3130 Jul 28, 2026
15106b5
Fixes for various PR issues
wayfarer3130 Jul 28, 2026
2e92344
chore(build): compile package builds with the React Compiler
jbocce Aug 12, 2026
155e97f
Merge remote-tracking branch 'origin/master' into ohifReact
jbocce Aug 12, 2026
cc590ee
Organize react-resize-detector dependencies.
jbocce Aug 13, 2026
bec56f2
fix(lint): split React 19 guardrails out of the compiler lint budget
jbocce Aug 13, 2026
3c4e12c
docs(migration): add 3.13 to 3.14 migration guide
jbocce Aug 14, 2026
90ea52e
Remove the version property for the various SmartScrollbar components…
jbocce Aug 17, 2026
35ec131
Add `use no memo` to the DataTable components because TanStack Table …
jbocce Aug 17, 2026
450cd3a
fix(datepicker): set explicit colors on the calendar's month/year select
jbocce Aug 18, 2026
95a9bc5
Comment React 19 resolution in pnpm-workspace.yaml regarding peers th…
jbocce Aug 18, 2026
1814ad1
Ignore GHSA-ggr8-5vv4-36mx in pnpm-workspace.yaml.
jbocce Aug 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ jobs:
- run:
name: 'JavaScript Test Suite'
command: pnpm run test:unit:ci
- run:
name: 'React Compiler lint budget'
command: pnpm run lint:compiler:ci
# platform/app
- run:
name: 'VIEWER: Combine report output'
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,6 @@ libs/
link-cs3d.js
unlink-cs3d.js
auth.json

# platform/ui-next UMD build emits font assets at the package root
platform/ui-next/*.woff2
4 changes: 4 additions & 0 deletions .react-compiler-lint-budget.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"errors": 171,
"warnings": 132
}
19 changes: 13 additions & 6 deletions .webpack/webpack.base.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,19 @@ module.exports = (env, argv, { SRC_DIR, ENTRY }) => {
{
test: /\.[jt]sx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-typescript', '@babel/preset-react'],
plugins: ['istanbul'],
},
loader: 'babel-loader',
options: {
// Rely on the root babel.config.js (preset-env,
// preset-react automatic runtime, preset-typescript, and
// babel-plugin-react-compiler) and only add coverage
// instrumentation. Supplying inline presets here
// re-added a classic-runtime preset-react that shadowed
// the compiler, so the coverage/e2e builds shipped the
// cleanup-era components without the memoization the
// compiler is meant to restore - breaking behavior (e.g.
// orientation markers after rotate/flip) that works in
// the production and dev builds.
plugins: ['istanbul'],
},
},
]
Expand Down
51 changes: 47 additions & 4 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,57 @@
// https://babeljs.io/docs/en/options#babelrcroots

// React Compiler (babel-plugin-react-compiler) must run before any other
// transform so it sees the original JSX/hooks. REACT_COMPILER=off is a manual
// kill switch for debugging a suspected compiler miscompile; nothing in the
// repo sets it.
//
// The package (UMD) builds deliberately compile too, so the published bytes
// match what the app build produces. There was a thought to disable the
// compiler there on the theory that `react/compiler-runtime` escapes an
// `externals: { react: 'React' }` map (true — object externals match the
// specifier exactly) and drags in a second copy of React. Measured on
// platform/ui-next, the only package that externalizes React: it does not.
// react/compiler-runtime is 463 bytes whose sole dependency is
// `require('react')`, which *does* hit the external, so the emitted module is
// three lines reading useMemoCache off the host's React. Building with the
// compiler on vs. off differs by +3.2%, all of it memo-cache scaffolding, with
// no React version string, error text, or dispatcher in the output.
const enableReactCompiler = process.env.REACT_COMPILER !== 'off';
const reactCompilerPlugin = ['babel-plugin-react-compiler', { target: '19' }];

// Legacy platform/ui is frozen and outside the app graph, so it is not worth
// compiling. Kept here rather than in the package's build script so the policy
// lives with the transform and cannot be lost when a build script is copied.
// Mirrors the `(?!ui[\\/])` clause in rsbuild.config.ts's REACT_COMPILER_INCLUDE
// — note both patterns require a separator after `ui`, so platform/ui-next is
// compiled normally.
const reactCompilerExclude = [/[\\/]platform[\\/]ui[\\/]/];

// Individual files that must not be compiled carry a `'use no memo'` directive
// at the top of the file, next to the code and the reason - see the components
// under extensions/cornerstone/src/Viewport/, which read and mutate external
// cornerstone3D state during render. They stay directives rather than joining
// the exclude list above because a per-file path list has to be mirrored in
// rsbuild.config.ts and the two copies drift silently; one entry for one frozen
// package does not move.

module.exports = {
babelrcRoots: ['./platform/*', './extensions/*', './modes/*'],
presets: ['@babel/preset-env', '@babel/preset-react', '@babel/preset-typescript'],
presets: [
'@babel/preset-env',
['@babel/preset-react', { runtime: 'automatic' }],
'@babel/preset-typescript',
],
plugins: [
['@babel/plugin-transform-class-properties', { loose: true }],
'@babel/plugin-transform-typescript',
['@babel/plugin-transform-private-property-in-object', { loose: true }],
['@babel/plugin-transform-private-methods', { loose: true }],
'@babel/plugin-transform-class-static-block',
],
overrides: enableReactCompiler
? [{ exclude: reactCompilerExclude, plugins: [reactCompilerPlugin] }]
: [],
env: {
test: {
presets: [
Expand All @@ -22,7 +65,7 @@ module.exports = {
bugfixes: true,
},
],
'@babel/preset-react',
['@babel/preset-react', { runtime: 'automatic' }],
'@babel/preset-typescript',
],
plugins: [
Expand All @@ -44,7 +87,7 @@ module.exports = {
presets: [
// WebPack handles ES6 --> Target Syntax
['@babel/preset-env', { modules: false }],
'@babel/preset-react',
['@babel/preset-react', { runtime: 'automatic' }],
'@babel/preset-typescript',
],
ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'],
Expand All @@ -53,7 +96,7 @@ module.exports = {
presets: [
// WebPack handles ES6 --> Target Syntax
['@babel/preset-env', { modules: false }],
'@babel/preset-react',
['@babel/preset-react', { runtime: 'automatic' }],
'@babel/preset-typescript',
],
ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'],
Expand Down
89 changes: 89 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Minimal flat config dedicated to React Compiler health (run via
// `pnpm lint:compiler`). eslint-plugin-react-hooks v7 ships the
// compiler-powered rules; every diagnostic it reports marks a component
// the compiler bails out on (or memoization it cannot preserve), which is
// exactly the "do not hand-remove memoization here" list for the cleanup
// waves. Deliberately not wired into the legacy .eslintrc.json world.
import reactHooks from 'eslint-plugin-react-hooks';
import tsParser from '@typescript-eslint/parser';

const hooksPreset = reactHooks.configs['recommended-latest'] ?? reactHooks.configs.recommended;

export default [
{
ignores: ['**/coverage/**', '**/dist/**', '**/build/**', 'platform/docs/**'],
},
{
files: [
'platform/app/src/**/*.{js,jsx,ts,tsx}',
'platform/core/src/**/*.{js,jsx,ts,tsx}',
'platform/i18n/src/**/*.{js,jsx,ts,tsx}',
'platform/ui-next/src/**/*.{js,jsx,ts,tsx}',
'extensions/*/src/**/*.{js,jsx,ts,tsx}',
'modes/*/src/**/*.{js,jsx,ts,tsx}',
],
ignores: ['**/*.test.*', '**/__tests__/**', '**/__mocks__/**'],
languageOptions: {
parser: tsParser,
parserOptions: {
ecmaFeatures: { jsx: true },
sourceType: 'module',
},
},
plugins: {
'react-hooks': reactHooks,
},
rules: {
...hooksPreset.rules,
// The formal gate for the memoization-removal codemods: a file is only
// eligible when the compiler provably preserves its manual memoization.
'react-hooks/preserve-manual-memoization': 'error',
},
},
{
// The workspace is compiler-first: React 19 idioms are enforced so the
// removed patterns (forwardRef wrappers, runtime propTypes) do not creep
// back in. Legacy platform/ui is exempt (frozen, outside the app graph).
files: [
'platform/app/src/**/*.{js,jsx,ts,tsx}',
'platform/core/src/**/*.{js,jsx,ts,tsx}',
'platform/i18n/src/**/*.{js,jsx,ts,tsx}',
'platform/ui-next/src/**/*.{js,jsx,ts,tsx}',
'extensions/*/src/**/*.{js,jsx,ts,tsx}',
'modes/*/src/**/*.{js,jsx,ts,tsx}',
],
ignores: ['**/*.test.*'],
rules: {
'no-restricted-imports': [
'error',
{
paths: [
{
name: 'prop-types',
message: 'propTypes were removed; use TypeScript types.',
},
],
},
],
'no-restricted-properties': [
'error',
{
object: 'React',
property: 'forwardRef',
message: 'React 19: accept ref as a regular prop instead of forwardRef.',
},
],
'no-restricted-syntax': [
'error',
{
selector: "CallExpression[callee.name='forwardRef']",
message: 'React 19: accept ref as a regular prop instead of forwardRef.',
},
{
selector: "AssignmentExpression[left.property.name='propTypes']",
message: 'propTypes were removed; use TypeScript types.',
},
],
},
},
];
5 changes: 2 additions & 3 deletions extensions/cornerstone-dicom-pmap/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,8 @@
"@ohif/extension-cornerstone": "workspace:*",
"@ohif/extension-default": "workspace:*",
"@ohif/i18n": "workspace:*",
"prop-types": "15.8.1",
"react": "18.3.1",
"react-dom": "18.3.1",
"react": "19.2.7",
"react-dom": "19.2.7",
"react-i18next": "12.3.1",
"react-router": "6.30.3",
"react-router-dom": "6.30.3"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import PropTypes from 'prop-types';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { useViewportGrid } from '@ohif/ui-next';
import { OHIFCornerstoneViewport } from '@ohif/extension-cornerstone';
Expand Down Expand Up @@ -159,12 +158,7 @@ function OHIFCornerstonePMAPViewport(props: withAppTypes) {
);
}

OHIFCornerstonePMAPViewport.propTypes = {
displaySets: PropTypes.arrayOf(PropTypes.object),
viewportId: PropTypes.string.isRequired,
dataSource: PropTypes.object,
children: PropTypes.node,
};


function _getReferencedDisplaySetMetadata(referencedDisplaySet, pmapDisplaySet) {
const { SharedFunctionalGroupsSequence } = pmapDisplaySet.instance;
Expand Down
5 changes: 2 additions & 3 deletions extensions/cornerstone-dicom-rt/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,8 @@
"@ohif/extension-cornerstone": "workspace:*",
"@ohif/extension-default": "workspace:*",
"@ohif/i18n": "workspace:*",
"prop-types": "15.8.1",
"react": "18.3.1",
"react-dom": "18.3.1",
"react": "19.2.7",
"react-dom": "19.2.7",
"react-i18next": "12.3.1",
"react-router": "6.30.3",
"react-router-dom": "6.30.3"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React, { Component, useCallback, useEffect, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import { useViewportGrid } from '@ohif/ui-next';
import {
utils,
Expand Down Expand Up @@ -236,12 +235,7 @@ function OHIFCornerstoneRTViewport(props: withAppTypes) {
);
}

OHIFCornerstoneRTViewport.propTypes = {
displaySets: PropTypes.arrayOf(PropTypes.object),
viewportId: PropTypes.string.isRequired,
dataSource: PropTypes.object,
children: PropTypes.node,
};


function _getReferencedDisplaySetMetadata(referencedDisplaySet) {
const image0 = referencedDisplaySet.images[0];
Expand Down
5 changes: 2 additions & 3 deletions extensions/cornerstone-dicom-seg/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,8 @@
"@ohif/extension-cornerstone": "workspace:*",
"@ohif/extension-default": "workspace:*",
"@ohif/i18n": "workspace:*",
"prop-types": "15.8.1",
"react": "18.3.1",
"react-dom": "18.3.1",
"react": "19.2.7",
"react-dom": "19.2.7",
"react-i18next": "12.3.1",
"react-router": "6.30.3",
"react-router-dom": "6.30.3"
Expand Down
4 changes: 1 addition & 3 deletions extensions/cornerstone-dicom-sr/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,10 @@
"peerDependencies": {
"@ohif/core": "workspace:*",
"@ohif/extension-cornerstone": "workspace:*",
"@ohif/ui": "workspace:*",
"dcmjs": "0.52.0",
"dicom-parser": "1.8.21",
"hammerjs": "2.0.8",
"prop-types": "15.8.1",
"react": "18.3.1"
"react": "19.2.7"
},
"dependencies": {
"@babel/runtime": "7.29.7",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import PropTypes from 'prop-types';
import React from 'react';
import { OHIFCornerstoneSRContentItem } from './OHIFCornerstoneSRContentItem';

Expand Down Expand Up @@ -52,27 +51,4 @@ export function OHIFCornerstoneSRContainer(props) {
);
}

OHIFCornerstoneSRContainer.propTypes = {
/**
* A tree node that may contain another container or one or more content items
* (text, code, uidref, pname, etc.)
*/
container: PropTypes.object,
/**
* A 0-based index list
*/
nodeIndexesTree: PropTypes.arrayOf(PropTypes.number),
/**
* A 1-based index list that represents a container in a multi-level numbered
* list (tree).
*
* Example:
* 1. History
* 1.1. Chief Complaint
* 1.2. Present Illness
* 1.3. Past History
* 1.4. Family History
* 2. Findings
* */
containerNumberedTree: PropTypes.arrayOf(PropTypes.number),
};

Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import PropTypes from 'prop-types';
import React from 'react';
import { CodeNameCodeSequenceValues } from '../enums';
import formatContentItemValue from '../utils/formatContentItem';
Expand Down Expand Up @@ -54,10 +53,6 @@ function OHIFCornerstoneSRContentItem(props) {
);
}

OHIFCornerstoneSRContentItem.propTypes = {
contentItem: PropTypes.object,
nodeIndexesTree: PropTypes.arrayOf(PropTypes.number),
continuityOfContent: PropTypes.string,
};


export { OHIFCornerstoneSRContentItem };
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import PropTypes from 'prop-types';
import React, { useCallback, useEffect, useState } from 'react';
import { setTrackingUniqueIdentifiersForElement } from '../tools/modules/dicomSRModule';

Expand Down Expand Up @@ -221,14 +220,7 @@ function OHIFCornerstoneSRMeasurementViewport(props) {
);
}

OHIFCornerstoneSRMeasurementViewport.propTypes = {
displaySets: PropTypes.arrayOf(PropTypes.object),
viewportId: PropTypes.string.isRequired,
dataSource: PropTypes.object,
children: PropTypes.node,
viewportLabel: PropTypes.string,
viewportOptions: PropTypes.object,
};


async function _getViewportReferencedDisplaySetData(
displaySet,
Expand Down
Loading
Loading