Skip to content

Commit fd37582

Browse files
committed
Merge branch 'release/0.1.0'
2 parents 86f4052 + f0858cb commit fd37582

8 files changed

Lines changed: 238 additions & 38 deletions

File tree

.travis.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
language: node_js
2+
node_js:
3+
- "0.11"
4+
- "0.10"

README.md

Lines changed: 50 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,86 @@
11
# App Root Path Module
22

3+
[![Build Status](https://travis-ci.org/inxilpro/node-app-root-path.svg)](https://travis-ci.org/inxilpro/node-app-root-path)
4+
35
This simple module helps you access your application's root path from anywhere in the application without resorting to `require("../../path")`.
46

57
## Installation
68

79
``` bash
8-
$ npm install app-root-path --save
10+
$ npm install app-root-path --save
911
```
1012

1113
## Usage
1214

1315
To simply access the app's root path:
1416

1517
``` js
16-
var appRoot = require('app-root-path'),
17-
myModule = require(appRoot + '/lib/my-module.js');
18+
var appRoot = require('app-root-path');
19+
var myModule = require(appRoot + '/lib/my-module.js');
1820
```
1921

2022
A helper function is also provided:
2123

2224
``` js
23-
var appRequire = require('app-root-path').require(require),
24-
myModule = appRequire('/lib/my-module.js');
25+
var reqlib = require('app-root-path').require;
26+
var myModule = reqlib('/lib/my-module.js');
2527
```
2628

27-
This works by passing the current module's `require` method (each module has its *own* `require` method) to the `app-root-path` module, which then returns a wrapper for that method that prepends the application's root path to whatever path is passed to it.
29+
It's a little hacky, but you can also put this method on your application's `global` object:
30+
31+
``` js
32+
// In app.js
33+
global.reqlib = require('app-root-path').require;
34+
35+
// In lib/module/component/subcomponent.js
36+
var myModule = reqlib('/lib/my-module.js');
37+
```
2838

2939
Finally, you can also just resolve a module path:
3040

3141
``` js
32-
var myModulePath = require('app-root-path').resolve('/lib/my-module.js');
42+
var myModulePath = require('app-root-path').resolve('/lib/my-module.js');
3343
```
3444

45+
You can also explicitly set the path, using the environmental variable `APP_ROOT_PATH` or by calling `require('app-root-path').setPath('/my/app/is/here')`
46+
3547
## How It Works
3648

37-
This module works on the assumption that your application's root path is the parent of the `node_modules` directory. Here's almost all the module's logic:
49+
This module uses two different methods to determine the app's root path, depending on the circumstances.
50+
51+
### Method One (preferred)
52+
53+
If the module is located inside your project's directory, somewhere within the `node_modules` directory (whether directly, or inside a submodule), we just do:
54+
55+
``` js
56+
path.resolve(__dirname).split('/node_modules')[0];
57+
```
58+
59+
This will take a path like `/var/www/node_modules/submodule/node_modules/app-root-path` and return `/var/www`. In 99% of cases, this is just what you need.
60+
61+
### Method Two (for edge cases)
62+
63+
The node module loader will also look in a few other places for modules (for example, ones that you install globally with `npm install -g`). Theses can be in one of:
64+
65+
- `$HOME/.node_modules`
66+
- `$HOME/.node_libraries`
67+
- `$PREFIX/lib/node`
68+
69+
Or, anywhere in the `NODE_PATH` environmental variable ([see documentation](http://nodejs.org/api/modules.html#modules_loading_from_the_global_folders)).
70+
71+
In these cases, we fall back to an alternate trick:
3872

3973
``` js
40-
var appRootPath = path.resolve(__dirname, '..', '..');
74+
path.dirname(require.main.filename);
4175
```
4276

43-
So, given this directory structure:
77+
When a file is run directly from Node, `require.main` is set to its `module`. `module.filename` refers to the filename of that module, so by fetching the directory name for that file, we at least get the directory of the file that was called directly. In some cases (process managers and test suites, for example) this doesn't actually give the correct directory, though, so this method is only used as a fallback.
78+
79+
## Change Log
4480

45-
my-app <-- ..
46-
node_modules <-- ..
47-
app-root-path <-- __dirname
48-
index.js
49-
lib
50-
my-module.js
51-
index.js
81+
### 0.1.0
82+
- Completely rewrote the path resolution method to account for most possible scenarios. This shouldn't cause and backwards compatibility issues, but always test your code.
83+
- Removed the need to pass a modules's `require()` method to the `appRootPath.require()` function. Which it's true that each module has its own `require()` method, in practice it doesn't matter, and it's **much** simpler this way.
84+
- Added tests
5285

53-
This may not work in every case--particularly if you try to use this in a module that is then used by other modules--but it should work 99% of the time when you're using it within the main application.
5486

index.js

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,4 @@
1-
var path = require('path'),
2-
appRootPath = path.resolve(__dirname, '..', '..');
1+
'use strict';
32

4-
exports.resolve = function(pathToModule) {
5-
return path.join(appRootPath, pathToModule);
6-
};
7-
8-
exports.require = function(moduleReqire) {
9-
return function(pathToModule) {
10-
return moduleReqire(exports.resolve(pathToModule));
11-
}
12-
};
13-
14-
exports.toString = function() {
15-
return appRootPath;
16-
};
17-
18-
exports.path = appRootPath;
3+
var lib = require('./lib/app-root-path.js');
4+
module.exports = lib(__dirname);

lib/app-root-path.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
'use strict';
2+
3+
module.exports = function(dirname) {
4+
var path = require('path');
5+
var resolve = require('./resolve.js');
6+
var appRootPath = resolve(dirname);
7+
8+
var publicInterface = {
9+
resolve: function(pathToModule) {
10+
return path.join(appRootPath, pathToModule);
11+
},
12+
13+
require: function(pathToModule) {
14+
// Backwards compatibility check
15+
if ('function' === typeof pathToModule) {
16+
console.warn('Just use appRootPath.require() -- no need to pass in your ' +
17+
'modules\'s require() function any more.');
18+
return function(pathToModule) {
19+
return publicInterface.require(pathToModule);
20+
}
21+
}
22+
23+
return require(publicInterface.resolve(pathToModule));
24+
},
25+
26+
toString: function() {
27+
return appRootPath;
28+
},
29+
30+
setPath: function(explicitlySetPath) {
31+
appRootPath = path.resolve(explicitlySetPath);
32+
},
33+
34+
path: appRootPath
35+
};
36+
37+
return publicInterface;
38+
};

lib/resolve.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
'use strict';
2+
3+
module.exports = function resolve(dirname) {
4+
var path = require('path');
5+
6+
// Check for environmental variable
7+
if (process.env.APP_ROOT_PATH) {
8+
return path.resolve(process.env.APP_ROOT_PATH);
9+
}
10+
11+
var globalPaths = require('module').globalPaths;
12+
var resolved = path.resolve(dirname);
13+
var alternateMethod = false;
14+
var appRootPath = null;
15+
16+
// Make sure that we're not loaded from a global include path
17+
// Eg. $HOME/.node_modules
18+
// $HOME/.node_libraries
19+
// $PREFIX/lib/node
20+
globalPaths.forEach(function(path) {
21+
if (!alternateMethod && 0 === resolved.indexOf(path)) {
22+
alternateMethod = true;
23+
}
24+
});
25+
26+
// If the app-root-path library isn't loaded globally,
27+
// and node_modules exists in the path, just split __dirname
28+
if (!alternateMethod && -1 !== resolved.indexOf('/node_modules')) {
29+
var parts = resolved.split('/node_modules');
30+
if (parts.length) {
31+
appRootPath = parts[0];
32+
parts = null;
33+
}
34+
}
35+
36+
// If the above didn't work, or this module is loaded globally, then
37+
// resort to require.main.filename (See http://nodejs.org/api/modules.html)
38+
if (alternateMethod || null == appRootPath) {
39+
appRootPath = path.dirname(require.main.filename);
40+
}
41+
42+
// Return
43+
return appRootPath;
44+
};

package.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
22
"name": "app-root-path",
3-
"version": "0.0.1",
3+
"version": "0.1.0",
44
"description": "Determine an app's root path from anywhere inside the app",
55
"main": "index.js",
66
"scripts": {
7-
"test": "echo \"Error: no test specified\" && exit 1"
7+
"test": "node node_modules/mocha/bin/mocha -R spec"
88
},
99
"repository": {
1010
"type": "git",
@@ -19,5 +19,8 @@
1919
"bugs": {
2020
"url": "https://github.com/inxilpro/node-app-root-path/issues"
2121
},
22-
"homepage": "https://github.com/inxilpro/node-app-root-path"
22+
"homepage": "https://github.com/inxilpro/node-app-root-path",
23+
"devDependencies": {
24+
"mocha": "^1.21.4"
25+
}
2326
}

test/index.js

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
'use strict';
2+
3+
var path = require('path');
4+
var assert = require('assert');
5+
6+
describe('The path resolution method', function() {
7+
var resolve = require('../lib/resolve.js');
8+
9+
// Check global paths
10+
it('should use require.main.filename if the path is in the globalPaths array', function() {
11+
var expected = path.dirname(require.main.filename);
12+
require('module').globalPaths.forEach(function(globalPath) {
13+
var testPath = globalPath + path.sep + 'node-app-root-path';
14+
assert.equal(resolve(testPath), expected);
15+
});
16+
});
17+
18+
// Check some standard path layouts
19+
it('should use String.split() in common cases', function() {
20+
var cases = [
21+
'/var/www/node_modules/node-app-root-path',
22+
'/var/www/node_modules/somemodule/node_modules/node-app-root-path',
23+
'/var/www/node_modules/somemodule/node_modules/someothermodules/node_modules/node-app-root-path',
24+
];
25+
var expected = '/var/www';
26+
cases.forEach(function(testPath) {
27+
assert.equal(resolve(testPath), expected);
28+
});
29+
});
30+
31+
// Check root path
32+
it('should still use String.split() in the root directory', function() {
33+
assert.equal(resolve('/node_modules'), '');
34+
});
35+
36+
// Check unexpected path
37+
it('should use require.main.filename on unexpected input', function() {
38+
var cases = [
39+
'just-some-jibberish',
40+
'/var/www/libs/node-app-root-path'
41+
];
42+
var expected = path.dirname(require.main.filename);
43+
cases.forEach(function(testPath) {
44+
assert.equal(resolve(testPath), expected);
45+
});
46+
});
47+
48+
// Check when setting via environmental variable
49+
it('should respect the APP_ROOT_PATH environmental variable', function() {
50+
var expected = '/some/arbirary/path';
51+
var originalPath = process.env.APP_ROOT_PATH;
52+
process.env.APP_ROOT_PATH = expected;
53+
assert.equal(resolve('/somewhere/else'), expected);
54+
process.env.APP_ROOT_PATH = originalPath;
55+
});
56+
});
57+
58+
describe('The public interface', function() {
59+
var lib = require('../lib/app-root-path.js');
60+
var root = path.resolve(__dirname);
61+
var pub = lib(root + '/node_modules/app-root-path');
62+
63+
it('should expose a resolve() method that resolves a relative path to the root path', function() {
64+
assert(pub.resolve);
65+
assert.equal(pub.resolve('subdir/filename.js'), root + '/subdir/filename.js');
66+
});
67+
68+
it('should expose a require() method that properly loads a module relative to root', function() {
69+
assert(pub.require);
70+
var testlib = pub.require('lib/testlib.js');
71+
assert.equal(testlib, 'hello world');
72+
});
73+
74+
it('should implement toString()', function() {
75+
assert(pub.toString);
76+
assert.equal(pub + '', root);
77+
assert.equal(pub.toString(), root);
78+
});
79+
80+
it('should allow explicitly setting the root path with setPath()', function() {
81+
assert(pub.setPath);
82+
var originalPath = pub.toString();
83+
pub.setPath('/path/to');
84+
assert.equal(pub.resolve('somewhere'), '/path/to/somewhere');
85+
pub.setPath(originalPath);
86+
});
87+
88+
it('should expose the app root path as a .path property', function() {
89+
assert(pub.path);
90+
assert.equal(pub.path, pub.toString());
91+
});
92+
});

test/lib/testlib.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = 'hello world';

0 commit comments

Comments
 (0)