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:
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.
Node has a simple module loading system by using require.
A module prefixed with '/' is an absolute path to the file. For example, require('/home/marco/foo.js') will load the file at /home/marco/foo.js.
A module prefixed with './' is relative to the file calling require(). That is, circle.js must be in the same directory as foo.js for require('./circle') to find it.
Without a leading '/' or './' to indicate a file, the module is either a “core module” or is loaded from a node_modules folder.
We can also use it to load JSON files by simply specifying the file path. File path can be relative or absolute. Giving the following file structure:
1
2
3
4
5
6
.
├── app.js
├── data
│ └── file.json
└── lib
└── util.js
Let’s say the root path of the application is /home/marco. Use relative path from the file lib/util.js:
1
var json = require('../data/file.json');
This will load the JSON content from data/file.json, because the require system knows that the file is relative to the calling file lib/util.js. But when using file system functions, relative path is not treat the same way:
Relative path to filename can be used, remember however that this path will be relative to process.cwd(). - File System
process.cwd() returns the current working directory, not the root directory of the application nor the directory of the calling file. For example:
1
var data = fs.readFileSync('../data/file.json');
If your current working directory is /home/marco/myapp, the application root directory, you will get the following error message:
Error: ENOENT, no such file or directory '../data/file.json'
at Object.fs.openSync (fs.js:410:18)
at Object.fs.readFileSync (fs.js:276:15)
Because process.cwd() is /home/marco/myapp, and reading file relative to this directory with ../data/file.json, Node will expect file path to be /home/marco/data/file.json. Therefore, the error is thrown.
The difference between require and fs in loading files is that if the relative path is used in require, it is relative to the file calling the require. If the relative path is used in fs, it is relative to process.cwd().
To fix the error in file system functions, we should always use absolute path. One thing we can do is:
1
var data = fs.readFileSync(__dirname + '/../data/file.json');
__dirname is “the name of the directory that the currently executing script is resides in” [dirname], and it has no trailing slash. The the structure looks odd, even though it works, but it might not work in all systems.
The correct solution here is to use path.join from Path:
1
var data = fs.readFileSync(path.join(__dirname, '../data/file.json'));
The path module “contains utilities for handling and transforming file paths”. The path.join function will call path.normalize to “normalize a string path, taking care of .. and . paths” [normalize], also see function normalizeArray in https://github.com/joyent/node/blob/master/lib/path.js on how the paths are normalized.
In conclusion:
1
2
3
4
5
6
// Use `require` to load JSON file.
var json = require('../data/file.json');
// Use `fs` to load JSON file.
var data = fs.readFileSync(path.join(__dirname, '../data/file.json'));
Because CoffeeScript binary (coffee) use the following format:
#!/usr/bin/env node
Which does not specify fully qualified path on the shebang line, and will result in:
/usr/bin/env: node: No such fileordirectory
We can compile CoffeeScript into JavaScript and use fully qualified node executable. But this is not necessary. Since we are using Debian based system, which uses Vixie cron. It allows environment variables to be defined. We can make Cron aware of the custom path in environment variable:
The second method is to use the current working directory of the process process.cwd():
filename = process.cwd() + '/data/file.txt';
The second approach is more portable. If you move lib/reader.js to lib/utils/reader.js, no code change will be needed. But make sure the directory of the Node process is the application root directory where node app.js is being issued.