feat(dev): move to rsbuild for dev - faster (#4674)

This commit is contained in:
Alireza 2025-01-10 15:05:35 -05:00 committed by GitHub
parent 847f1da8e0
commit d4a4267429
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 13177 additions and 5660 deletions

View File

@ -171,7 +171,8 @@ also supports a number of commands that can be found in their respective
| Yarn Commands | Description |
| ---------------------------- | ------------------------------------------------------------- |
| **Develop** | |
| `dev` or `start` | Default development experience for Viewer |
| `dev` | Default development experience for Viewer |
| `dev:fast` | Our experimental fast dev mode that uses rsbuild instead of webpack |
| `test:unit` | Jest multi-project test runner; overall coverage |
| **Deploy** | |
| `build`\* | Builds production output for our PWA Viewer | |

View File

@ -22,9 +22,13 @@
"devDependencies": {
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.15",
"@svgr/webpack": "^8.1.0",
"@swc/helpers": "^0.5.15",
"@types/jest": "^27.5.0",
"@typescript-eslint/eslint-plugin": "^6.3.0",
"@typescript-eslint/parser": "^6.3.0",
"@rsbuild/core": "^1.1.13",
"@rsbuild/plugin-react": "^1.1.0",
"@rsbuild/plugin-node-polyfill": "1.2.0",
"autoprefixer": "^10.4.4",
"babel-eslint": "9.x",
"babel-loader": "^8.2.4",

View File

@ -51,7 +51,6 @@ module.exports = {
'@babel/preset-react',
'@babel/preset-typescript',
],
plugins: ['react-refresh/babel'],
ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'],
},
},

BIN
bun.lockb

Binary file not shown.

View File

@ -37,7 +37,6 @@ module.exports = {
'@babel/preset-react',
'@babel/preset-typescript',
],
plugins: ['react-refresh/babel'],
ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'],
},
},

View File

@ -37,7 +37,6 @@ module.exports = {
'@babel/preset-react',
'@babel/preset-typescript',
],
plugins: ['react-refresh/babel'],
ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'],
},
},

View File

