On Writing Platform Independent Node Scripts

One day you write a simple Node script that traverses the file system and copies all the files you need from A-Z to destination folder B. In a matter of seconds your new script will accomplish something that would have taken much longer, in a way that is less error prone.

You make a juicy PR containing your changes. However, when others try to run your script many immediately hit an error:

Error: ENOENT: no such file or directory

Yikes. Your primary development environment is Linux but other people are using Windows. You hard-coded the path to a file you wanted

path/to/file

…but this won’t be recognized by Windows because it doesn’t recognize those forward slashes. You consider using some sort of conditional statement to handle filepaths for these two types of systems, but that seems messy. And the reality is that you shouldn’t do that because there is, in fact, a much cleaner option.

Node’s Path Module

The Node path module is a core module that provides a set of functions/utilities that can be used to deal with filepaths. It is a very useful tool for cross-platform development and is great for open source projects, where different contributors are likely to use different systems.

Windows vs. POSIX

These are the two main file systems a developer will need to worry about when working with Node (both Mac and Linux use POSIX), and most of Path’s functions support both by default. There are a lot of useful utilities provided by this module and you might find it worthwhile to look through them all, but here I’ll cover a few I thought were especially noteworthy and relevant to this post.

path.join

At first glance it might seem like this function simply joins filepath segments. However when reading through its documentation you’ll notice it mentions an extra benefit.

The path.join() method joins all given path segments together using the platform-specific separator as a delimiter, then normalizes the resulting path.

To avoid hard-coding slashes in filepaths and therefore introducing bugs, path.join should be used instead. This function will output a string with forward or backward slashes based on the system. For example:

path.join('path', 'to', 'file');
// On Linux/Mac returns: 'path/to/file'
// On Windows returns: 'path\to\file'

path.sep

What if you have a path, and want to create an array from each of its segments? For example, you do something like the following:

'path/to/file'.split('/');
// Returns ['path', 'to', 'file'] on Linux/Mac

If you write a function that searches for the / delimiter and creates an array based off that, it will create an error on Windows.

'path\\to\\file'.split('/');
// Throws error on Windows

To avoid this problem path.sep can be used. It isn’t a function, but a constant, and will be the path segment separator used by the corresponding system.

'path/to/file'.split(path.sep); 
// path.sep == '/'
// Returns['path', 'to', 'file'] on Linux/Mac
'path\\to\\file'.split(path.sep); 
// path.sep == '\'
// Returns ['path', 'to', 'file'] on Windows

path.delimiter

Like path.sep, path.delimiter provides a single constant value; on Windows this will be ; , on POSIX it’s :.

One place where you’ll likely see this sort of delimiter being used is in the PATH environment variable for your system. So if you ever need to parse that value, path.delimiter will be useful to you.

process.env.PATH.split(path.delimiter);
// On POSIX returns ['/usr/bin', '/bin', '/usr/sbin', '/sbin', '/usr/local/bin']
// On Windows returns ['C:\\Windows\\system32', 'C:\\Windows', 'C:\\Program Files\\node\\']

In Summary

When dealing with paths in your Javascript applications, use Node’s Path module to make filepaths platform agnostic. This is especially important when working in open source since you can’t guarantee that all contributors will be using a specific type of system.

Further Reading