marcog83 / RoboJS

RoboJS is a library that aims to dynamically load JS modules depending on how the DOM is composed.
MIT License
134 stars 4 forks source link
dom loader mediator mutationobserver systemjs webcomponents

RoboJS is a library that aims to dynamically load JS modules depending on how the DOM is composed. Add a node to the DOM and a JS will be loaded! Remove a node and the JS will be disposed!! Not further framework frontend , but a tool that lets you manage the association DOM and JS in less than 4k gzipped;

NPM Greenkeeper badge Build Status codebeat badge Maintainability Test Coverage Coverage Status Inline docs NSP Status devDependencies Status Known Vulnerabilities

The idea behind the code

To understand how and why I decided to write this tool, please read this post

Quick Demo

A quick demo can be found HERE. It simulate a bunch of modules loaded from server and a page where to place them (on the right).

Installation


npm install robojs

How it works.

robojs will iterate the DOM trying to match components id with data-mediator attributes. Each time it finds a match, a request is send to load the right script. The first time the script is loaded from network, while the next one is retrived from cache. MutationObserver is used to handle DOM changes, and when it happens robojs iterates over the new added nodes.

Usage

You set a data-mediator attribute with an ID (whatever you want)

    <div data-mediator="my-mediator">a-2</div>

in definitions.js you define a Map where the key is an ID , and the value is the file to request in order to register the element.

    {
        "my-mediator": "component/mediator"
    }

By default robojs supposes the presence of an AMD Loader like RequireJS in order to request the component and its dependencies. For example "component/mediator" looks like the follow

//mediator.js
define(function(){
    return function Mediator(node){
        //
    }
})

When robojs finds a match between a data-mediator attribute and an ID from definitions.js, it will load component/mediator.js file and it will execute the Mediator function. The node parameter is a reference to the DOM element.

import {bootstrap} from "robojs"
const definitions={
     "my-mediator": "component/mediator"
}
//basic usage
bootstrap({definitions}) // return {dispose,promise}

you can store, and use later, the returned Object from bootstrap function.

import {bootstrap} from "robojs"
const definitions={
     "my-mediator": "component/mediator"
};
var application=bootstrap({definitions}) // return {dispose:Function,promise:Promise<any>}

//you can handle when every Mediators in page are executed
application.promise.then(function(){
    console.log("all mediators loaded")
}).catch(function(e){
    console.log("something went wrong",e);
})

//later in your code you can dispose the RoboJS instance.
application.dispose();

Mediator Function.

Mediator is the context where your logic runs for a specific Mediator. It is a simple function. When a data-mediator attribute matches an ID from the component definitions the Mediator function is called and a function returns.

The returned function is called later when the module will be disposed. Mediator function takes two parameters, node and dispatcher. node is a reference to DOM element, dispatcher is a reference to EventDispatcher Object.


    function Mediator(node,dispacther) {
        return function(){
           // destroy everything, es. handlers
        }
    }

Loader Object

Default loader is AMD based, it means that by default any module should be exported as amd. You can customize script loading strategy passing a function to Loader.

For instance if you use SystemJS module loader, you can do something like the follow.

import {bootstrap,Loader} from "robojs"
const definitions={
     "my-mediator": "component/mediator"
};
//this is the strategy used to load external modules
function loaderFn(id, resolve, reject) {
        System.import(id).then(resolve).catch(reject)
    }
bootstrap({definitions,loader:Loader(loaderFn)})

If you use ES2015 import statement, you can create something different. You don't need to load Mediator from external file, but just retrieve the Mediator function from definitions Map

import {bootstrap,Loader} from "robojs"
import Mediator from "./component/mediator";
const definitions={
     "my-mediator": Mediator
};

//this is the strategy used to get Mediator from definitions
function loaderFn(id, resolve, reject) {    
        resolve(definitions[id]);
    }

bootstrap({definitions,loader:Loader(loaderFn)});

EventDispatcher Object.

The EventDispatcher can be your messaging System. It dispatches and listens to Events from your Application. It's meant to be a Singleton in your application. Every robojs instance has one.

You can get a new instance of EventDispatcher by calling makeDispatcher function

import {makeDispatcher} from "robojs"
var myNewEventDispatcher=makeDispatcher();

Configurations

Using RequireJS


requirejs.config({
    paths: {        
        robojs: "../../dist/robojs"
    }
});
//
require(["robojs"],({bootstrap})=>{
    const definitions={
         "my-mediator": "component/mediator"
    }
    bootstrap({definitions});
});

Using ES2015

import {bootstrap} from "robojs"
const definitions={
     "my-mediator": "component/mediator"
};

bootstrap({definitions});

Using SystemJS


System.config({
        defaultJSExtensions: true,
        paths:{
            robojs:"../../dist/robojs"
        }
    });
//
System.import("robojs").then(({bootstrap})=>{
    const definitions={
         "my-mediator": "component/mediator"
    };

    bootstrap({definitions});
});

Dependencies

no dependencies

Build project

You can run npm script named build.

npm run build

Test project

from test folder you can run script named test.

npm run test

Polyfills

If you need to support old browsers, you need to something for

  1. MutationObserver by megawac.
  2. Webcomponents. If you use custom element extension.