@ -1,6 +1,5 @@
// import { api } from 'dicomweb-client';
// import DICOMWeb from '../../../DICOMWeb/';
import { createStudyFromSOPInstanceList } from './studyInstanceHelpers';
import RetrieveMetadataLoader from './retrieveMetadataLoader';
/**

View File

@ -15,7 +15,7 @@ function DataSourceSelector() {
return (
<div style={{ width: '100%', height: '100%' }}>
<div className="flex h-screen w-screen items-center justify-center ">
<div className="flex h-screen w-screen items-center justify-center">
<div className="bg-secondary-dark mx-auto space-y-2 rounded-lg py-8 px-8 drop-shadow-md">
<img
className="mx-auto block h-14"

View File

@ -1,7 +1,6 @@
import React from 'react';
import { useNavigate } from 'react-router-dom';
import { useNavigate, useLocation } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { useLocation } from 'react-router';
import { UserPreferences, AboutModal, useModal } from '@ohif/ui';
import { Header } from '@ohif/ui-next';

View File

@ -37,7 +37,6 @@ module.exports = {
'@babel/preset-react',
'@babel/preset-typescript',
],
plugins: ['react-refresh/babel'],
ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'],
},
},

View File

@ -10,7 +10,6 @@ import { StudyBrowser } from '@ohif/ui-next';
import { useTrackedMeasurements } from '../../getContextModule';
import { Separator } from '@ohif/ui-next';
import { PanelStudyBrowserHeader } from '@ohif/extension-default';
import { useAppConfig } from '@state';
import { defaultActionIcons, defaultViewPresets } from './constants';
const { formatDate, createStudyBrowserTabs } = utils;

View File

@ -1,4 +1,4 @@
import toolbarButtons from './toolbarButtons.js';
import toolbarButtons from './toolbarButtons';
import { hotkeys } from '@ohif/core';
import { id } from './id';
import i18n from 'i18next';

View File

@ -1,6 +1,4 @@
import {
WindowLevelMenuItem,
} from '@ohif/ui';
import { WindowLevelMenuItem } from '@ohif/ui';
import { defaults, ToolbarService } from '@ohif/core';
import type { Button } from '@ohif/core/types';
@ -23,7 +21,7 @@ function _createWwwcPreset(preset, title, subtitle) {
};
}
function _createSetToolActiveCommands(toolName, toolGroupIds = ['default', 'mpr', ]) {
function _createSetToolActiveCommands(toolName, toolGroupIds = ['default', 'mpr']) {
return toolGroupIds.map(toolGroupId => ({
commandName: 'setToolActive',
commandOptions: {
@ -149,7 +147,7 @@ const toolbarButtons: Button[] = [
],
},
},
{
{
id: 'Layout',
uiType: 'ohif.layoutSelector',
props: {
@ -164,101 +162,100 @@ const toolbarButtons: Button[] = [
},
},
{
id: 'MoreTools',
uiType: 'ohif.splitButton',
props: {
groupId: 'MoreTools',
evaluate: 'evaluate.group.promoteToPrimaryIfCornerstoneToolNotActiveInTheList',
primary: ToolbarService.createButton({
id: 'Reset',
icon: 'tool-reset',
label: 'Reset View',
tooltip: 'Reset View',
commands: [
{
commandName: 'resetViewport',
context: 'CORNERSTONE',
},
id: 'MoreTools',
uiType: 'ohif.splitButton',
props: {
groupId: 'MoreTools',
evaluate: 'evaluate.group.promoteToPrimaryIfCornerstoneToolNotActiveInTheList',
primary: ToolbarService.createButton({
id: 'Reset',
icon: 'tool-reset',
label: 'Reset View',
tooltip: 'Reset View',
commands: [
{
commandName: 'resetViewport',
context: 'CORNERSTONE',
},
],
evaluate: 'evaluate.action',
}),
secondary: {
icon: 'chevron-down',
tooltip: 'More Tools',
},
items: [
ToolbarService.createButton({
id: 'Reset',
icon: 'tool-reset',
label: 'Reset View',
tooltip: 'Reset View',
commands: [
{
commandName: 'resetViewport',
context: 'CORNERSTONE',
},
],
evaluate: 'evaluate.action',
}),
ToolbarService.createButton({
id: 'RotateRight',
icon: 'tool-rotate-right',
label: 'Rotate Right',
tooltip: 'Rotate Right +90',
commands: [
{
commandName: 'rotateViewportCW',
context: 'CORNERSTONE',
},
],
evaluate: 'evaluate.action',
}),
ToolbarService.createButton({
id: 'FlipHorizontal',
icon: 'tool-flip-horizontal',
label: 'Flip Horizontally',
tooltip: 'Flip Horizontally',
commands: [
{
commandName: 'flipViewportHorizontal',
context: 'CORNERSTONE',
},
],
evaluate: 'evaluate.action',
}),
ToolbarService.createButton({
id: 'StackScroll',
icon: 'tool-stack-scroll',
label: 'Stack Scroll',
tooltip: 'Stack Scroll',
commands: _createSetToolActiveCommands('StackScroll'),
evaluate: 'evaluate.cornerstoneTool',
}),
ToolbarService.createButton({
id: 'Invert',
icon: 'tool-invert',
label: 'Invert Colors',
tooltip: 'Invert Colors',
commands: [
{
commandName: 'invertViewport',
context: 'CORNERSTONE',
},
],
evaluate: 'evaluate.action',
}),
ToolbarService.createButton({
id: 'CalibrationLine',
icon: 'tool-calibration',
label: 'Calibration Line',
tooltip: 'Calibration Line',
commands: _createSetToolActiveCommands('CalibrationLine'),
evaluate: 'evaluate.cornerstoneTool',
}),
],
evaluate: 'evaluate.action',
}),
secondary: {
icon: 'chevron-down',
tooltip: 'More Tools',
},
items: [
ToolbarService.createButton({
id: 'Reset',
icon: 'tool-reset',
label: 'Reset View',
tooltip: 'Reset View',
commands: [
{
commandName: 'resetViewport',
context: 'CORNERSTONE',
},
],
evaluate: 'evaluate.action',
}),
ToolbarService.createButton({
id: 'RotateRight',
icon: 'tool-rotate-right',
label: 'Rotate Right',
tooltip: 'Rotate Right +90',
commands: [
{
commandName: 'rotateViewportCW',
context: 'CORNERSTONE',
},
],
evaluate: 'evaluate.action',
}),
ToolbarService.createButton({
id: 'FlipHorizontal',
icon: 'tool-flip-horizontal',
label: 'Flip Horizontally',
tooltip: 'Flip Horizontally',
commands: [
{
commandName: 'flipViewportHorizontal',
context: 'CORNERSTONE',
},
],
evaluate: 'evaluate.action',
}),
ToolbarService.createButton({
id: 'StackScroll',
icon: 'tool-stack-scroll',
label: 'Stack Scroll',
tooltip: 'Stack Scroll',
commands: _createSetToolActiveCommands('StackScroll'),
evaluate: 'evaluate.cornerstoneTool',
}),
ToolbarService.createButton({
id: 'Invert',
icon: 'tool-invert',
label: 'Invert Colors',
tooltip: 'Invert Colors',
commands: [
{
commandName: 'invertViewport',
context: 'CORNERSTONE',
},
],
evaluate: 'evaluate.action',
}),
ToolbarService.createButton({
id: 'CalibrationLine',
icon: 'tool-calibration',
label: 'Calibration Line',
tooltip: 'Calibration Line',
commands: _createSetToolActiveCommands('CalibrationLine'),
evaluate: 'evaluate.cornerstoneTool',
}),
],
},
},
];
export default toolbarButtons;

View File

@ -37,7 +37,6 @@ module.exports = {
'@babel/preset-react',
'@babel/preset-typescript',
],
plugins: ['react-refresh/babel'],
ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'],
},
},

View File

@ -37,7 +37,6 @@ module.exports = {
'@babel/preset-react',
'@babel/preset-typescript',
],
plugins: ['react-refresh/babel'],
ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'],
},
},

View File

@ -1,5 +1,5 @@
module.exports = {
plugins: [ '@babel/plugin-proposal-class-properties'],
plugins: ['@babel/plugin-proposal-class-properties'],
env: {
test: {
presets: [
@ -37,7 +37,6 @@ module.exports = {
'@babel/preset-react',
'@babel/preset-typescript',
],
plugins: ['react-refresh/babel'],
ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'],
},
},

View File

@ -3,7 +3,12 @@
"private": true,
"workspaces": {
"packages": [
"platform/*",
"platform/app",
"platform/cli",
"platform/ui-next",
"platform/ui",
"platform/core",
"platform/i18n",
"extensions/*",
"modes/*",
"addOns/externals/*"
@ -30,6 +35,7 @@
"build:demo": "lerna run build:viewer:demo --stream",
"build:package-all": "lerna run build:package --parallel --stream",
"build:package-all-1": "lerna run build:package-1 --parallel --stream",
"dev:fast": "cd platform/app && yarn run dev:fast",
"show:config": "echo Config is $APP_CONFIG on $PUBLIC_URL",
"dev": "lerna run dev:viewer --stream",
"dev:no:cache": "lerna run dev:no:cache --stream",

View File

@ -27,6 +27,7 @@
"build": "node --max_old_space_size=8096 ./../../node_modules/webpack/bin/webpack.js --progress --config .webpack/webpack.pwa.js",
"clean": "shx rm -rf dist",
"clean:deep": "yarn run clean && shx rm -rf node_modules",
"dev:fast": "rsbuild dev --config ../../rsbuild.config.ts",
"dev": "cross-env NODE_ENV=development webpack serve --config .webpack/webpack.pwa.js",
"dev:no:cache": "cross-env NODE_ENV=development webpack serve --no-cache --config .webpack/webpack.pwa.js",
"dev:orthanc": "cross-env NODE_ENV=development PROXY_TARGET=http://localhost:3000/pacs/dicom-web PROXY_DOMAIN=http://localhost:8042 PROXY_PATH_REWRITE_FROM=/pacs/dicom-web PROXY_PATH_REWRITE_TO=/dicom-web APP_CONFIG=config/docker-nginx-orthanc.js webpack serve --config .webpack/webpack.pwa.js",

View File

@ -206,7 +206,6 @@
/>
<link
href="https://fonts.googleapis.com/css?family=Inter:100,300,400,500,700&display=swap"
rel="stylesheet"
rel="preload"
as="style"
/>

View File

@ -3,10 +3,10 @@ import React, { useCallback, useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { Enums, ExtensionManager, MODULE_TYPES, log } from '@ohif/core';
//
import { extensionManager } from '../App.tsx';
import { extensionManager } from '../App';
import { useParams, useLocation } from 'react-router';
import { useNavigate } from 'react-router-dom';
import useSearchParams from '../hooks/useSearchParams.ts';
import useSearchParams from '../hooks/useSearchParams';
/**
* Determines if two React Router location objects are the same.

View File

@ -6,9 +6,9 @@ import { DicomMetadataStore, MODULE_TYPES } from '@ohif/core';
import Dropzone from 'react-dropzone';
import filesToStudies from './filesToStudies';
import { extensionManager } from '../../App.tsx';
import { extensionManager } from '../../App';
import { Button, Icon, LoadingIndicatorProgress } from '@ohif/ui';
import { Button, LoadingIndicatorProgress } from '@ohif/ui';
import { Icons } from '@ohif/ui-next';
const getLoadButton = (onDrop, text, isDir) => {

View File

@ -1,5 +1,5 @@
import React from 'react';
import { Routes, Route } from 'react-router-dom';
import { Routes, Route, Link } from 'react-router-dom';
import { ErrorBoundary } from '@ohif/ui-next';
// Route Components
@ -11,7 +11,6 @@ import NotFound from './NotFound';
import buildModeRoutes from './buildModeRoutes';
import PrivateRoute from './PrivateRoute';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import publicUrl from '../utils/publicUrl';
const NotFoundServer = ({

View File

@ -43,7 +43,6 @@ module.exports = {
'@babel/preset-react',
'@babel/preset-typescript',
],
plugins: ['react-refresh/babel'],
ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'],
},
},

View File

@ -1,8 +1,6 @@
{
"repository": "OHIF/Viewers",
"keywords": [
"ohif-extension"
],
"keywords": ["ohif-extension"],
"module": "src/index.tsx",
"engines": {
"node": ">=14",
@ -25,8 +23,8 @@
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-i18next": "^12.2.2",
"react-router": "^6.8.1",
"react-router-dom": "^6.8.1",
"react-router": "^6.23.1",
"react-router-dom": "^6.23.1",
"webpack": "5.89.0",
"webpack-merge": "^5.7.3"
},

View File

@ -37,7 +37,6 @@ module.exports = {
'@babel/preset-react',
'@babel/preset-typescript',
],
plugins: ['react-refresh/babel'],
ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'],
},
},

View File

@ -13,3 +13,23 @@ window.PUBLIC_URL = '<%= PUBLIC_URL %>';
```
Was added before the `<!-- EXTENSIONS -->` comment block.
## OHIF Docs
OHIF platform/docs is no longer part of the workspace.
- Builds are faster for 99.99% of users since only maintainers need to run the docs development.
If you need to run the docs website locally, you must install it first, as it is not installed by default.
Before:
```bash
yarn run dev
```
After:
```bash
yarn install
yarn run dev
```

View File

@ -52,19 +52,6 @@ module.exports = {
favicon: 'img/favicon.ico',
themes: ['@docusaurus/theme-live-codeblock'],
plugins: [
() => ({
name: 'resolve-react',
configureWebpack() {
return {
resolve: {
alias: {
// assuming root node_modules is up from "./packages/<your-docusaurus>
react: path.resolve('../../node_modules/react'),
},
},
};
},
}),
// path.resolve(__dirname, './pluginOHIFWebpackConfig.js'),
// /path.resolve(__dirname, './postcss.js'),
'docusaurus-plugin-image-zoom', // 3rd party plugin for image click to pop

View File

@ -2,17 +2,6 @@
"name": "ohif-docs",
"version": "3.10.0-beta.54",
"private": true,
"workspaces": {
"nohoist": [
"@docusaurus/core/**",
"@docusaurus/core",
"@docusaurus/preset-classic",
"@docusaurus/preset-classic/**",
"**/react-router-dom",
"**/react-router",
"**/history"
]
},
"scripts": {
"docusaurus": "docusaurus",
"clean": "shx rm -rf dist",
@ -68,6 +57,7 @@
"@radix-ui/react-tabs": "^1.1.0",
"@radix-ui/react-toggle": "^1.1.0",
"@radix-ui/react-tooltip": "^1.1.2",
"tailwindcss-animate": "^1.0.7",
"@svgr/webpack": "^5.5.0",
"autoprefixer": "^10.4.20",
"class-variance-authority": "^0.7.0",
@ -90,13 +80,9 @@
"sonner": "^1.4.41",
"tailwind-merge": "^2.3.0",
"tailwindcss": "^3.4.13",
"url-loader": "^4.1.1"
},
"peerDependencies": {
"url-loader": "^4.1.1",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"devDependencies": {
"react-dom": "^18.3.1",
"@docusaurus/module-type-aliases": "3.0.0",
"@docusaurus/tsconfig": "3.0.0",
"@docusaurus/types": "3.0.0",

12162
platform/docs/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View File

@ -50,7 +50,6 @@ module.exports = {
'@babel/preset-react',
'@babel/preset-typescript',
],
plugins: ['react-refresh/babel'],
ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'],
},
},

