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

dt-clean

v1.2.2

Published

Ensures the only DefinitelyTyped (`@types`) packages you have installed are the ones you need

Readme

dt-clean Version Badge

github actions coverage License Downloads

npm badge

Ensures the only DefinitelyTyped (@types/*) packages you have installed are the ones you need, and points out the ones you're missing.

dt-clean inspects your package.json and, for each dependency, decides what should happen to its DefinitelyTyped types package:

  • add: the runtime package ships no types of its own, but a matching @types/* package exists on the registry.
  • move: a @types/* package is in dependencies, but belongs in devDependencies.
  • remove: a @types/* package is installed but no longer needed: the runtime package now bundles its own types, or it no longer corresponds to any dependency.
  • keep: the @types/* package is present and still needed. @types/node is always kept.

Usage

# report only (the default)
npx dt-clean [dir]

# apply the changes to package.json
npx dt-clean --update [dir]

dir is the directory containing the package.json to inspect, and defaults to the current directory.

By default, dt-clean only prints a report and changes nothing:

Package         State    Action  Version
--------------  -------  ------  -------
@types/express  present  remove  ^4.0.0
@types/lodash   missing  add     ^4.17
@types/node     present  keep    ^25.0.0

3 `@types/*` packages: 1 keep, 0 move, 1 remove, 1 add.

The State column tells you whether each DefinitelyTyped package is currently present or missing; the Action column tells you what --update would do about it.

With --update (-u), dt-clean edits package.json in place - adding, moving, and removing the relevant @types/* entries - and then reminds you to run npm install (or your package manager's equivalent) to sync node_modules.

Options

  • -u, --update: apply the changes to package.json (default: report only).
  • --setup: idempotently wire (or upgrade) dt-clean --auto in a dependencies lifecycle script, without overwriting any script it did not author (see Automatic cleanup).
  • --auto: for use in a dependencies lifecycle script (run directly or via npx) - apply the changes like --update during npm install, but during npm ci only print what would change and exit 0. Through npx, the script must pipe the command into stdin for the npm ci no-op to work (see Automatic cleanup).
  • --help: show usage.

Automatic cleanup

To keep your @types/* set tidy on every install, run dt-clean --auto from a dependencies lifecycle script - the one npm's installer (arborist) runs after any operation that changes node_modules.

The one-step way to set this up, in any project, is:

npx dt-clean --setup

--setup edits package.json for you and is safe to run anywhere:

  • if you have no dependencies script, it adds one that runs dt-clean --auto via npx (so dt-clean itself need not be a dependency);
  • if you already have one, it adds the invocation to a free postdependencies (or predependencies) hook instead, so your existing script is never touched - and if every hook is taken, it appends && … to your dependencies script rather than clobbering it;
  • if it already placed the invocation in a post/pre hook and the preferred dependencies slot later frees up, re-running moves it back to the most-preferred available hook;
  • if an older form of the invocation it authored is present - a bare dt-clean --auto, an npx-wrapped one, the POSIX-only DT_CLEAN_NPM_COMMAND env prefix, or an earlier piped form with a stale version pin - re-running upgrades it in place to the current form;
  • if the current invocation is already in the best available hook, it does nothing; and if some other dt-clean invocation (or a customized one you wrote) is already present, it leaves that alone rather than adding a duplicate.

It only ever manages this one invocation and leaves the rest of your package.json (and its formatting) alone, so it's safe to re-run - repeated runs converge on the same result. That result is the equivalent of:

{
  "scripts": {
    "dependencies": "node -p \"process.env.npm_command\" | npx \"dt-clean@^1.2.0\" --auto"
  }
}

The node -p "process.env.npm_command" | pipe is why this looks more involved than a bare dt-clean --auto: npx (npm exec) runs dt-clean in a fresh environment where npm's own npm_command has been overwritten (to exec), so without a forward dt-clean could not tell npm install from npm ci. Node prints the real command and the pipe carries it into dt-clean's stdin, past npx. It is written this way because it has to run on every OS npm supports: no environment-variable prefix works in both POSIX shells and Windows cmd.exe, but a pipe does - and the quotes around the version keep cmd.exe from swallowing the ^. (Older setups used a DT_CLEAN_NPM_COMMAND="$npm_command" env prefix, which dt-clean still honors but which runs on POSIX shells only; --setup upgrades it to the pipe.)

Once it's wired in, dt-clean --auto decides what to do from the npm command - read straight from npm_command when invoked directly, or from the piped stdin (or the legacy DT_CLEAN_NPM_COMMAND env var) when invoked through npx:

  • under npm install, it applies the changes for you, so a fresh install keeps your @types/* set tidy automatically;
  • under npm ci (typically used in CI pipelines, where package.json must not be mutated), it only prints what would change and exits 0, so it never edits a checked-in file and never fails the install.

When invoked directly and npm_command is anything else (or absent), --auto applies the changes, exactly as under npm install. When invoked through npx without a forwarded command (for example a hand-written npx dt-clean --auto that omits the pipe), dt-clean cannot tell install from ci, so rather than risk mutating package.json during a ci it errors and exits nonzero, printing the pipe to add (or just run dt-clean --setup). It never guesses.

To avoid surprising edits, --auto runs only inside the dependencies lifecycle (or its predependencies/postdependencies hooks), or via npx: it checks npm_lifecycle_event, and if it is invoked any other way (for example directly from the shell) it refuses to do anything and exits nonzero. Use --update to apply changes manually.

On a node older than dt-clean's own engines.node, --auto skips silently and exits 0 rather than fail the install, so wiring it into a project with a wide node-version matrix is safe - the cleanup simply no-ops on the versions that can't run it. (Any other invocation on an unsupported node still prints the version it needs and exits nonzero.)

Exit codes

In the default report-only mode, the exit code is a bitmask of the kinds of pending changes, so a clean project exits 0 and you can fail CI (or a git pre-commit hook) on drift:

| Value | Meaning | | ----- | ---------------------------------------- | | 1 | there are @types/* packages to remove | | 2 | there are @types/* packages to add | | 4 | there are @types/* packages to move |

The bits combine, so a project that needs both an add and a remove exits 3, and one that needs all three exits 7.

With --update, dt-clean applies the changes, leaving the project clean, so a successful run always exits 0; a nonzero exit then means the update itself failed.