Skip to content

Latest commit

 

History

60 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@nktkas/dtn

JSR coveralls

Deno to Node — build a Deno project into a Node-compatible project (no bundling).

Install (Deno 2.9+)

deno add jsr:@nktkas/dtn

Usage

import { build } from "@nktkas/dtn";
import denoJson from "./deno.json" with { type: "json" };
// -> {
//   "name": "@scope/lib",
//   "version": "1.0.0",
//   "exports": "./src/mod.ts",
//   "imports": {
//     "@valibot/valibot": "jsr:@valibot/valibot@^1",
//     "@std/encoding/hex": "jsr:@std/encoding@^1/hex"
//   }
// }

await build({
  outDir: "dist",
  denoJson,
  npmReplacements: { "@valibot/valibot": "valibot" },
  copyFiles: ["README.md", "LICENSE"],
});
// dist/
// ├── README.md
// ├── LICENSE
// ├── package.json
// └── esm/
//     ├── mod.js  (+ mod.js.map, mod.d.ts)
//     ├── _deps/jsr.io/@std/encoding/1.0.0/hex.js  (+ hex.js.map, hex.d.ts)
//     └── ...     other local files related to mod.js

Config

interface BuildConfig {
  /** Output directory, relative to the project root. */
  outDir: string;
  /** Project root against which relative paths resolve. @default Deno.cwd() */
  root?: string;
  /** Package facts from `deno.json`. */
  denoJson: {
    name: string;
    version: string;
    /** Explicit runtime `.ts` entry or subpath map; wildcards and `.d.ts`-only entries are unsupported. */
    exports: string | Record<string, string>;
    /** Aliases targeting `jsr:` or `npm:` packages. */
    imports?: Record<string, string>;
  };
  /**
   * Replaces an import-map alias — which must resolve to a `jsr:`/`npm:` specifier — with an npm package instead of
   * vendoring it, given as `"name"` or `"name@version"`; an omitted version is taken from that specifier.
   */
  npmReplacements?: Record<string, string>;
  /** Fields merged into the generated `package.json`; dtn-generated values take precedence. */
  packageJson?: PackageJson;
  /** Files copied verbatim into the package root. */
  copyFiles?: string[];
  /** Directory under the package code root that holds inlined (vendored) dependencies. @default "_deps" */
  depsDir?: string;
}

Errors

build() throws a BuildError with a machine-readable code and, when known, the offending subject:

import { build, BuildError } from "@nktkas/dtn";

try {
  await build(config);
} catch (e) {
  if (e instanceof BuildError && e.code === "INVALID_CONFIG") { /* ... */ }
}
code Raised when
INVALID_CONFIG Exports, registry aliases, or npm replacements violate the supported contract.
UNSUPPORTED_MODULE A reachable module has an unsupported media type.
DEPENDENCY_FAILED Loading/resolution fails, npm requirements conflict, or vendored paths overlap.
EMIT_FAILED Transpilation, expected artifacts, rewriting, or source maps fail.
BUILD_FAILED Another platform or library operation fails.

The original platform or library error is available through error.cause when one exists.

Limitations

The intentionally supported scope is narrower than Deno's module system:

  • Import-map scopes are unsupported; aliases may target only jsr: or npm: packages.
  • Local and vendored remote modules support .ts, .mts, .js, .mjs, .json, .d.ts, .d.mts, and .d.cts. TSX, JSX, CommonJS, and Wasm are unsupported.
  • Specifier rewriting covers static ESM, string-literal runtime import(), TypeScript import() types, and string-literal module declarations/augmentations; computed runtime import(), import.meta.resolve(), CommonJS, TypeScript import = require, triple-slash references, and JavaScript JSDoc are not covered.
  • Remote type-sidecar directives (@ts-types/@deno-types/@ts-self-types) are unsupported.
  • Vendored URLs whose package paths overlap are rejected.
  • Generated absolute file: imports fail; use explicit type annotations.
  • Deno runtime APIs are not shimmed for Node.
  • Dependency graph resolution ignores deno.lock.
  • Only transpiled TypeScript gets source maps; copied JavaScript/MJS maps and mapping directives are omitted.
  • Validation and graph analysis preserve existing output; emission failures may leave partial output.

Alternatives

Starting with Deno 2.8, a similar tool was added to build a Deno project into a publication-ready npm package.

But it has some serious (for me) issues:

  • To convert a JSR import to its npm equivalent, you must first manually edit deno.json#imports.
  • After installing, the npm user needs to configure the .npmrc file in their project to work with jsr dependencies.
  • Slow types are not supported; they will be converted to any.

A popular tool for converting a Deno project into a Node-compatible project.

But it also has a few issues:

  • Does not support a mapping from JSR imports to their npm equivalents; requires manual modification of deno.json#imports beforehand (denoland/dnt#437)
  • Most likely, active support has been suspended (based on: the latest git commit date and the number of active issues)

License

@nktkas/dtn is licensed under the MIT License.

Copyright © 2026-present nktkas.

About

Deno to Node

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages