DEV Community

Cover image for How to code like playing LEGO™
Guillaume Martigny
Guillaume Martigny

Posted on • Updated on

How to code like playing LEGO™

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 :

  1. Object Oriented Programming
  2. How to write JS
  3. 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
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

Next, we create a sub-directory for our module.

mkdir packages/model
cd packages/model
npm init # and name it @unicorn/model
Enter fullscreen mode Exit fullscreen mode

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 🦄
    }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Perfect !
Perfect

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
Enter fullscreen mode Exit fullscreen mode

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",
    },
};

Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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.

Oldest comments (3)

Collapse
 
elarcis profile image
Elarcis

Just a note though: these are not vanilla JS modules, which it a bit confusing regarding the intro of the article.

Collapse
 
gmartigny profile image
Guillaume Martigny

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 ?

Collapse
 
michaeljota profile image
Michael De Abreu

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.