An on-going guide to help you write better cross-platform Node.js.

Since most Node.js users are running OS X or Linux, “cross-platform” means “works with Windows too.” Fortunately, almost everything does: Node.js and the whole ecosystem are super Windows friendly. Unlike other, even-more-homogenous ecosystems like Ruby’s, in the three years I’ve been playing with Node.js I’ve only had trouble running Windows a handful of times. So here are some problems you might hit and solutions for dealing with them.

Paths # Avoid 99% of path-related issues by following two rules: Do not use paths and URLs interchangeably When modifying, creating, or parsing a path, use Node’s built-in path APIs instead of string manipulation. Domenic Denicola has some good advice here. Hard-coding executable paths # It’s fine to hard-code relative paths to an executable if you’re careful and you: Don’t hard-code more than you need to. The ./node_modules/.bin folder is automatically added to your shell’s path when running an npm script, so use: { "scripts" : { "test" : "mocha" } } instead of { "scripts" : { "test" : "./node_modules/.bin/mocha" } } Make sure the path is something any shell can understand. If your path is to a bash shell script it obviously won’t work on Windows. This is why node modules have a “.cmd” version of executables side-by-side with a bash script. Each shell will do the right thing. Don’t leak the hard-coded path as data. If the path will ever be passed to another module, returned to the user, etc, then use the Node.js path APIs to build it instead so that the path conforms to what a path should look like on that platform. And if you’re a Windows dev, use forward slashes. They’ll work everywhere while backslashes won’t. upath as a path replacement # The upath package is a drop-in replacement for path that makes all paths returned by the API have forward slashes instead of backslashes (among other things). I wouldn’t recommend using this library regularly but I’ve used it to quickly fix some Metalsmith plugins that were treating paths as URLs. Switching to this library was an extremely small and easy one-line fix I could make to these plugins that fixed all the downstream code.

Scripts in package.json # I don’t want to deal with the complexity of grunt and gulp when I’m only performing simple tasks, so using the package.json “scripts” section as a simple task runner is appealing. For example, the package.json for this site is: { "scripts" : { "start" : "npm run watch" , "watch" : "node ./bin/watch.js" , "build:staging" : "cross-env NODE_ENV=development node ./bin/build.js" , "build:staging:debug" : "cross-env DEBUG=cdnify npm run build:staging" , "build:production" : "cross-env NODE_ENV=production node ./bin/build.js" , "build" : "npm-run-all build:*" } } Task runners abstract away platform incompatibilities from you while npm scripts will run your scripts as shell commands, verbatim. You can avoid any issues by writing as little shell code as possible and deferring to Node.js scripts instead. Some common pitfalls and ways to avoid them: Running multiple scripts # Most suggest using && to run multiple commands which works in Bash and cmd.exe. Good advice, but I prefer npm-run-all. It abstracts shell syntax, gives you the ability to run wildcards, and by default will bail out when a command fails. It’s as simple as this: npm i npm-run-all --save-dev "scripts": {"build": "npm-run-all build test"} Shell scripts that rely on Unix shebangs # cmd.exe/PowerShell don’t understand shebangs. Call scripts using node.exe explicitly: { "scripts" : { "watch" : "node ./bin/watch.js" } } instead of { "scripts" : { "watch" : "./bin/watch.js" } } Setting one-off environment variables # In bash you can run a command with an environment variable set without changing the environment variable in the shell: NODE_ENV = production webpack The closest you can get with cmd.exe is: set NODE_ENV=production ; webpack But now your shell has NODE_ENV=production set for all future scripts you run. This is easily fixed by using cross-env, e.g.: { "scripts" : { "build" : "cross-env NODE_ENV=production webpack" } } This runs webpack with the production NODE_ENV and doesn’t dirty your shell session.

Line-endings in test fixtures # The default option in Git for Windows is to set the git config option core.autocrlf to true . I’ve seen this be an issue multiple times with test fixtures. The output of some module will only contain line-feed characters and it is compared to the contents of a file on disk. If that file was created by git on a Windows machine, it’ll have crlf newlines instead of just lf and the test will fail, e.g.: assert ( "Hello world!\r

" === "Hello world!

" ) The fix is easy: add a .gitattributes file to your git repository with something like this: test/fixtures/** text eol = lf This will make the line endings for all files in the test/fixtures directory (and subdirectories) be line-feed characters regardless of platform. I don’t recommend turning this on for your whole repository, or worse, disabling git’s core.autocrlf option, which would both cause more headaches than they would avoid. GitHub has some good instructions on how to refresh your already-cloned repository to reflect the changes to the .gitattributes if you’re applying this fix after-the-fact.