Fourteen commands, on one broken project
A project called acme-release-notes that imports chalk, semver, lodash and
minimist. Every command below was really run against it while this page was being built -- by
spawning the actual bin/nirdep.mjs, in order, on one copy of the project, so
scan after apply is quiet because the apply above it really
happened. What you click is the replay: the bytes it printed, exit code and colours included.
Nothing executes in your browser here, and the page would rather say so than pretend.
Or type one. help, clear and
the names above work; anything else is answered honestly.
Four ways a dependency turns on you
Every row in these stories comes out of src/scan/advisories.mjs, the same
table nirdep scan checks your lockfile against. The build fails unless all
0 rows are claimed by exactly one story, so these cannot quietly go stale
while the table moves on. Open one and the advisories are listed underneath the argument.
Reviewed . This is a hand-kept table with a date on it, not a
feed. It is not npm audit and does not pretend to be complete.
Measured, including where we lose
Written by node tools/bench.mjs and committed as bench.json.
The page reads that file; it never runs the benchmark, because a number that changes every
time the page is built is a number nobody can check. Three of these are honestly slower than the
package they replace.
What the five files cost you to keep
The whole tool, on 300 files
The gap between those last two rows is the syntax gate: every rewritten file goes to Node's own parser before it is written, which costs a process per file. It is the most expensive thing nirdep does and the reason it can be trusted to write.
And what an install weighs
A project that cannot run, then the same project running
Seven stages, recorded by running the real commands on a real temporary project. Nothing was installed in between, and nothing here is a screenshot: click a stage to read what it does and see exactly what it printed.
Rewrite your own file
This is not a mock-up. src/rules/rewrite.mjs, the lexer under it and the
diff printer are all loaded into this page and run on the text below, the same way the CLI runs them
on your disk. Edit it, break it, import something it refuses -- it will tell you which line and why.
The one thing this page cannot do: the CLI parses every
rewritten file before it writes it, using node:vm, and a browser has no equivalent.
So the diff above is real and the syntax gate is not run here. In the CLI, a file that would not
parse is not written, and if any file fails the whole run writes nothing.
Try the replacements
Four of the five runtime modules, loaded from source and running here. Same functions the rewritten project imports; no shims around them except a fake filesystem for the glob walk, because a browser has no disk.
semver
Ranges, comparators, prereleases. Not one regular expression anywhere in it -- which is how the ReDoS class of bug stops being possible rather than being patched.
glob
Segment-by-segment matching with no backtracking, over the tree on the right. The walk is the real one; the filesystem under it is an object in this page.
src/**/!(*.test).mjs or {src,test}/**collect
The subset people actually import: get, groupBy, isEqual, merge, cloneDeep, debounce. Own properties only, so a rewritten path cannot walk into a prototype.
colour
Chalk's whole job is writing four bytes before your text and four after. Here are the bytes, spelled out, from the module that writes them.
The fifth module, args, replaces minimist,
commander and yargs. It reads process.argv, so there is nothing for it to read in a
browser and no panel here pretends otherwise. nirdep --help is it, running.
Why, and what it will not do
The argument
A dependency is not a library. It is a standing permission for somebody you have never met to run code on your machine, every install, forever. Most of the ones at the top of the list are doing something the standard library has done for years -- padding a string, comparing two version numbers, colouring a line of output.
nirdep does not ask you to trust that argument. It shows you the imports, the blast radius, the diff, and the two packages it will not touch, and then you decide.
The same bug, twice, in two packages
semver and minimatch both had a denial-of-service bug of the identical shape: a regular expression that backtracks on input somebody else controls. Patching one version of that is a fix; removing the regular expression is the end of the class. Neither replacement here contains one, and the tests prove the answers still match, hundreds of thousands of comparisons at a time.
lodash had two advisories of its own, one of which compiled its input into a function. The walkthrough's scan names all of them, per advisory, including the one against a package nirdep refuses to rewrite -- because that one is still in the lockfile afterwards.
What is honest about this page
- The walkthrough is a recording of a real run made when the page was built, colours and all. The counts, the diff and the error in stage 2 are what the commands printed.
- The two sandboxes are not recordings. Eleven of nirdep's modules are embedded verbatim and loaded as ES modules; what they print is what they computed from your input.
- The syntax gate needs
node:vm, so it does not run here. In the CLI it runs on every file, and a run where one file fails writes none of them. - The glob panel walks a directory tree that is an object in this page. The traversal is the
real one; six functions of
node:fs,node:pathandnode:utilare stood in for, and nothing else is. - No requests. This page works from a
file://URL, on a plane, forever.
Run it on your own project
No install step, because there is nothing to install:
clone it and run node bin/nirdep.mjs. scan and plan only
read. apply is the only command that writes, and guard is the one you
put in CI so the packages you removed cannot come back in somebody's pull request.