· 4 min read
How to Work Out What an npm Script Does
Heshan Fernando
Co-founder & COO
The build script is a hundred and forty characters of chained commands, environment variables and flags. It works, nobody has touched it in two years, and it just started failing on one developer’s machine.
Understanding it means reading it as a sequence rather than as a string.
The operators do most of the work
Three characters determine the entire control flow, and they behave very differently.
&& runs the next command only if the previous succeeded. Sequential, with failure stopping the chain. This is what you almost always want.
& runs commands in parallel and does not wait. The script may exit before background commands finish, and their failures may not be reported.
|| runs the next command only if the previous failed. Used for fallbacks, and occasionally used to swallow errors, which is worth spotting.
; runs the next command regardless of the outcome. Rare in npm scripts and a source of builds that report success after a step failed.
A script using & where && was intended appears to work — everything runs — and silently ignores failures. That is the pattern behind a CI job that passes while producing broken output.
| Operator | Runs next when | Waits? |
|---|---|---|
&& | Previous succeeded | Yes |
|| | Previous failed | Yes |
; | Always | Yes |
& | Immediately | No |
Pre and post hooks run invisibly
npm runs prebuild before build and postbuild after it, automatically, for any script name.
That is convenient and it is invisible at the call site. Running npm run build may execute three scripts, and someone reading only the build line will not know.
This is a frequent source of confusion: a build doing something unexpected, or taking longer than the visible script explains, is often a pre hook nobody remembered. Checking the whole scripts block rather than the one line is the habit.
The convention was deprecated for some lifecycle events in newer npm versions and still applies to arbitrary script names, so it is worth confirming behaviour for your version.
Cross-platform breakage is predictable
Scripts written on one operating system fail on another in a small number of consistent ways:
Environment variables inline. NODE_ENV=production node app.js works on Unix-like systems and fails on Windows cmd. This is what cross-env exists for.
Path separators. Forward slashes work almost everywhere; backslashes do not.
File operations. rm -rf against del, cp against copy. Packages like rimraf and shx provide cross-platform equivalents.
Quoting. Single and double quote handling differs between shells, and a glob quoted for one may not expand correctly in another.
A script that works for most of the team and fails for one person is nearly always one of these, and the failing person is usually on a different operating system.
Long scripts belong in a file
When a script exceeds a line or two, moving it out of package.json is nearly always an improvement.
A shell script or a Node script in a scripts directory can have comments, error handling, conditional logic and line breaks. A package.json entry can have none of those, and the result is a hundred and forty characters nobody wants to touch.
The package.json entry then becomes a one-line call to the file, which is readable, and the logic lives somewhere it can be reviewed properly in a diff.
It also makes cross-platform handling easier, since a Node script runs identically everywhere while a shell one-liner does not.
Common mistakes to avoid
- Using
&where&&was meant, producing silent failures. - Not checking for pre and post hooks.
- Inline environment variables without a cross-platform wrapper.
- Very long single-line scripts that nobody can read — extracting them into a script file is usually better.
- Assuming a script that passes locally will pass in CI, where the shell and environment differ.
How to do it with package.json Script Explainer
The package.json Script Explainer breaks the line into its parts.
- Paste the script line from package.json.
- Read the operator explanation —
&&and&behave very differently. - Check the scripts block for pre and post hooks on the same name.
- Look for the cross-platform patterns if it fails on one machine only.
Other developer tools are in the tools directory.
Frequently asked questions
What is the difference between && and &?
&& runs the next command only if the previous succeeded. A single & runs them in parallel without waiting, which means failures may go unreported and the script can exit early.
What are pre and post scripts?
Scripts named prebuild and postbuild run automatically around build. They are convenient and invisible at the call site, and they are a common cause of unexplained build behaviour.
Why does a script fail on Windows but not Mac?
Shell syntax differs. Inline environment variables, path separators and file commands all break across platforms, which is why cross-platform helper packages exist.
Final thought
Read the whole scripts block, not the one line you ran. The command that surprised you is frequently a hook you did not know was there.