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 --helpIf you will generate rundown snapshots during your build, it is recommended to install via devDependencies:
$ cd my-tool $ npm install @rushstack/rundown --save-devSuppose 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 --verboseAnd 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-importsThe 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.logThe 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.