Slow startup times for Node.js commands or services? Rundown can invoke a Node.js process and:
- View imported files: Intercept all
require()
calls and show which paths were loaded. - Find culprits: Show the chain for
require()
calls for each import, explaining why it was imported. - Detect regressions over time: Generate a concise "snapshot" report that can be committed to Git. Changes to this file may indicate potential performance regressions.
You can install this tool globally:
$ npm install --global @rushstack/rundown
# View command line help
$ rundown --help
If you will generate rundown snapshots during your build, it is recommended to install via devDependencies
:
$ cd my-tool
$ npm install @rushstack/rundown --save-dev
Suppose you maintain a small NPM project that is invoked as follows:
# The folder where your tool is developed
$ cd my-tool
# When you invoke "my-tool --my-option 123 --verbose" from the shell, let's suppose that it invokes
# this Node.js command:
$ node lib/start.js --my-option 123 --verbose
And suppose that your tool's startup time is rather slow, because the code calls require()
to load many different
NPM packages. We can create a report to see all the imports:
# We use "--args" to pass the command-line arguments for "my-tool"
$ rundown inspect --script lib/start.js --args="--my-option 123 --verbose"
The report may look like this:
rundown-inspect.log
/path/to/my-tool/lib/start.js
/path/to/my-tool/node_modules/at-least-node/index.js
/path/to/my-tool/node_modules/fs-extra/lib/copy-sync/copy-sync.js
/path/to/my-tool/node_modules/fs-extra/lib/copy-sync/index.js
/path/to/my-tool/node_modules/fs-extra/lib/copy/copy.js
/path/to/my-tool/node_modules/fs-extra/lib/copy/index.js
/path/to/my-tool/node_modules/fs-extra/lib/empty/index.js
/path/to/my-tool/node_modules/fs-extra/lib/ensure/file.js
/path/to/my-tool/node_modules/fs-extra/lib/ensure/index.js
/path/to/my-tool/node_modules/fs-extra/lib/ensure/link.js
/path/to/my-tool/node_modules/fs-extra/lib/ensure/symlink-paths.js
/path/to/my-tool/node_modules/fs-extra/lib/ensure/symlink-type.js
/path/to/my-tool/node_modules/fs-extra/lib/ensure/symlink.js
/path/to/my-tool/node_modules/fs-extra/lib/fs/index.js
/path/to/my-tool/node_modules/fs-extra/lib/index.js
/path/to/my-tool/node_modules/fs-extra/lib/json/jsonfile.js
/path/to/my-tool/node_modules/fs-extra/lib/json/output-json-sync.js
/path/to/my-tool/node_modules/fs-extra/lib/json/output-json.js
/path/to/my-tool/node_modules/fs-extra/lib/mkdirs/index.js
/path/to/my-tool/node_modules/fs-extra/lib/mkdirs/make-dir.js
/path/to/my-tool/node_modules/fs-extra/lib/move-sync/index.js
/path/to/my-tool/node_modules/fs-extra/lib/move-sync/move-sync.js
/path/to/my-tool/node_modules/fs-extra/lib/move/index.js
/path/to/my-tool/node_modules/fs-extra/lib/move/move.js
/path/to/my-tool/node_modules/fs-extra/lib/output/index.js
/path/to/my-tool/node_modules/fs-extra/lib/path-exists/index.js
/path/to/my-tool/node_modules/fs-extra/lib/remove/index.js
/path/to/my-tool/node_modules/fs-extra/lib/remove/rimraf.js
/path/to/my-tool/node_modules/fs-extra/lib/util/stat.js
/path/to/my-tool/node_modules/fs-extra/lib/util/utimes.js
/path/to/my-tool/node_modules/graceful-fs/clone.js
/path/to/my-tool/node_modules/graceful-fs/graceful-fs.js
/path/to/my-tool/node_modules/graceful-fs/legacy-streams.js
/path/to/my-tool/node_modules/graceful-fs/polyfills.js
/path/to/my-tool/node_modules/jsonfile/index.js
/path/to/my-tool/node_modules/jsonfile/utils.js
/path/to/my-tool/node_modules/universalify/index.js
To see how each file is imported, you can add the --trace-imports
switch.
# We use "--args" to pass the command-line arguments for "my-tool"
$ rundown inspect --script lib/start.js --args="--my-option 123 --verbose" --trace-imports
The report now shows more detail:
rundown-inspect.log
. . .
/path/to/my-tool/node_modules/graceful-fs/legacy-streams.js
imported by /path/to/my-tool/node_modules/graceful-fs/graceful-fs.js
imported by /path/to/my-tool/node_modules/fs-extra/lib/fs/index.js
imported by /path/to/my-tool/node_modules/fs-extra/lib/index.js
imported by /path/to/my-tool/lib/start.js
imported by /rundown/lib/launcher.js
/path/to/my-tool/node_modules/graceful-fs/polyfills.js
imported by /path/to/my-tool/node_modules/graceful-fs/graceful-fs.js
imported by /path/to/my-tool/node_modules/fs-extra/lib/fs/index.js
imported by /path/to/my-tool/node_modules/fs-extra/lib/index.js
imported by /path/to/my-tool/lib/start.js
imported by rundown/lib/launcher.js
. . .
It may be the case that many of these imports are not actually used. You can avoid preloading them
by converting them to lazy imports using the Import.lazy()
from
@rushstack/node-core-library
or import-lazy.
To detect future regressions, use the rundown snapshot
command to write a snapshot file:
# We use "--args" to pass the command-line arguments for "my-tool"
$ rundown snapshot --script lib/start.js --args="--my-option 123 --verbose"
# This file can be committed to Git to track regressions
$ git add rundown-snapshot.log
The snapshot file format eliminates spurious diffs, by showing only the names of the imported packages. For local projects in a monorepo, it will show relative paths. Example output:
rundown-snapshot.log
../path/to/monorepo-sibling
at-least-node
fs-extra
graceful-fs
jsonfile
universalify
usage: rundown [-h] <command> ...
Detect load time regressions by running an app, tracing require() calls, and
generating a deterministic report
Positional arguments:
<command>
snapshot Invoke a Node.js script and generate a test snapshot
inspect Invoke a Node.js script and generate detailed diagnostic output
Optional arguments:
-h, --help Show this help message and exit.
For detailed help about a specific command, use: rundown <command> -h
usage: rundown snapshot [-h] -s PATH [-a STRING] [-q] [-i]
Invoke a Node.js script and generate a test snapshot. This command creates a
concise report that can be added to Git, so that its diff can be used to
detect performance regressions
Optional arguments:
-h, --help Show this help message and exit.
-s PATH, --script PATH
The path to a .js file that will be the entry point
for the target Node.js process
-a STRING, --args STRING
Specifies command-line arguments to be passed to the
target Node.js process. The value should be a single
text string delimited by spaces. Example: rundown
inspect --scripts ./example.js --args="--flag
--option=123"
-q, --quiet Suppress STDOUT/STDERR for the target Node.js process
-i, --ignore-exit-code
Do not report an error if the target Node.js process
returns a nonzero exit code
usage: rundown inspect [-h] -s PATH [-a STRING] [-q] [-i] [-t]
Invoke a Node.js script and generate detailed diagnostic output. This command
is used to inspect performance regressions.
Optional arguments:
-h, --help Show this help message and exit.
-s PATH, --script PATH
The path to a .js file that will be the entry point
for the target Node.js process
-a STRING, --args STRING
Specifies command-line arguments to be passed to the
target Node.js process. The value should be a single
text string delimited by spaces. Example: rundown
inspect --scripts ./example.js --args="--flag
--option=123"
-q, --quiet Suppress STDOUT/STDERR for the target Node.js process
-i, --ignore-exit-code
Do not report an error if the target Node.js process
returns a nonzero exit code
-t, --trace-imports Reports the call chain for each module path, showing
how it was imported
- CHANGELOG.md - Find out what's new in the latest version
Rundown is part of the Rush Stack family of projects.