Windows and Node: Writing Portable Code

24 May 2012 | By Alex Young | Tags node tutorials windows windows-and-node

I’ve been surveying popular Node modules and the nodejs Google Group to find common portability issues people have found when testing modules in Windows.

For the most part, Node code seems very portable – there are only a few problem areas that seem to crop up frequently. Let’s take a look at these problems and the solutions so we can write code that runs everywhere.

Platform-Specific Code

Despite Node code’s inherent portability, there are times when platform-specific code is required. This is dealt with in Node’s core modules like this:

var isWindows = process.platform === 'win32';

if (isWindows) {
  // Windows-specific code
}

This example is based on path.js.

For more detailed information on the operating system, the os module can come in handy.

Nodes OS module in Windows

File System

Windows can actually accept backslashes or forward slashes as a path separator. This means you don’t need to change all of your require calls to use different slashes; most things should just work. There are a few cases where we need to be careful, however, particularly if a path name is absolute or it’s going to be displayed somewhere.

One common issue I’ve found is where the developer has made certain assumptions about the structure of absolute paths. In a commit to Express, Fixed absolute path checking on windows, we can see where the authors have adapted a method called isAbsolute to support Windows:

exports.isAbsolute = function(path){
  if ('/' == path[0]) return true;
  if (':' == path[1] && '\\' == path[2]) return true;
};

Isaac Schlueter recommends using path.resolve to make relative paths absolute in a cross-platform way.

When dealing with fragments of paths, using path.join will automatically insert the correct slash based on platform. For example, the Windows version will insert backslashes:

var joined = paths.join('\\');

Notice that JavaScript strings require two backslashes because one acts as an escape, so when working with Windows path names don’t be surprised if there are lot of double slashes.

Another big source of Windows issues is fs.watch. This module is routinely used by programs that watch for file system changes. Node’s documentation makes it clear that the API isn’t portable, so the slower but more compatible fs.watchFile can be used instead.

In this patch for the Derby web framework, we can see where the developers opted to branch based on process.platform to use fs.watchFile in Windows, but fs.watch elsewhere.

Text Interfaces

Be aware that not everybody has a super-fancy UTF-8 terminal that supports colours. Certain programs depend on text output, but people may have trouble seeing it correctly if your program relies on symbols their terminal or font doesn’t support.

Mocha is a good example of such a program, and in the issue Ability to configure passed/failed checkmarks for the spec reporter, we can see where someone has struggled to read the output with cmd.exe.

Environment

Assuming certain environmental variables will exist (or mean the same thing) on every platform is a good way to create portability headaches.

James Halliday’s Browserify had its fair share of Windows issues, which was problematic due to several other popular modules depending on it.

This commit to Browserify demonstrates a fix Christopher Bennage submitted that replaces calls to process.env.HOME with the following:

var home = (process.env.HOME || process.env.USERPROFILE);

I tried this in Windows 7 and found process.env.HOME wasn’t set, but process.env.USERPROFILE worked as expected.

Sockets

Node’s TCP sockets are portable, but Unix domain sockets are not. However, Windows has named pipes. The following code is almost exactly the same as the Unix equivalent, it just has a different path to the named pipe:

var net = require('net');

net.createServer(function(socket) {
  console.log('Connected');
}).listen('\\\\.\\pipe\\named-pipe-test');

Notice the escaped backslashes – forgetting to insert them here will raise a confusing EACCESS error. In node/test/common.js, there’s a branch based on platform to set the name of the pipe so it works in Windows and Unix:

if (process.platform === 'win32') {
  exports.PIPE = '\\\\.\\pipe\\libuv-test';
} else {
  exports.PIPE = exports.tmpDir + '/test.sock';
}

References


blog comments powered by Disqus