The examples here are based in the examples in https://github.com/benjamn/ast-types README.md
➜ hello-ast-types git:(master) node introduction/index.js
The AST built for
if (foo) {
foo();
}
is:
{
"test": {
"name": "foo",
"loc": null,
"type": "Identifier",
"comments": null,
"optional": false,
"typeAnnotation": null
},
"consequent": {
"body": [
{
"expression": {
"callee": {
"name": "foo",
"loc": null,
"type": "Identifier",
"comments": null,
"optional": false,
"typeAnnotation": null
},
"arguments": [],
"loc": null,
"type": "CallExpression",
"comments": null,
"optional": false,
"typeArguments": null
},
"loc": null,
"type": "ExpressionStatement",
"comments": null
}
],
"loc": null,
"type": "BlockStatement",
"comments": null,
"directives": []
},
"alternate": null,
"loc": null,
"type": "IfStatement",
"comments": null
}
The 5th edition of ECMAScript (ES5) forbids the use of arguments.callee
.
The goal of this code example: you want to detect uses of this old trick to update the code.
Here is an execution of the example:
➜ hello-ast-types git:(flow-parser) ✗ node visitmemberexpression.js
Input:
/* Early versions of JavaScript did not allow named function expressions,
and for this reason you could not make a recursive function expression.
May be you want to detect uses of this old trick to update the code.
*/
var fac = function(n) { return !(n > 1) ? 1 : arguments.callee(n - 1) * n; }
---
Warning! 'arguments.callee' is used in this code
The summarized AST for the input code
var fac = function(n) { return !(n > 1) ? 1 : arguments.callee(n - 1) * n; }
is:
✗ compast -p 'var fac = function(n) { return !(n > 1) ? 1 : arguments.callee(n - 1) * n; }'
['Program',
[ 'VariableDeclaration',
[ 'VariableDeclarator',
[ 'Identifier', 'fac' ],
[ 'FunctionExpression',
[ 'Identifier', 'n' ],
[ 'BlockStatement',
[ 'ReturnStatement',
[ 'ConditionalExpression',
[ 'UnaryExpression', '!',
[ 'BinaryExpression', '>', [ 'Identifier', 'n' ], [ 'Literal', 1 ] ]
],
[ 'Literal', 1 ],
[ 'BinaryExpression',
'*',
[ 'CallExpression',
[ 'MemberExpression',
[ 'Identifier', 'arguments' ], [ 'Identifier', 'callee' ]
],
[ 'BinaryExpression', '-', [ 'Identifier', 'n' ], [ 'Literal', 1 ] ]
],
[ 'Identifier', 'n' ]
]
]
]
]
]
]
]
]
In the following code n
is an abbreviation for the namedTypes
object provided by ast-types
:
import { visit, namedTypes as n, } from "ast-types";
Remmber that:
- The object
n
has acheck
method to check the type of a node. - The children of a
MemberExpression
node have namesobject
andproperty
:
> e = require('espree')
> e.VisitorKeys.MemberExpression
[ 'object', 'property' ]
> b = require("ast-types").builder
> gfn = require("ast-types").getFieldNames, null
> gfn({type: "MemberExpression"})
[ 'type', 'optional', 'object', 'property', 'computed' ]
Notice how
- We traverse the AST visiting the
MemberExpression
nodes - We check that the child
object
of theMemberExpression
node is of typeIdentifier
and its name isarguments
- We check that the child
property
of theMemberExpression
node is of typeIdentifier
and its name iscallee
visit(ast, {
visitMemberExpression(path) {
var node = path.node;
if (
n.Identifier.check(node.object) &&
node.object.name === "arguments" &&
n.Identifier.check(node.property)
) {
if (node.property.name == "callee") console.error("Warning! 'arguments.callee' is used in this code");
}
this.traverse(path);
}
});
See the solution in file visit/visitmemberexpression.js
Translate ES6 spread operator to older versions of JS.
Transforming ...rest
parameters into browser-runnable ES5 JavaScript:
For the input:
let code = `
function tutu(x, ...rest) {
return x + rest[0];
}
module.exports = tutu;
`;
gives the output:
➜ visit git:(master) ✗ node spread-operator.js > salida.cjs
➜ visit git:(master) ✗ cat salida.cjs
function tutu(x) {
var rest = Array.prototype.slice.call(arguments, 1);
return x + rest[0];
}
module.exports = tutu;
That we can use this way:
➜ visit git:(master) ✗ node
> tutu = require("./salida.cjs")
[Function: tutu]
> tutu(2,3,4,5)
5
See the full code at visit/spread-operator.js
Implement a function that determines if a given function node refers to this
This example has been modified regarding not only the usage of super
but also due to the fact
that visitFunction
is triggered by the outer function and the original produced an erroneous result
➜ hello-ast-types git:(master) ✗ npm run this
> hello-ast-types@1.0.0 this
> node check-this-usage.js
function tutu() {
return this.prop+4;
}
Inside Function visitor tutu
inside thisexpression
true
----
function tutu() {
return prop+4;
}
Inside Function visitor tutu
false
----
function tutu() {
function titi() {
return this.prop+4;
}
return prop+4;
}
Inside Function visitor tutu
Inside Function visitor titi
false
----
function tutu() {
return super();
}
Inside Function visitor tutu
true
----
function tutu() {
return super.meth();
}
Inside Function visitor tutu
true
----