ohif-viewer/rsbuild.config.ts
Bill Wallace dbb8b1526e
feat: Add support for labelmap seg images in any supported tsuid (also for compressed bitmap) (#5806)
* feat: Update to use pnpm (#6031) [simulated squash-merge]

* feat: load DICOM SEG images via imageLoader

* feat: load DICOM SEG images via imageLoader

* PR review fixes

* Test fragment of compressed multiframes

* fix: Removed dependencies incorrectly

* Update frame as a targetted change to avoid stripping remaining params

* lock

* Update test to agree with the missing representation branch

* test(contour): contour color change coverage (#6042)

* test(contour): contour interactions delete segment (#6069)

---------

Co-authored-by: Bill Wallace <wayfarer3130@gmail.com>

* chore(version): Update package versions to 3.13.0-beta.98 [skip ci]

* feat(testing): OHIF Test Agent Skills (#5993)

* chore(version): Update package versions to 3.13.0-beta.99 [skip ci]

* test(contour): Add the ContourSegmentToggleLock.spec.ts test file to test contour locking (#6072)

* chore(version): Update package versions to 3.13.0-beta.100 [skip ci]

* chore(testing): Flock lock for playwright tests; Pin node version for OHIF; add more workers (#6099)

* update Cypress apt deps for Ubuntu Noble (drop libgconf-2-4, libasound2→libasound2t64)

* chore(version): Update package versions to 3.13.0-beta.101 [skip ci]

* Debug fixes

* perf(seg): pass explicit frame decode concurrency (16) to SEG loader

Pass an explicit concurrency value (SEG_FRAME_DECODE_CONCURRENCY = 16) into
createFromDicomSegImageId rather than relying on the adapter default, so the
SEG frame fetch/decode parallelism is set at the call site and is ready to
become configurable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(behaviours): add behaviours section + multiframe Part 10 prefetch proposal

Start a "Behaviours" docs section for documenting how the system and UI behave
end-to-end (observed behaviours, design proposals, and failure modes), with an
index README and a Docusaurus category.

First entry is the proposal for loading a multiframe SEG as a single Part 10
instance: prefetch the whole instance (gated by loadMultiframeAsPart10RaceTimeMs),
parse it with dcmjs (handling multipart/related vs raw DICOM), and register the
per-frame compressed pixels into the Cornerstone3D core image cache (the single
uniform frame registry) so the per-frame load path is served locally while the
standard decode path is unchanged. Best-effort: falls back to per-frame fetches
on any failure.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Pre-cache the image data using a full part10 file for performance

* Add customizations to specify type of segmentation save

* Add clearcache of the cacheData

* Bump @cornerstonejs/* pins 5.1.3 -> 5.4.10 to match libs/@cornerstonejs base

libs/@cornerstonejs (fix/use-imageLoader-for-seg) is based on the released
cs3d 5.4.10 (merge-base with origin/main is the 5.4.10 version bump), so pin
OHIF to that release. The local branch changes still reach the app via the
cs3d:link symlinks; these pins keep the lockfile/npm fallback aligned.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Fix seg OOM

* fix: file meta garbage element, bogus NumberOfFrames, unguarded source-map-loader

- dicomWriter: drop the naturalized meta.TransferSyntaxUID assignment that
  dcmjs wrote as a garbage (0000,0000) element into every saved file's meta
  group (download / clipboard / local wadouri blob / local store); keep the
  hex 00020010 assignment, which is required for string-form _meta fallbacks.
- registerNaturalizedDatasetForLocalWadouri: keep the computed frame count
  local so single-frame IODs (SR, RTSTRUCT) no longer gain a bogus
  NumberOfFrames element in their serialized form.
- rsbuild.config: resolve source-map-loader opportunistically (it is not a
  project dependency; the rule only serves the gitignored cs3d-link
  workflow) so fresh clones no longer crash at config load.
- Regression tests for both writer fixes (mutation-checked).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* PR review comment fixes

* Update versions

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: diattamo <mmddiatta@gmail.com>
Co-authored-by: ohif-bot <danny.ri.brown+ohif-bot@gmail.com>
Co-authored-by: Joe Boccanfuso <109477394+jbocce@users.noreply.github.com>
2026-07-09 20:54:23 -04:00

219 lines
7.7 KiB
TypeScript

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';
// Module-resolution rules shared with the webpack/rspack build (webpack.base.js)
// so the two pipelines resolve identically.
import resolveConfig from './.webpack/resolveConfig';
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)
// rsbuild is used only by the dev server (`dev:fast`), so default to the
// full-featured `config/dev.js` while still honoring an explicit APP_CONFIG.
const APP_CONFIG = process.env.APP_CONFIG || 'config/dev.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);
const OHIF_OPEN = process.env.OHIF_OPEN !== 'false';
// Ignore node_modules except @cornerstonejs (symlinked local development).
const WATCH_IGNORED = /node_modules[\\/](?!@cornerstonejs(?:[\\/]|$))/;
const WATCH_AGGREGATE_TIMEOUT = Number(process.env.WATCH_AGGREGATE_TIMEOUT || 1500);
// `source-map-loader` is not a project dependency — it only serves the local
// cs3d-linking workflow (libs/@cornerstonejs, gitignored), so it is resolved
// opportunistically and the rule is skipped on installs that lack it.
const SOURCE_MAP_LOADER = (() => {
try {
return require.resolve('source-map-loader');
} catch {
return null;
}
})();
export default defineConfig({
dev: {
lazyCompilation: false,
},
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,
},
// Leave __filename / __dirname references alone. rsbuild's default
// ('warn-mock') noisily warns whenever bundled deps reference them
// (e.g. Emscripten-compiled cornerstone codecs). Those references sit
// inside `if (ENVIRONMENT_IS_NODE)` branches that never execute in the
// browser, so leaving them un-substituted is harmless at runtime.
node: {
__filename: false,
__dirname: false,
},
module: {
rules: [
// Consume the source maps emitted by the linked local Cornerstone
// packages (libs/@cornerstonejs, via cs3d:link + cs3d:watch) so browser
// stack traces and breakpoints resolve to the original .ts instead of
// the bundled dist/esm .js. Scoped to the linked packages only.
...(SOURCE_MAP_LOADER
? [
{
test: /\.js$/,
enforce: 'pre' as const,
use: [SOURCE_MAP_LOADER],
include: /libs[\\/]@cornerstonejs[\\/]packages[\\/][^\\/]+[\\/]dist[\\/]esm/,
},
]
: []),
{
test: /\.css$/,
use: [
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
},
},
},
],
type: 'javascript/auto',
},
{
test: /\.wasm$/,
type: 'asset/resource',
},
],
},
resolve: {
// Extensions/modes are resolved from their source dirs (see the
// resolve.alias above), so their imports of shared OHIF packages
// (@ohif/ui-next, @ohif/core, ...) must resolve against platform/app's
// installed dependencies rather than only the importer-relative
// node_modules. Shared with webpack.base.js via ./.webpack/resolveConfig.
modules: resolveConfig.getModules(SRC_DIR),
fallback: {
buffer: require.resolve('buffer'),
},
},
watchOptions: {
ignored: WATCH_IGNORED,
followSymlinks: true,
aggregateTimeout: WATCH_AGGREGATE_TIMEOUT,
},
},
},
resolve: {
alias: {
// Resolve every extension/mode declared in pluginConfig.json to its
// source directory, so the dynamic import()s in the generated
// pluginImports.js link without the plugins being dependencies of
// platform/app. Merged in separately since it depends on pluginConfig.json.
...writePluginImportsFile.getPluginResolveAliases(),
// App-level aliases (@ohif/app, @, @components, ...) shared with the
// webpack/rspack build via ./.webpack/resolveConfig.
...resolveConfig.alias,
},
},
output: {
copy: [
// Copy plugin files (handled by writePluginImportsFile)
...(writePluginImportsFile(SRC_DIR, DIST_DIR) || []),
// Copy public directory except config and html-templates
{
from: path.resolve(__dirname, 'node_modules/onnxruntime-web/dist'),
to: `${DIST_DIR}/ort`,
force: true,
},
{
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',
},
],
},
html: {
template: path.resolve(PUBLIC_DIR, 'html-templates/index.html'),
templateParameters: {
PUBLIC_URL,
},
},
server: {
port: OHIF_PORT,
open: OHIF_OPEN,
// 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`,
},
},
});