A quick one today. I was setting up FINOS git-proxy on a Windows machine and tried to run the test suite. The script in package.json looked like this:
"test": "NODE_ENV=test ts-mocha './test/**/*.test.js' --exit"
Works on macOS and Linux, but fails on Windows. The reason is that npm runs scripts through cmd.exe by default on Windows, and cmd.exe does not understand the Unix shell convention of putting an environment variable assignment inline before a command. So NODE_ENV=test ts-mocha ... gets read as “run the program literally called NODE_ENV=test”, which of course does not exist.
My first attempt was to rewrite it in PowerShell, since that is what I actually use day to day:
"test-windows": "$env:NODE_ENV='test'; ts-mocha './test/**/*.test.js' --exit"
That also fails, with a confusing error:
The filename, directory name, or volume label syntax is incorrect.
The error is from cmd.exe again. npm does not switch to PowerShell just because the script body happens to look like PowerShell. It still feeds the whole line to cmd.exe, which sees $env:NODE_ENV='test' and gives up.
The right fix: cross-env
cross-env is a tiny package that sets environment variables in a portable way, so one script works on every OS your team is on.
npm install --save-dev cross-env
Then the script becomes:
"test": "cross-env NODE_ENV=test ts-mocha './test/**/*.test.js' --exit"
That is the whole fix. Just one word added to the script, one dev dependency. The same line runs whether you are on macOS, Linux, or Windows. CI configs stay clean too, since there is no OS branching at the script level.
The less-ideal fallback: separate scripts per platform
If for some reason you cannot add a dependency (corporate policy, supply-chain restrictions, whatever the reason), you can keep two scripts and use the Windows set builtin instead:
"test": "NODE_ENV=test ts-mocha './test/**/*.test.js' --exit",
"test:win": "set NODE_ENV=test&& ts-mocha './test/**/*.test.js' --exit"
Two things to watch out for. First, there must be no space between test and &&. If you write set NODE_ENV=test && with a space, that space becomes part of the variable value, and process.env.NODE_ENV ends up as "test " with a trailing space rather than "test". That is a painful bug to track down because the value looks right when you log it casually. The behaviour is documented in the Windows set command reference.
Second, you now have two scripts to keep in sync, and your CI workflow has to choose between them based on the runner OS. For one variable that is tolerable, but the moment you have two or three flags it gets messy fast. That is exactly the case cross-env was built for.
For reference, the underlying Node API being set here is process.env, and the npm scripts documentation covers how npm picks which shell to invoke. You can override that with the script-shell npm config if you really want to force PowerShell, but for portability cross-env is still the better answer.