Home » JavaScript » Node.js » How NodeJs Processes Modules?

About Himansh Jain

Himansh Jain
Works as the technical leader of an ADF Development team, An active member of OTN Jdev/ADF Forum of Oracle Community. He has written more than 200 article about Oracle ADF and JDeveloper. Many of his articles got published in WebLogic community newsletter and Oracle ACE newsletter from time to time. Awarded with Oracle ACE title (♠️) in the year 2015 for his contribution in Oracle Technology Network.

How NodeJs Processes Modules?

We all know that to execute a node application, we have to type the command. In this post, we’ll see how NodeJS processes Modules.

whereapp.jsis the entry point to our application.

But seldom do we wonder about what’s happening when we type this command. How does the node magically come to take our js file and executes it? This is exactly what we’ll be discussing in this article.

In another article, I described how nodejs passes the js file to the V8 engine and converts it into processor comprehensible language. There’s a catch here. Node doesn’t send the content of entry module(app.js) directly to V8. First, it processes this code and augments it by wrapping it inside a wrapper function as shown below.

(function (exports, require, module, __filename, __dirname) {
    // content of app.js
});

After wrapping the code, Node passes it through V8 engine for compilation into m/c code. So how does node accomplish that? What are the steps involved to achieve this? Let’s talk about this in detail.

It starts with runMain function which is bootstrap main module (analogous to C++ main() function) for node (this is not the first function to execute actually. Call stack goes like listOnTimeout -> tryOnTimeout -> ontimeOut -> Module.runMain ….). When we type node app.js, the node environment executes this main module.

...
...
Module._load(process.argv[1], null, true);
...
...

Hereprocess.argv[1]corresponds to the string"app.js".  So basically node is being instructed to_loadthe fileapp.js.

In the Module._load function, a new Module object is created and then this module is passed totryModuleLoadfunction for loading.

var module = new Module(filename, parent);
...
tryModuleLoad(module, filename);

tryModuleLoad loads the module.

...
module.load(filename)
...

Modulefunction’s load performs a check on the extension. If no extension is specified, it takes default extension to be .js

...
var extension = findLongestRegisteredExtension(filename);
Module._extensions[extension](this, filename);
...

Since our file extension is.js,Module._extension['.js']will be called.

var content = fs.readFileSync(filename, 'utf8');
module._compile(stripBOM(content), filename);

Following things happens in this function.

// Run the file contents in the correct scope or sandbox. Expose
// the correct helper variables (require, module, exports) to the file.
// Returns exception, if any.

Each module has exports, require, module, __filename, __dirnameand thisattached to it. this comes as per EcmaScript standard. This is where these properties are declared for this module.

var dirname = path.dirname(filename);
var require = makeRequireFunction(this);
var result;
var exports = this.exports;
var thisValue = exports;
var module = this;

And then there is something about wrapping the code,

const wrapper = Module.wrap(content);

We set to find out what Module.wrap does, which is nothing but

let wrap = function(script) {
  return Module.wrapper[0] + script + Module.wrapper[1];
};
const wrapper = [
  '(function (exports, require, module, __filename, __dirname) { ',
  '\n});'
];

This is how our programs have access to the magic variables;exports,;require,module,__filename, and__dirname

Now we can see how we’ve got our wrapped function we talked about initially.

Then this wrapped function is compiled and executed here withrunInThisContextdefined in vm.jsmodule

compiledWrapper = vm.runInThisContext(wrapper, {
      filename,
      lineOffset: 0,
      displayErrors: true,
      importModuleDynamically: experimentalModules ? async (specifier) => {
        if (asyncESM === undefined) lazyLoadESM();
        const loader = await asyncESM.loaderPromise;
        return loader.import(specifier, normalizeReferrerURL(filename));
      } : undefined,
});

Then finally the module’s compiled wrapped function object is invoked like this, with values populated for exports, require, module, __filename and __dirname

result = compiledWrapper.call(thisValue, exports, require, module, filename, dirname);

This value is then finally returned

return result;

I hope this would have provided you with a deep understanding of the node and the processes that happen under the loop.

Keep learning!

Published on Web Code Geeks with permission by Himansh Jain, partner at our WCG program. See the original article here: How NodeJs Processes Modules?

Opinions expressed by Web Code Geeks contributors are their own.

(0 rating, 0 votes)
You need to be a registered member to rate this.
Start the discussion Views Tweet it!
Do you want to know how to develop your skillset to become a Web Rockstar?
Subscribe to our newsletter to start Rocking right now!
To get you started we give you our best selling eBooks for FREE!
1. Building web apps with Node.js
2. HTML5 Programming Cookbook
3. CSS Programming Cookbook
4. AngularJS Programming Cookbook
5. jQuery Programming Cookbook
6. Bootstrap Programming Cookbook
and many more ....
I agree to the Terms and Privacy Policy

Leave a Reply

avatar

This site uses Akismet to reduce spam. Learn how your comment data is processed.

  Subscribe  
Notify of