Sample Video Frame
31: Modules and import
Originally JavaScript had no concept of "modules" because it lived entirely in the browser. A systems language needs to have modules, so Node.JS added modules through the CommonJS system. This uses the require
function to load modules, which you've been using this whole time.
In Exercise 13 you wrote a simple module named geometry.js
:
const area = (r) => Math.PI * r ** 2;
const circumference = (r) => 2 * Math.PI * r;
module.exports = {
area: area,
circumference: circumference
}
This code exposed the area
and circumference
functions using the module.exports
"magic" variable.
You then used require("./geometry")
to access it:
const geometry = require('./geometry');
let area51 = geometry.area(2.8);
let circ2 = geometry.circumference(6);
console.log(`Area: ${area51}, Circumference: ${circ2}`);
This works as the default system in Node.JS, and during the course I stuck to the default to keep things simple. You now need to learn about the "module" version of...modules.
Using import
and .mjs Files
The new style of modules (confusingly called...modules) use a new syntax with the import
keyword rather than functions and special variables. Let's start by rewriting the Exercise 13 code:
import geometry from "./geometry.mjs";
let area51 = geometry.area(2.8);
let circ2 = geometry.circumference(6);
console.log(`Area: ${area51}, Circumference: ${circ2}`);
You can see that the code is almost exactly the same except the first line is import geometry from "./geometry.mjs";
. Wait, why does it end in .mjs
? That's how you tell Node.JS that this code is a module.
That means, you can't just change the code.js
file, but you also have to rename it to code.mjs
so Node.JS will run it. If you don't then node will give you this error:
import geometry from "./geometry.mjs";
^^^^^^
SyntaxError: Cannot use import statement outside a module
Rename this file to code.mjs
so you can run it, then rename the geometry.js
file to geometry.mjs
before making these changes:
export const area = (r) => Math.PI * r ** 2;
export const circumference = (r) => 2 * Math.PI * r;
export default {
area,
circumference
}
The changes in this file are:
- I change the
const area
andconst circumference
lines to beexport const area
andexport const circumference
. This exports those individually. module.exports
is replaced withexport default
.- I use the short-hand syntax for the
{ }
used withexport default
rather than repeatingarea: area
. This isn't a special thing just forexport default
but a common help for making{ }
data objects. If the key is the same as a variable then you can put just the variable in the object and it'll create thekey:key
for you. - When you add something to the
export default
it becomes available when someone usesimport geometry from "geometry.mjs";
.
Once you do all that you get almost the same setup as with the CommonJS style.
Named Exports
If you want only one function out of this module you can do this:
import { area } from "./geometry.mjs";
This will import only area
from the module, and make that name directly available so you can write code like this:
let area51 = area(2.8);
The only problem is you have to use export const
on each function (or variable) you want to allow this kind of import.
You can also rename these imports:
import { area as BigArea } from "./geometry.mjs";
After you do this BigArea
is the name for that function. This is useful when functions you import conflict with common variable names like path
.
Modules in package.json
It is annoying to have to name every file with .mjs
just to keep Node.JS happy, so you can add the following to your package.json
to tell node
that all .js
files are modules:
"type": "module",
After you add this, node
will treat all .js
files as modules, so you can stop renaming them to .mjs
.
Then .cjs Files, Sigh
Now that you have package.json
telling node
everything is a module you then have to name some files with .cjs
so they'll run correctly. For example, Knex is a database library that uses the old CommonJS system and it expects its configuration file to be a CommonJS module. That means you have to name its knexfile.js
to knexfile.cjs
before it will work.
If you run into any JavaScript tool that's relatively old and it has a configuration .js
file then chances are you'll have to name it with .cjs
to make it work.
Dynamic Imports with import()
If you ever need to load a module based on a string you'll run into trouble with the new import syntax. The standard requires that import
has to be at the top of the file, so you can't do something like this:
if(load_another_module) {
import other_module from "other_module";
}
This will fail, so what can you do? The require()
function had the advantage that you could use it anywhere, and the import()
function does the same thing:
if(load_another_module) {
const other_module = await import("other_module");
}
This is new enough that some IDEs and "linters" will claim that it's not allowed, even though import()
is completely valid.
The Full Study
Mozilla's MDN has the most complete documentation on import and it also covers the import()
function.
Mozilla MDN also has documentation for export that you should read in combination with the import
documentation.
What you should do next is study the documentation and try to use it in some code. Try with this geometry.mjs
code until you know the different ways to use export
and import
, then try using it after creating a package.json
with "type":"module"
. In the video I'll demonstrate this, but try it yourself first.
Learn JavaScript Today
Register today for the Early Access course and get the all currently available videos and lessons, plus all future modules for no extra charge.
Still Not Sure? Check out more curriculum.