Reputation: 8063
Is there a different way, other than process.cwd()
, to get the pathname of the current project's root-directory. Does Node implement something like ruby's property, Rails.root
,. I'm looking for something that is constant, and reliable.
Upvotes: 507
Views: 693408
Reputation: 1
I have a js script under the src folder (src's parent folder is the project root dir), then I write a utility function to get the root:
function getRootDir(filePath) {
let index = filePath.lastIndexOf("/src");
if (index === -1) {
return filePath;
}
return filePath.substring(0, index);
}
Then in the script I can easily get the root dir:
let root = getRootDir(__dirname);
// or if you are using the process.cwd()
let root = getRootDir(process.cwd());
Upvotes: 0
Reputation: 393
const __root = `${__dirname.substring(0, __dirname.lastIndexOf('projectName'))}projectName`
`
Upvotes: 0
Reputation: 5875
Try traversing upwards from __dirname
until you find a package.json
, and decide that's the app main root directory your current file belongs to.
According to Node docs
The package.json file is normally located at the root directory of a Node.js project.
const fs = require('fs')
const path = require('path')
function getAppRootDir () {
let currentDir = __dirname
while(!fs.existsSync(path.join(currentDir, 'package.json'))) {
currentDir = path.join(currentDir, '..')
}
return currentDir
}
Upvotes: 21
Reputation: 16233
A very simple and reliable solution would be to cd ..
recursively searching for package.json
, considering that this file is always on the project root directory.
const fs = require('fs')
const path = require('path')
function getAppRootDir () {
let currentDir = __dirname
while(!fs.existsSync(path.join(currentDir, 'package.json'))) {
currentDir = path.join(currentDir, '..')
}
return currentDir
}
Upvotes: 1
Reputation: 20572
There are many ways to approach this, each with their own pros and cons:
require.main.filename
From http://nodejs.org/api/modules.html:
When a file is run directly from Node,
require.main
is set to itsmodule
. That means that you can determine whether a file has been run directly by testingrequire.main === module
Because
module
provides afilename
property (normally equivalent to__filename
), the entry point of the current application can be obtained by checkingrequire.main.filename
.
So if you want the base directory for your app, you can do:
const { dirname } = require('path');
const appDir = dirname(require.main.filename);
This will work great most of the time, but if you're running your app with a launcher like pm2 or running mocha tests, this method will fail. This also won't work when using Node.js ES modules, where require.main
is not available.
module.paths
Node publishes all the module search paths to module.paths
. We can traverse these and pick the first one that resolves.
async function getAppPath() {
const { dirname } = require('path');
const { constants, promises: { access } } = require('fs');
for (let path of module.paths) {
try {
await access(path, constants.F_OK);
return dirname(path);
} catch (e) {
// Just move on to next path
}
}
}
This will sometimes work, but is not reliable when used in a package because it may return the directory that the package is installed in rather than the directory that the application is installed in.
Node has a global namespace object called global
— anything that you attach to this object will be available everywhere in your app. So, in your index.js
(or app.js
or whatever your main app
file is named), you can just define a global variable:
// index.js
var path = require('path');
global.appRoot = path.resolve(__dirname);
// lib/moduleA/component1.js
require(appRoot + '/lib/moduleB/component2.js');
Works consistently, but you have to rely on a global variable, which means that you can't easily reuse components/etc.
process.cwd()
This returns the current working directory. Not reliable at all, as it's entirely dependent on what directory the process was launched from:
$ cd /home/demo/
$ mkdir subdir
$ echo "console.log(process.cwd());" > subdir/demo.js
$ node subdir/demo.js
/home/demo
$ cd subdir
$ node demo.js
/home/demo/subdir
To address this issue, I've created a node module called app-root-path. Usage is simple:
const appRoot = require('app-root-path');
const myModule = require(`${ appRoot }/lib/my-module.js`);
The app-root-path module uses several techniques to determine the root path of the app, taking into account globally installed modules (for example, if your app is running in /var/www/
but the module is installed in ~/.nvm/v0.x.x/lib/node/
). It won't work 100% of the time, but it's going to work in most common scenarios.
Works without configuration in most circumstances. Also provides some nice additional convenience methods (see project page). The biggest con is that it won't work if:
node_modules
directory (for example, if you installed it globally)You can get around this by either setting a APP_ROOT_PATH
environmental variable, or by calling .setPath()
on the module, but in that case, you're probably better off using the global
method.
If you're looking for a way to determine the root path of the current app, one of the above solutions is likely to work best for you. If, on the other hand, you're trying to solve the problem of loading app modules reliably, I highly recommend looking into the NODE_PATH
environmental variable.
Node's Modules system looks for modules in a variety of locations. One of these locations is wherever process.env.NODE_PATH
points. If you set this environmental variable, then you can require
modules with the standard module loader without any other changes.
For example, if you set NODE_PATH
to /var/www/lib
, the the following would work just fine:
require('module2/component.js');
// ^ looks for /var/www/lib/module2/component.js
A great way to do this is using npm
:
{
"scripts": {
"start": "NODE_PATH=. node app.js"
}
}
Now you can start your app with npm start
and you're golden. I combine this with my enforce-node-path module, which prevents accidentally loading the app without NODE_PATH
set. For even more control over enforcing environmental variables, see checkenv.
One gotcha: NODE_PATH
must be set outside of the node app. You cannot do something like process.env.NODE_PATH = path.resolve(__dirname)
because the module loader caches the list of directories it will search before your app runs.
[added 4/6/16] Another really promising module that attempts to solve this problem is wavy.
Upvotes: 886
Reputation: 519
I've found this works consistently for me, even when the application is invoked from a sub-folder, as it can be with some test frameworks, like Mocha:
process.mainModule.paths[0].split('node_modules')[0].slice(0, -1);
Why it works:
At runtime node creates a registry of the full paths of all loaded files. The modules are loaded first, and thus at the top of this registry. By selecting the first element of the registry and returning the path before the 'node_modules' directory we are able to determine the root of the application.
It's just one line of code, but for simplicity's sake (my sake), I black boxed it into an NPM module:
https://www.npmjs.com/package/node-root.pddivine
Enjoy!
EDIT:
process.mainModule
is deprecated as of v14.0.0
Use require.main
instead:
require.main.paths[0].split('node_modules')[0].slice(0, -1);
Upvotes: 20
Reputation: 66
You can also use
git rev-parse --show-toplevel
Assuming you are working on a git repository
Upvotes: 0
Reputation: 6115
As simple as adding this line to your module in the root, usually it is app.js
or app.ts
.
global.__basedir = __dirname;
Then _basedir will be accessible to all your modules.
Note: For typescript implementation, follow the above step and then you will be able to use the root directory path using global.__basedir
Upvotes: 26
Reputation: 10345
Preamble
This is a very old question, but it seems to hit the nerve in 2020 as much as back in 2012. I've checked all the other answers and could not find the following technique mentioned (it has its own limitations, but the others are not applicable to every situation either):
Git + child process
If you are using Git as your version control system, the problem of determining the project root can be reduced to (which I would consider the proper root of the project - after all, you would want your VCS to have the fullest visibility scope possible):
retrieve repository root path
Since you have to run a CLI command to do that, we need to spawn a child process. Additionally, as project root is highly unlikely to change mid-runtime, we can use the synchronous version of the child_process
module at startup.
I found spawnSync()
to be the most suitable for the job. As for the actual command to run, git worktree
(with a --porcelain
option for ease of parsing) is all that is needed to retrieve the absolute path of the root.
In the sample at the end of the answer, I opted to return an array of paths because there might be multiple worktrees (although they are likely to have common paths) just to be sure. Note that as we utilize a CLI command, shell
option should be set to true
(security shouldn't be an issue as there is no untrusted input).
Approach comparison and fallbacks
Understanding that a situation where VCS can be inaccessible is possible, I've included a couple of fallbacks after analyzing docs and other answers. The proposed solutions boil down to (excluding third-party modules & packages):
Solution | Advantage | Main Problem |
---|---|---|
__filename |
points to module file | relative to module |
__dirname |
points to module dir | same as __filename |
node_modules tree walk |
nearly guaranteed root | complex tree walking if nested |
path.resolve(".") |
root if CWD is root | same as process.cwd() |
process.argv\[1\] |
same as __filename |
same as __filename |
process.env.INIT_CWD |
points to npm run dir |
requires npm && CLI launch |
process.env.PWD |
points to current dir | relative to (is the) launch dir |
process.cwd() |
same as env.PWD |
process.chdir(path) at runtime |
require.main.filename |
root if === module |
fails on require d modules |
From the comparison table above, the following approaches are the most universal:
require.main.filename
as an easy way to get the root if require.main === module
is metnode_modules
tree walk proposed recently uses another assumption:if the directory of the module has
node_modules
dir inside, it is likely to be the root
For the main app, it will get the app root and for a module — its project root.
Fallback 1. Tree walk
My implementation uses a more lax approach by stopping once a target directory is found as for a given module its root is the project root. One can chain the calls or extend it to make the search depth configurable:
/**
* @summary gets root by walking up node_modules
* @param {import("fs")} fs
* @param {import("path")} pt
*/
const getRootFromNodeModules = (fs, pt) =>
/**
* @param {string} [startPath]
* @returns {string[]}
*/
(startPath = __dirname) => {
//avoid loop if reached root path
if (startPath === pt.parse(startPath).root) {
return [startPath];
}
const isRoot = fs.existsSync(pt.join(startPath, "node_modules"));
if (isRoot) {
return [startPath];
}
return getRootFromNodeModules(fs, pt)(pt.dirname(startPath));
};
Fallback 2. Main module
The second implementation is trivial:
/**
* @summary gets app entry point if run directly
* @param {import("path")} pt
*/
const getAppEntryPoint = (pt) =>
/**
* @returns {string[]}
*/
() => {
const { main } = require;
const { filename } = main;
return main === module ?
[pt.parse(filename).dir] :
[];
};
Implementation
I would suggest using the tree walker as the preferred fallback because it is more versatile:
const { spawnSync } = require("child_process");
const pt = require('path');
const fs = require("fs");
/**
* @summary returns worktree root path(s)
* @param {function : string[] } [fallback]
* @returns {string[]}
*/
const getProjectRoot = (fallback) => {
const { error, stdout } = spawnSync(
`git worktree list --porcelain`,
{
encoding: "utf8",
shell: true
}
);
if (!stdout) {
console.warn(`Could not use GIT to find root:\n\n${error}`);
return fallback ? fallback() : [];
}
return stdout
.split("\n")
.map(line => {
const [key, value] = line.split(/\s+/) || [];
return key === "worktree" ? value : "";
})
.filter(Boolean);
};
Disadvantages
The most obvious one is having Git installed and initialized which might be undesirable/implausible (side note: having Git installed on production servers is not uncommon, nor is it unsafe). Can be mediated by fallbacks as described above.
Upvotes: 22
Reputation: 9846
In modern versions of npm, you can add an entry to exports
, to use as a shorthand. Note that if you want to be able to reference both the root itself and files within that root, you'll need both ./
and ./*
respectively:
package.json
:
{
"imports": {
"#root": "./",
"#root/*": "./*",
...
},
...
}
./index.js
:
import {namedExport} from '#root/file.js'
./file.js
:
export const namedExport = {
hi: "world",
};
Then:
$ node --experimental-specifier-resolution=node index.js
You could extend this further with a constants.js
file, where you may use one of the methods in the above answers, or input an absolute path, should you require the path itself
Upvotes: 0
Reputation: 13425
process.mainModule
is deprecated since v 14.0.0. When referring to the answer, please userequire.main
, the rest still holds.
process.mainModule.paths
.filter(p => !p.includes('node_modules'))
.shift()
Get all paths in main modules and filter out those with "node_modules",
then get the first of remaining path list. Unexpected behavior will not throw error, just an undefined
.
Works well for me, even when calling ie $ mocha
.
Upvotes: 3
Reputation: 5729
This will step down the directory tree until it contains a node_modules
directory, which usually indicates your project root:
const fs = require('fs')
const path = require('path')
function getProjectRoot(currentDir = __dirname.split(path.sep)) {
if (!currentDir.length) {
throw Error('Could not find project root.')
}
const nodeModulesPath = currentDir.concat(['node_modules']).join(path.sep)
if (fs.existsSync(nodeModulesPath) && !currentDir.includes('node_modules')) {
return currentDir.join(path.sep)
}
return this.getProjectRoot(currentDir.slice(0, -1))
}
It also makes sure that there is no node_modules
in the returned path, as that means that it is contained in a nested package install.
Upvotes: 1
Reputation: 662
All these "root dirs" mostly need to resolve some virtual path to a real pile path, so may be you should look at path.resolve
?
var path= require('path');
var filePath = path.resolve('our/virtual/path.ext');
Upvotes: 16
Reputation: 61
if you want to determine project root from a running node.js application you can simply just too.
process.mainModule.path
Upvotes: 2
Reputation: 23717
INIT_CWD
property on process.env
. This is what I'm currently working with in my project.const {INIT_CWD} = process.env; // process.env.INIT_CWD
const paths = require(`${INIT_CWD}/config/paths`);
Good Luck...
Upvotes: 10
Reputation: 6414
This will do:
path.join(...process.argv[1].split(/\/|\\/).slice(0, -1))
Upvotes: 0
Reputation: 1825
__dirname will give you the root directory, as long as you're in a file that's in the root directory.
// ProjectDirectory.js (this file is in the project's root directory because you are putting it there).
module.exports = {
root() {
return __dirname;
}
}
In some other file:
const ProjectDirectory = require('path/to/ProjectDirectory');
console.log(`Project root directory is ${ProjectDirectory.root}`);
Upvotes: -5
Reputation: 1335
Finding the root path of an electron app could get tricky. Because the root path is different for the main process and renderer under different conditions such as production, development and packaged conditions.
I have written a npm package electron-root-path to capture the root path of an electron app.
$ npm install electron-root-path
or
$ yarn add electron-root-path
// Import ES6 way
import { rootPath } from 'electron-root-path';
// Import ES2015 way
const rootPath = require('electron-root-path').rootPath;
// e.g:
// read a file in the root
const location = path.join(rootPath, 'package.json');
const pkgInfo = fs.readFileSync(location, { encoding: 'utf8' });
Upvotes: 0
Reputation: 8430
I know this one is already too late. But we can fetch root URL by two methods
1st method
var path = require('path');
path.dirname(require.main.filename);
2nd method
var path = require('path');
path.dirname(process.mainModule.filename);
Reference Link:- https://gist.github.com/geekiam/e2e3e0325abd9023d3a3
Upvotes: 4
Reputation: 264
Actually, i find the perhaps trivial solution also to most robust: you simply place the following file at the root directory of your project: root-path.js which has the following code:
import * as path from 'path'
const projectRootPath = path.resolve(__dirname)
export const rootPath = projectRootPath
Upvotes: 7
Reputation: 71
Just use:
path.resolve("./") ... output is your project root directory
Upvotes: -1
Reputation: 3689
Add this somewhere towards the start of your main app file (e.g. app.js):
global.__basedir = __dirname;
This sets a global variable that will always be equivalent to your app's base dir. Use it just like any other variable:
const yourModule = require(__basedir + '/path/to/module.js');
Simple...
Upvotes: 4
Reputation: 6610
Old question, I know, however no question mention to use progress.argv
. The argv array includes a full pathname and filename (with or without .js extension) that was used as parameter to be executed by node. Because this also can contain flags, you must filter this.
This is not an example you can directly use (because of using my own framework) but I think it gives you some idea how to do it. I also use a cache method to avoid that calling this function stress the system too much, especially when no extension is specified (and a file exist check is required), for example:
node myfile
or
node myfile.js
That's the reason I cache it, see also code below.
function getRootFilePath()
{
if( !isDefined( oData.SU_ROOT_FILE_PATH ) )
{
var sExt = false;
each( process.argv, function( i, v )
{
// Skip invalid and provided command line options
if( !!v && isValidString( v ) && v[0] !== '-' )
{
sExt = getFileExt( v );
if( ( sExt === 'js' ) || ( sExt === '' && fileExists( v+'.js' )) )
{
var a = uniformPath( v ).split("/");
// Chop off last string, filename
a[a.length-1]='';
// Cache it so we don't have to do it again.
oData.SU_ROOT_FILE_PATH=a.join("/");
// Found, skip loop
return true;
}
}
}, true ); // <-- true is: each in reverse order
}
return oData.SU_ROOT_FILE_PATH || '';
}
};
Upvotes: 0
Reputation: 2672
You can simply add the root directory path in the express app variable and get this path from the app. For this add app.set('rootDirectory', __dirname);
in your index.js or app.js file. And use req.app.get('rootDirectory')
for getting the root directory path in your code.
Upvotes: 0
Reputation: 4729
const users = require('../../../database/users'); // 👎 what you have
// OR
const users = require('$db/users'); // 👍 no matter how deep you are
const products = require('/database/products'); // 👍 alias or pathing from root directory
npm install sexy-require --save
Include require('sexy-require')
once on the top of your main application file.
require('sexy-require');
const routers = require('/routers');
const api = require('$api');
...
Optional step. Path configuration can be defined in .paths
file on root directory of your project.
$db = /server/database
$api-v1 = /server/api/legacy
$api-v2 = /server/api/v2
Upvotes: 0
Reputation: 5986
I use this.
For my module named mymodule
var BASE_DIR = __dirname.replace(/^(.*\/mymodule)(.*)$/, '$1')
Upvotes: 0
Reputation: 151
Try path._makeLong('some_filename_on_root.js');
example:
cons path = require('path');
console.log(path._makeLong('some_filename_on_root.js');
That will return full path from root of your node application (same position of package.json)
Upvotes: -1