2020-02-16
|~3 min read
|539 words
Progress toward a unified module system in Javascript is proceeding, but we’ve yet to arrive at a unified experience.
While we wait, I ran into a bit of a headache with some utility functions I’d written. (I have written a primer to some of the differences from a tactical perspective between the module standards previously. Unfortunately, that didn’t save me.)
I almost always start writing functions as ES Modules, however, I noticed some of my code needed to run in Node, so I converted it over to CommonJS.1
This is when I ran into problems because I didn’t refactor correctly.
Let’s look at some simple examples and some error messages we might see if we make a misstep.
This:
import React from 'react'
export function myFunc(args) {
/*... */
return (/*...*/);
}
Becomes this:
const React = require('react')
function myFunc(args) {
/*... */
return (/*...*/);
}
module.exports = { myFunc }
Note that not only did we change the export type, but we no longer import react
. Instead we require
it. If we don’t, we will get the error: TypeError: Cannot assign to read only property 'exports' of object '#<Object
.
Furthermore, the way we handle this with an index.js
file is also different than when the files are using ESModules.
const myFunc = require("./myFunc")
module.exports = { ...myFunc }
Because the export from myFunc
is an object, we need to spread the methods within the new exports object. While we could have simply written module.exports = myFunc
in utils/myFunc.js
, the use of the {}
makes it more easily extendable in the future.
This raises another potentially easy error to get. Imagine not refactoring to CommonJS modules and instead of a named export, we use a default export:
import React from 'react'
function myFunc(args) {
/*... */
return (/*...*/);
}
export default myFunc
If we tried to reference the myFunc
in the module.exports
within utils/index.js
we’d get the error: TypeError: Object(...) is not a function
.
This is true in any of these situations:
const myFunc = require("./myFunc")
//OR
const myFunc = require("./myFunc").myFunc
module.exports = { myFunc }
//OR
module.exports = { ...myFunc }
I’m a little fuzzy on why, however, we can resolve this by using the named export:
import React from 'react'
export function myFunc(args) {
/*... */
return (/*...*/);
}
And then in utils/index.js
:
const myFunc = require("./myFunc").myFunc
module.exports = { getBlurb }
In this case, we’re pulling the specific function out of the module that’s required and then passing it along to the module.exports
object in utils/index.js
.
My key takeaway: Don’t mix CommonJS and ES Modules within the same file.
Beyond that, it’s just a matter of syntax. Hopefully my enduring this headache and writing about it will help you avoid a similarly painful experience in the future!
import <module> from <package>
Whereas the same function in CommonJS would be:
const <module> = require(<package>)
Flavio Copes has a nice introduction on ES MOdules that’s worth a read if you would like more info.
Hi there and thanks for reading! My name's Stephen. I live in Chicago with my wife, Kate, and dog, Finn. Want more? See about and get in touch!