Skip to content

feat(extensibility): plugin externals contract, runtime plugin loading, rsbuild 2 pipeline, and create-ohif scaffolding - #6168

Open
sedghi wants to merge 32 commits into
ohifReactfrom
ohifNextExtensions
Open

feat(extensibility): plugin externals contract, runtime plugin loading, rsbuild 2 pipeline, and create-ohif scaffolding#6168
sedghi wants to merge 32 commits into
ohifReactfrom
ohifNextExtensions

Conversation

@sedghi

@sedghi sedghi commented Jul 20, 2026

Copy link
Copy Markdown
Member

What this branch does

A rework of how extensions and modes are built, distributed, and loaded, so plugins can live outside the monorepo:

  • Plugin externals contract: .rspack/pluginExternals.js is the single canonical list of packages the host shares with plugins at runtime; per-package builds, scaffolded templates, and the runtime loader all derive from it, with parity tests (platform/create-ohif/tests/externals-parity.test.mjs) enforcing that template externals, host-provided globals, and template peerDependencies stay in sync.
  • rsbuild 2 pipeline consolidation: the .webpack config dirs are renamed to .rspack, rsbuild is upgraded to 2.x, and production and dev builds converge on a single repo-root rsbuild.config.ts pipeline (fonts as native asset modules, e2e coverage instrumentation restored, singleton aliases deduped, Tailwind made plugin-aware).
  • Runtime plugin loading with gating: prebuilt UMD plugins declared in window.config.extensions[] load through runtime plugin descriptors with host-shared globals and an audit trail; dynamic loading is gated, invalid pluginConfig.json fails fast against a JSON schema, and dangerouslyUseDynamicConfig requires an explicit regex.
  • create-ohif scaffolding: pnpm create ohif templates for extensions, modes, workspaces, and deployment, verified end to end by a scaffold smoke harness (scaffold, install, build, UMD global contract, in-scaffold tests).
  • ohif-plugin CLI helper: one-command add/remove of plugins plus a doctor check; the legacy OHIF CLI is removed and docs repointed at the pluginConfig flow.
  • Publish surface tiering: only the SDK packages (@ohif/core, @ohif/ui-next, @ohif/i18n, @ohif/extension-default, @ohif/extension-cornerstone) remain publishable; the rest of the workspace is marked private.
  • Docs: guides for out-of-tree development, scaffolding, publishing, runtime plugins and CSP deployment, porting/de-forking, and the extension gallery.

Merge order

This branch overlaps with the rsbuild work in #6164 (React 19 + rsbuild production build) and should land after #6164; the build-pipeline commits here will need a follow-up reconcile once #6164 merges.

Testing

  • platform/create-ohif suite and the app contract suites pass locally.
  • node platform/create-ohif/scripts/scaffold-smoke.mjs is the documented manual end-to-end gate for the scaffolding flow.

Summary by CodeRabbit

  • New Features

    • Added runtime loading for extensions and modes without rebuilding the viewer, with origin allowlisting, integrity checks, compatibility validation, and load auditing.
    • Added the create-ohif tool for scaffolding workspaces, extensions, modes, and deployments, plus migration support for older plugins.
    • Added plugin management commands for adding, removing, linking, listing, and diagnosing configured plugins.
  • Security

    • Improved plugin asset headers, caching, MIME handling, CSP support, and missing-file responses.
  • Documentation

    • Added guidance for runtime plugins, scaffolding, publishing, migration, deployment, and extension galleries.

sedghi added 28 commits July 20, 2026 12:48
…normalize mode main fields

All 26 per-package prod configs now import the shared externals contract
(react/react-dom/react/jsx-runtime added as externals everywhere). Fixes the
ohif-ui-next UMD library name collision with @ohif/ui. Normalizes the four
non-conforming mode main fields to dist/ohif-<name>.umd.js and rewrites
modes/segmentation's legacy standalone config to the standard mode shape.
pluginConfig.json gains a $schema pointer; the never-read version fields are
removed. Schema is draft-07 with additionalProperties:false.
…ce-worker bypass

Both nginx templates gain an opt-in CSP_HEADER add_header (envsubst allowlist
updated) and a /plugins/ location block with explicit MIME types, cache tiers,
and no SPA fallback. The service-worker precache excludes plugins/ and a
NetworkOnly route bypasses /plugins/ fetches (rspack.pwa.js only; the rsbuild
dev pipeline generates no service worker). netlify.toml's Report-Only header
now mirrors the documented baseline with interim unsafe-inline.
…nConfig

runtimeExtensionLoader.ts is the single gate for the loadModule fallthrough:
URL-shaped specifiers must pass a deny-by-default origin allowlist (same-origin
implicit, extras via window.config.runtimeExtensionOrigins) and unknown bare
names throw a descriptive error. The codegen emits the gated epilogue and the
config is structurally validated at build with actionable messages.
pnpm plugin add installs via pnpm and writes the pluginConfig entry in one
step; doctor validates declared plugins, peer ranges against version.txt,
dangling references, and singleton copies in directory plugins.
The published set is now exactly the shared-surface packages (@OHIF/core,
@ohif/ui-next, @ohif/i18n, @ohif/extension-default, @ohif/extension-cornerstone)
with repaired tarball metadata (publishConfig dist rewrites, files, keywords,
peer hygiene). The remaining extensions, all modes, and @ohif/ui are
private: true and stay version-stamped. verify-tarballs enforces the tier
invariant and publish-list parity; verify-umd-global proves a built UMD
assigns its window global. @ohif/app now devDepends on @ohif/ui.
Adds Track B runtime loading: descriptors in window.config.extensions/modes
are loaded through runtimeExtensionLoader with a strict globalName discriminator
(present => UMD global, absent => ESM default), a fail-closed coreVersionRange
gate (includePrerelease), stylesheet injection, and a window.__ohif audit
record reconciled and surfaced via NotificationService. runtimeShared.ts
exposes the twelve host singletons (react, @OHIF/core, @ohif/ui-next,
@ohif/i18n, @cornerstonejs/*, ...) on window so runtime bundles resolve them,
with a parity test binding the list to pluginExternals.hostSharedPackages.
Shared resolveConfig gains exact-match dedupe aliases so directory and
node_modules plugins resolve one copy of react/@OHIF/core/etc. through both
pipelines. Tailwind content globs extend from pluginConfig entries so
out-of-tree and installed plugins render styled. Adds the directory-plugin
verification recipe.
…oyment templates

pnpm create ohif scaffolds standalone extensions/modes, a full workspace (a
user-owned repo with a committed ohif.config.json manifest and a managed .ohif
viewer harness), or a config-only deployment. A migrate subcommand ports
CLI-era extensions to the current contract. Templates vendor the externals
contract; unit and e2e smoke tests cover scaffold, build, and UMD global.
Adds the curated gallery (data file + published JSON Schema), the
build-with-an-agent contract page, the CLI-era porting companion to
create-ohif migrate, and the de-forking guide. The docs build now runs
gallery and AGENTS.md sync validation ahead of docusaurus build.
Deletes platform/cli, the root cli script, ohif-cli.md and its images.
Installation, pluginConfig, and migration-guide pages are rewritten around
pnpm add + pluginConfig + create-ohif; a client redirect sends the old
/development/ohif-cli path to the CLI-removal migration page. AGENTS.md drops
the stale module-federation and yarn claims and gains the extension contract
summary.
rsbuild.config.ts gains a production path that reproduces the old rspack.pwa.js
build: the service-worker manifest injection (ported via tools.rspack, /plugins/
excluded), the VERSION_NUMBER/COMMIT_HASH/BUILD_NUM defines, and the two inline
index.html scripts kept byte-stable. platform/app build and dev scripts now run
rsbuild; @rspack/cli and core move to the root so per-package UMD builds keep
resolving them; rspack.pwa.js is deleted. scripts/verify-build-parity.mjs became
a single-pipeline production build smoke. Parity proven by a 17-check contract
harness, a headless boot with a valid sw.js, and a docker container smoke.
…peline

The single-pipeline cutover dropped the babel-plugin-istanbul post-loader that
rspack.pwa.js applied under COVERAGE=true; re-added it (COVERAGE-gated only, so
dev and production builds are unaffected) so playwright coverage keeps working.
…ferences

Adds .rspack/CONTRACT.md documenting why contract v2 (ESM + import map) stays
deferred (rslib pre-1.0) and the criteria to revisit. Reconciles comments and
docs that still named the deleted rspack.pwa.js to point at rsbuild.config.ts,
and documents that vtk.js is externalized without a host global (a v1 gap a
runtime plugin must not rely on).
Adds a pnpm-workspace.yaml carrying allowBuilds to the extension and mode
templates so pnpm 11 stops erroring on dependency build scripts and
pnpm install succeeds unattended. Switches the extension template from a
separate extracted stylesheet to style-loader runtime injection, so a plugin's
CSS loads whether it is bundled into the host, loaded at runtime, or compiled
from source (an extracted dist/index.css was silently unloaded under the
bundled path). The example panel now sets a theme-aware color; the dead
index.css runtime-descriptor hint and docs references are dropped.
…onfig

The glsl rule was only ever consumed by vtk.js sources and has been
commented out of the shared base config for the whole branch; remove the
orphaned rules/loadShaders.js module and its commented require/usage
lines instead of carrying dead code into the consolidated pipeline.
…face

Eight extensions import @ohif/ui-next at runtime without declaring it, so
a standalone (de-forked) build of any of them would not externalize the
package correctly; declare the workspace peer explicitly. platform/core
no longer uses @ohif/ui anywhere, so drop the stale peer from its
published metadata. Lockfile regenerated for both.
…d a scaffold smoke harness

externals-parity gains a fourth invariant leg: every peerDependency a
scaffolded plugin declares must be a package the host actually shares at
runtime (pluginExternals.hostSharedPackages), since anything else would
be externalized by the build but resolve to undefined when the UMD loads.

scripts/scaffold-smoke.mjs is the documented manual end-to-end gate:
scaffold both templates, pnpm install, build, assert the dist artifacts
and the UMD global contract under stubbed host globals, then run each
scaffold's own test suite. It lives outside the published files list and
requires network, so it stays a repo-side gate rather than a CI job.
…webpack-named deps

AGENTS.md still described extensions as self-contained webpack builds;
point it at the shared .rspack pipeline and note that webpack-merge and
@svgr/webpack are kept on purpose because both are rspack-compatible
despite their names.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.

Tip: disable this comment in your organization's Code Review settings.

@sedghi
sedghi temporarily deployed to fork-pr-approval July 20, 2026 16:56 — with GitHub Actions Inactive
@netlify

netlify Bot commented Jul 20, 2026

Copy link
Copy Markdown

Deploy Preview for ohif-dev ready!

Name Link
🔨 Latest commit cde390b
🔍 Latest deploy log https://app.netlify.com/projects/ohif-dev/deploys/6a5e5336654930000818f534
😎 Deploy Preview https://deploy-preview-6168--ohif-dev.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

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.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6f83c3e7-fc41-4aa3-948f-d8e135aa9642

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

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR migrates OHIF build and publishing workflows to Rspack/Rsbuild, adds runtime plugin loading with validation and auditing, introduces create-ohif scaffolding and migration tooling, removes the legacy CLI, updates plugin packaging, and adds deployment, verification, and documentation support.

Changes

Plugin platform migration

Layer / File(s) Summary
Build and runtime contracts
.rspack/*, rsbuild.config.ts, platform/app/src/*, platform/app/pluginConfig*, Dockerfile, .docker/*
Shared Rspack configuration, package externals, runtime descriptors, origin/integrity checks, audit reporting, shared host globals, plugin asset serving, and CSP handling are added or updated.
Extension and mode build wiring
extensions/*/.rspack/*, modes/*/.rspack/*, platform/*/.rspack/*, extensions/*/package.json, modes/*/package.json
Per-package builds switch to Rspack, use centralized externals, emit UMD-oriented artifacts, and update package visibility, peer dependencies, and entrypoints.
Plugin management and verification
scripts/ohif-plugin.mjs, scripts/verify-*.mjs, scripts/verify-directory-plugin.ps1, .circleci/config.yml
Plugin add/remove/link/unlink/doctor commands and build, UMD, directory-plugin, and tarball contract checks are added to local and CI workflows.
create-ohif scaffolding and migration
platform/create-ohif/**
A new ESM scaffolder supports extension, mode, workspace, and deployment templates, plus CLI-era migration, contract tests, smoke tests, and workspace harness management.
Documentation and migration guidance
platform/docs/**, AGENTS.md, platform/app/.rspack/CONTRACT.md
Plugin contracts, runtime loading, publishing, scaffolding, CLI removal, out-of-tree development, gallery validation, and build migration guidance are documented.
Legacy CLI removal
platform/cli/**, package.json
The legacy CLI package, commands, utilities, root CLI script, and related exports are removed in favor of create-ohif and plugin helper workflows.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested labels: ohif-integration

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning The description covers the PR broadly, but it does not follow the required template sections or checklist items. Rewrite it using the repo template: add Context, Changes & Results, Testing, and the checklist with all boxes checked.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title is concise, semantic-release style, and summarizes the main extensibility/build/runtime changes in the PR.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ohifNextExtensions

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.

Comment thread platform/create-ohif/templates/extension/src/getSopClassHandlerModule.ts Dismissed
@cypress

cypress Bot commented Jul 20, 2026

Copy link
Copy Markdown

Viewers    Run #6609

Run Properties:  status check failed Failed #6609  •  git commit 61821f3302: A few additional bugs in the PR
Project Viewers
Branch Review ohifNextExtensions
Run status status check failed Failed #6609
Run duration 02m 19s
Commit git commit 61821f3302: A few additional bugs in the PR
Committer Bill Wallace
View all properties for this run ↗︎

Test results
Tests that failed  Failures 12
Tests that were flaky  Flaky 0
Tests that did not run due to a developer annotating a test with .skip  Pending 0
Tests that did not run due to a failure in a mocha hook  Skipped 16
Tests that passed  Passing 0
View all changes introduced in this branch ↗︎

Tests for review

Failed  OHIFVideoDisplay.spec.js • 2 failed tests

View Output Video

Test Artifacts
OHIF Video Display > checks if series thumbnails are being displayed Test Replay Screenshots Video
OHIF Video Display > performs double-click to load thumbnail in active viewport Test Replay Screenshots Video
Failed  measurement-tracking/OHIFCornerstoneToolbar.spec.js • 1 failed test

View Output Video

Test Artifacts
OHIF Cornerstone Toolbar > checks if all primary buttons are being displayed Test Replay Screenshots Video
Failed  measurement-tracking/OHIFMeasurementPanel.spec.js • 1 failed test

View Output Video

Test Artifacts
OHIF Measurement Panel > checks if Measurements right panel can be hidden/displayed Test Replay Screenshots Video
Failed  customization/HangingProtocol.spec.js • 1 failed test

View Output Video

Test Artifacts
OHIF HP > Should display 3 up Test Replay Screenshots Video
Failed  measurement-tracking/OHIFCornerstoneHotkeys.spec.js • 1 failed test

View Output Video

Test Artifacts
OHIF Cornerstone Hotkeys > checks if hotkeys "R" and "L" can rotate the image Test Replay Screenshots Video

The first 5 failed specs are shown, see all 11 specs in Cypress Cloud.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 11

🧹 Nitpick comments (4)
extensions/measurement-tracking/package.json (1)

24-26: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the dev:dicom-pdf script.

The dev:dicom-pdf script appears to be a copy-paste error from the dicom-pdf extension, as it just aliases the standard dev script in the measurement-tracking package.

♻️ Proposed fix
     "dev": "cross-env NODE_ENV=development rspack build --config .rspack/rspack.dev.js --watch",
-    "dev:dicom-pdf": "pnpm run dev",
     "build": "cross-env NODE_ENV=production rspack build --config .rspack/rspack.prod.js",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@extensions/measurement-tracking/package.json` around lines 24 - 26, Remove
the dev:dicom-pdf entry from the measurement-tracking package scripts, leaving
the standard dev and build scripts unchanged.
platform/docs/docs/configuration/dataSources/dicom-web.md (1)

106-108: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Change the code block language to bash or sh.

The code block contains a shell command, not JavaScript.

♻️ Proposed fix
-```js
+```bash
 cross-env NODE_ENV=development PROXY_TARGET=/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 rsbuild dev --config ../../rsbuild.config.ts
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @platform/docs/docs/configuration/dataSources/dicom-web.md around lines 106 -
108, Change the fenced code block containing the cross-env command from
JavaScript syntax highlighting to bash or sh, without modifying the command
itself.


</details>

<!-- cr-comment:v1:9d4256e8fb97756ef0178743 -->

</blockquote></details>
<details>
<summary>platform/docs/docs/configuration/configurationFiles.md (1)</summary><blockquote>

`448-448`: _📐 Maintainability & Code Quality_ | _🔵 Trivial_ | _💤 Low value_

**Specify a language for fenced code blocks.**

As indicated by static analysis, several fenced code blocks are missing a language specifier. Adding a language like `text` or `csp` improves formatting and resolves the markdownlint warnings.
- `platform/docs/docs/configuration/configurationFiles.md#L448-L448`: Add `text` to the code fence.
- `platform/docs/docs/deployment/runtime-plugins.md#L28-L28`: Add `text` to the code fence.
- `platform/docs/docs/deployment/runtime-plugins.md#L99-L99`: Add `text` to the code fence.
- `platform/docs/docs/deployment/runtime-plugins.md#L145-L145`: Add `text` to the code fence.
- `platform/docs/docs/development/create-ohif.md#L48-L48`: Add `text` to the code fence.
- `platform/docs/docs/development/create-ohif.md#L86-L86`: Add `text` to the code fence.

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @platform/docs/docs/configuration/configurationFiles.md at line 448, Update
the fenced code blocks at
platform/docs/docs/configuration/configurationFiles.md:448-448,
platform/docs/docs/deployment/runtime-plugins.md:28-28, 99-99, and 145-145, and
platform/docs/docs/development/create-ohif.md:48-48 and 86-86 to include the
text language specifier on each opening fence.


</details>

<!-- cr-comment:v1:399fed159e3df243e39ede2d -->

_Source: Linters/SAST tools_

</blockquote></details>
<details>
<summary>platform/docs/docs/development/out-of-tree.md (1)</summary><blockquote>

`134-136`: _📐 Maintainability & Code Quality_ | _🔵 Trivial_ | _💤 Low value_

**Add a language identifier to the fenced code block.**

Specifying a language like `text` helps with syntax highlighting and resolves markdown linter warnings.




<details>
<summary>♻️ Proposed fix</summary>

```diff
-```
+```text
 <directory>/src/**/*.{jsx,js,ts,tsx,css}
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @platform/docs/docs/development/out-of-tree.md around lines 134 - 136, Add
the text language identifier to the fenced code block containing the directory
glob, changing the opening fence to use text while preserving the block
contents.


</details>

<!-- cr-comment:v1:7ce01cb2e0ba3816f88262e9 -->

_Source: Linters/SAST tools_

</blockquote></details>

</blockquote></details>

<details>
<summary>🤖 Prompt for all review comments with AI agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.rspack/resolveConfig.js:

  • Around line 69-75: Remove the path.resolve(__dirname, '../../../node_modules')
    entry from the moduleSearchPaths array in resolveConfig.js. Preserve the
    existing repository-root and platform-specific node_modules paths.

In @.rspack/rules/cssToJavaScript.js:

  • Around line 19-22: Update the plugins configuration in the PostCSS setup to
    pass tailwindcss and autoprefixer directly as plugin instances, removing the
    extra nested arrays so postcss-loader can apply both plugins.

In @platform/app/src/routes/Mode/Mode.tsx:

  • Around line 104-108: Update the registerExtension catch block in the
    loadModules flow to report failures for built-in extensions even when
    recordRegistrationError finds no audit record. Log the caught error
    unconditionally or update recordRegistrationError to emit a console.error when
    no matching record exists, while preserving existing auditing and runtime
    failure surfacing.

In @platform/core/package.json:

  • Line 24: Update the package.json sideEffects setting from the string value
    "false" to the boolean false, preserving the existing configuration intent.

In @platform/create-ohif/README.md:

  • Around line 17-25: The README usage block for create-ohif must match the
    bin/create-ohif.mjs USAGE definition: expand --template to include workspace and
    deployment, and document the migrate [--dry-run] subcommand. Update only
    the CLI arguments documentation while preserving the existing option
    descriptions.

In @platform/create-ohif/templates/mode/src/toolbarButtons.ts:

  • Around line 4-10: Update setToolActiveToolbar.commandOptions.toolGroupIds to
    remove the dangling 'mpr' entry, leaving only the 'default' tool group created
    by onModeEnter.

In @platform/create-ohif/templates/workspace/scripts/ohif.mjs:

  • Line 144: Update the package classification logic in the section assignment to
    recognize both scoped and unscoped mode package names, including names such as
    mode-basic and @ohif/mode-basic, while continuing to classify other packages as
    extensions.

In
@platform/docs/versioned_docs/version-3.11/configuration/dataSources/dicom-web.md:

  • Around line 107-114: Restore the historical Webpack references in all three
    affected version-3.11 documents: in
    platform/docs/versioned_docs/version-3.11/configuration/dataSources/dicom-web.md
    lines 107-114, replace .rspack/rspack.pwa.js with .webpack/webpack.pwa.js in
    both the command and explanation; in
    platform/docs/versioned_docs/version-3.11/development/ohif-cli.md line 309,
    replace rspack.pwa.js with webpack.pwa.js; and in
    platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/0-general.md
    line 108, replace .rspack/rspack.pwa.js with .webpack/webpack.pwa.js.

In @platform/ui-next/package.json:

  • Around line 25-30: Update the publishConfig.exports map in package.json to
    avoid exposing raw TypeScript and TSX source paths through ./lib/* and
    ./components/*. If the SDK supports only root-module runtime imports, remove
    these deep exports; otherwise, redirect them to transpiled JavaScript artifacts
    that are included in the published package.

In @scripts/verify-build-parity.mjs:

  • Around line 65-74: Guard the spawnSync results in both treeContains and
    hasWasm before accessing stdout.trim(). Handle r.error or a non-success status
    explicitly, producing a clear command-unavailable/failure result instead of
    allowing null stdout to cause a TypeError, while preserving the existing match
    behavior for successful grep/sh execution.

In @scripts/verify-directory-plugin.ps1:

  • Line 1: Save scripts/verify-directory-plugin.ps1 with a UTF-8 BOM, preserving
    its existing content and Windows PowerShell 5.1 compatibility.

Nitpick comments:
In @extensions/measurement-tracking/package.json:

  • Around line 24-26: Remove the dev:dicom-pdf entry from the
    measurement-tracking package scripts, leaving the standard dev and build scripts
    unchanged.

In @platform/docs/docs/configuration/configurationFiles.md:

  • Line 448: Update the fenced code blocks at
    platform/docs/docs/configuration/configurationFiles.md:448-448,
    platform/docs/docs/deployment/runtime-plugins.md:28-28, 99-99, and 145-145, and
    platform/docs/docs/development/create-ohif.md:48-48 and 86-86 to include the
    text language specifier on each opening fence.

In @platform/docs/docs/configuration/dataSources/dicom-web.md:

  • Around line 106-108: Change the fenced code block containing the cross-env
    command from JavaScript syntax highlighting to bash or sh, without modifying the
    command itself.

In @platform/docs/docs/development/out-of-tree.md:

  • Around line 134-136: Add the text language identifier to the fenced code block
    containing the directory glob, changing the opening fence to use text while
    preserving the block contents.

</details>

<details>
<summary>🪄 Autofix (Beta)</summary>

Fix all unresolved CodeRabbit comments on this PR:

- [ ] <!-- {"checkboxId": "4b0d0e0a-96d7-4f10-b296-3a18ea78f0b9"} --> Push a commit to this branch (recommended)
- [ ] <!-- {"checkboxId": "ff5b1114-7d8c-49e6-8ac1-43f82af23a33"} --> Create a new PR with the fixes

</details>

---

<details>
<summary>ℹ️ Review info</summary>

<details>
<summary>⚙️ Run configuration</summary>

**Configuration used**: defaults

**Review profile**: CHILL

**Plan**: Pro Plus

**Run ID**: `79d665b9-9811-4593-b285-58a8a44982bb`

</details>

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 28550c962fa10a456b18a27bc57e6434404f01bb and cde390bdd6d2b572899d9fc727850d3f4aced783.

</details>

<details>
<summary>⛔ Files ignored due to path filters (24)</summary>

* `.webpack/resolveConfig.js` is excluded by `!**/.webpack/**`
* `.webpack/rules/loadShaders.js` is excluded by `!**/.webpack/**`
* `extensions/cornerstone/.webpack/webpack.dev.js` is excluded by `!**/.webpack/**`
* `extensions/default/.webpack/webpack.dev.js` is excluded by `!**/.webpack/**`
* `extensions/dicom-microscopy/.webpack/webpack.dev.js` is excluded by `!**/.webpack/**`
* `extensions/measurement-tracking/.webpack/webpack.dev.js` is excluded by `!**/.webpack/**`
* `extensions/test-extension/.webpack/webpack.dev.js` is excluded by `!**/.webpack/**`
* `modes/preclinical-4d/.webpack/webpack.dev.js` is excluded by `!**/.webpack/**`
* `modes/segmentation/.webpack/webpack.prod.js` is excluded by `!**/.webpack/**`
* `modes/tmtv/.webpack/webpack.dev.js` is excluded by `!**/.webpack/**`
* `modes/usAnnotation/.webpack/webpack.dev.js` is excluded by `!**/.webpack/**`
* `platform/app/.webpack/webpack.pwa.js` is excluded by `!**/.webpack/**`
* `platform/core/.webpack/webpack.dev.js` is excluded by `!**/.webpack/**`
* `platform/create-ohif/test/fixtures/cli-era/extension/.webpack/webpack.prod.js` is excluded by `!**/.webpack/**`
* `platform/create-ohif/test/fixtures/cli-era/mode/.webpack/webpack.prod.js` is excluded by `!**/.webpack/**`
* `platform/docs/docs/assets/img/add-mode.png` is excluded by `!**/*.png`
* `platform/docs/docs/assets/img/cli-search-no-verbose.png` is excluded by `!**/*.png`
* `platform/docs/docs/assets/img/cli-search-with-verbose.png` is excluded by `!**/*.png`
* `platform/docs/docs/assets/img/clock-mode.png` is excluded by `!**/*.png`
* `platform/docs/docs/assets/img/clock-mode1.png` is excluded by `!**/*.png`
* `platform/docs/docs/assets/img/create-mode.png` is excluded by `!**/*.png`
* `platform/docs/docs/assets/img/mode-template.png` is excluded by `!**/*.png`
* `platform/docs/docs/assets/img/ohif-cli-list.png` is excluded by `!**/*.png`
* `pnpm-lock.yaml` is excluded by `!**/pnpm-lock.yaml`

</details>

<details>
<summary>📒 Files selected for processing (273)</summary>

* `.circleci/config.yml`
* `.docker/Viewer-v3.x/default.conf.template`
* `.docker/Viewer-v3.x/default.ssl.conf.template`
* `.docker/Viewer-v3.x/entrypoint.sh`
* `.rspack/helpers/excludeNodeModulesExcept.js`
* `.rspack/pluginExternals.js`
* `.rspack/resolveConfig.js`
* `.rspack/rspack.base.js`
* `.rspack/rules/cssToJavaScript.js`
* `.rspack/rules/loadWebWorkers.js`
* `.rspack/rules/stylusToJavaScript.js`
* `.rspack/rules/transpileJavaScript.js`
* `AGENTS.md`
* `Dockerfile`
* `extensions/cornerstone-dicom-pmap/.rspack/rspack.dev.js`
* `extensions/cornerstone-dicom-pmap/.rspack/rspack.prod.js`
* `extensions/cornerstone-dicom-pmap/package.json`
* `extensions/cornerstone-dicom-rt/.rspack/rspack.dev.js`
* `extensions/cornerstone-dicom-rt/.rspack/rspack.prod.js`
* `extensions/cornerstone-dicom-rt/package.json`
* `extensions/cornerstone-dicom-seg/.rspack/rspack.dev.js`
* `extensions/cornerstone-dicom-seg/.rspack/rspack.prod.js`
* `extensions/cornerstone-dicom-seg/package.json`
* `extensions/cornerstone-dicom-sr/.rspack/rspack.dev.js`
* `extensions/cornerstone-dicom-sr/.rspack/rspack.prod.js`
* `extensions/cornerstone-dicom-sr/package.json`
* `extensions/cornerstone-dynamic-volume/.rspack/rspack.dev.js`
* `extensions/cornerstone-dynamic-volume/.rspack/rspack.prod.js`
* `extensions/cornerstone-dynamic-volume/package.json`
* `extensions/cornerstone/.rspack/rspack.dev.js`
* `extensions/cornerstone/.rspack/rspack.prod.js`
* `extensions/cornerstone/package.json`
* `extensions/default/.rspack/rspack.dev.js`
* `extensions/default/.rspack/rspack.prod.js`
* `extensions/default/package.json`
* `extensions/dicom-microscopy/.rspack/rspack.dev.js`
* `extensions/dicom-microscopy/.rspack/rspack.prod.js`
* `extensions/dicom-microscopy/package.json`
* `extensions/dicom-pdf/.rspack/rspack.dev.js`
* `extensions/dicom-pdf/.rspack/rspack.prod.js`
* `extensions/dicom-pdf/package.json`
* `extensions/dicom-video/.rspack/rspack.dev.js`
* `extensions/dicom-video/.rspack/rspack.prod.js`
* `extensions/dicom-video/package.json`
* `extensions/measurement-tracking/.rspack/rspack.dev.js`
* `extensions/measurement-tracking/.rspack/rspack.prod.js`
* `extensions/measurement-tracking/package.json`
* `extensions/test-extension/.rspack/rspack.dev.js`
* `extensions/test-extension/.rspack/rspack.prod.js`
* `extensions/test-extension/package.json`
* `extensions/tmtv/.rspack/rspack.dev.js`
* `extensions/tmtv/.rspack/rspack.prod.js`
* `extensions/tmtv/package.json`
* `extensions/usAnnotation/.rspack/rspack.dev.js`
* `extensions/usAnnotation/.rspack/rspack.prod.js`
* `extensions/usAnnotation/package.json`
* `modes/basic-dev-mode/.rspack/rspack.dev.js`
* `modes/basic-dev-mode/.rspack/rspack.prod.js`
* `modes/basic-dev-mode/package.json`
* `modes/basic-test-mode/.rspack/rspack.dev.js`
* `modes/basic-test-mode/.rspack/rspack.prod.js`
* `modes/basic-test-mode/package.json`
* `modes/basic/.rspack/rspack.prod.js`
* `modes/basic/package.json`
* `modes/longitudinal/.rspack/rspack.dev.js`
* `modes/longitudinal/.rspack/rspack.prod.js`
* `modes/longitudinal/package.json`
* `modes/microscopy/.rspack/rspack.dev.js`
* `modes/microscopy/.rspack/rspack.prod.js`
* `modes/microscopy/package.json`
* `modes/preclinical-4d/.rspack/rspack.dev.js`
* `modes/preclinical-4d/.rspack/rspack.prod.js`
* `modes/preclinical-4d/package.json`
* `modes/segmentation/.rspack/rspack.prod.js`
* `modes/segmentation/package.json`
* `modes/tmtv/.rspack/rspack.dev.js`
* `modes/tmtv/.rspack/rspack.prod.js`
* `modes/tmtv/package.json`
* `modes/usAnnotation/.rspack/rspack.dev.js`
* `modes/usAnnotation/.rspack/rspack.prod.js`
* `modes/usAnnotation/package.json`
* `netlify.toml`
* `package.json`
* `platform/app/.rspack/CONTRACT.md`
* `platform/app/.rspack/rules/fontsToJavaScript.js`
* `platform/app/.rspack/writePluginImportsFile.js`
* `platform/app/package.json`
* `platform/app/pluginConfig.json`
* `platform/app/pluginConfig.schema.json`
* `platform/app/src/App.tsx`
* `platform/app/src/__tests__/pluginImportsCodegen.test.js`
* `platform/app/src/appInit.js`
* `platform/app/src/index.js`
* `platform/app/src/loadDynamicConfig.js`
* `platform/app/src/loadDynamicConfig.test.js`
* `platform/app/src/routes/Mode/Mode.tsx`
* `platform/app/src/runtimeExtensionLoader.test.js`
* `platform/app/src/runtimeExtensionLoader.ts`
* `platform/app/src/runtimeShared.test.js`
* `platform/app/src/runtimeShared.ts`
* `platform/app/src/service-worker.js`
* `platform/app/src/types/runtimeExtensions.d.ts`
* `platform/app/tailwind.config.js`
* `platform/cli/CHANGELOG.md`
* `platform/cli/package.json`
* `platform/cli/src/commands/addExtension.js`
* `platform/cli/src/commands/addExtensions.js`
* `platform/cli/src/commands/addMode.js`
* `platform/cli/src/commands/constants/notFound.js`
* `platform/cli/src/commands/createPackage.js`
* `platform/cli/src/commands/enums/colors.js`
* `platform/cli/src/commands/enums/endPoints.js`
* `platform/cli/src/commands/enums/index.js`
* `platform/cli/src/commands/enums/keywords.js`
* `platform/cli/src/commands/index.js`
* `platform/cli/src/commands/linkPackage.js`
* `platform/cli/src/commands/listPlugins.js`
* `platform/cli/src/commands/removeExtension.js`
* `platform/cli/src/commands/removeExtensions.js`
* `platform/cli/src/commands/removeMode.js`
* `platform/cli/src/commands/searchPlugins.js`
* `platform/cli/src/commands/unlinkPackage.js`
* `platform/cli/src/commands/utils/addToConfig.js`
* `platform/cli/src/commands/utils/createDirectoryContents.js`
* `platform/cli/src/commands/utils/createLicense.js`
* `platform/cli/src/commands/utils/createReadme.js`
* `platform/cli/src/commands/utils/editPackageJson.js`
* `platform/cli/src/commands/utils/findOhifExtensionsToRemoveAfterRemovingMode.js`
* `platform/cli/src/commands/utils/findRequiredOhifExtensionsForMode.js`
* `platform/cli/src/commands/utils/getVersionedPackageName.js`
* `platform/cli/src/commands/utils/getYarnInfo.js`
* `platform/cli/src/commands/utils/index.js`
* `platform/cli/src/commands/utils/initGit.js`
* `platform/cli/src/commands/utils/installNPMPackage.js`
* `platform/cli/src/commands/utils/prettyPrint.js`
* `platform/cli/src/commands/utils/private/getPackageNameAndScope.js`
* `platform/cli/src/commands/utils/private/index.js`
* `platform/cli/src/commands/utils/private/manipulatePluginConfigFile.js`
* `platform/cli/src/commands/utils/private/readPluginConfigFile.js`
* `platform/cli/src/commands/utils/private/writePluginConfigFile.js`
* `platform/cli/src/commands/utils/removeFromConfig.js`
* `platform/cli/src/commands/utils/throwIfExtensionUsedByInstalledMode.js`
* `platform/cli/src/commands/utils/uninstallNPMPackage.js`
* `platform/cli/src/commands/utils/validate.js`
* `platform/cli/src/commands/utils/validatePnpm.js`
* `platform/cli/src/index.js`
* `platform/cli/src/questions.js`
* `platform/core/.rspack/rspack.dev.js`
* `platform/core/.rspack/rspack.prod.js`
* `platform/core/package.json`
* `platform/create-ohif/README.md`
* `platform/create-ohif/bin/create-ohif.mjs`
* `platform/create-ohif/package.json`
* `platform/create-ohif/scripts/scaffold-smoke.mjs`
* `platform/create-ohif/templates/deployment/README.md`
* `platform/create-ohif/templates/deployment/app-config.js`
* `platform/create-ohif/templates/deployment/docker-compose.yml`
* `platform/create-ohif/templates/extension/AGENTS.md`
* `platform/create-ohif/templates/extension/README.md`
* `platform/create-ohif/templates/extension/_gitignore`
* `platform/create-ohif/templates/extension/_npmrc`
* `platform/create-ohif/templates/extension/_prettierrc`
* `platform/create-ohif/templates/extension/_rspack/pluginExternals.js`
* `platform/create-ohif/templates/extension/_rspack/rspack.prod.js`
* `platform/create-ohif/templates/extension/package.json`
* `platform/create-ohif/templates/extension/pnpm-workspace.yaml`
* `platform/create-ohif/templates/extension/public/.gitkeep`
* `platform/create-ohif/templates/extension/src/__tests__/extension.test.ts`
* `platform/create-ohif/templates/extension/src/__tests__/harness.ts`
* `platform/create-ohif/templates/extension/src/commandsModule.ts`
* `platform/create-ohif/templates/extension/src/getHangingProtocolModule.ts`
* `platform/create-ohif/templates/extension/src/getPanelModule.tsx`
* `platform/create-ohif/templates/extension/src/getSopClassHandlerModule.ts`
* `platform/create-ohif/templates/extension/src/getToolbarModule.tsx`
* `platform/create-ohif/templates/extension/src/getViewportModule.tsx`
* `platform/create-ohif/templates/extension/src/id.ts`
* `platform/create-ohif/templates/extension/src/index.tsx`
* `platform/create-ohif/templates/extension/src/panels/ExamplePanel.tsx`
* `platform/create-ohif/templates/extension/src/styles.css`
* `platform/create-ohif/templates/extension/src/viewports/ExampleViewport.tsx`
* `platform/create-ohif/templates/extension/tailwind.config.js`
* `platform/create-ohif/templates/extension/tsconfig.json`
* `platform/create-ohif/templates/mode/AGENTS.md`
* `platform/create-ohif/templates/mode/README.md`
* `platform/create-ohif/templates/mode/_gitignore`
* `platform/create-ohif/templates/mode/_npmrc`
* `platform/create-ohif/templates/mode/_prettierrc`
* `platform/create-ohif/templates/mode/_rspack/pluginExternals.js`
* `platform/create-ohif/templates/mode/_rspack/rspack.prod.js`
* `platform/create-ohif/templates/mode/package.json`
* `platform/create-ohif/templates/mode/pnpm-workspace.yaml`
* `platform/create-ohif/templates/mode/src/__tests__/mode.test.ts`
* `platform/create-ohif/templates/mode/src/id.ts`
* `platform/create-ohif/templates/mode/src/index.ts`
* `platform/create-ohif/templates/mode/src/toolbarButtons.ts`
* `platform/create-ohif/templates/mode/tsconfig.json`
* `platform/create-ohif/templates/workspace/Dockerfile`
* `platform/create-ohif/templates/workspace/_gitignore`
* `platform/create-ohif/templates/workspace/config/app-config.js`
* `platform/create-ohif/templates/workspace/extensions/.gitkeep`
* `platform/create-ohif/templates/workspace/modes/.gitkeep`
* `platform/create-ohif/templates/workspace/ohif.config.json`
* `platform/create-ohif/templates/workspace/package.json`
* `platform/create-ohif/templates/workspace/scripts/ohif.mjs`
* `platform/create-ohif/test/fixtures/cli-era/extension/.gitignore`
* `platform/create-ohif/test/fixtures/cli-era/extension/.prettierrc`
* `platform/create-ohif/test/fixtures/cli-era/extension/babel.config.js`
* `platform/create-ohif/test/fixtures/cli-era/extension/dependencies.json`
* `platform/create-ohif/test/fixtures/cli-era/extension/src/id.js`
* `platform/create-ohif/test/fixtures/cli-era/extension/src/index.tsx`
* `platform/create-ohif/test/fixtures/cli-era/mode/.gitignore`
* `platform/create-ohif/test/fixtures/cli-era/mode/.prettierrc`
* `platform/create-ohif/test/fixtures/cli-era/mode/babel.config.js`
* `platform/create-ohif/test/fixtures/cli-era/mode/dependencies.json`
* `platform/create-ohif/test/fixtures/cli-era/mode/src/id.js`
* `platform/create-ohif/test/fixtures/cli-era/mode/src/index.tsx`
* `platform/create-ohif/tests/externals-parity.test.mjs`
* `platform/create-ohif/tests/manifest.test.mjs`
* `platform/create-ohif/tests/migrate.test.mjs`
* `platform/create-ohif/tests/scaffold.test.mjs`
* `platform/docs/docs/configuration/configurationFiles.md`
* `platform/docs/docs/configuration/dataSources/dicom-web.md`
* `platform/docs/docs/deployment/runtime-plugins.md`
* `platform/docs/docs/development/create-ohif.md`
* `platform/docs/docs/development/ohif-cli.md`
* `platform/docs/docs/development/out-of-tree.md`
* `platform/docs/docs/development/publishing.md`
* `platform/docs/docs/development/video-tutorials.md`
* `platform/docs/docs/migration-guide/3p12-to-3p13/build-tooling.md`
* `platform/docs/docs/migration-guide/3p12-to-3p13/cli-removal.md`
* `platform/docs/docs/migration-guide/3p12-to-3p13/customization-url.md`
* `platform/docs/docs/migration-guide/3p12-to-3p13/node-version.md`
* `platform/docs/docs/migration-guide/3p12-to-3p13/package-manager.md`
* `platform/docs/docs/migration-guide/3p8-to-3p9/0-general.md`
* `platform/docs/docs/migration-guide/cli-era-extensions.md`
* `platform/docs/docs/migration-guide/deforking.md`
* `platform/docs/docs/migration-guide/from-v2.md`
* `platform/docs/docs/platform/extensions/building-with-agents.md`
* `platform/docs/docs/platform/extensions/gallery.mdx`
* `platform/docs/docs/platform/extensions/index.md`
* `platform/docs/docs/platform/extensions/installation.md`
* `platform/docs/docs/platform/extensions/pluginConfig.md`
* `platform/docs/docs/platform/extensions/runtime-extensions.md`
* `platform/docs/docs/platform/modes/index.md`
* `platform/docs/docs/platform/modes/installation.md`
* `platform/docs/docusaurus.config.js`
* `platform/docs/package.json`
* `platform/docs/scripts/check-agents-md-sync.mjs`
* `platform/docs/scripts/validate-gallery.mjs`
* `platform/docs/src/data/extension-gallery.json`
* `platform/docs/static/schemas/extension-gallery.schema.json`
* `platform/docs/versioned_docs/version-3.11/configuration/dataSources/dicom-web.md`
* `platform/docs/versioned_docs/version-3.11/development/ohif-cli.md`
* `platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/0-general.md`
* `platform/docs/versioned_docs/version-3.11/migration-guide/from-v2.md`
* `platform/i18n/.rspack/rspack.dev.js`
* `platform/i18n/.rspack/rspack.prod.js`
* `platform/i18n/package.json`
* `platform/ui-next/.rspack/rspack.dev.js`
* `platform/ui-next/.rspack/rspack.prod.js`
* `platform/ui-next/.rspack/template.html`
* `platform/ui-next/package.json`
* `platform/ui/.rspack/rspack.dev.js`
* `platform/ui/.rspack/rspack.prod.js`
* `platform/ui/package.json`
* `playwright.config.ts`
* `publish-package.mjs`
* `rsbuild.config.ts`
* `scripts/ohif-plugin.mjs`
* `scripts/verify-build-parity.mjs`
* `scripts/verify-directory-plugin.ps1`
* `scripts/verify-tarballs.mjs`
* `scripts/verify-umd-global.mjs`

</details>

<details>
<summary>💤 Files with no reviewable changes (44)</summary>

* platform/cli/src/commands/enums/colors.js
* platform/cli/src/questions.js
* platform/cli/src/commands/utils/editPackageJson.js
* platform/cli/src/commands/enums/keywords.js
* platform/cli/src/commands/constants/notFound.js
* platform/cli/src/commands/removeExtension.js
* platform/cli/src/commands/utils/createDirectoryContents.js
* platform/cli/src/commands/utils/installNPMPackage.js
* platform/cli/src/commands/utils/getYarnInfo.js
* platform/cli/src/commands/enums/index.js
* platform/cli/src/commands/addMode.js
* platform/docs/docs/development/ohif-cli.md
* platform/cli/src/commands/index.js
* platform/cli/src/commands/createPackage.js
* platform/cli/src/commands/utils/createLicense.js
* platform/cli/src/commands/utils/private/manipulatePluginConfigFile.js
* platform/cli/src/commands/utils/getVersionedPackageName.js
* platform/cli/src/commands/utils/index.js
* platform/cli/src/commands/addExtensions.js
* platform/cli/src/commands/enums/endPoints.js
* platform/cli/src/commands/utils/initGit.js
* platform/cli/src/commands/utils/uninstallNPMPackage.js
* platform/cli/src/commands/listPlugins.js
* platform/cli/src/commands/utils/createReadme.js
* platform/cli/src/commands/utils/private/index.js
* platform/cli/src/commands/utils/findOhifExtensionsToRemoveAfterRemovingMode.js
* platform/cli/src/commands/utils/throwIfExtensionUsedByInstalledMode.js
* platform/cli/package.json
* platform/cli/src/commands/utils/validate.js
* platform/cli/src/commands/removeExtensions.js
* platform/cli/src/commands/utils/private/getPackageNameAndScope.js
* platform/cli/src/commands/removeMode.js
* platform/cli/src/commands/utils/removeFromConfig.js
* platform/cli/src/commands/linkPackage.js
* platform/cli/src/commands/addExtension.js
* platform/cli/src/commands/utils/prettyPrint.js
* platform/cli/src/commands/utils/private/writePluginConfigFile.js
* platform/cli/src/index.js
* platform/cli/src/commands/utils/private/readPluginConfigFile.js
* platform/cli/src/commands/searchPlugins.js
* platform/cli/src/commands/utils/addToConfig.js
* platform/cli/src/commands/utils/validatePnpm.js
* platform/cli/src/commands/utils/findRequiredOhifExtensionsForMode.js
* platform/cli/src/commands/unlinkPackage.js

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 11

🧹 Nitpick comments (4)
extensions/measurement-tracking/package.json (1)

24-26: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the dev:dicom-pdf script.

The dev:dicom-pdf script appears to be a copy-paste error from the dicom-pdf extension, as it just aliases the standard dev script in the measurement-tracking package.

♻️ Proposed fix
     "dev": "cross-env NODE_ENV=development rspack build --config .rspack/rspack.dev.js --watch",
-    "dev:dicom-pdf": "pnpm run dev",
     "build": "cross-env NODE_ENV=production rspack build --config .rspack/rspack.prod.js",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@extensions/measurement-tracking/package.json` around lines 24 - 26, Remove
the dev:dicom-pdf entry from the measurement-tracking package scripts, leaving
the standard dev and build scripts unchanged.
platform/docs/docs/configuration/dataSources/dicom-web.md (1)

106-108: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Change the code block language to bash or sh.

The code block contains a shell command, not JavaScript.

♻️ Proposed fix
-```js
+```bash
 cross-env NODE_ENV=development PROXY_TARGET=/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 rsbuild dev --config ../../rsbuild.config.ts
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @platform/docs/docs/configuration/dataSources/dicom-web.md around lines 106 -
108, Change the fenced code block containing the cross-env command from
JavaScript syntax highlighting to bash or sh, without modifying the command
itself.


</details>

<!-- cr-comment:v1:9d4256e8fb97756ef0178743 -->

</blockquote></details>
<details>
<summary>platform/docs/docs/configuration/configurationFiles.md (1)</summary><blockquote>

`448-448`: _📐 Maintainability & Code Quality_ | _🔵 Trivial_ | _💤 Low value_

**Specify a language for fenced code blocks.**

As indicated by static analysis, several fenced code blocks are missing a language specifier. Adding a language like `text` or `csp` improves formatting and resolves the markdownlint warnings.
- `platform/docs/docs/configuration/configurationFiles.md#L448-L448`: Add `text` to the code fence.
- `platform/docs/docs/deployment/runtime-plugins.md#L28-L28`: Add `text` to the code fence.
- `platform/docs/docs/deployment/runtime-plugins.md#L99-L99`: Add `text` to the code fence.
- `platform/docs/docs/deployment/runtime-plugins.md#L145-L145`: Add `text` to the code fence.
- `platform/docs/docs/development/create-ohif.md#L48-L48`: Add `text` to the code fence.
- `platform/docs/docs/development/create-ohif.md#L86-L86`: Add `text` to the code fence.

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @platform/docs/docs/configuration/configurationFiles.md at line 448, Update
the fenced code blocks at
platform/docs/docs/configuration/configurationFiles.md:448-448,
platform/docs/docs/deployment/runtime-plugins.md:28-28, 99-99, and 145-145, and
platform/docs/docs/development/create-ohif.md:48-48 and 86-86 to include the
text language specifier on each opening fence.


</details>

<!-- cr-comment:v1:399fed159e3df243e39ede2d -->

_Source: Linters/SAST tools_

</blockquote></details>
<details>
<summary>platform/docs/docs/development/out-of-tree.md (1)</summary><blockquote>

`134-136`: _📐 Maintainability & Code Quality_ | _🔵 Trivial_ | _💤 Low value_

**Add a language identifier to the fenced code block.**

Specifying a language like `text` helps with syntax highlighting and resolves markdown linter warnings.




<details>
<summary>♻️ Proposed fix</summary>

```diff
-```
+```text
 <directory>/src/**/*.{jsx,js,ts,tsx,css}
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @platform/docs/docs/development/out-of-tree.md around lines 134 - 136, Add
the text language identifier to the fenced code block containing the directory
glob, changing the opening fence to use text while preserving the block
contents.


</details>

<!-- cr-comment:v1:7ce01cb2e0ba3816f88262e9 -->

_Source: Linters/SAST tools_

</blockquote></details>

</blockquote></details>

<details>
<summary>🤖 Prompt for all review comments with AI agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.rspack/resolveConfig.js:

  • Around line 69-75: Remove the path.resolve(__dirname, '../../../node_modules')
    entry from the moduleSearchPaths array in resolveConfig.js. Preserve the
    existing repository-root and platform-specific node_modules paths.

In @.rspack/rules/cssToJavaScript.js:

  • Around line 19-22: Update the plugins configuration in the PostCSS setup to
    pass tailwindcss and autoprefixer directly as plugin instances, removing the
    extra nested arrays so postcss-loader can apply both plugins.

In @platform/app/src/routes/Mode/Mode.tsx:

  • Around line 104-108: Update the registerExtension catch block in the
    loadModules flow to report failures for built-in extensions even when
    recordRegistrationError finds no audit record. Log the caught error
    unconditionally or update recordRegistrationError to emit a console.error when
    no matching record exists, while preserving existing auditing and runtime
    failure surfacing.

In @platform/core/package.json:

  • Line 24: Update the package.json sideEffects setting from the string value
    "false" to the boolean false, preserving the existing configuration intent.

In @platform/create-ohif/README.md:

  • Around line 17-25: The README usage block for create-ohif must match the
    bin/create-ohif.mjs USAGE definition: expand --template to include workspace and
    deployment, and document the migrate [--dry-run] subcommand. Update only
    the CLI arguments documentation while preserving the existing option
    descriptions.

In @platform/create-ohif/templates/mode/src/toolbarButtons.ts:

  • Around line 4-10: Update setToolActiveToolbar.commandOptions.toolGroupIds to
    remove the dangling 'mpr' entry, leaving only the 'default' tool group created
    by onModeEnter.

In @platform/create-ohif/templates/workspace/scripts/ohif.mjs:

  • Line 144: Update the package classification logic in the section assignment to
    recognize both scoped and unscoped mode package names, including names such as
    mode-basic and @ohif/mode-basic, while continuing to classify other packages as
    extensions.

In
@platform/docs/versioned_docs/version-3.11/configuration/dataSources/dicom-web.md:

  • Around line 107-114: Restore the historical Webpack references in all three
    affected version-3.11 documents: in
    platform/docs/versioned_docs/version-3.11/configuration/dataSources/dicom-web.md
    lines 107-114, replace .rspack/rspack.pwa.js with .webpack/webpack.pwa.js in
    both the command and explanation; in
    platform/docs/versioned_docs/version-3.11/development/ohif-cli.md line 309,
    replace rspack.pwa.js with webpack.pwa.js; and in
    platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/0-general.md
    line 108, replace .rspack/rspack.pwa.js with .webpack/webpack.pwa.js.

In @platform/ui-next/package.json:

  • Around line 25-30: Update the publishConfig.exports map in package.json to
    avoid exposing raw TypeScript and TSX source paths through ./lib/* and
    ./components/*. If the SDK supports only root-module runtime imports, remove
    these deep exports; otherwise, redirect them to transpiled JavaScript artifacts
    that are included in the published package.

In @scripts/verify-build-parity.mjs:

  • Around line 65-74: Guard the spawnSync results in both treeContains and
    hasWasm before accessing stdout.trim(). Handle r.error or a non-success status
    explicitly, producing a clear command-unavailable/failure result instead of
    allowing null stdout to cause a TypeError, while preserving the existing match
    behavior for successful grep/sh execution.

In @scripts/verify-directory-plugin.ps1:

  • Line 1: Save scripts/verify-directory-plugin.ps1 with a UTF-8 BOM, preserving
    its existing content and Windows PowerShell 5.1 compatibility.

Nitpick comments:
In @extensions/measurement-tracking/package.json:

  • Around line 24-26: Remove the dev:dicom-pdf entry from the
    measurement-tracking package scripts, leaving the standard dev and build scripts
    unchanged.

In @platform/docs/docs/configuration/configurationFiles.md:

  • Line 448: Update the fenced code blocks at
    platform/docs/docs/configuration/configurationFiles.md:448-448,
    platform/docs/docs/deployment/runtime-plugins.md:28-28, 99-99, and 145-145, and
    platform/docs/docs/development/create-ohif.md:48-48 and 86-86 to include the
    text language specifier on each opening fence.

In @platform/docs/docs/configuration/dataSources/dicom-web.md:

  • Around line 106-108: Change the fenced code block containing the cross-env
    command from JavaScript syntax highlighting to bash or sh, without modifying the
    command itself.

In @platform/docs/docs/development/out-of-tree.md:

  • Around line 134-136: Add the text language identifier to the fenced code block
    containing the directory glob, changing the opening fence to use text while
    preserving the block contents.

</details>

<details>
<summary>🪄 Autofix (Beta)</summary>

Fix all unresolved CodeRabbit comments on this PR:

- [ ] <!-- {"checkboxId": "4b0d0e0a-96d7-4f10-b296-3a18ea78f0b9"} --> Push a commit to this branch (recommended)
- [ ] <!-- {"checkboxId": "ff5b1114-7d8c-49e6-8ac1-43f82af23a33"} --> Create a new PR with the fixes

</details>

---

<details>
<summary>ℹ️ Review info</summary>

<details>
<summary>⚙️ Run configuration</summary>

**Configuration used**: defaults

**Review profile**: CHILL

**Plan**: Pro Plus

**Run ID**: `79d665b9-9811-4593-b285-58a8a44982bb`

</details>

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 28550c962fa10a456b18a27bc57e6434404f01bb and cde390bdd6d2b572899d9fc727850d3f4aced783.

</details>

<details>
<summary>⛔ Files ignored due to path filters (24)</summary>

* `.webpack/resolveConfig.js` is excluded by `!**/.webpack/**`
* `.webpack/rules/loadShaders.js` is excluded by `!**/.webpack/**`
* `extensions/cornerstone/.webpack/webpack.dev.js` is excluded by `!**/.webpack/**`
* `extensions/default/.webpack/webpack.dev.js` is excluded by `!**/.webpack/**`
* `extensions/dicom-microscopy/.webpack/webpack.dev.js` is excluded by `!**/.webpack/**`
* `extensions/measurement-tracking/.webpack/webpack.dev.js` is excluded by `!**/.webpack/**`
* `extensions/test-extension/.webpack/webpack.dev.js` is excluded by `!**/.webpack/**`
* `modes/preclinical-4d/.webpack/webpack.dev.js` is excluded by `!**/.webpack/**`
* `modes/segmentation/.webpack/webpack.prod.js` is excluded by `!**/.webpack/**`
* `modes/tmtv/.webpack/webpack.dev.js` is excluded by `!**/.webpack/**`
* `modes/usAnnotation/.webpack/webpack.dev.js` is excluded by `!**/.webpack/**`
* `platform/app/.webpack/webpack.pwa.js` is excluded by `!**/.webpack/**`
* `platform/core/.webpack/webpack.dev.js` is excluded by `!**/.webpack/**`
* `platform/create-ohif/test/fixtures/cli-era/extension/.webpack/webpack.prod.js` is excluded by `!**/.webpack/**`
* `platform/create-ohif/test/fixtures/cli-era/mode/.webpack/webpack.prod.js` is excluded by `!**/.webpack/**`
* `platform/docs/docs/assets/img/add-mode.png` is excluded by `!**/*.png`
* `platform/docs/docs/assets/img/cli-search-no-verbose.png` is excluded by `!**/*.png`
* `platform/docs/docs/assets/img/cli-search-with-verbose.png` is excluded by `!**/*.png`
* `platform/docs/docs/assets/img/clock-mode.png` is excluded by `!**/*.png`
* `platform/docs/docs/assets/img/clock-mode1.png` is excluded by `!**/*.png`
* `platform/docs/docs/assets/img/create-mode.png` is excluded by `!**/*.png`
* `platform/docs/docs/assets/img/mode-template.png` is excluded by `!**/*.png`
* `platform/docs/docs/assets/img/ohif-cli-list.png` is excluded by `!**/*.png`
* `pnpm-lock.yaml` is excluded by `!**/pnpm-lock.yaml`

</details>

<details>
<summary>📒 Files selected for processing (273)</summary>

* `.circleci/config.yml`
* `.docker/Viewer-v3.x/default.conf.template`
* `.docker/Viewer-v3.x/default.ssl.conf.template`
* `.docker/Viewer-v3.x/entrypoint.sh`
* `.rspack/helpers/excludeNodeModulesExcept.js`
* `.rspack/pluginExternals.js`
* `.rspack/resolveConfig.js`
* `.rspack/rspack.base.js`
* `.rspack/rules/cssToJavaScript.js`
* `.rspack/rules/loadWebWorkers.js`
* `.rspack/rules/stylusToJavaScript.js`
* `.rspack/rules/transpileJavaScript.js`
* `AGENTS.md`
* `Dockerfile`
* `extensions/cornerstone-dicom-pmap/.rspack/rspack.dev.js`
* `extensions/cornerstone-dicom-pmap/.rspack/rspack.prod.js`
* `extensions/cornerstone-dicom-pmap/package.json`
* `extensions/cornerstone-dicom-rt/.rspack/rspack.dev.js`
* `extensions/cornerstone-dicom-rt/.rspack/rspack.prod.js`
* `extensions/cornerstone-dicom-rt/package.json`
* `extensions/cornerstone-dicom-seg/.rspack/rspack.dev.js`
* `extensions/cornerstone-dicom-seg/.rspack/rspack.prod.js`
* `extensions/cornerstone-dicom-seg/package.json`
* `extensions/cornerstone-dicom-sr/.rspack/rspack.dev.js`
* `extensions/cornerstone-dicom-sr/.rspack/rspack.prod.js`
* `extensions/cornerstone-dicom-sr/package.json`
* `extensions/cornerstone-dynamic-volume/.rspack/rspack.dev.js`
* `extensions/cornerstone-dynamic-volume/.rspack/rspack.prod.js`
* `extensions/cornerstone-dynamic-volume/package.json`
* `extensions/cornerstone/.rspack/rspack.dev.js`
* `extensions/cornerstone/.rspack/rspack.prod.js`
* `extensions/cornerstone/package.json`
* `extensions/default/.rspack/rspack.dev.js`
* `extensions/default/.rspack/rspack.prod.js`
* `extensions/default/package.json`
* `extensions/dicom-microscopy/.rspack/rspack.dev.js`
* `extensions/dicom-microscopy/.rspack/rspack.prod.js`
* `extensions/dicom-microscopy/package.json`
* `extensions/dicom-pdf/.rspack/rspack.dev.js`
* `extensions/dicom-pdf/.rspack/rspack.prod.js`
* `extensions/dicom-pdf/package.json`
* `extensions/dicom-video/.rspack/rspack.dev.js`
* `extensions/dicom-video/.rspack/rspack.prod.js`
* `extensions/dicom-video/package.json`
* `extensions/measurement-tracking/.rspack/rspack.dev.js`
* `extensions/measurement-tracking/.rspack/rspack.prod.js`
* `extensions/measurement-tracking/package.json`
* `extensions/test-extension/.rspack/rspack.dev.js`
* `extensions/test-extension/.rspack/rspack.prod.js`
* `extensions/test-extension/package.json`
* `extensions/tmtv/.rspack/rspack.dev.js`
* `extensions/tmtv/.rspack/rspack.prod.js`
* `extensions/tmtv/package.json`
* `extensions/usAnnotation/.rspack/rspack.dev.js`
* `extensions/usAnnotation/.rspack/rspack.prod.js`
* `extensions/usAnnotation/package.json`
* `modes/basic-dev-mode/.rspack/rspack.dev.js`
* `modes/basic-dev-mode/.rspack/rspack.prod.js`
* `modes/basic-dev-mode/package.json`
* `modes/basic-test-mode/.rspack/rspack.dev.js`
* `modes/basic-test-mode/.rspack/rspack.prod.js`
* `modes/basic-test-mode/package.json`
* `modes/basic/.rspack/rspack.prod.js`
* `modes/basic/package.json`
* `modes/longitudinal/.rspack/rspack.dev.js`
* `modes/longitudinal/.rspack/rspack.prod.js`
* `modes/longitudinal/package.json`
* `modes/microscopy/.rspack/rspack.dev.js`
* `modes/microscopy/.rspack/rspack.prod.js`
* `modes/microscopy/package.json`
* `modes/preclinical-4d/.rspack/rspack.dev.js`
* `modes/preclinical-4d/.rspack/rspack.prod.js`
* `modes/preclinical-4d/package.json`
* `modes/segmentation/.rspack/rspack.prod.js`
* `modes/segmentation/package.json`
* `modes/tmtv/.rspack/rspack.dev.js`
* `modes/tmtv/.rspack/rspack.prod.js`
* `modes/tmtv/package.json`
* `modes/usAnnotation/.rspack/rspack.dev.js`
* `modes/usAnnotation/.rspack/rspack.prod.js`
* `modes/usAnnotation/package.json`
* `netlify.toml`
* `package.json`
* `platform/app/.rspack/CONTRACT.md`
* `platform/app/.rspack/rules/fontsToJavaScript.js`
* `platform/app/.rspack/writePluginImportsFile.js`
* `platform/app/package.json`
* `platform/app/pluginConfig.json`
* `platform/app/pluginConfig.schema.json`
* `platform/app/src/App.tsx`
* `platform/app/src/__tests__/pluginImportsCodegen.test.js`
* `platform/app/src/appInit.js`
* `platform/app/src/index.js`
* `platform/app/src/loadDynamicConfig.js`
* `platform/app/src/loadDynamicConfig.test.js`
* `platform/app/src/routes/Mode/Mode.tsx`
* `platform/app/src/runtimeExtensionLoader.test.js`
* `platform/app/src/runtimeExtensionLoader.ts`
* `platform/app/src/runtimeShared.test.js`
* `platform/app/src/runtimeShared.ts`
* `platform/app/src/service-worker.js`
* `platform/app/src/types/runtimeExtensions.d.ts`
* `platform/app/tailwind.config.js`
* `platform/cli/CHANGELOG.md`
* `platform/cli/package.json`
* `platform/cli/src/commands/addExtension.js`
* `platform/cli/src/commands/addExtensions.js`
* `platform/cli/src/commands/addMode.js`
* `platform/cli/src/commands/constants/notFound.js`
* `platform/cli/src/commands/createPackage.js`
* `platform/cli/src/commands/enums/colors.js`
* `platform/cli/src/commands/enums/endPoints.js`
* `platform/cli/src/commands/enums/index.js`
* `platform/cli/src/commands/enums/keywords.js`
* `platform/cli/src/commands/index.js`
* `platform/cli/src/commands/linkPackage.js`
* `platform/cli/src/commands/listPlugins.js`
* `platform/cli/src/commands/removeExtension.js`
* `platform/cli/src/commands/removeExtensions.js`
* `platform/cli/src/commands/removeMode.js`
* `platform/cli/src/commands/searchPlugins.js`
* `platform/cli/src/commands/unlinkPackage.js`
* `platform/cli/src/commands/utils/addToConfig.js`
* `platform/cli/src/commands/utils/createDirectoryContents.js`
* `platform/cli/src/commands/utils/createLicense.js`
* `platform/cli/src/commands/utils/createReadme.js`
* `platform/cli/src/commands/utils/editPackageJson.js`
* `platform/cli/src/commands/utils/findOhifExtensionsToRemoveAfterRemovingMode.js`
* `platform/cli/src/commands/utils/findRequiredOhifExtensionsForMode.js`
* `platform/cli/src/commands/utils/getVersionedPackageName.js`
* `platform/cli/src/commands/utils/getYarnInfo.js`
* `platform/cli/src/commands/utils/index.js`
* `platform/cli/src/commands/utils/initGit.js`
* `platform/cli/src/commands/utils/installNPMPackage.js`
* `platform/cli/src/commands/utils/prettyPrint.js`
* `platform/cli/src/commands/utils/private/getPackageNameAndScope.js`
* `platform/cli/src/commands/utils/private/index.js`
* `platform/cli/src/commands/utils/private/manipulatePluginConfigFile.js`
* `platform/cli/src/commands/utils/private/readPluginConfigFile.js`
* `platform/cli/src/commands/utils/private/writePluginConfigFile.js`
* `platform/cli/src/commands/utils/removeFromConfig.js`
* `platform/cli/src/commands/utils/throwIfExtensionUsedByInstalledMode.js`
* `platform/cli/src/commands/utils/uninstallNPMPackage.js`
* `platform/cli/src/commands/utils/validate.js`
* `platform/cli/src/commands/utils/validatePnpm.js`
* `platform/cli/src/index.js`
* `platform/cli/src/questions.js`
* `platform/core/.rspack/rspack.dev.js`
* `platform/core/.rspack/rspack.prod.js`
* `platform/core/package.json`
* `platform/create-ohif/README.md`
* `platform/create-ohif/bin/create-ohif.mjs`
* `platform/create-ohif/package.json`
* `platform/create-ohif/scripts/scaffold-smoke.mjs`
* `platform/create-ohif/templates/deployment/README.md`
* `platform/create-ohif/templates/deployment/app-config.js`
* `platform/create-ohif/templates/deployment/docker-compose.yml`
* `platform/create-ohif/templates/extension/AGENTS.md`
* `platform/create-ohif/templates/extension/README.md`
* `platform/create-ohif/templates/extension/_gitignore`
* `platform/create-ohif/templates/extension/_npmrc`
* `platform/create-ohif/templates/extension/_prettierrc`
* `platform/create-ohif/templates/extension/_rspack/pluginExternals.js`
* `platform/create-ohif/templates/extension/_rspack/rspack.prod.js`
* `platform/create-ohif/templates/extension/package.json`
* `platform/create-ohif/templates/extension/pnpm-workspace.yaml`
* `platform/create-ohif/templates/extension/public/.gitkeep`
* `platform/create-ohif/templates/extension/src/__tests__/extension.test.ts`
* `platform/create-ohif/templates/extension/src/__tests__/harness.ts`
* `platform/create-ohif/templates/extension/src/commandsModule.ts`
* `platform/create-ohif/templates/extension/src/getHangingProtocolModule.ts`
* `platform/create-ohif/templates/extension/src/getPanelModule.tsx`
* `platform/create-ohif/templates/extension/src/getSopClassHandlerModule.ts`
* `platform/create-ohif/templates/extension/src/getToolbarModule.tsx`
* `platform/create-ohif/templates/extension/src/getViewportModule.tsx`
* `platform/create-ohif/templates/extension/src/id.ts`
* `platform/create-ohif/templates/extension/src/index.tsx`
* `platform/create-ohif/templates/extension/src/panels/ExamplePanel.tsx`
* `platform/create-ohif/templates/extension/src/styles.css`
* `platform/create-ohif/templates/extension/src/viewports/ExampleViewport.tsx`
* `platform/create-ohif/templates/extension/tailwind.config.js`
* `platform/create-ohif/templates/extension/tsconfig.json`
* `platform/create-ohif/templates/mode/AGENTS.md`
* `platform/create-ohif/templates/mode/README.md`
* `platform/create-ohif/templates/mode/_gitignore`
* `platform/create-ohif/templates/mode/_npmrc`
* `platform/create-ohif/templates/mode/_prettierrc`
* `platform/create-ohif/templates/mode/_rspack/pluginExternals.js`
* `platform/create-ohif/templates/mode/_rspack/rspack.prod.js`
* `platform/create-ohif/templates/mode/package.json`
* `platform/create-ohif/templates/mode/pnpm-workspace.yaml`
* `platform/create-ohif/templates/mode/src/__tests__/mode.test.ts`
* `platform/create-ohif/templates/mode/src/id.ts`
* `platform/create-ohif/templates/mode/src/index.ts`
* `platform/create-ohif/templates/mode/src/toolbarButtons.ts`
* `platform/create-ohif/templates/mode/tsconfig.json`
* `platform/create-ohif/templates/workspace/Dockerfile`
* `platform/create-ohif/templates/workspace/_gitignore`
* `platform/create-ohif/templates/workspace/config/app-config.js`
* `platform/create-ohif/templates/workspace/extensions/.gitkeep`
* `platform/create-ohif/templates/workspace/modes/.gitkeep`
* `platform/create-ohif/templates/workspace/ohif.config.json`
* `platform/create-ohif/templates/workspace/package.json`
* `platform/create-ohif/templates/workspace/scripts/ohif.mjs`
* `platform/create-ohif/test/fixtures/cli-era/extension/.gitignore`
* `platform/create-ohif/test/fixtures/cli-era/extension/.prettierrc`
* `platform/create-ohif/test/fixtures/cli-era/extension/babel.config.js`
* `platform/create-ohif/test/fixtures/cli-era/extension/dependencies.json`
* `platform/create-ohif/test/fixtures/cli-era/extension/src/id.js`
* `platform/create-ohif/test/fixtures/cli-era/extension/src/index.tsx`
* `platform/create-ohif/test/fixtures/cli-era/mode/.gitignore`
* `platform/create-ohif/test/fixtures/cli-era/mode/.prettierrc`
* `platform/create-ohif/test/fixtures/cli-era/mode/babel.config.js`
* `platform/create-ohif/test/fixtures/cli-era/mode/dependencies.json`
* `platform/create-ohif/test/fixtures/cli-era/mode/src/id.js`
* `platform/create-ohif/test/fixtures/cli-era/mode/src/index.tsx`
* `platform/create-ohif/tests/externals-parity.test.mjs`
* `platform/create-ohif/tests/manifest.test.mjs`
* `platform/create-ohif/tests/migrate.test.mjs`
* `platform/create-ohif/tests/scaffold.test.mjs`
* `platform/docs/docs/configuration/configurationFiles.md`
* `platform/docs/docs/configuration/dataSources/dicom-web.md`
* `platform/docs/docs/deployment/runtime-plugins.md`
* `platform/docs/docs/development/create-ohif.md`
* `platform/docs/docs/development/ohif-cli.md`
* `platform/docs/docs/development/out-of-tree.md`
* `platform/docs/docs/development/publishing.md`
* `platform/docs/docs/development/video-tutorials.md`
* `platform/docs/docs/migration-guide/3p12-to-3p13/build-tooling.md`
* `platform/docs/docs/migration-guide/3p12-to-3p13/cli-removal.md`
* `platform/docs/docs/migration-guide/3p12-to-3p13/customization-url.md`
* `platform/docs/docs/migration-guide/3p12-to-3p13/node-version.md`
* `platform/docs/docs/migration-guide/3p12-to-3p13/package-manager.md`
* `platform/docs/docs/migration-guide/3p8-to-3p9/0-general.md`
* `platform/docs/docs/migration-guide/cli-era-extensions.md`
* `platform/docs/docs/migration-guide/deforking.md`
* `platform/docs/docs/migration-guide/from-v2.md`
* `platform/docs/docs/platform/extensions/building-with-agents.md`
* `platform/docs/docs/platform/extensions/gallery.mdx`
* `platform/docs/docs/platform/extensions/index.md`
* `platform/docs/docs/platform/extensions/installation.md`
* `platform/docs/docs/platform/extensions/pluginConfig.md`
* `platform/docs/docs/platform/extensions/runtime-extensions.md`
* `platform/docs/docs/platform/modes/index.md`
* `platform/docs/docs/platform/modes/installation.md`
* `platform/docs/docusaurus.config.js`
* `platform/docs/package.json`
* `platform/docs/scripts/check-agents-md-sync.mjs`
* `platform/docs/scripts/validate-gallery.mjs`
* `platform/docs/src/data/extension-gallery.json`
* `platform/docs/static/schemas/extension-gallery.schema.json`
* `platform/docs/versioned_docs/version-3.11/configuration/dataSources/dicom-web.md`
* `platform/docs/versioned_docs/version-3.11/development/ohif-cli.md`
* `platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/0-general.md`
* `platform/docs/versioned_docs/version-3.11/migration-guide/from-v2.md`
* `platform/i18n/.rspack/rspack.dev.js`
* `platform/i18n/.rspack/rspack.prod.js`
* `platform/i18n/package.json`
* `platform/ui-next/.rspack/rspack.dev.js`
* `platform/ui-next/.rspack/rspack.prod.js`
* `platform/ui-next/.rspack/template.html`
* `platform/ui-next/package.json`
* `platform/ui/.rspack/rspack.dev.js`
* `platform/ui/.rspack/rspack.prod.js`
* `platform/ui/package.json`
* `playwright.config.ts`
* `publish-package.mjs`
* `rsbuild.config.ts`
* `scripts/ohif-plugin.mjs`
* `scripts/verify-build-parity.mjs`
* `scripts/verify-directory-plugin.ps1`
* `scripts/verify-tarballs.mjs`
* `scripts/verify-umd-global.mjs`

</details>

<details>
<summary>💤 Files with no reviewable changes (44)</summary>

* platform/cli/src/commands/enums/colors.js
* platform/cli/src/questions.js
* platform/cli/src/commands/utils/editPackageJson.js
* platform/cli/src/commands/enums/keywords.js
* platform/cli/src/commands/constants/notFound.js
* platform/cli/src/commands/removeExtension.js
* platform/cli/src/commands/utils/createDirectoryContents.js
* platform/cli/src/commands/utils/installNPMPackage.js
* platform/cli/src/commands/utils/getYarnInfo.js
* platform/cli/src/commands/enums/index.js
* platform/cli/src/commands/addMode.js
* platform/docs/docs/development/ohif-cli.md
* platform/cli/src/commands/index.js
* platform/cli/src/commands/createPackage.js
* platform/cli/src/commands/utils/createLicense.js
* platform/cli/src/commands/utils/private/manipulatePluginConfigFile.js
* platform/cli/src/commands/utils/getVersionedPackageName.js
* platform/cli/src/commands/utils/index.js
* platform/cli/src/commands/addExtensions.js
* platform/cli/src/commands/enums/endPoints.js
* platform/cli/src/commands/utils/initGit.js
* platform/cli/src/commands/utils/uninstallNPMPackage.js
* platform/cli/src/commands/listPlugins.js
* platform/cli/src/commands/utils/createReadme.js
* platform/cli/src/commands/utils/private/index.js
* platform/cli/src/commands/utils/findOhifExtensionsToRemoveAfterRemovingMode.js
* platform/cli/src/commands/utils/throwIfExtensionUsedByInstalledMode.js
* platform/cli/package.json
* platform/cli/src/commands/utils/validate.js
* platform/cli/src/commands/removeExtensions.js
* platform/cli/src/commands/utils/private/getPackageNameAndScope.js
* platform/cli/src/commands/removeMode.js
* platform/cli/src/commands/utils/removeFromConfig.js
* platform/cli/src/commands/linkPackage.js
* platform/cli/src/commands/addExtension.js
* platform/cli/src/commands/utils/prettyPrint.js
* platform/cli/src/commands/utils/private/writePluginConfigFile.js
* platform/cli/src/index.js
* platform/cli/src/commands/utils/private/readPluginConfigFile.js
* platform/cli/src/commands/searchPlugins.js
* platform/cli/src/commands/utils/addToConfig.js
* platform/cli/src/commands/utils/validatePnpm.js
* platform/cli/src/commands/utils/findRequiredOhifExtensionsForMode.js
* platform/cli/src/commands/unlinkPackage.js

</details>

</details>

<details>
<summary>🛑 Comments failed to post (11)</summary><blockquote>

<details>
<summary>.rspack/resolveConfig.js (1)</summary><blockquote>

69-75: _🎯 Functional Correctness_ | _🟡 Minor_ | _⚡ Quick win_

<details>
<summary>🧩 Analysis chain</summary>

🌐 Web query:

`rspack resolve.modules relative absolute path resolution behavior`

💡 Result:

In Rspack, the resolve.modules option specifies a list of directories that are searched when resolving "module" requests—requests that do not start with './', '../', or '/' [1][2]. The behavior of path resolution in Rspack distinguishes between three types of paths [1][2]: 1. Absolute Paths: These paths are already fully qualified. Rspack returns the path directly without further parsing [1][2]. 2. Relative Paths: These start with './' or '../'. Rspack resolves these relative to the directory of the file containing the import or require statement (the context directory) [1][2]. 3. Module Paths: These do not start with './', '../', or '/'. Rspack resolves these by searching the directories listed in resolve.modules, which defaults to ["node_modules"] [3][1][2]. Regarding the resolve.modules configuration specifically: - When you provide a path in resolve.modules, it can be either an absolute path or a folder name [4]. - If a folder name is provided (e.g., "node_modules"), Rspack searches for this folder in the current directory and its ancestors until it reaches the root, following standard Node.js module resolution rules [1][2]. - If an absolute path is provided, Rspack treats it as a specific directory to search for modules [4]. Additional configuration options that influence this behavior include: - resolve.preferAbsolute: If set to true, Rspack opts for absolute paths during resolution, relative to resolve.roots [3][5]. - resolve.roots: A list of directories used to resolve server-relative URLs (those beginning with '/') [3][5]. - resolve.preferRelative: When enabled, this directs the resolver to prefer resolving requests as relative requests rather than looking into the directories defined in resolve.modules [4]. Rspack utilizes the rspack-resolver library, which is a high-performance, Rust-based implementation compatible with webpack's enhanced-resolve algorithm [6][7].

Citations:

- 1: https://rspack.rs/guide/features/module-resolution
- 2: https://rspack.rs/guide/features/module-resolution.md
- 3: https://rspack.rs/config/resolve
- 4: https://docs.rs/rspack_resolver/latest/rspack_resolver/struct.ResolveOptions.html
- 5: https://v2.rspack.rs/config/resolve
- 6: https://www.rspack.rs/api/javascript-api/resolver
- 7: https://docs.rs/rspack_resolver/latest/rspack_resolver/index.html

---

🏁 Script executed:

```shell
#!/bin/bash
set -euo pipefail

printf '\n## file listing\n'
git ls-files .rspack/resolveConfig.js .rspack 2>/dev/null || true

printf '\n## line count\n'
wc -l .rspack/resolveConfig.js

printf '\n## relevant excerpt\n'
sed -n '1,180p' .rspack/resolveConfig.js

printf '\n## sibling tree\n'
find .rspack -maxdepth 2 -type f | sort

Repository: OHIF/Viewers

Length of output: 5132


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
p = Path('.rspack/resolveConfig.js')
text = p.read_text()
for i, line in enumerate(text.splitlines(), 1):
    if 'moduleSearchPaths' in line or 'node_modules' in line or 'All paths are anchored' in line:
        print(f"{i}: {line}")
PY

Repository: OHIF/Viewers

Length of output: 1570


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show only the surrounding resolve config references.
rg -n "moduleSearchPaths|resolve\.modules|node_modules|platform/app/node_modules|platform/ui/node_modules|All paths are anchored" .rspack -S

Repository: OHIF/Viewers

Length of output: 2640


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n "\.\./\.\./\.\./node_modules|node_modules.*\.\./\.\./\.\./" . -S

Repository: OHIF/Viewers

Length of output: 901


Remove the repo-escaping ../../../node_modules fallback
.rspack/resolveConfig.js:72 points outside the repository and can shadow the intended singleton copy if a parent node_modules exists. Keep this resolver anchored to the repo-root paths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.rspack/resolveConfig.js around lines 69 - 75, Remove the
path.resolve(__dirname, '../../../node_modules') entry from the
moduleSearchPaths array in resolveConfig.js. Preserve the existing
repository-root and platform-specific node_modules paths.
.rspack/rules/cssToJavaScript.js (1)

19-22: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check existing postcss configurations for the correct format
rg -C 5 "plugins:" --type js

Repository: OHIF/Viewers

Length of output: 150


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== .rspack/rules/cssToJavaScript.js =="
cat -n .rspack/rules/cssToJavaScript.js | sed -n '1,220p'

echo
echo "== Search for postcssOptions / plugins usage =="
rg -n -C 4 "postcssOptions|tailwindcss\\(|autoprefixer\\(" .rspack . --type js || true

Repository: OHIF/Viewers

Length of output: 3002


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the PostCSS loader documentation-like usage in the repo, if any.
rg -n -C 3 "postcss-loader|postcssOptions|plugins:" . --type js --type ts --type cjs --type mjs || true

Repository: OHIF/Viewers

Length of output: 178


🌐 Web query:

postcss-loader plugins array nested arrays plugin instances documentation

💡 Result:

In postcss-loader, the plugins array is designed to hold PostCSS plugin instances, strings (plugin names), or configuration tuples [1][2]. A configuration tuple is represented as an array where the first element is the plugin name or instance, and the second element is the options object [1][3]. Specifically, the syntax is: ["plugin-name-or-instance", { pluginOptions }] While the postcss-loader documentation primarily demonstrates this tuple structure for configuring plugins, it does not explicitly provide for or document the use of deeply "nested arrays" (e.g., arrays within arrays within the plugins list) for standard plugin definition [1][4]. If you are attempting to pass configuration, you should follow the official tuple format [1][5]: // Example of proper tuple syntax in webpack.config.js plugins: [ ["postcss-preset-env", { stage: 0 }], "postcss-import" ] If you have a complex scenario requiring a different structure, it is recommended to use the function form of postcssOptions, which allows you to programmatically define your plugins array based on the loader context [1][6]: // Example using a function for complex plugin logic options: { postcssOptions: (loaderContext) => { return { plugins: [ ["postcss-short", { prefix: "x" }], "postcss-preset-env" ] }; } } If you encounter unexpected behavior with nested structures, it is likely because the loader expects either a direct plugin instance, a string identifier, or the specific [plugin, options] tuple format [1][2].

Citations:


Flatten the PostCSS plugins array
postcss-loader expects plugin instances or [plugin, options] tuples here. Remove the extra brackets so both Tailwind and Autoprefixer are applied.

plugins: [tailwindcss(tailwindConfigPath), autoprefixer('last 2 version', 'ie >= 11')]

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.rspack/rules/cssToJavaScript.js around lines 19 - 22, Update the plugins
configuration in the PostCSS setup to pass tailwindcss and autoprefixer directly
as plugin instances, removing the extra nested arrays so postcss-loader can
apply both plugins.
platform/app/src/routes/Mode/Mode.tsx (1)

104-108: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Registration failures for built-in extensions are now silently swallowed.

recordRegistrationError only mutates an existing audit record and is a no-op (no logging) when none exists. Extensions loaded through loadModules (the in-tree/built-in ones) are never audited, so if registerExtension throws for one of them, the error is caught here, recordRegistrationError finds no matching record, surfaceRuntimeExtensionFailures has nothing to show, and loading proceeds with setExtensionDependenciesLoaded(true). Previously such a throw propagated loudly. This hides real registration bugs in first-party extensions.

Consider logging unconditionally in the catch (or making recordRegistrationError console.error when no record is found).

🐛 Proposed fix (Mode.tsx)
             try {
               await extensionManager.registerExtension(extension);
             } catch (error) {
+              console.error(`Failed to register extension "${extensionId}"`, error);
               recordRegistrationError(extensionId, error);
             }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

            try {
              await extensionManager.registerExtension(extension);
            } catch (error) {
              console.error(`Failed to register extension "${extensionId}"`, error);
              recordRegistrationError(extensionId, error);
            }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@platform/app/src/routes/Mode/Mode.tsx` around lines 104 - 108, Update the
registerExtension catch block in the loadModules flow to report failures for
built-in extensions even when recordRegistrationError finds no audit record. Log
the caught error unconditionally or update recordRegistrationError to emit a
console.error when no matching record exists, while preserving existing auditing
and runtime failure surfacing.
platform/core/package.json (1)

24-24: 🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== package.json =="
sed -n '1,120p' platform/core/package.json

echo
echo "== search for sideEffects usage =="
rg -n '"sideEffects"|sideEffects' -S .

echo
echo "== top-level package manager clues =="
git ls-files | rg '(^|/)(package.json|webpack|vite|rollup|tsup|esbuild|next\.config|nx\.json|turbo\.json|babel\.config|vite\.config|rollup\.config|tsup\.config)'

Repository: OHIF/Viewers

Length of output: 4688


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import json, pathlib
p = pathlib.Path('platform/core/package.json')
data = json.loads(p.read_text())
print("sideEffects type:", type(data.get("sideEffects")).__name__, "value:", data.get("sideEffects"))
PY

Repository: OHIF/Viewers

Length of output: 185


🌐 Web query:

webpack package.json sideEffects string value boolean array glob pattern documentation

💡 Result:

The sideEffects property in package.json is used to inform webpack whether a package or its modules contain side effects, allowing webpack to perform tree shaking (pruning unused exports) more effectively [1][2]. The property accepts the following values: 1. Boolean (false): Setting "sideEffects": false tells webpack that none of the files in the package contain side effects [1][2]. This is the safest way to enable tree shaking for a package [2]. 2. Array of glob patterns: If a package contains some files with side effects (such as CSS imports or polyfills), you can provide an array of glob patterns to specify which files have side effects [1][2]. Files not matched by these patterns are assumed to be side-effect-free [1][2]. Glob patterns used in the array support syntax such as ,, {a,b}, and [a-z] [1]. Patterns that do not include a forward slash (/)—for example, ".css"—are treated as /*.css [1]. Important Considerations: - If a module is incorrectly marked as side-effect-free, it can lead to broken application code [2]. Only set "sideEffects": false if you are certain the code is pure [2]. - Side effects include actions like modifying global state, modifying prototypes, or importing CSS files [2]. - The optimization.sideEffects option in your webpack.config.js controls how this property is respected; it can be set to true (default in production), false (disabled), or 'flag' (only use the manually placed sideEffects flag in package.json, without analyzing source code) [3][4]. Note: The value should be a boolean or an array of strings [1][5]. A string value (e.g., "sideEffects": "false") is generally not used for the same purpose as the boolean false and is not recommended as a configuration pattern [6].

Citations:


Use false for sideEffects
sideEffects expects a boolean or an array of glob patterns; the string "false" won’t act as the boolean flag.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@platform/core/package.json` at line 24, Update the package.json sideEffects
setting from the string value "false" to the boolean false, preserving the
existing configuration intent.
platform/create-ohif/README.md (1)

17-25: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

README arguments are out of date with the CLI.

The bin supports --template <workspace|extension|mode|deployment> and a migrate <path> [--dry-run] subcommand, but this arguments block only lists <extension|mode> and omits migrate. Align the README with bin/create-ohif.mjs's USAGE.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@platform/create-ohif/README.md` around lines 17 - 25, The README usage block
for create-ohif must match the bin/create-ohif.mjs USAGE definition: expand
--template to include workspace and deployment, and document the migrate <path>
[--dry-run] subcommand. Update only the CLI arguments documentation while
preserving the existing option descriptions.
platform/create-ohif/templates/mode/src/toolbarButtons.ts (1)

4-10: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Remove the dangling 'mpr' toolGroupId.

toolGroupIds: ['default', 'mpr'] references an 'mpr' tool group that this scaffold never creates — src/index.ts's onModeEnter only creates 'default'. This is likely leftover from an MPR-capable template and will confuse authors who copy this scaffold as-is.

🔧 Proposed fix
 export const setToolActiveToolbar = {
   commandName: 'setToolActive',
   commandOptions: {
-    toolGroupIds: ['default', 'mpr'],
+    toolGroupIds: ['default'],
   },
   context: 'CORNERSTONE',
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

export const setToolActiveToolbar = {
  commandName: 'setToolActive',
  commandOptions: {
    toolGroupIds: ['default'],
  },
  context: 'CORNERSTONE',
};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@platform/create-ohif/templates/mode/src/toolbarButtons.ts` around lines 4 -
10, Update setToolActiveToolbar.commandOptions.toolGroupIds to remove the
dangling 'mpr' entry, leaving only the 'default' tool group created by
onModeEnter.
platform/create-ohif/templates/workspace/scripts/ohif.mjs (1)

144-144: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Support unscoped mode package names.

The current regex /\/mode-/ correctly matches scoped packages like @ohif/mode-basic, but fails to identify unscoped mode packages (e.g., mode-basic), incorrectly classifying them as extensions.

🐛 Proposed fix
-      const section = /\/mode-/.test(plugin.packageName) ? 'modes' : 'extensions';
+      const section = /(^|\/)mode-/.test(plugin.packageName) ? 'modes' : 'extensions';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

      const section = /(^|\/)mode-/.test(plugin.packageName) ? 'modes' : 'extensions';
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@platform/create-ohif/templates/workspace/scripts/ohif.mjs` at line 144,
Update the package classification logic in the section assignment to recognize
both scoped and unscoped mode package names, including names such as mode-basic
and `@ohif/mode-basic`, while continuing to classify other packages as extensions.
platform/docs/versioned_docs/version-3.11/configuration/dataSources/dicom-web.md (1)

107-114: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Revert erroneous find-and-replace in historical versioned documentation.

A global find-and-replace appears to have accidentally modified the versioned_docs/ for version 3.11 and older migration guides. In version 3.11, the project used Webpack (and webpack-dev-server with .webpack/webpack.pwa.js), not Rspack. Updating these historical documents to reference Rspack breaks the instructions for users consulting historical versions (as seen where webpack-dev-server is instructed to consume an .rspack/ config).

  • platform/docs/versioned_docs/version-3.11/configuration/dataSources/dicom-web.md#L107-L114: Revert .rspack/rspack.pwa.js back to .webpack/webpack.pwa.js in both the code block and the explanation below it.
  • platform/docs/versioned_docs/version-3.11/development/ohif-cli.md#L309-L309: Revert rspack.pwa.js back to webpack.pwa.js.
  • platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/0-general.md#L108-L108: Revert .rspack/rspack.pwa.js back to .webpack/webpack.pwa.js.
📍 Affects 3 files
  • platform/docs/versioned_docs/version-3.11/configuration/dataSources/dicom-web.md#L107-L114 (this comment)
  • platform/docs/versioned_docs/version-3.11/development/ohif-cli.md#L309-L309
  • platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/0-general.md#L108-L108
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@platform/docs/versioned_docs/version-3.11/configuration/dataSources/dicom-web.md`
around lines 107 - 114, Restore the historical Webpack references in all three
affected version-3.11 documents: in
platform/docs/versioned_docs/version-3.11/configuration/dataSources/dicom-web.md
lines 107-114, replace .rspack/rspack.pwa.js with .webpack/webpack.pwa.js in
both the command and explanation; in
platform/docs/versioned_docs/version-3.11/development/ohif-cli.md line 309,
replace rspack.pwa.js with webpack.pwa.js; and in
platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/0-general.md
line 108, replace .rspack/rspack.pwa.js with .webpack/webpack.pwa.js.
platform/ui-next/package.json (1)

25-30: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Avoid exporting raw .ts/.tsx files to npm consumers.

The publishConfig.exports map points deep imports like ./components/* directly to the src/*.tsx source files. Since third-party plugin authors install this SDK package from npm, they will receive raw TypeScript/JSX files.

  • If their bundler attempts to process them, it will likely fail with parse errors because bundlers typically exclude node_modules from TS/JSX compilation (this is also true for the create-ohif template).
  • If it is externalized by ^@ohif/`` during bundling, it becomes a runtime dependency on @ohif/ui-next/components/.... However, the OHIF host runtime loader only provisions the root `@ohif/ui-next` UMD global, causing the deep import to fail at runtime.

If the SDK contract only supports importing the root module at runtime, consider removing these deep exports to prevent consumers from relying on auto-imports that will break. If deep imports are strictly needed, the individual components must be transpiled to JavaScript before publishing.

📦 Proposed fix to limit exports to built artifacts
     "exports": {
       ".": "./dist/ohif-ui-next.umd.js",
-      "./tailwind.config": "./tailwind.config.js",
-      "./lib/*": "./src/lib/*.ts",
-      "./components/*": "./src/components/*.tsx"
+      "./tailwind.config": "./tailwind.config.js"
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    "exports": {
      ".": "./dist/ohif-ui-next.umd.js",
      "./tailwind.config": "./tailwind.config.js"
    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@platform/ui-next/package.json` around lines 25 - 30, Update the
publishConfig.exports map in package.json to avoid exposing raw TypeScript and
TSX source paths through ./lib/* and ./components/*. If the SDK supports only
root-module runtime imports, remove these deep exports; otherwise, redirect them
to transpiled JavaScript artifacts that are included in the published package.
scripts/verify-build-parity.mjs (1)

65-74: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Guard spawnSync result before calling .trim() on stdout.

treeContains and hasWasm both call .trim() on r.stdout without checking r.error/r.status first. If grep or sh aren't resolvable on the host (e.g. a non-Linux dev machine, since this script has no OS guard), spawnSync sets r.error and leaves r.stdout as null, producing an opaque TypeError instead of a clear "grep/sh not found" failure.

🛡️ Proposed fix
 function treeContains(dir, needle, includeGlob) {
   const args = ['-r', '-l', '-F', needle];
   if (includeGlob) {
     args.push(`--include=${includeGlob}`);
   }
   args.push(dir);
   const r = spawnSync('grep', args, { encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 });
+  if (r.error) {
+    throw new Error(`treeContains: failed to run grep - ${r.error.message}`);
+  }
   // grep exit 0 = match, 1 = no match, >1 = error.
-  return r.status === 0 && r.stdout.trim().length > 0;
+  return r.status === 0 && (r.stdout || '').trim().length > 0;
 }

Also applies to: 85-90

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/verify-build-parity.mjs` around lines 65 - 74, Guard the spawnSync
results in both treeContains and hasWasm before accessing stdout.trim(). Handle
r.error or a non-success status explicitly, producing a clear
command-unavailable/failure result instead of allowing null stdout to cause a
TypeError, while preserving the existing match behavior for successful grep/sh
execution.
scripts/verify-directory-plugin.ps1 (1)

1-1: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Save this script with a UTF-8 BOM.

Static analysis flags this file as containing non-ASCII characters without a BOM. Windows PowerShell 5.1 (which this script explicitly targets, and whose Set-Content encoding quirks it already works around at lines 64-66) can misinterpret non-BOM files under the system codepage, garbling any non-ASCII content in comments/strings.

🧰 Tools
🪛 PSScriptAnalyzer (1.25.0)

[warning] Missing BOM encoding for non-ASCII encoded file 'verify-directory-plugin.ps1'

(PSUseBOMForUnicodeEncodedFile)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/verify-directory-plugin.ps1` at line 1, Save
scripts/verify-directory-plugin.ps1 with a UTF-8 BOM, preserving its existing
content and Windows PowerShell 5.1 compatibility.

Source: Linters/SAST tools

@wayfarer3130
wayfarer3130 changed the base branch from master to ohifReact July 28, 2026 14:43
@wayfarer3130
wayfarer3130 changed the base branch from ohifReact to master July 28, 2026 14:44
@wayfarer3130
wayfarer3130 changed the base branch from master to ohifReact July 28, 2026 14:45
'@ohif/i18n': ohifI18n,
'@ohif/extension-default': ohifExtensionDefault,
'@ohif/extension-cornerstone': ohifExtensionCornerstone,
'@cornerstonejs/core': cornerstoneCore,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What about modes/basic to allow the basic mode to be extended.

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.

3 participants