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

@davidcornelson/tsf

v1.0.0

Published

Multi-target TypeScript build tool for monorepos

Downloads

102

Readme

tsf

Publish workspace packages to npm without rewriting your imports by hand.

tsf is a multi-target TypeScript build tool for monorepos. It compiles one source into multiple outputs — rewriting @scope/pkg workspace imports to relative paths for npm, preserving them for local dev, and bundling them for browsers. Declarations included.

The Problem

You have a TypeScript monorepo. Locally, import { Thing } from '@scope/core' resolves via workspace symlinks. But when you npm publish, that import means nothing — consumers need real relative paths.

Today your options are:

  • A custom build script that rewrites imports, manages declarations, and coordinates builds across packages (often hundreds of lines of bash)
  • A bundler that inlines everything, losing tree-shaking for your consumers
  • Manual parallel tsconfig files per output target, kept in sync by hand

tsf replaces all of that with a config file:

{
  "projects": ["packages/*/tsconfig.json"],
  "targets": {
    "local": {
      "module": "commonjs",
      "outDir": "dist",
      "imports": "preserve",
      "declarations": true
    },
    "npm": {
      "module": "commonjs",
      "outDir": "dist-npm",
      "imports": "relative",
      "declarations": true,
      "condition": "publish"
    }
  }
}

The local target keeps workspace imports intact for development. The npm target rewrites them to relative paths — only for packages that have publishConfig. One source, two outputs.

Before and after

Your source code:

import { createLogger } from '@myorg/logger';
import { validate } from '@myorg/schema';

Local build (imports: "preserve") — unchanged:

const { createLogger } = require("@myorg/logger");
const { validate } = require("@myorg/schema");

npm build (imports: "relative") — rewritten:

const { createLogger } = require("../logger/dist-npm/index.js");
const { validate } = require("../schema/dist-npm/index.js");

Declarations are rewritten too. No manual work. No build script.

Install

pnpm add -D @davidcornelson/tsf
# or
npm install -D @davidcornelson/tsf

Quick Start

tsf init          # Generate config from existing project
tsf build         # Build default target
tsf build --all   # Build all targets
tsf version 1.0.0 --condition publish  # Set version on npm packages
tsf publish                            # Publish to npm in dependency order
tsf list --condition publish           # List npm packages
tsf info          # Show resolved build plan

Configuration Reference

Target Options

| Option | Description | |---|---| | module | Output module format: commonjs, esnext, es2020, es2022, node16, nodenext | | format | Bundler format: cjs, esm, iife, umd | | outDir | Output directory (relative to each package) | | outFile | Single output file (alternative to outDir) | | imports | Import resolution strategy (see below) | | declarations | Generate .d.ts files | | condition | Conditional target — "publish" only applies to packages with publishConfig | | transpiler | Compiler: tsc (default), esbuild, swc | | bundler | Bundler: esbuild, rollup (requires imports: "bundle") | | banner | Prepend to output (e.g., "#!/usr/bin/env node" for CLI tools) | | external | Dependencies to exclude from bundling |

Per-Package Overrides

Add ts-forge.json in any package directory:

{
  "targets": {
    "local": { "skip": true }
  }
}

Import Resolution Strategies

| Strategy | Use Case | What Happens | |---|---|---| | preserve | Local dev | Imports left as-is | | relative | npm publish | @scope/pkg → relative paths | | bundle | Browser/CLI | All imports inlined | | specifier-map | Deno | Rewritten per import map |

Commands

tsf build [options]

Build targets across all packages in dependency order.

tsf build                       # Build default (unconditional) targets
tsf build --all                 # Build all targets
tsf build --target npm          # Build specific target
tsf build --condition publish   # Build targets matching condition
tsf build --all --clean         # Clean output dirs first
tsf build --all --no-check      # Skip type checking
tsf build --watch               # Watch mode
tsf build --parallel 4          # Limit concurrency
tsf build --all --sync-package-json  # Sync package.json after build

tsf check

