Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
238 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
language: node_js | ||
node_js: | ||
- "0.11" | ||
- "0.10" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,54 +1,86 @@ | ||
# App Root Path Module | ||
|
||
[![Build Status](https://travis-ci.org/inxilpro/node-app-root-path.svg)](https://travis-ci.org/inxilpro/node-app-root-path) | ||
|
||
This simple module helps you access your application's root path from anywhere in the application without resorting to `require("../../path")`. | ||
|
||
## Installation | ||
|
||
``` bash | ||
$ npm install app-root-path --save | ||
$ npm install app-root-path --save | ||
``` | ||
|
||
## Usage | ||
|
||
To simply access the app's root path: | ||
|
||
``` js | ||
var appRoot = require('app-root-path'), | ||
myModule = require(appRoot + '/lib/my-module.js'); | ||
var appRoot = require('app-root-path'); | ||
var myModule = require(appRoot + '/lib/my-module.js'); | ||
``` | ||
|
||
A helper function is also provided: | ||
|
||
``` js | ||
var appRequire = require('app-root-path').require(require), | ||
myModule = appRequire('/lib/my-module.js'); | ||
var reqlib = require('app-root-path').require; | ||
var myModule = reqlib('/lib/my-module.js'); | ||
``` | ||
|
||
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. | ||
It's a little hacky, but you can also put this method on your application's `global` object: | ||
|
||
``` js | ||
// In app.js | ||
global.reqlib = require('app-root-path').require; | ||
|
||
// In lib/module/component/subcomponent.js | ||
var myModule = reqlib('/lib/my-module.js'); | ||
``` | ||
|
||
Finally, you can also just resolve a module path: | ||
|
||
``` js | ||
var myModulePath = require('app-root-path').resolve('/lib/my-module.js'); | ||
var myModulePath = require('app-root-path').resolve('/lib/my-module.js'); | ||
``` | ||
|
||
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')` | ||
|
||
## How It Works | ||
|
||
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: | ||
This module uses two different methods to determine the app's root path, depending on the circumstances. | ||
|
||
### Method One (preferred) | ||
|
||
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: | ||
|
||
``` js | ||
path.resolve(__dirname).split('/node_modules')[0]; | ||
``` | ||
|
||
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. | ||
|
||
### Method Two (for edge cases) | ||
|
||
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: | ||
|
||
- `$HOME/.node_modules` | ||
- `$HOME/.node_libraries` | ||
- `$PREFIX/lib/node` | ||
|
||
Or, anywhere in the `NODE_PATH` environmental variable ([see documentation](http://nodejs.org/api/modules.html#modules_loading_from_the_global_folders)). | ||
|
||
In these cases, we fall back to an alternate trick: | ||
|
||
``` js | ||
var appRootPath = path.resolve(__dirname, '..', '..'); | ||
path.dirname(require.main.filename); | ||
``` | ||
|
||
So, given this directory structure: | ||
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. | ||
|
||
## Change Log | ||
|
||
my-app <-- .. | ||
node_modules <-- .. | ||
app-root-path <-- __dirname | ||
index.js | ||
lib | ||
my-module.js | ||
index.js | ||
### 0.1.0 | ||
- 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. | ||
- 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. | ||
- Added tests | ||
|
||
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. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,4 @@ | ||
var path = require('path'), | ||
appRootPath = path.resolve(__dirname, '..', '..'); | ||
'use strict'; | ||
|
||
exports.resolve = function(pathToModule) { | ||
return path.join(appRootPath, pathToModule); | ||
}; | ||
|
||
exports.require = function(moduleReqire) { | ||
return function(pathToModule) { | ||
return moduleReqire(exports.resolve(pathToModule)); | ||
} | ||
}; | ||
|
||
exports.toString = function() { | ||
return appRootPath; | ||
}; | ||
|
||
exports.path = appRootPath; | ||
var lib = require('./lib/app-root-path.js'); | ||
module.exports = lib(__dirname); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
'use strict'; | ||
|
||
module.exports = function(dirname) { | ||
var path = require('path'); | ||
var resolve = require('./resolve.js'); | ||
var appRootPath = resolve(dirname); | ||
|
||
var publicInterface = { | ||
resolve: function(pathToModule) { | ||
return path.join(appRootPath, pathToModule); | ||
}, | ||
|
||
require: function(pathToModule) { | ||
// Backwards compatibility check | ||
if ('function' === typeof pathToModule) { | ||
console.warn('Just use appRootPath.require() -- no need to pass in your ' + | ||
'modules\'s require() function any more.'); | ||
return function(pathToModule) { | ||
return publicInterface.require(pathToModule); | ||
} | ||
} | ||
|
||
return require(publicInterface.resolve(pathToModule)); | ||
}, | ||
|
||
toString: function() { | ||
return appRootPath; | ||
}, | ||
|
||
setPath: function(explicitlySetPath) { | ||
appRootPath = path.resolve(explicitlySetPath); | ||
}, | ||
|
||
path: appRootPath | ||
}; | ||
|
||
return publicInterface; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
'use strict'; | ||
|
||
module.exports = function resolve(dirname) { | ||
var path = require('path'); | ||
|
||
// Check for environmental variable | ||
if (process.env.APP_ROOT_PATH) { | ||
return path.resolve(process.env.APP_ROOT_PATH); | ||
} | ||
|
||
var globalPaths = require('module').globalPaths; | ||
var resolved = path.resolve(dirname); | ||
var alternateMethod = false; | ||
var appRootPath = null; | ||
|
||
// Make sure that we're not loaded from a global include path | ||
// Eg. $HOME/.node_modules | ||
// $HOME/.node_libraries | ||
// $PREFIX/lib/node | ||
globalPaths.forEach(function(path) { | ||
if (!alternateMethod && 0 === resolved.indexOf(path)) { | ||
alternateMethod = true; | ||
} | ||
}); | ||
|
||
// If the app-root-path library isn't loaded globally, | ||
// and node_modules exists in the path, just split __dirname | ||
if (!alternateMethod && -1 !== resolved.indexOf('/node_modules')) { | ||
var parts = resolved.split('/node_modules'); | ||
if (parts.length) { | ||
appRootPath = parts[0]; | ||
parts = null; | ||
} | ||
} | ||
|
||
// If the above didn't work, or this module is loaded globally, then | ||
// resort to require.main.filename (See http://nodejs.org/api/modules.html) | ||
if (alternateMethod || null == appRootPath) { | ||
appRootPath = path.dirname(require.main.filename); | ||
} | ||
|
||
// Return | ||
return appRootPath; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
'use strict'; | ||
|
||
var path = require('path'); | ||
var assert = require('assert'); | ||
|
||
describe('The path resolution method', function() { | ||
var resolve = require('../lib/resolve.js'); | ||
|
||
// Check global paths | ||
it('should use require.main.filename if the path is in the globalPaths array', function() { | ||
var expected = path.dirname(require.main.filename); | ||
require('module').globalPaths.forEach(function(globalPath) { | ||
var testPath = globalPath + path.sep + 'node-app-root-path'; | ||
assert.equal(resolve(testPath), expected); | ||
}); | ||
}); | ||
|
||
// Check some standard path layouts | ||
it('should use String.split() in common cases', function() { | ||
var cases = [ | ||
'/var/www/node_modules/node-app-root-path', | ||
'/var/www/node_modules/somemodule/node_modules/node-app-root-path', | ||
'/var/www/node_modules/somemodule/node_modules/someothermodules/node_modules/node-app-root-path', | ||
]; | ||
var expected = '/var/www'; | ||
cases.forEach(function(testPath) { | ||
assert.equal(resolve(testPath), expected); | ||
}); | ||
}); | ||
|
||
// Check root path | ||
it('should still use String.split() in the root directory', function() { | ||
assert.equal(resolve('/node_modules'), ''); | ||
}); | ||
|
||
// Check unexpected path | ||
it('should use require.main.filename on unexpected input', function() { | ||
var cases = [ | ||
'just-some-jibberish', | ||
'/var/www/libs/node-app-root-path' | ||
]; | ||
var expected = path.dirname(require.main.filename); | ||
cases.forEach(function(testPath) { | ||
assert.equal(resolve(testPath), expected); | ||
}); | ||
}); | ||
|
||
// Check when setting via environmental variable | ||
it('should respect the APP_ROOT_PATH environmental variable', function() { | ||
var expected = '/some/arbirary/path'; | ||
var originalPath = process.env.APP_ROOT_PATH; | ||
process.env.APP_ROOT_PATH = expected; | ||
assert.equal(resolve('/somewhere/else'), expected); | ||
process.env.APP_ROOT_PATH = originalPath; | ||
}); | ||
}); | ||
|
||
describe('The public interface', function() { | ||
var lib = require('../lib/app-root-path.js'); | ||
var root = path.resolve(__dirname); | ||
var pub = lib(root + '/node_modules/app-root-path'); | ||
|
||
it('should expose a resolve() method that resolves a relative path to the root path', function() { | ||
assert(pub.resolve); | ||
assert.equal(pub.resolve('subdir/filename.js'), root + '/subdir/filename.js'); | ||
}); | ||
|
||
it('should expose a require() method that properly loads a module relative to root', function() { | ||
assert(pub.require); | ||
var testlib = pub.require('lib/testlib.js'); | ||
assert.equal(testlib, 'hello world'); | ||
}); | ||
|
||
it('should implement toString()', function() { | ||
assert(pub.toString); | ||
assert.equal(pub + '', root); | ||
assert.equal(pub.toString(), root); | ||
}); | ||
|
||
it('should allow explicitly setting the root path with setPath()', function() { | ||
assert(pub.setPath); | ||
var originalPath = pub.toString(); | ||
pub.setPath('/path/to'); | ||
assert.equal(pub.resolve('somewhere'), '/path/to/somewhere'); | ||
pub.setPath(originalPath); | ||
}); | ||
|
||
it('should expose the app root path as a .path property', function() { | ||
assert(pub.path); | ||
assert.equal(pub.path, pub.toString()); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module.exports = 'hello world'; |