best-practice

Dirname Does Not Follow Symlink

Node’s global object __dirname returns the name of the directory that the currently executing script resides in. However, if a directory is symbolic linked or a symlink, it still return the original path.

For example, the following directory structure:

1
2
3
4
5
6
7
8
/tmp/dirname/
├── foo
│ └── app.js
└── lib
├── bar -> ../foo
└── util.js
3 directories, 2 files

The app.js contains the following line:

1
console.log(__dirname);

Run the script:

1
2
$ cd /tmp/dirname/foo && node app.js
/tmp/dirname/foo

You get the directory the app.js script is residing it. But if you do the same from the symlinked directory:

1
2
$ cd /tmp/dirname/lib/bar && node app.js
/tmp/dirname/foo

Well, you get the same answer, even the current working directory is different:

1
2
$ cd /tmp/dirname/lib/bar && pwd
/tmp/dirname/lib/bar

The return of __dirname does not follow symbolic link.

So, have to be extra careful when requiring files that is symbolic linked. If you think you are in lib/bar directory and try to require the util.js script in app.js:

1
2
3
var path = require('path');
console.log(require('../util'));
console.log(require(path.join(__dirname, '..', 'util')));

Both statements will throw module not found error. Path join does not help either.

The best practice is to avoid symlinks and relative directory requiring. Instead, set up required as modules, and install them in node_modules directory, then they will become top-level modules.

Written in CoffeeScript, Required in JavaScript

If you are writing in CoffeeScript, then requiring another module written in CoffeeScript works the same as as both scripts are in JavaScript:

1
cm = require 'coffee-module'

But if you are writing in JavaScript, and the dependent module is in CoffeeScript, you have to include CoffeeScript as a dependency with require statement:

1
2
require('coffee-script');
var cm = require('coffee-module');

For better compatibility, source codes written in CoffeeScript should be compiled into JavaScript. But what is the best practice? You don’t want to maintain two languages. When to compile? Here is two options:

  1. Compile before publish the module
  2. Compile after module install

The advantage of the first approach is that there is no dependency on CoffeeScript. The module has been compiled into JavaScript prior to submitting to module registry. However, this approach requires two repositories, one is source code repository, and another one is the published module repository. If you are working on a private project, it is less likely that you will publish your module to an public NPM registry or running your own private one. It is more likely to have a single source code repository. Therefore, the second approach might be better in this situation. However, coffee-script must be added in dependency, or it must be installed globally with coffee command available during preinstall phase. Although this approach is not recommended in npm-scripts, before setting up a private NPM registry, this is the way to go.

Here is the required fields in package.json:

1
2
3
4
5
{
"scripts": {
"preinstall": "coffee --compile --bare --output lib/ src/"
}
}