Type-check all projects without emitting.

tsf info

Display the resolved build plan: packages, dependency order, and targets with per-target package counts.

tsf init

Generate ts-forge.config.json by detecting existing project structure. Reads package.json, tsconfig.json, and workspace configuration. Safe to re-run — merges new targets without overwriting existing config.

tsf sync

Generate main, types, and exports fields in each package's package.json from target configuration. Preserves all other fields. Publish-conditioned targets are preferred for field values.

tsf validate

Verify build outputs:

  • Entry points declared in package.json exist on disk
  • Declaration files (.d.ts) exist alongside JavaScript files
  • No workspace specifiers (@scope/pkg) leaked into non-preserve output

Exit code 1 if any errors found.

tsf version <version> | --bump <level> [options]

Set or bump version in package.json for workspace packages.

tsf version 0.9.64-beta                     # Set all packages to explicit version
tsf version 0.9.64-beta --condition publish  # Only npm-published packages
tsf version --bump patch                     # Increment patch version
tsf version --bump prerelease --preid beta   # Bump prerelease suffix
tsf version 0.9.64-beta --filter @scope/pkg  # Specific package(s)
tsf version 0.9.64-beta --changed --condition publish  # Only changed packages
tsf version 0.9.64-beta --dry-run           # Preview without writing

| Option | Description | |---|---| | <version> | Explicit version string (mutually exclusive with --bump) | | --bump <level> | Semver increment: major, minor, patch, prerelease | | --preid <tag> | Prerelease identifier (default: beta) | | --condition <name> | Only packages matching target condition (e.g., publish) | | --filter <name> | Restrict to specific package(s), repeatable | | --changed | Only bump packages that changed since last npm publish | | --dry-run | Show changes without writing |

tsf publish [options]

Publish workspace packages to npm in dependency order. Only publishes packages that have publishConfig in their package.json. Verifies npm login before publishing.

tsf publish                          # publish all publishable packages
tsf publish --dry-run                # preview without publishing
tsf publish --tag beta               # publish with npm dist-tag
tsf publish --filter @scope/pkg      # specific package(s)

| Option | Description | |---|---| | --tag <tag> | npm dist-tag (default: latest) | | --condition <name> | Only packages matching target condition | | --filter <name> | Restrict to specific package(s), repeatable | | --dry-run | Pass --dry-run to npm publish |

tsf changed [options]

Show packages that have changed since their last npm publish. Compares each package against the npm registry — a package is "changed" if it has never been published, its local version differs, or it has git changes since the version tag.

tsf changed                        # all packages
tsf changed --condition publish    # only npm-published packages

| Option | Description | |---|---| | --condition <name> | Only packages matching target condition | | --filter <name> | Restrict to specific package(s), repeatable |

tsf list [options]

List workspace packages, one per line, in dependency order. Useful for scripting and verifying which packages match a condition.

tsf list                        # all packages
tsf list --condition publish    # only npm-published packages

| Option | Description | |---|---| | --condition <name> | Only packages matching target condition | | --filter <name> | Restrict to specific package(s), repeatable |

tsf gh-action

Generate .github/workflows/tsf.yml with auto-detected package manager setup and Node.js version matrix.

Workspace Support

tsf detects pnpm, npm, and yarn workspaces. It respects pnpm-workspace.yaml exclusion patterns (e.g., !packages/forge).

Publish Target Scoping

Targets with condition: "publish" automatically apply only to packages that have publishConfig in their package.json. Other packages are skipped.

$ tsf info
Targets:
  local: commonjs → dist, imports=preserve
  npm: commonjs → dist-npm, imports=relative [condition: publish] (18 packages)

Caching

tsf caches builds based on source content, package.json version, target config, and dependency cache keys. Unchanged packages are skipped on subsequent builds. Version bumps automatically invalidate the cache. Use --clean to bypass the cache entirely. Cache is stored in .tsf-cache/ at the workspace root.

Development

pnpm install
pnpm build
pnpm test

License

MIT