Summary
molstar-components is currently published only to JSR. JSR packages work great in Deno, but as demonstrated by the mol-view-stories integration, consuming them from Node.js / pnpm / Next.js projects requires a series of non-obvious workarounds that raise the barrier to adoption for any future Node-based consumer.
Current pain points
The primary consumer, mol-view-stories, currently requires a series of non-obvious workarounds to integrate the library:
-
Import-rewriting webpack plugin — Deno uses bare specifiers prefixed with npm: (e.g., npm:react@19.2.3). Webpack doesn't understand these, so a NormalModuleReplacementPlugin must be added to strip the protocol prefix:
// next.config.ts — boilerplate no consumer should need
new webpack.NormalModuleReplacementPlugin(/^npm:/, (resource) => {
resource.request = resource.request.slice(4).replace(…);
});
-
transpilePackages — because JSR ships TypeScript source, Next.js can't import it directly. Consumers must add transpilePackages: ["@jsr/molstar__molstar-components"] to next.config.ts.
-
Tailwind CSS sourcing — Tailwind 4 needs to scan the library's source files for used class names, which requires pointing content paths into the node_modules/@jsr/… directory — fragile, version-path-dependent, and breaks on pnpm's virtual store layout.
-
TypeScript path mappings — jsconfig.json / tsconfig.json often needs custom paths entries to resolve JSR package names to their on-disk locations.
None of these are hard to solve individually, but together they make "just add the library" a multi-step integration task and a recurring source of friction for any new Node-based consumer.
Goal
Publish an npm-compatible package to npmjs.com (as @molstar/molstar-components) in parallel with the existing JSR release. The npm package would ship pre-compiled .js + .d.ts files with standard bare specifiers — no npm: prefixes, no TS source — so it just works with any Node/bundler toolchain out of the box. JSR publishing stays unchanged for Deno consumers.
The tool: dnt
dnt (Deno-to-npm Transform), maintained by the Deno team, automates most of this. It:
- Compiles TypeScript to ESM + CJS
- Rewrites Deno-style specifiers (
npm:, jsr:) to plain npm specifiers
- Generates a
package.json with proper exports map and type declarations
- Runs the existing test suite against the npm output to verify correctness
A build-npm.ts script using dnt can be added alongside the existing build.ts and wired to a deno task build:npm command, then called from CI before npm publish.
Real-world precedent
dnt itself is the most direct example of this pattern — the tool is a Deno package that uses dnt to publish itself to npm. It is available as both jsr:@deno/dnt and npm:@deno/dnt, and Node consumers install it from npm with no Deno-specific glue required. If the tool that solves the problem uses itself to solve its own problem, that's a reasonable signal it works.
What changes for consumers
- Install:
pnpm add @molstar/molstar-components instead of the mangled pnpm add jsr:@molstar/molstar-components
- No webpack plugin needed to rewrite
npm: specifiers
- No
transpilePackages entry in next.config.ts
- Tailwind content path becomes a standard
node_modules path rather than a fragile version-dependent JSR store path
- TypeScript works out of the box without custom
paths entries
- Deno consumers are unaffected — JSR publish stays unchanged
Scope
- Add
build-npm.ts using dnt
- Add
deno task build:npm and wire it into the release CI step
- Publish to
npmjs.com under @molstar/molstar-components
- Keep existing JSR publish unchanged
- Update
README.md with the npm install path and remove the workaround instructions
Summary
molstar-componentsis currently published only to JSR. JSR packages work great in Deno, but as demonstrated by themol-view-storiesintegration, consuming them from Node.js / pnpm / Next.js projects requires a series of non-obvious workarounds that raise the barrier to adoption for any future Node-based consumer.Current pain points
The primary consumer,
mol-view-stories, currently requires a series of non-obvious workarounds to integrate the library:Import-rewriting webpack plugin — Deno uses bare specifiers prefixed with
npm:(e.g.,npm:react@19.2.3). Webpack doesn't understand these, so aNormalModuleReplacementPluginmust be added to strip the protocol prefix:transpilePackages— because JSR ships TypeScript source, Next.js can't import it directly. Consumers must addtranspilePackages: ["@jsr/molstar__molstar-components"]tonext.config.ts.Tailwind CSS sourcing — Tailwind 4 needs to scan the library's source files for used class names, which requires pointing
contentpaths into thenode_modules/@jsr/…directory — fragile, version-path-dependent, and breaks on pnpm's virtual store layout.TypeScript path mappings —
jsconfig.json/tsconfig.jsonoften needs custompathsentries to resolve JSR package names to their on-disk locations.None of these are hard to solve individually, but together they make "just add the library" a multi-step integration task and a recurring source of friction for any new Node-based consumer.
Goal
Publish an npm-compatible package to
npmjs.com(as@molstar/molstar-components) in parallel with the existing JSR release. The npm package would ship pre-compiled.js+.d.tsfiles with standard bare specifiers — nonpm:prefixes, no TS source — so it just works with any Node/bundler toolchain out of the box. JSR publishing stays unchanged for Deno consumers.The tool:
dntdnt(Deno-to-npm Transform), maintained by the Deno team, automates most of this. It:npm:,jsr:) to plain npm specifierspackage.jsonwith properexportsmap and type declarationsA
build-npm.tsscript usingdntcan be added alongside the existingbuild.tsand wired to adeno task build:npmcommand, then called from CI beforenpm publish.Real-world precedent
dntitself is the most direct example of this pattern — the tool is a Deno package that usesdntto publish itself to npm. It is available as bothjsr:@deno/dntandnpm:@deno/dnt, and Node consumers install it from npm with no Deno-specific glue required. If the tool that solves the problem uses itself to solve its own problem, that's a reasonable signal it works.What changes for consumers
pnpm add @molstar/molstar-componentsinstead of the mangledpnpm add jsr:@molstar/molstar-componentsnpm:specifierstranspilePackagesentry innext.config.tsnode_modulespath rather than a fragile version-dependent JSR store pathpathsentriesScope
build-npm.tsusingdntdeno task build:npmand wire it into the release CI stepnpmjs.comunder@molstar/molstar-componentsREADME.mdwith the npm install path and remove the workaround instructions