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 codeThe 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
nhas acheckmethod to check the type of a node. - The children of a
MemberExpressionnode have namesobjectandproperty:
> 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
MemberExpressionnodes - We check that the child
objectof theMemberExpressionnode is of typeIdentifierand its name isarguments - We check that the child
propertyof theMemberExpressionnode is of typeIdentifierand 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)
5See 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
----