npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ts-node-pack

v0.2.1

Published

Pack a TypeScript package into a Node-compatible npm tarball without modifying the source tree

Readme

ts-node-pack

Pack a TypeScript package into a Node-compatible npm tarball — without modifying the source tree, without bundling, and without changing module resolution semantics.

Given a TypeScript package whose sources use .ts files and .ts in relative import specifiers, ts-node-pack produces a .tgz whose contents are plain .js + .d.ts with correct .js specifiers, ready to npm install into any Node ESM project.

Why

TypeScript 5.7 introduced rewriteRelativeImportExtensions, which lets you author import './foo.ts' and have tsc emit import './foo.js' in the compiled .js. But:

  • tsc does not rewrite .ts specifiers inside emitted .d.ts files.
  • Your package.json (main, module, exports, types) still points at .ts.
  • You probably don't want the .ts sources in the published tarball at all.

ts-node-pack wraps tsc + npm pack and fills in exactly those gaps.

Install

npm install --save-dev ts-node-pack

Requires Node ≥ 20 and TypeScript ≥ 5.7 available in the package being packed (resolved via npx tsc).

Usage

ts-node-pack <packageDir> [--tsconfig <path>] [--stage-to <dir>] [--skip-pack] [--force] [--verbose]

| Flag | Description | | ------------------- | -------------------------------------------------------------------------------------------- | | --tsconfig <path> | tsconfig to extend. Defaults to tsconfig.build.json if present, otherwise tsconfig.json. | | --stage-to <dir> | Stage into <dir> instead of an auto-created temp dir. Caller owns its lifecycle. | | --skip-pack | Skip the final npm pack step. Requires --stage-to. | | --force | With --stage-to, clear <dir> if it already has contents. | | -v, --verbose | Log each pipeline phase to stderr. | | -h, --help | Show help. |

The resulting <name>-<version>.tgz is written to the current working directory (unless --skip-pack is set).

Example

cd my-project
ts-node-pack ./packages/core --verbose
npm install ./my-core-1.2.3.tgz

--stage-to and --skip-pack

By default ts-node-pack stages into an auto-created mkdtemp() directory, runs npm pack against it, copies the resulting .tgz to the current working directory, and removes the temp dir.

Pass --stage-to <dir> when you want to keep the staged contents — for example, to let another tool pack from that directory instead (lerna publish --contents <dir>, an alternate tarball builder, etc.). <dir> must either not exist, be empty, or be opted-in for clearing via --force.

Combine with --skip-pack to stop after staging and never run npm pack at all. --skip-pack is only valid with --stage-to (otherwise the staged contents would have no accessible location).

| Invocation | Behavior | | ------------------------------------------------- | ------------------------------------------------------------------- | | ts-node-pack <pkg> | Stage to temp dir, pack, copy .tgz to CWD, delete temp dir. | | ts-node-pack <pkg> --stage-to <dir> | Stage to <dir>, pack, copy .tgz to CWD, leave <dir> in place. | | ts-node-pack <pkg> --stage-to <dir> --skip-pack | Stage to <dir>, skip pack, leave <dir> in place. | | ts-node-pack <pkg> --skip-pack | Error: skipPack requires stageTo. |

Pipeline

  1. Resolve package — read package.json, pick tsconfig.
  2. Stage — use --stage-to <dir> if given, otherwise create mkdtemp()/package/. A small separate mkdtemp() work dir always holds auxiliary files (e.g. the derived tsconfig) so they never land in the packed contents.
  3. Derived tsconfig — write tsconfig.emit.json inside the work dir that extends the chosen tsconfig (by absolute path) and forces outDir, declaration, rewriteRelativeImportExtensions: true, noEmit: false. If the base tsconfig enables sourceMap, inlineSourceMap, or declarationMap, inlineSources: true is also set so debuggers get full source-level fidelity without any .ts files in the tarball.
  4. Emit — run tsc -p against the derived config.
  5. Rewrite .d.ts — for each emitted .d.ts, rewrite ./foo.ts./foo.js in import / export from / dynamic import() specifiers. Non-relative specifiers are left alone.
  6. Rewrite package.json — rewrite .ts.js (and → .d.ts under types conditions) in main, module, types, typings, bin, exports, and the files array. Strip devDependencies and scripts.
  7. Copy assetsREADME*, LICENSE*, CHANGELOG*, NOTICE*. Source .ts files are never copied.
  8. Validate — fail if any .ts specifier remains in emitted .js / .d.ts / package.json, or if a referenced entry point does not exist.
  9. Pack — unless --skip-pack: npm pack in the staging directory and move the tarball to the original CWD.
  10. Cleanup — always remove the work dir. In default mode this also removes the staging dir (which is nested inside). In --stage-to mode the staging dir is the caller's, and survives.

The source tree is never mutated.

Sourcemaps

If your tsconfig has sourceMap (or inlineSourceMap / declarationMap) enabled, ts-node-pack automatically forces inlineSources: true so each emitted .map embeds its source text via sourcesContent. This gives full source-level debugging and "Go to Definition" without shipping .ts files — sidestepping dual-resolution hazards where a downstream bundler or TS project might pick ./foo.ts over ./foo.js. Detection is a shallow read of the chosen tsconfig; if you inherit sourceMap from a base config via extends, set it explicitly at the leaf.

Non-goals

  • No bundling.
  • No AST transforms.
  • No custom module resolution.
  • No npm publish logic.

License

MIT