npm.io
1.2.2 • Published 2d agoCLI

dt-clean

Licence
MIT
Version
1.2.2
Deps
5
Size
40 kB
Vulns
0
Weekly
0
Stars
3

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.