Understanding export and import in CommonJS
Advocate for better developer's productivity and experience
Documenting my learnings and sharing it
Search for a command to run...
Advocate for better developer's productivity and experience
Documenting my learnings and sharing it
No comments yet. Be the first to comment.
Background Recently, I redesigned my team's pipeline running on a multi-module Maven monorepo using GitLab CI. It wasn't that the previous setup was broken, but my team faced a few persistent issues t
Background At work, there’s some test written where I wanted to query the database (repository) after mockMvc request has been made to verify the result. This is a very simple illustration of what it looks like. @SpringBootTest @AutoConfigureMockMvc ...

It's been a long time coming, but I finally came around to set up MongoDB with replicas for local development. TLDR; clone the repository, run docker compose up -d, and you are good to go! If you want to know more, continue to read below. Context Run...

Context I recently came across TaskFile and wanted to use it to automate some of my workflows, which underlying invokes PowerShell commands. However, I faced several errors while doing so, and this post is to reflect on it, and hopefully help myself ...

Problem I was always out of disk space in my C drive, and even though I clear my stuff often, I notice that my space is never reclaimed, instead, it kept going down. I used WizTree to view which file is taking up my disk space, and I noticed this No...

I'm pretty sure this is not the first time that I got confused by the various exports' syntax in CommonJS and definitely will not be the last. Hence, this article will hopefully be useful for my future self whenever I need a reference.
Consider a simple scenario where there you have 2 files; index.js and reference.js where index.js will import reference.js to use some object exported by reference.js.
// reference.js
const address = {
street: 'old middle road',
postal: 123456
}
exports.address = address;
// index.js
const address = require('./reference.js');
console.log(address);
The output would be
{ address: { street: 'old middle road', postal: 123456 } }
Trying to access the object value street by calling
console.log(address.street);
which result as
undefined
What happened? Essentially, the exported object actually nested another address object, hence, in order to access the value, I should run
console.log(address.address.street);
Which would have resulted in
old middle road
It's actually quite obvious when I had the imported address object printed out, but it just didn't register to me straightaway
Let's try to figure out what actually happens, and see how we can understand a little more what's happening
For each of the javascript file, we can think of it as being a standalone module which can be imported and exported.
Let's take a look at reference.js
const address = {
street: 'old middle road',
postal: 123456
}
console.log(module);
---
// output
Module {
id: 'C:\\Users\\Joseph\\Desktop\\scripts\\reference.js',
path: 'C:\\Users\\Joseph\\Desktop\\scripts',
exports: {},
filename: 'C:\\Users\\Joseph\\Desktop\\scripts\\reference.js',
loaded: false,
children: [],
paths: [
'C:\\Users\\Joseph\\Desktop\\scripts\\node_modules',
'C:\\Users\\Joseph\\Desktop\\node_modules',
'C:\\Users\\Joseph\\node_modules',
'C:\\Users\\node_modules',
'C:\\node_modules'
]
}
Let's zoom into Module.exports where we have yet to export anything from reference.js, hence, it shows an empty object. Now, let's export it like we did previously.
const address = {
street: 'old middle road',
postal: 123456
}
exports.address = address;
console.log(module);
---
// output
Module {
id: 'C:\\Users\\Joseph\\Desktop\\scripts\\reference.js',
path: 'C:\\Users\\Joseph\\Desktop\\scripts',
exports: { address: { street: 'old middle road', postal: 123456 } },
filename: 'C:\\Users\\Joseph\\Desktop\\scripts\\reference.js',
loaded: false,
children: [],
paths: [
'C:\\Users\\Joseph\\Desktop\\scripts\\node_modules',
'C:\\Users\\Joseph\\Desktop\\node_modules',
'C:\\Users\\Joseph\\node_modules',
'C:\\Users\\node_modules',
'C:\\node_modules'
]
}
Notice that it now shows the address object being exported as part of the Module object.
As mentioned, it wasn't obvious to me before that there was another nested level of address object due to the way I exported the object. What I did was to add another level of property before I exported address object
// this
const address = {
street: 'old middle road',
postal: 123456
}
exports.address = address;
// is the same as doing this
module.exports = {
address: {
street: 'old middle road',
postal: 123456
}
};
// or doing this
module.exports = { address };
And what I really wanted was
module.exports = {
street: 'old middle road',
postal: 123456
};
// or
module.exports = address;
So if this was exported correctly, the Module object output should be
Module {
id: 'C:\\Users\\Joseph\\Desktop\\scripts\\reference.js',
path: 'C:\\Users\\Joseph\\Desktop\\scripts',
exports: { street: 'old middle road', postal: 123456 },
filename: 'C:\\Users\\Joseph\\Desktop\\scripts\\reference.js',
loaded: false,
children: [],
paths: [
'C:\\Users\\Joseph\\Desktop\\scripts\\node_modules',
'C:\\Users\\Joseph\\Desktop\\node_modules',
'C:\\Users\\Joseph\\node_modules',
'C:\\Users\\node_modules',
'C:\\node_modules'
]
}
And to use it by importing from another javascript file, it would simply be just
// index.js
const address = require('./reference.js');
console.log(address.street);
// output
old middle road
I feel that another point of confusion was that the exported property name was defined as module.exports.address = address and I had it imported as const address = require('./reference.js');
// reference.js
module.exports.address = address
// index.js
const address = require('./reference.js');
Had I imported it with a different value, it would have been less confusing
// index.js
const reference = require('./reference.js');
// accessing
console.log(reference.address.street); // old middle road
Well, that's all nice and well, but what if I need to export or import multiple objects?
One way is simply to export as multiple property
const address = {
street: 'old middle road',
postal: 123456
}
const employment = {
company: 'google',
yearStart: 2000,
yearEnd: 2011
}
module.exports.address = address;
module.exports.employment = employment;
// Module output
Module {
exports: {
address: { street: 'old middle road', postal: 123456 },
employment: { company: 'google', yearStart: 2000, yearEnd: 2011 }
}
}
Or to export as module.exports which would have resulted in the same Module output as above
module.exports = { address, employment };
And import to use as such
const reference = require('./reference.js');
console.log(reference.address.street); // old middle road
console.log(reference.employment.company); // google
Or we could use destructure way, in other words, named import
const { address, employment } = require('./reference.js');
console.log(address.street); // old middle road
console.log(employment.company); // google
To view what is it like to import from the external file is to log out the import
// given export in reference.js
module.exports.address = address;
// index.js
console.log(require('./reference.js'));
// would result in
{ address: { street: 'old middle road', postal: 123456 } }
// given export in reference.js
module.exports.address = address;
module.exports.employment = employment;
// or
module.exports = { address, employment };
// index.js
console.log(require('./reference.js'));
// would result in
{
address: { street: 'old middle road', postal: 123456 },
employment: { company: 'google', yearStart: 2000, yearEnd: 2011 }
}
The imported object should have matched the exports in Module output
I talked about how does the various export look like and exporting single or multiple values. How logging out the Module or import statement would have helped to view the exact value that was exported or imported. Hope that this gives you a little more insights of what's happening behind the scene.