How does NodeJS module, Chalk's, chaining syntax work?

Viewed 191

I don't understand how Chalk's (NodeJS module) syntax works (and it bothers me). I have searched far and wide for an answer, but I am not having any luck and likely don't know the technical term(s) for what I need to look for. I have tried looking up chalk-specific questions here on StackOverflow, "method chaining", "prototypes", etc. Tried looking at Chalk's source code, but still can't seem to figure out my answer. The syntax in question is:

// Code snippet from the Chalk NPM Page.
log(chalk.blue.bgRed.bold('Hello world!'));
log(chalk.blue('Hello', 'World!', 'Foo', 'bar', 'biz', 'baz'));

I am familiar with method chaining and familiar with storing a function in an object. The main question I have is: how can chalk.blue be a property and a function at the same time?

Any help would be greatly appreciated. Whether it is a full explanation or a push in the right direction.

2 Answers

Chalk is probably using getters and setters to "call" the appropriate functions. The following is a simple example of what's going on:

let a = function (txt) {
    console.log(a.buffer + txt);
    a.buffer = ''
}

a.buffer = '';

Object.defineProperty(a,'b',{
    get: function(){
        this.buffer += '<B>';
        return this
    }
});

Object.defineProperty(a,'c',{
    get: function(){
        this.buffer += '<C>';
        return this
    }
});

Object.defineProperty(a,'d',{
    get: function(){
        this.buffer += '<D>';
        return this
    }
});

Basically it's just regular method chaining but using getters to make it fancy! Another trick is to make the base object a Function instead of a regular object so that the this you return becomes callable.

Now you can do:

a.b.c.b.d('hello'); // prints <B><C><B><D>hello

The main question I have is: how can chalk.blue be a property and a function at the same time?

In Javascript, a function is an object so in addition to be callable as chalk.blue(), it can also have properties such as chalk.blue.bgRed.

For example, you can do this:

function callMe() {
    console.log("callMe");    
}

callMe.greeting = "hello";

callMe();
console.log(callMe.greeting);

Then, for indefinite chaining, you can use getters to create endless chains of objects.

Related