DEV Community

Rohan Bagchi
Rohan Bagchi

Posted on • Originally published at rohanbagchi.hashnode.dev

Building a custom RequireJS

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

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

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

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

Finished product:

Top comments (0)