You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
feat(app-utils): support ES module remote plugins with lazy loading - #2764
Allow remotely-loaded plugins to be modern ES modules that can code-split
and lazy-load parts of their components, while resolving the same host
singletons (react, @deephaven/*, ...) as the existing CommonJS plugins.
Add esmPluginLoader with ESM/CJS auto-detection (es-module-lexer), a
runtime import map exposing host singletons via blob re-export modules,
and es-module-shims polyfill loading via importShim.
Wire detection/loading into PluginUtils; CommonJS plugins still supported.
Add example ESM widget plugin (packages/plugin-example) with a
lazy-loaded SCSS component and a dependency-free dev server.
Add unit tests, README guidance, and a CJS->ESM migration guide.
❌ Patch coverage is 96.96970% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 53.06%. Comparing base (6be18b5) to head (9c5f33d).
Replacing createLoadRemoteModule also removes its URL-level promise cache. Repeated calls for the same CommonJS URL now fetch and evaluate the bundle again, so module-scope plugin side effects can run multiple times; ESM entries remain cached by the module loader. Preserve the previous per-URL caching behavior, with an explicit policy for whether rejected loads are retryable.
The tests mock es-module-shims, so they never exercise the integration this line introduces: loading the real shim, consuming the source hook, resolving host blob modules, rewriting a dynamic import, and fetching its lazy CSS chunk. Add an automated browser test using the example plugin (including shared React and deferred JS/CSS assertions); otherwise the core cross-browser behavior can regress while all unit tests pass.
Use cross-platform concurrency for the dev command
packages/plugin-example/package.json:18
Using shell & makes the documented dev command platform-dependent: under Windows cmd, the long-running watch command prevents node serve.js from starting. This repository already uses run-p for concurrent scripts (for example, root package.json:42); use that mechanism here so the example remains runnable cross-platform.
Update the Relevant files entry for the deleted file
plans/esm-remote-plugins.md:122
This “Relevant files” entry points to a file deleted by this PR and says it is unchanged. Point readers to the new CommonJS evaluator instead.
This first-wins registry becomes stale when pluginsUrl changes. PluginsBootstrap explicitly reloads on that prop (PluginsBootstrap.tsx:39-64), so entries are fetched from the new base URL, but cross-plugin bare imports still resolve to the previous base and can mix plugin versions or origins. Support replacing plugin mappings (with shim map overrides configured before initialization) or scope mappings by plugin base URL.
Use a portable concurrent process runner instead of shell &
packages/plugin-example/package.json:17
Using shell & makes this workspace script non-portable, so the documented example command will not launch both processes on Windows. Repository concurrent scripts use run-p instead (for example, root package.json:42); use the same mechanism here.
Include the example package in TypeScript project references
packages/plugin-example/tsconfig.json:2
This package is omitted from the repository's TypeScript project-reference tree, so npm run types never checks any of the new example source; Vite only transpiles it. It is also the only package config not extending ../../tsconfig.json, contrary to the invariant documented at root tsconfig.json:20-24. Extend the root config, add references for its workspace dependencies, and add this top-level package to the root references.
Allow remotely-loaded plugins to be modern ES modules that can code-split
and lazy-load parts of their components, while resolving the same host
singletons (react, @deephaven/*, ...) as the existing CommonJS plugins.
- Add esmPluginLoader with ESM/CJS auto-detection (es-module-lexer), a
runtime import map exposing host singletons via blob re-export modules,
and es-module-shims polyfill loading via importShim.
- Wire detection/loading into PluginUtils; CommonJS plugins still supported.
- Add example ESM widget plugin (packages/plugin-example) with a
lazy-loaded SCSS component and a dependency-free dev server.
- Add unit tests, README guidance, and a CJS->ESM migration guide.
Detect the module format from the fetched source and hand that same source
to es-module-shims (shim mode, source hook) or a CommonJS evaluator, so the
plugin entry is no longer fetched twice. Import maps are registered via
importShim.addImportMap instead of injected script tags.
Address PR review feedback:
- buildPluginImportMap read a top-level `package` field, but the manifest
contract stores it at `loader.package`, so real manifests produced an empty
plugin import map and cross-plugin package imports failed to resolve.
- The example plugin manifest emitted `package` at the top level instead of
`loader.package`.
- The example dev server's traversal guard compared a string prefix, which an
encoded separator could bypass to reach sibling directories; it now validates
the dist-relative path.
- `start:plugin-example` matched the `start:*` glob that `npm start` runs, so
the example server was launched twice and failed with EADDRINUSE. Renamed to
`plugin-example`.
- Docs described es-module-shims polyfill mode with native passthrough, but the
loader ships `shimMode: true` and loads every plugin through importShim().
pluginImports is process-global and permanently keeps the first URL for each package. PluginsBootstrap explicitly reloads when pluginsUrl changes (packages/app-utils/src/components/PluginsBootstrap.tsx:63), but a second manifest using the same package name will still resolve ESM cross-plugin imports against the old server. Allow plugin-package mappings to be replaced (es-module-shims supports this with mapOverrides) or scope mappings per plugin base URL while continuing to protect host singleton entries.
Add the package to the root TypeScript project references
packages/plugin-example/tsconfig.json:2
This new top-level TypeScript package is not reachable from the root project-reference tree, so npm run types/watch:types never checks any of its source. That conflicts with the explicit root convention in tsconfig.json:23-27; add the package to the root references and give this config the appropriate workspace dependency references so the runnable example cannot silently accumulate type errors.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Allow remotely-loaded plugins to be modern ES modules that can code-split
and lazy-load parts of their components, while resolving the same host
singletons (react, @deephaven/*, ...) as the existing CommonJS plugins.
runtime import map exposing host singletons via blob re-export modules,
and es-module-shims polyfill loading via importShim.
lazy-loaded SCSS component and a dependency-free dev server.