Overview

Last time, I wrote a research note about Green-san's investigation into CommonJS exports.

In that research note, the focus was mainly on how the module, a module-scoped variable, exports values from within the module to the outside. But now that we've gotten this far, I can't help but take a peek inside require, which is the importing side of things.

And guess what, I stumbled upon something interesting which ended up being a discussion about Python. This guy always ends up talking about Python.

 

require Experimental Notes

Let's take a quick look inside.

node -v
# v20.3.0

node --eval 'console.info({ require })'
# {
#   require: [Function: require] {
#     resolve: [Function: resolve] { paths: [Function: paths] },
#     main: undefined,
#     extensions: [Object: null prototype] {
#       '.js': [Function (anonymous)],
#       '.json': [Function (anonymous)],
#       '.node': [Function (anonymous)]
#     },
#     cache: [Object: null prototype] {}
#   }
# }

Huh? To be honest, I thought require would be something akin to a function, but it seems there's a lot more going on. Apparently, require is not only a function but also an object.

 

Focusing on require.main Ends Up Being a Discussion About Python

The first thing I want to pay attention to is require.main. It shows undefined on the command line, but if you look at require inside a file, it contains the module of that file.

echo 'console.info({ require })' > baz.js
node baz.js
# {
#   require: [Function: require] {
#     resolve: [Function: resolve] { paths: [Function: paths] },
#     main: Module {
#       id: '.',
#       path: '/Users/user/Documents/GitHub/ts-lab/js-commonjs-export-lab',
#       exports: {},
#       filename: '/Users/user/Documents/GitHub/ts-lab/js-commonjs-export-lab/baz.js',
#       loaded: false,
#       children: [],
#       paths: [Array]
#     },
#     extensions: [Object: null prototype] {
#       '.js': [Function (anonymous)],
#       '.json': [Function (anonymous)],
#       '.node': [Function (anonymous)]
#     },
#     cache: [Object: null prototype] {
#       '/Users/user/Documents/GitHub/ts-lab/js-commonjs-export-lab/baz.js': [Module]
#     }
#   }
# }

Upon further investigation, it seems that it's not "the module of the file" that is contained, but the module of the first module that was launched with node. In other words, you can do the equivalent of Python's if __name__ == "__main__" in JavaScript too! Wha-what?!

echo 'if (require.main === module) { console.info("I am main!"); }' > qux.js

# When launching qux.js, this if statement becomes true...
node qux.js
# I am main!

# But when calling qux.js from another file, this if statement becomes false.
node --eval 'require("./qux.js")'
# (Nothing is displayed)

 

Focusing on Being Both a Function and an Object Ends Up Being a Discussion About Python

Wait, so JavaScript can do something like being a function and an object at the same time?

node --eval 'const foo = (x, y) => x + y;
foo.bar = 1;
console.info(foo.bar);
console.info(foo(1, 2));
console.info({ foo });'
# 1
# 3
# { foo: [Function: foo] { bar: 1 } }

It can! Seriously? Good grief, this risky thing, I'm sure my beloved Python can't do such a...

def foo(x: int, y: int) -> int:
    return x + y

foo.bar = 1
print(foo.bar) # 1
print(foo(1, 2)) # 3
print(type(foo), vars(foo)) # <class 'function'> {'bar': 1}

So it can!!

Well, of course, from a type hint perspective, I'm doing something very messy, so if I check it with mypy, I'll get scolded. Like this↓

foo.bar = 1
# "Callable[[int, int], int]" has no attribute "bar"  [attr-defined]

If you want to write proper type hints, you have to do something like this↓

class Foo:
    def __init__(self):
        self.bar = 0

    def __call__(self, x: int, y: int) -> int:
        return x + y

foo = Foo()
foo.bar = 1

print(foo.bar)  # 1
print(foo(1, 2))  # 3
print(type(foo), vars(foo))  # <class '__main__.Foo'> {'bar': 1}

Hmm, that's good Python. It's clear that it's Callable and has an instance variable bar. It's good. Even though I was supposed to be investigating JavaScript (CommonJS), I can't help but think about Python.