Since ECMAScript 5 specification it was introduced the ‘use strict’ directive, which is used to declared that the code inside the scope of the ‘use strict’ should use a subset of the JavaScript language.


It means that the same code should run different of the same code without it, for instance, you can’t declare a global variable in the following way:

    someNumber = 123;

The code above will run in any browser, declaring someNumber in the global scope because of the missing var.


    'use strict';
    someNumber = 123; // ReferenceError: someNumber is not defined

The second snippet runs in strict mode and will throw a error on browsers ES5 compatible, because of using a variable that was not defined.

And the same code will run without any problems in all browsers that do not support the ES5 especification.

Recently ASM started using a annotation use asm to define that a code should be run as a fast subset of JS (Pretty fast accually).


Time to use it too!

So we already know that annotations exists in JS, they look like code, in fact they are a Literal Expression, but they just get vaccued into the void, not meaning anything to normal JS code.

How can we use it?

Pretty good question, JS does not provide a simple way of using it. I was going to suggest writing a parser to read the code and detect those annotations.
JS is such a powerful language that you can accually do it.

But this would take a little bit of time, and reading annotations is not what we need right now, we need to use it.

Then I’ve found there are a lot of JS parsers out there and I chose Esprima to the job.

Esprima is pretty cool, it reads your JS code and returns a Node Tree with the parsed code.

Let’s read the following function and see what we get.

function Person(name){};
JSON.stringify(esprima.parse('function Person(name){}'), null, 4) 
// Or JSON.stringify(esprima.parse(Person.toString()), null, 4) 
{
    "type": "Program",
    "body": [
        {
            "type": "FunctionDeclaration",
            "id": {
                "type": "Identifier",
                "name": "Person"
            },
            "params": [
                {
                    "type": "Identifier",
                    "name": "name"
                }
            ],
            "defaults": [],
            "body": {
                "type": "BlockStatement",
                "body": []
            },
            "generator": false,
            "expression": false
        }
    ]
}

Esprima read a bunch of data out of the function.

It has identified that this is a FunctionDeclaration, with name Person and a parameter called name.
And also the body of the function, which is empty.

That’s cool! How about this:


var code = "'use strict'" +
           "\n" +
           "function Person(name){}";

JSON.stringify(esprima.parse(code), null, 4)
{
    "type": "Program",
    "body": [
        {
            "type": "ExpressionStatement",
            "expression": {
                "type": "Literal",
                "value": "use strict",
                "raw": "\"use strict\""
            }
        },
        {
            "type": "FunctionDeclaration",
            "id": {
                "type": "Identifier",
                "name": "Person"
            }
            ...
        }
    ]
}

Now we can read the annotations from the source code, we can see in the JSON that we have a use strict before our function.

This is how we extract annotations from JS files on js-annotation-reader, which is the module used by node-dependency to enable annotation support.

Using this technique I wrote js-annotation-reader, and using it you can read a file like this:

'package com.pedro'

'@RequestHandler("/user")'
function UserHandler () {
    
    '@Get("/all")'
    this.fetchAll = function() {
        return [{
            name : 'USER!'
        }, {
            anotherOne : 1234567
        }];
    };
    
    '@Get("/id/:id")'
    this.getByID = function($id) {
        return {
            userID : $id,
            name : "aSDFGHJKL"
        };
    };

}
And we get this result:
{
    "name": "UserHandler",
    "packaged": "com.pedro",
    "parameters": [],
    "annotations": [
        {
            "name": "RequestHandler",
            "value": "/user",
            "targets": "UserHandler"
        }
    ],
    "imports": [],
    "methods": [
        {
            "annotations": [
                {
                    "name": "Get",
                    "value": "/all",
                    "targets": "fetchAll"
                }
            ],
            "parameters": [],
            "name": "fetchAll"
        },
        {
            "annotations": [
                {
                    "name": "Get",
                    "value": "/id/:id",
                    "targets": "getByID"
                }
            ],
            "parameters": [
                "$id"
            ],
            "name": "getByID"
        }
    ]
}

If you are wondering in what this can be used, just check this out Express Plugin

References:

Mozilla’s MDN

ECMAScript 5

ASM.js