View File

@ -10,7 +10,7 @@ import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { Icons } from '@ohif/ui-next';
import PatientInfo from './PatientInfo';
import { PatientInfo } from './PatientInfo';
/**
* This is the modern Viewport Action Bar, showing patient info, series date,

165
rsbuild.config.ts Normal file
View File

@ -0,0 +1,165 @@
import { defineConfig } from '@rsbuild/core';
import { pluginReact } from '@rsbuild/plugin-react';
import { pluginNodePolyfill } from '@rsbuild/plugin-node-polyfill';
import path from 'path';
import writePluginImportsFile from './platform/app/.webpack/writePluginImportsFile';
import fs from 'fs';
const SRC_DIR = path.resolve(__dirname, './platform/app/src');
const DIST_DIR = path.resolve(__dirname, './platform/app/dist');
const PUBLIC_DIR = path.resolve(__dirname, './platform/app/public');
// Environment variables (similar to webpack.pwa.js)
const APP_CONFIG = process.env.APP_CONFIG || 'config/default.js';
const PUBLIC_URL = process.env.PUBLIC_URL || '/';
// Add these constants
const NODE_ENV = process.env.NODE_ENV;
const BUILD_NUM = process.env.CIRCLE_BUILD_NUM || '0';
const VERSION_NUMBER = fs.readFileSync(path.join(__dirname, './version.txt'), 'utf8') || '';
const COMMIT_HASH = fs.readFileSync(path.join(__dirname, './commit.txt'), 'utf8') || '';
const PROXY_TARGET = process.env.PROXY_TARGET;
const PROXY_DOMAIN = process.env.PROXY_DOMAIN;
const PROXY_PATH_REWRITE_FROM = process.env.PROXY_PATH_REWRITE_FROM;
const PROXY_PATH_REWRITE_TO = process.env.PROXY_PATH_REWRITE_TO;
// Add port constant
const OHIF_PORT = Number(process.env.OHIF_PORT || 3000);
export default defineConfig({
source: {
entry: {
index: `${SRC_DIR}/index.js`,
},
define: {
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
'process.env.NODE_DEBUG': JSON.stringify(process.env.NODE_DEBUG),
'process.env.DEBUG': JSON.stringify(process.env.DEBUG),
'process.env.PUBLIC_URL': JSON.stringify(process.env.PUBLIC_URL || '/'),
'process.env.BUILD_NUM': JSON.stringify(BUILD_NUM),
'process.env.VERSION_NUMBER': JSON.stringify(VERSION_NUMBER),
'process.env.COMMIT_HASH': JSON.stringify(COMMIT_HASH),
'process.env.USE_LOCIZE': JSON.stringify(process.env.USE_LOCIZE || ''),
'process.env.LOCIZE_PROJECTID': JSON.stringify(process.env.LOCIZE_PROJECTID || ''),
'process.env.LOCIZE_API_KEY': JSON.stringify(process.env.LOCIZE_API_KEY || ''),
'process.env.REACT_APP_I18N_DEBUG': JSON.stringify(process.env.REACT_APP_I18N_DEBUG || ''),
},
},
plugins: [pluginReact(), pluginNodePolyfill()],
tools: {
rspack: {
experiments: {
asyncWebAssembly: true,
},
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
},
},
},
],
type: 'javascript/auto',
},
{
test: /\.wasm$/,
type: 'asset/resource',
},
],
},
resolve: {
fallback: {
buffer: require.resolve('buffer'),
},
},
watchOptions: {
ignored: /node_modules\/@cornerstonejs/,
},
},
},
resolve: {
alias: {
'@': path.resolve(__dirname, './platform/app/src'),
'@components': path.resolve(__dirname, './platform/app/src/components'),
'@hooks': path.resolve(__dirname, './platform/app/src/hooks'),
'@routes': path.resolve(__dirname, './platform/app/src/routes'),
'@state': path.resolve(__dirname, './platform/app/src/state'),
'dicom-microscopy-viewer':
'dicom-microscopy-viewer/dist/dynamic-import/dicomMicroscopyViewer.min.js',
},
},
output: {
copy: [
// Copy plugin files (handled by writePluginImportsFile)
...(writePluginImportsFile(SRC_DIR, DIST_DIR) || []),
// Copy public directory except config and html-templates
{
from: PUBLIC_DIR,
to: DIST_DIR,
globOptions: {
ignore: ['**/config/**', '**/html-templates/**', '.DS_Store'],
},
},
// Copy Google config
{
from: path.resolve(PUBLIC_DIR, 'config/google.js'),
to: 'google.js',
},
// Copy app config
{
from: path.resolve(PUBLIC_DIR, APP_CONFIG),
to: 'app-config.js',
},
// Copy Dicom Microscopy Viewer files
{
from: path.resolve(__dirname, 'node_modules/dicom-microscopy-viewer/dist/dynamic-import'),
to: DIST_DIR,
globOptions: {
ignore: ['**/*.min.js.map'],
},
},
],
},
html: {
template: path.resolve(PUBLIC_DIR, 'html-templates/index.html'),
templateParameters: {
PUBLIC_URL,
},
},
server: {
port: OHIF_PORT,
open: true,
// Configure proxy
proxy: {
'/dicomweb': {
target: 'http://localhost:5000',
},
// Add conditional proxy based on env vars
...(PROXY_TARGET && PROXY_DOMAIN
? {
[PROXY_TARGET]: {
target: PROXY_DOMAIN,
changeOrigin: true,
pathRewrite: {
[`^${PROXY_PATH_REWRITE_FROM}`]: PROXY_PATH_REWRITE_TO,
},
},
}
: {}),
},
// Configure history API fallback
historyApiFallback: {
disableDotRule: true,
index: `${PUBLIC_URL}index.html`,
},
},
});

6207
yarn.lock

File diff suppressed because it is too large Load Diff