loading...
Cover image for How to code like playing LEGO™

How to code like playing LEGO™

gmartigny profile image Guillaume Martigny Updated on ・3 min read

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

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

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

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.

Discussion

pic
Editor guide
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 Author

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.