Home

Modules

Modules.
Modules are js code in a file as a stand-alone unit of code - the file can be responsible for performing some fiunctionality.
Modules are all about making these bits of code exportable and importable.

There are a few different syntaxes between browsers and node: commonJS, ECMAScript, and node has node:modules syntax.

Load A Module with the require keyword

node_modules can be loaded into a file using the require keyword.
As an example, take a basic express api:

const express = require('express')
const expressObj = express()
const port = process.env.PORT || 3000

expressObj.get('/', helloWorldFunction)
expressObj.listen(port, listenFollowup)


/*
  functions
*/
function helloWorldFunction(req, res){
  return res.send('Hello World!')
}
function listenFollowup(){
  console.log(`Example expressObj listening on port ${port}`)
}

The first line uses the require keyword to import the "express" node module. The required module result is stored in the variable named express.

Create A Module That can be Imported

Create the module, here in add.js

// add.js
function addTwo(a,b){ return a + b}
module.exports = { addTwo }

In node, there is a module object that has a handful of methods and values attached to it. Here, the exports gets assigned an object {addTwo} (the shorthand key/value assignment), which is the addTwo function created immediately prior. This assignment is what allows the addTwo function to be exported from the file.

Use the module, here in index.js:

const myModule = require('./addTwo');
console.log(myModule.addTwo(3,8))

A few things are happening here:

  • creates a variable called myModule. This is an interesting implementation detail, as the myModule variable text, itself, is irrelevant in implementation of the module being imported.
  • the myModule is assigned the result of the require() statement
  • the require() statement, here, refers to a relative path, ./addTwo. This is a detail that is different from importing a node_module, which is referred to as an absolute name (like require('express'))
  • the addTwo function, which is bound as a key on the exported object from add.js, is called with myModule.addTwo(3,8) inside a console.log statement, which prints the result to the console

Two Ways To Run A Module With the Node CLI

Run A Program As A Program

With the above two files sitting next to each other in a directory, this will run the index.js file:

// . is a shorthand for index.js
node .

// or
node index.js

Here, node is treating the index.js as an entrypoint and maybe even more specifically like a program.

This may seem obvious! Node has verbose docs on its module system with many details that relate to how node is handling its work for running an index.js file like this.

Perhaps just a subtle differentiator between how the index.js and the addTwo are being used is that index.js is being used with the node keyword from a terminal, and addTwo is being used with the keyword and function require. That require keyword can be used to run the index.js file, too, treating the whole program as a module (with a few more lines of code)!!

Run A Program As A Module

One key detail to make a program into a module is to Access the main module.
The addTwo.js can be setup to "figure out" if it is being run with the node command or as a module.

if(require.main === module){
  // this is being run with "node ."
}else{
  // this is being run as a module
}

Why might this differentiator between the program's run method matter? Here, the addTwo function expects arguments & the cli version, node addTwo, will not accepts args && will only work with node addTwo.js.

Let's update the file to do two things:

  • figure out if it is being run directly from node
  • optionally take 2 cli arguments instead of 2 function params, so that when running from node it can log a result with something like node addTwo.js 3 11 where it adds 3 and 11 (or any othe 2 values)
// addTwo.js with the update!
function addTwo(a,b){ return a + b};

// this is being run with "node ."
if (require.main === module) {
  console.log(`addTwo Result:`, addTwo(process.argv[2], process.argv[3]));
}

module.exports = { addTwo };

Discover A Module's Path With require.resolve

Let's set up a simple node module to illustrate how require.resolve returns a string representing the location of a module:

# 1. build a repo & initialize it as an npm repo
mkdir requireResolvePractice
cd requireResolvePractice

# 2. initialize it as a node module + setup some files
npm init -y
npm i express
touch index.js
touch addTwo.js

Here, some code for index.js to illustrate the output of require.resolve

console.log({
  expressResolved: require.resolve('express'),
  selfResolved: require.resolve('.'),
  addTwoResolved: require.resolve('./addTwo'),
  fsResolved: require.resolve('fs')
})

Running node index.js will return something like....

{
  "expressResolved": "<path-to>/requireResolvePractice/node_modules/express/index.js",
  "selfResolved": "<path-to>/requireResolvePractice/index.js",
  "addTwoResolved": "<path-to>/requireResolvePractice/addTwo.js",
  "fsResolved": "fs"
}

Interesting details to note:

  • modules resolve to the node_module main file
  • core "absolute" dependencies, like fs return seemingly the same string
  • relative modules, like the . and ./addTwo, return absolute path strings
Tags: