Modularity is a big trend and I'm not the first to hop on this train. Today, I'm going to show you how easy you can build a multi-module app with vanilla Javascript and some awesome tools.
Recipe
Ingredients
First of all, I'm going to assume you know a few things beforehand :
- Object Oriented Programming
- How to write JS
- Basics of NPM
Steps
The ground
Lets start with an empty directory for your project (we'll name it unicorn) and initialize it
npm init
and create the main file index.js
with an old-school JS class
function Unicorn(name) {
this.name = name;
}
Unicorn.prototype = {
shine: function() {
// All kind of good stuff here 🦄
}
}
var dazzle = new Unicorn("Dazzle");
dazzle.shine();
Decoupling
Now image that you want to use the Unicorn
class in another project, or just open-source it to the Humanity. You could create another directory with another repo, but let's be smarter. The Unicorn
class is so linked to the Unicorn project that we'll use NPM scoped package name for clarity.
All that reduce index.js
to 3 lines of codes.
import Unicorn from "@unicorn/model";
var dazzle = new Unicorn("Dazzle");
dazzle.shine();
Next, we create a sub-directory for our module.
mkdir packages/model
cd packages/model
npm init # and name it @unicorn/model
Which will have an index.js
too with the class inside it. Since we left the plain browser JS with import/export statement, why not use the beautiful ES6 class syntax.
export default class Unicorn {
constructor(name) {
this.name = name;
}
shine () {
// All kind of good stuff here 🦄
}
}
At that point, the import
statement is targeted at a package name that should be installed under the node_modules
sub-directory. We could use a relative path like import Unicorn from "./packages/model/index.js";
. What could be better is to create a link between packages.
Thankfully, npm can do that for you with the link command. Here's what it looks in our case.
cd packages/model
npm link
cd ..
npm link @unicorn/model
Wrapping
Ok nice one, but now I can't use it in my browser, you dumbo !
First, how are you calling me ?
Then yeah, I know, for now it's all experimental syntax and stuff, but there's tools to handle it for you. I like to use webpack with babel, of course, it's not the only solution.
Adding some package on project's root.
npm install --save-dev babel-loader babel-core babel-preset-env webpack
The whole webpack configuration could fill another article, so I'll just show one that work. Set a new file called webpack.config.js
with some instructions inside.
module.exports = {
entry: "./index.js", // Main file to read
module: {
rules: [{
test: /\.js$/, // For all file ending with ".js"
use: {
loader: "babel-loader", // Use babel
options: {
presets: ["babel-preset-env"],
},
},
}],
},
output: {
filename: "dist/unicorn.js", // Output the result in another file
library: "Unicorn", // Under "Unicorn" namespace
libraryTarget: "this",
libraryExport: "default",
},
};
Then, if you run npx webpack
it will build all your sources into one file usable by plain web browser.
Managing
You can now create lots of sub-modules and wrap them all in one file. You can even have sub-sub-modules and so on. Just put them all in the modules
directory.
As your project grows, it'll be harder and harder to manage all this menagerie.
That where lerna come into play.
npm install -save-dev lerna
Think of it as a npm link
on steroids.
Check out the full documentation on the project page, but here's a few useful commands.
npx lerna clean # Remove all node_modules directories
npx lerna bootstrap # Install remote dependencies and link local ones
npx lerna add package # Install a package to all sub-modules
npx lerna add package --scope=sub-module # Install a package to a specific sub-module
npx lerna publish # Bump, tag and publish all your modules over NPM
Enjoy
You should now be on track to write the most elegant project possible. I'm counting on you ;)
If you want more in-depth examples, I'm currently building yet another JS drawing library using the very same techniques.
Next time, we'll talk about automated tests and how to catch lots of bugs and ensure consistency over time.
Top comments (3)
Just a note though: these are not vanilla JS modules, which it a bit confusing regarding the intro of the article.
Thanks for your comment. The code is pure ES6 syntax (no post-process, no frame-work). The use of webpack + babel is just there to make it work on browser. It feels vanilla to me. How would you have done ?
The thing is, this would not work, either in Node, you would need to name it
.jsm
and run it in Node 8 LTS, nor in most browsers.Node use another syntax to uses modules, and I think only Chrome supports import/export modules.
So, for now, you need to compile now. I think that's what Elarcis means.