Building a custom RequireJS

RequireJS works on the principle that we can define functions that depend on values returned by other functions. This helps us write modular code. Here, we will assume all functions are in a single file.

The ask goes like this:

define('moduleName',function(){
    return 1;
})


require(['moduleName'],function(module1){
    console.log(module1) // 1;
})

Here, we add dependencies using define and functions using require. 1st param to require is a dependency array. Dependencies can be registered using define function.

Sometimes we can also require beforehand and then define functions. Once all dependencies are resolved, the user defined callback will be triggered automatically.

First let us declare module level variables to hold our results, dependencies and a registry of functions to dependency mapping.

const fnMap = {}; // mapping between a dependency function name to actual function reference
let registry = [] // list of functions and the names of their dependencies
const resultMap = {}; // mapping between dependency name and it's return value

Now that we have the vars, let us define define :)

function define(name, cb){
    fnMap[name] = cb;

    const unresolvedRegistryItems = registry.filter(({ cb: userDefinedCb, dependencies }) => {
        const unresolvedDeps = dependencies.filter(dependencyName => {
            const fn = fnMap[dependencyName];
            return !fn;
        });

        if (unresolvedDeps.length === 0) {
            require(dependencies, userDefinedCb);
        } else {
            return true;
        }
    });
    registry = unresolvedRegistryItems;
}

As you can see, we are first recording the dependency in fnMap and then checking the registry for functions whose all dependencies are resolved. If so, we are triggering the require (to be implemented). Finally, we update the registry list with the filtered list containing only functions with unresolved dependencies.

Let us now define require

function require(dependencies, cb){
    const results = [];
    let isResolved = true;

    for (let i = 0; i < dependencies.length; i++) {
        const dependencyName = dependencies[i];
        const fn = fnMap[dependencyName];

        if (!fn) {
            isResolved = false;
            registry.push({
                cb,
                dependencies
            });
            return;
        } 

        const result = resultMap[dependencyName] || fn();
        results.push(result);
        resultMap[dependencyName] = result;
    }
    isResolved && cb.apply(null, results);
}

Finished product: