The Path module is a useful tool to work with paths and directories. It can merge directories, parse them, get the current file's or directory's name, and even more. The way paths are constructed is different on POSIX and Windows. For example, POSIX uses '/' in paths, whereas Windows goes with '\'. The path module takes it into account and provides functions that help us stay consistent across different platforms. That means that our projects will work fine on any of these OS platforms.
Are you ready? Let's dive deep into it then!
Get directory and filename
Imagine we want to see what directory our file is located in. If we have a full path name, we can easily find the parent directory of our file. Let's import our path module by writing const path = requre('node:path'); and see the dirname function in action:
const path = require('node:path');
const fullpath = 'Users/El/node-course/be-cool.js';
const dirname = path.dirname(fullpath);
console.log(dirname); // Users/El/node-course
As you can see, we passed in the full path to the dirname function and got a path that is parent to the be-cool.js file.
Now let's find the tail of our path, that is whatever comes at the end of the path. It can be retrieved with the basename function. Here's an example:
const basename = path.basename(fullpath);
const basenameNoExt = path.basename(fullpath, '.js')
console.log(basename); // be-cool.js
console.log(basenameNoExt); // be-cool
The basename function takes a path as the first parameter and an extension as the second optional parameter. In our example above, we first logged the basename with the extension and then without it. So if you provide the second parameter, the program will omit the extension and only output the file name.
The third method we'd like to cover is extname. Can you guess what it does? As the name of the method suggests, it returns the extension of a path. If the path contains several full stops (.), we'll get the last occurrence of it. Take a look:
path.extname('learn-node/path/extname.html') // returns .html
path.extname('learn-node/path/extname.'); // returns .
path.extname('learn-node/path/extname.md.js'); // returns .js
path.extname('learn-node/path/extname'); // returns ''
Pretty straightforward! We've seen dirname, basename, and extname so far, time to move on to joining paths.
Join paths
join and resolve are the two most common methods of the path module. Developers use them all the time, especially when creating directories with the fs module. The methods look similar, though return different results. In short, join just concatenates given segments of a path into a single string, whereas resolve merges path segments into an absolute path. Let's compare the two with different examples.
- Join simple fragments:
path.join('one', 'two', 'three'); // returns one\two\three
path.resolve('one', 'two', 'three'); // returns C:\Users\El\node-course\one\two\three
If you console log the results, you should see that the first method put three parts of the path into one string, whilst the second one constructed an absolute path out of the given parts. You may ask, why not use string concatenation like 'one' + '\two' + '\three' instead of join? Good question! As we've discussed at the beginning, POSIX and non-POSIX systems use different path separators (/ vs \). Without the path.join() method, we'd have to write two different codes to support both systems. Thanks to the path module, it handles it itself.
2. Handle fragments with '/':
path.join('/one', '/two', 'three'); // returns \one\two\three
path.resolve('/one', '/two', 'three'); // returns C:\two\three
Here, we added forward slashes before the first two segments. The first method works as expected but take a look at the resolve function: it ignored the first argument. It goes from right to left and once it encounters a forward slash, it starts to treat it as the root path and ignores everything that comes after it (in our case '/one'). Try to play around with resolve a bit to better understand its caveats.
__dirname and __filename. Parse and format
Parse and format are two opposite functions. The former parses a string into an object, and the latter builds up a string from a given object. Here's the parse function:
path.parse('/courses/node/stay-cool.js');
After logging this into the console, you will get the following result: the root, the directory, the base and the name, which are identical, and the extension.
The opposite behavior can be achieved with the format method. It takes an object as a parameter with the following properties: dir, root, base, name, ext. Basically, the same things that we got in our previous example. These properties have priorities, so if you specify all of them, some of them might be ignored if they are of lower priority. You may read more about it in Node.js Docs.
Here are a couple of examples on Windows, with explanations given as comments:
// root is ignored if dir is povided
// returns \stay-calm\and\learn-nodejs\path-module.txt
path.format({
root: 'C:\\have-agood-day',
dir: '\\stay-calm\\and\\learn-nodejs',
base: 'path-module.txt',
});
// ext is ignored if base is provided
// returns C:\docs\my-doc.html
path.format({
root: 'C:\\docs\\',
base: 'my-doc.html',
ext: '.css',
});
Note that this will work differently on POSIX. Please, take a look at Node.js Docs for more info.
Conclusion
We've covered the main methods of the path module, including joining and parsing paths, formatting path objects, and getting info about a given directory or file. This built-in module allows us to work with paths in an OS-independent way, meaning it's suitable for both POSIX and non-POSIX platforms.