All Projects → inikulin → mods

inikulin / mods

Licence: MIT license
Modular JavaScript library in just 470 bytes of minified code

Programming Languages

javascript
184084 projects - #8 most used programming language
CSS
56736 projects
HTML
75241 projects

modsmods

Nice modular Javascript library in just 470 bytes of minified code.

or

  • Install with bower: $ bower install mods

What?

Nowadays building any non-trivial JavaScript application requires a significant amount of code. Historically JavaScript doesn't have any modular system, which allows you to split your code in modules, separate files and control their dependencies. Moreover a big application doesn't need to initialize all its subsystems on start. Here a lazy loading of submodules comes to help. There are a lot of great libraries that provide such functionality (RequireJS is the most notable). However, I feel a constant frustration with their complexity and strange design decisions. I feel that things are not DRY and my code is not narrative when I'm writing things like this in RequireJS:

requirejs(['jquery', 'canvas', 'app/sub'], function   ($, canvas, sub) {
    //jQuery, canvas and the app/sub module are loaded and can be used here now.
    
    return {
        helloWorld: function() {
          console.log('Hi there!');
        }
    };
});

This design is dictated by the fact, that RequireJS should load dependencies from server before module initialization. But in most cases I have the only one good-old concatinated script file.

So, this is how I want it to be done in my code:

MyApp.define('Main', function(require, exports) {
    var $ = require('jquery'),
        canvas = require('canvas'),
        sub = require('sub');
        
    exports.helloWorld = function(){
      console.log('Hi there');
    };
});

Another issue of RequireJs/CommonJs is that a module structure mimics a filesystem structure. So rebasing a single file may become a significant pain in the ass. The most common process of building your scripts (e.g. with grunt) is:

  • Concat all JS files in the given directory into a single file, which is used at run time
  • Minify this file

With such approach, usage of the RequireJS may become really painful.

So, if you want

  • nice, modular and narrative code
  • filesystem/URL-agnostic AMD library with nice error handling
  • concat all your scripts (without taking care of their order/filesystem path) into a single file

then mods is your choice.

Usage

1. Init your app/module container

var MyApp = new Mods();

2. Encapsulate used libs

MyApp.define('jQuery', function() {
    this.exports = jQuery.noConflict();
});

MyApp.define('async', function() {
    this.exports = async.noConflict();
});

3. Define your modules

//Use objects as exports...
//---------------------------------------------------------
MyApp.define('Greetings.Settings', function() {
    this.exports = {
      text: 'Hello world',
      color: 'red'
    };
});


//...or use functions as exports...
//----------------------------------------------------------
MyApp.define('Greetings.Printer.DOM', function(require) {
    var $ = require('jQuery');
    
    this.exports = function(text, color) {
        $('<div>')
            .text(text)
            .css({color: color})
            .appendTo('body');
    };
});


MyApp.define('Greetings.Printer.Console', function(require) {
    this.exports = function(text) {
        console.log(text);
    };
});


//...or just extend exports object (like in node.js)
//----------------------------------------------------------
MyApp.define('Main', function(require, exports) {
    var Settings = require('Greetings.Settings'),
        printToDOM = require('Greetings.Printer.DOM'),
        printToConsole = require('Greetings.Printer.Console');

    exports.helloToDOM = function(){
        printToDOM(Settings.text, Settings.color);
    };

    exports.helloToConsole = function(){
        printToConsole(Settings.text);
    };
});

4. Use your app/module container

var Main = MyApp.get('Main');

Main.helloToDOM();
Main.helloToConsole();

Hm, but I want backward compatibility with RequireJS/CommonJS!

Ok. In your startup code do this

var MyApp = new Mods();

window.define = function(name, mod) {
    MyApp.define(name, mod);
};

so you can turn this

MyApp.define('i.am.rock', function(require, exports) {
   var app = require('app');
   
   exports.awesomeness = 'My ' + app.name;
});

into this

define('i.am.rock', function(require, exports) {
   var app = require('app');
   
   exports.awesomeness = 'My ' + app.name;
});

I need an example project

We have one. As you can see it mimics superb app from Usage section.

By the way, it can handle issues in your code

###If you have a circular dependency in your code:

var MyApp = new Mods();

MyApp.define('Module1', function(require){
  var Module2 = require('Module2');
});

MyApp.define('Module2', function(){
  var Module1 = require('Module1');
});

var Module1 = MyApp.get('Module1');

You will get an error from mods in your console: Mods: circular dependency: "Module1" -> "Module2" -> "Module1"

###If you load a module that is not defined:

var MyApp = new Mods();

MyApp.define('Main', function(require){
   var dummy = require('MyUndefinedModule');
});

//or

var dummy = MyApp.get('MyUndefinedModule'); 

You will get an error from mods in your console: Mods: required "MyUndefinedModule" is undefined

###If you redefine already defined module:

var MyApp = new Mods();

MyApp.define('Main', function(){
});

MyApp.define('Main', function(){
});

You will get an error from mods in your console: Mods: "Main" is already defined

Questions or suggestions?

If you have any questions, please feel free to create an issue here on github.

Author

Ivan Nikulin ([email protected])

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].