__filename, __dirname, and others are not really globals.

andersonjoseph

Anderson. J

Posted on October 20, 2022

__filename, __dirname, and others are not really globals.

A global variable is a variable that is declared in the global scope, in other words, a variable that is visible from all other scopes.

In NodeJS we have multiple variables and global objects, some of them are only specific to Node (Buffer, Event, process) while others are part of the Javascript language (JSON, Proxy, console)

__dirname is a variable that tells you the path of the folder where the current javascript file lives. So __dirname has a different value depending on the file path where this variable it's used.

__filename is a variable that holds the filename of the code that it's running.

You can use both of them anywhere in your code, so they must be globals. Right?

Right?

Right meme


It turns out that __dirname, __filename, exports, module and even require() are not globals. You can use all these objects anywhere in your code, but they only exist in the module scope. Node does not execute our module code directly. Instead of that, code lives within a closed scope, and this scope is inside the module wrapper.

Inside the module wrapper

Every module is wrapped in a function that looks like this:



(function(exports, require, module, __filename, __dirname) {

  // Module code actually lives here

});


Enter fullscreen mode Exit fullscreen mode

You can take a look at the module wrapper in the Node source code But you can also check it in your terminal by running the following code:



const Module = require('module');

console.log(Module.wrap());


Enter fullscreen mode Exit fullscreen mode

The output should be something like this:



> node main.js

(function(exports, require, module, __filename, __dirname) { undefined
});


Enter fullscreen mode Exit fullscreen mode

But what is that undefined after the curly brackets? Well, that is where your actual code lives. The wrapper function takes one argument, and it's the module code.

In Node source code, wrap function looks like this:



Module.wrapper = [
  '(function (exports, require, module, __filename, __dirname) { ',
  '\n});',
];

let wrap = function(script) {
  return Module.wrapper[0] + script + Module.wrapper[1];
};


Enter fullscreen mode Exit fullscreen mode

So that means that if you call wrap and pass some code as argument:



const Module = require("module");

console.log(Module.wrap('console.log("hello from the module wrapper")'));


Enter fullscreen mode Exit fullscreen mode

You will get:



> node main.js

(function (exports, require, module, __filename, __dirname) {    
  console.log("hello from the module wrapper")
});


Enter fullscreen mode Exit fullscreen mode

Now that we know this, we can modify the module wrapper (just for fun, of course)

We're going to need two files. The first one will be module.js



function sum(a, b) {
  return a + b;
}

console.log(sum(1, 2));


Enter fullscreen mode Exit fullscreen mode

It'll be just a simple two-sum.

The second file we'll need, it's the one where we'll modify the module wrapper. This file will be calledmain.js



const Module = require("module");

Module.wrap = function(script) {
  console.log('hello from the module wrapper, today we will be executing the following code:');
  console.log(script);

  return Module.wrapper[0] + script + Module.wrapper[1];
}

require('./module');


Enter fullscreen mode Exit fullscreen mode

Here we're replacing wrap() function with our own. We know that this function takes the module code as argument, and should return the code wrapped.

Finally, we call require('./module') to import our previously created file.

Now we just need to run node main.js and the output should be:



> node main.js

hello from the module wrapper, today we will be executing the following code:
function sum(a, b) {
  return a + b;
}

console.log(sum(1,2));

3


Enter fullscreen mode Exit fullscreen mode

Very cool, very cool.

ok but why


Why do we need a wrapper?

Node docs says it is used to keep top-level variables scoped to the current module, in that way those variables are not propagating to the global object. It is also used to provide global-looking variables like __filename and __dirname, if you think about it, those variables are related to each module, so it makes sense they not being truly globals.

That's it for today! I just wanted to share something interesting I discovered in the last few days. I'm starting to dig more and more into NodeJS source code, so probably I'll be sharing more stuff here or on Twitter.

Please, share the post with your friends and let me know in the comments if you know about another interesting piece of code from NodeJS source.

πŸ’– πŸ’ͺ πŸ™… 🚩
andersonjoseph
Anderson. J

Posted on October 20, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related