How to resolve a relative path in node? - node.js

Came across this situation recently - I have an environment variable for a directory path like so:
var fooDir = process.env.FOO_DIR;
and I want to make sure this directory exists with a synchronous mkdir (at some point later):
fs.mkdirSync(fooDir, mode);
however if the user has supplied the environment variable via a realtive ~/ path node cannot resolve it
export FOO_DIR='~/foodir'
is there a way in node to resolve this without invoking a child process exec call to the actual shell? currently my solution is to do a replace myself like so:
fooDir = fooDir.replace(/^~\//, process.env.HOME + '/');
just curious if someone has a better solution.

You have it right: ~ is expanded by the shell, not the OS, just like *. None of the C file functions that node.js wraps handle ~ either, and if you want this you have to do the replacement yourself, just as you've shown. I've done this myself in C when supporting config files that allow relative file paths.
However, you should probably handle the case where HOME isn't defined; I believe this happens with non-interaction logins with bash, for example, and the user could always choose to unset it.

You can convert it to absolute path with path.join:
const path = require('path')
const absolutePath = path.join(process.cwd(), relativePath)

Check out Rekuire, it is a node module that solves the relative paths problem in NodeJs.

Related

Is it possible to use computer path in Node?

Normally in node one would use a path similar to this:
../js/hereIsMyJs.js
However in mac for example (pc is different)
the path can be ~/Desktop/Ohms/js/hereIsMyJs.js
Is there any module or way to use the computer path I just presented in node?
I'm using a module that requires the path to where the file should be placed.
It has to work dynamic so the optimal solution would be for me just to feed it the "computer" path.
const fileName = '~/Desktop/Ohms/somewhere/here.jpg'
QRCode.toFile(fileName, 'https://example.com')
To achieve what you want, it's normal to use node's native path.resolve https://nodejs.org/api/path.html#path_path_resolve_paths
// Start from current directory, and gives absolute path valid to the current OS.
console.log( path.resolve("../js/hereIsMyJs.js") ); // ie: C:\\projects\\js\\hereIsMyJs.js
// It also accepts multiple arguments, so you can feed it partial paths
path.resolve( "..", "js" ,"hereIsMyJs.js" ); // same result as above.
Other things worth to mention:
The tilde character ~ is short for the home directory in the *nix world. And works most places not windows.
In node you can use require('os').homedir() to get the home directory
There is also __dirname (gives absolute path of the directory containing the currently executing file) and process.cwd() which gives the directory from where you executed your file
And finally there is path.join() which is similar to resolve, but works for joining relative paths, and doesn't care about the current directory.

Nodejs difference between ./folderpath vs ../folderpath

I'm new in nodejs. I try to use some file in my filesystem. I would like to get them through some realtive path. Here is the structure:
-app
-resources
-tmp
-file1
-file2
-common
-common.js
-etc
So, I would like to reach the file1 and file2 in my resources/tmp folder from the common.js file in int common folder. I used two relative path methodic:
// First , wrong one
var tmpfolder = '../resources/tmp';
// Second, good one
var tmpfolder = './resources/tmp';
The file absolute path is something like this:
C:\Users\xy\Documents\work\nodejs\projects\project_wrapper_folder\project_folder\resources\tmp
If I log the first relative path I got this result:
C:\Users\xy\Documents\work\nodejs\projects\project_folder\resources\tmp
which is wrong, because it does not contains the wrapper folder.
But the second works fine.
Can somebody explain me this behaviour?
UPDATE
I see the meaning if '../', thanks your explanations!
I have tried #Lissy answer: "So baring that in mind ./ will resolve to the value of your project root..." that sounds great, but the result is not.
I have this logic in my app:
var tmpfolder = require('./otherFolder/orhetFile');
where otherFolder is the subfolder of my project_folder. So, when I used this here, I got an error called Cannot find module ...
But if I use this './' in fs module, here: /project_folder/otherFolder_2/otherFile_2 like:
var path = `./resources/tmp`;
fs.mkdirsSync(path);
It works!
these is strange for me. If './' means the current folder, than the example above sould not work (but it's works).
But if the './' means path of the project root, the example with require should work (but does not work).
Strange for me, is there some meaning of require or fs??
In Summary
./my-file means look at the root, whereas ../my-file, means come out of the current directory, and look in the parent folder.
Explanation of Relative and Absolute Paths in Node.js
The default document root is set with the NODE_PATH environment variable (however it would be terrible practice to modify that). So baring that in mind ./ will resolve to the value of your project root.
And so let tmpfolder = require('./resources/tmp'); would resolve to
C:\Users\......\project_wrapper_folder\project_folder\resources\tmp as you have seen
A relative path is denoted by not starting with ./, so for example let tmpfolder = require('my-scripts/some-script'); would look in the current directory for another folder called my-scripts.
Following on from that ../ is relative to the current directory, but goes out one (to it's parent) and looks from there. You can go out two directories by doing ../../my-dir and so on (this is not great practice though, as it can get hard to manage if you have ../../../../../../)
Better method
You can use the __dirname constant in Node to locate the directory name of the current module.
e.g.
fs.readFile(__dirname + '/../foo.bar');
Useful Links
I think you'll find this article on Better local require() paths for Node.js very useful.
Also, for a bit of further reading, here is the official Node.js documentation on paths
The .. means the previous dir. So you are getting out from project_wrapper_folder and then add to the script dir resources\tmp and that way the project_wrapper_folder is no longer in the relative path.
Lets assume you are inside a folder called app.
when you use ../resources/tmp, it will come out of the app folder and will search for tmp inside resources folder.
when you use ./resources/tmp, it will look within the app folder for resources folder and tmp inside it.
../ points to the parent directory while ./ points to current working directory.
A dot basically means "go one folder back in the path"
So:
'./' points at the folder you are in.(in your case the folder common/)
'../' points at the folder that contains the folder you are in. (in your case the folder app/)

Require for a relative path across modules

I have project A that has a config.json file in its root. The project has a dependency on external module B, calling B.setConfig('./config.json').
While inside B.setConfig(path), if I call fs.existsSync(path), it says Ok, file exists, but calling require(path) fails with Cannot find module "./config.json".
Is it possible to adjust the relative path while inside module B to make require work?
I would prefer not to call setConfig with the full path, as it makes things awkward.
I found it, eventually, that if we want to take a relative path from module A (call it remotePath), and use it within require in module B, then to get the full path inside module B we can use the following:
var path = require('path');
var fullPath = path.join(path.dirname(process.argv[1]), remotePath);
var moduleInsideA = require(localPath); // this now works
process.argv[1] gives us the module A start-up file, from which we take the directory path, and then join it with the remote relative path, which then gives us the full path.

Alternative to path module that always uses forward-slashes?

I've been (mis)using the native path module for manipulating URL paths (e.g. using path.relative() to work out the shortest relative link from one URL path to another). But this breaks on Windows, because path uses backslashes on Windows.
Is there an alternative to path that always uses forward-slashes, regardless of OS?
(There is a url module but it doesn't have equivalents for path.relative, path.dirname, etc.)
Answering my own question...
It looks like Browserify's shim for path works well for this.
var path = require('path-browserify');
Works exactly like the native path module running on Unix, regardless of your OS.
You could use the slash module for this:
var path = require('path');
var slash = require('slash');
var yourPath = slash(path.join('foo', 'bar'));
// foo/bar

Proper way to reference files relative to application root in Node.JS

I have a Node.JS application running on Linux at AWS EC2 that uses the fs module to read in HTML template files. Here is the current structure of the application:
/server.js
/templates/my-template.html
/services/template-reading-service.js
The HTML templates will always be in that location, however, the template-reading-service may move around to different locations (deeper subdirectories, etc.) From within the template-reading-service I use fs.readFileSync() to load the file, like so:
var templateContent = fs.readFileSync('./templates/my-template.html', 'utf8');
This throws the following error:
Error: ENOENT, no such file or directory './templates/my-template.html'
I'm assuming that is because the path './' is resolving to the '/services/' directory and not the application root. I've also tried changing the path to '../templates/my-template.html' and that worked, but it seems brittle because I imagine that is just resolving relative to 'up one directory'. If I move the template-reading-service to a deeper subdirectory, that path will break.
So, what is the proper way to reference files relative to the root of the application?
Try
var templateContent = fs.readFileSync(path.join(__dirname, '../templates') + '/my-template.html', 'utf8');
To get an absolute filesystem path to the directory where the node process is running, you can use process.cwd(). So assuming you are running /server.js as a process which implements /services/template-reading-service.js as a module, then you can do the following from /service/template-reading-service.js:
var appRoot = process.cwd(),
templateContent = fs.readFileSync(appRoot + '/templates/my-template.html', 'utf8');
If that doesn't work then you may be running /service/template-reading-service.js as a separate process, in which case you will need to have whatever launches that process pass it the path you want to treat as the primary application root. For example, if /server.js launches /service/template-reading-service.js as a separate process then /server.js should pass it its own process.cwd().
Accepted answer is wrong. Hardcoding path.join(__dirname, '../templates') will do exactly what is not wanted, making the service-XXX.js file break the main app if it moves to a sub location (as the given example services/template).
Using process.cwd() will return the root path for the file that initiated the running process (so, as example a /Myuser/myproject/server.js returns /Myuser/myproject/).
This is a duplicate of question Determine project root from a running node.js application.
On that question, the __dirname answer got the proper whipping it deserves.
Beware of the green mark, passers-by.
For ES modules, __dirname is not available, so read this answer and use:
import { resolve, dirname, join } from 'path'
import { fileURLToPath } from 'url'
import fs from 'fs'
const relativePath = a => join(dirname(fileURLToPath(import.meta.url)), a)
const content1 = fs.readFileSync(relativePath('./file.xyz'), 'utf8') // same dir
const content2 = fs.readFileSync(relativePath('../file.xyz'), 'utf8') // parent dir
We can use path madule to access the current path
const dirname = __dirname;
const path = require('path');
path.resolve(dirname, 'file.txt')
where
dirname - is give us present working directory path name
file.txt - file name required to access

Resources