Skip to content
This repository was archived by the owner on Mar 29, 2018. It is now read-only.

Commit 1924f9e

Browse files
Update recast to v0.7.0 and use the visitor API.
1 parent 5304f7a commit 1924f9e

File tree

2 files changed

+88
-33
lines changed

2 files changed

+88
-33
lines changed

lib/index.js

Lines changed: 86 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ var recast = require('recast');
77
var types = recast.types;
88
var n = types.namedTypes;
99
var b = types.builders;
10+
var PathVisitor = types.PathVisitor;
1011

1112
var util = require('ast-util');
1213

@@ -17,51 +18,104 @@ assert.ok(
1718

1819
/**
1920
* Visits a node of an AST looking for arrow function expressions. This is
20-
* intended to be used with the ast-types `traverse()` function.
21+
* intended to be used with the ast-types `visit()` function.
2122
*
22-
* @param {Object} node
23-
* @this {ast-types.Path}
23+
* @constructor
24+
* @extends PathVisitor
2425
*/
25-
function visitNode(node) {
26-
if (!n.ArrowFunctionExpression.check(node)) {
27-
return;
28-
}
26+
function ArrowFunctionExpressionVisitor() {
27+
PathVisitor.call(this);
28+
}
29+
ArrowFunctionExpressionVisitor.prototype = Object.create(PathVisitor.prototype);
30+
ArrowFunctionExpressionVisitor.prototype.constructor = ArrowFunctionExpressionVisitor;
31+
32+
/**
33+
* Visits arrow function expressions and replaces them with normal functions.
34+
*
35+
* @param {types.NodePath} path
36+
* @return {?Node}
37+
*/
38+
ArrowFunctionExpressionVisitor.prototype.visitArrowFunctionExpression = function(path) {
39+
var node = path.node;
40+
41+
this.traverse(path);
42+
43+
// In the future, ArrowFunctionExpression and FunctionExpression nodes may
44+
// get new fields (like .async) that we can't anticipate yet, so we simply
45+
// switch the type and let all the other fields carry over.
46+
node.type = 'FunctionExpression';
2947

3048
if (node.expression) {
3149
node.expression = false;
3250
node.body = b.blockStatement([b.returnStatement(node.body)]);
3351
}
3452

35-
// In the future, ArrowFunctionExpression and FunctionExpression nodes
36-
// may get new fields (like .async) that we can't anticipate yet, so we
37-
// simply switch the type and let all the other fields carry over.
38-
node.type = 'FunctionExpression';
53+
if (node.hasThisExpression) {
54+
return b.callExpression(
55+
b.memberExpression(node, b.identifier('bind'), false),
56+
[b.thisExpression()]
57+
);
58+
}
59+
};
3960

40-
var foundThisExpression = false;
41-
var scope = this.scope.parent;
61+
/**
62+
* Ensures that any arrow function directly containing `this` is appropriately
63+
* marked as such.
64+
*
65+
* @param {types.NodePath} path
66+
* @return {?Node}
67+
*/
68+
ArrowFunctionExpressionVisitor.prototype.visitThisExpression = function(path) {
69+
var arrowFnPath = this.associatedArrowFunctionPath(path);
70+
if (arrowFnPath) {
71+
arrowFnPath.node.hasThisExpression = true;
72+
}
73+
this.traverse(path);
74+
};
4275

43-
types.traverse(node.body, function(child) {
44-
// don't look inside non-arrow functions
45-
if (n.Function.check(child) && !n.ArrowFunctionExpression.check(child)) {
46-
return false;
47-
}
76+
/**
77+
* Ensures that `arguments` directly contained in arrow functions is hoisted.
78+
*
79+
* @param {types.NodePath} path
80+
* @return {?Node}
81+
*/
82+
ArrowFunctionExpressionVisitor.prototype.visitIdentifier = function(path) {
83+
var node = path.node;
4884

49-
if (n.ThisExpression.check(child)) {
50-
foundThisExpression = true;
85+
if (node.name === 'arguments' && util.isReference(path)) {
86+
var functionScope = this.associatedFunctionScope(path);
87+
if (functionScope) {
88+
return util.sharedFor(functionScope, node.name);
5189
}
90+
}
5291

53-
if (util.isReference(this) && child.name === 'arguments') {
54-
this.replace(util.sharedFor(scope, 'arguments'));
55-
}
56-
});
92+
this.traverse(path);
93+
};
5794

58-
if (foundThisExpression) {
59-
this.replace(b.callExpression(
60-
b.memberExpression(node, b.identifier('bind'), false),
61-
[b.thisExpression()]
62-
));
95+
/**
96+
* @private
97+
* @param {types.NodePath} path
98+
* @return {?types.NodePath} The arrow function directly `path`, if any.
99+
*/
100+
ArrowFunctionExpressionVisitor.prototype.associatedArrowFunctionPath = function(path) {
101+
var scope = path.scope;
102+
if (n.ArrowFunctionExpression.check(scope.path.node)) {
103+
return scope.path;
63104
}
64-
}
105+
};
106+
107+
/**
108+
* @private
109+
* @param {types.NodePath} path
110+
* @return {?types.Scope} The nearest non-arrow function scope above `path`.
111+
*/
112+
ArrowFunctionExpressionVisitor.prototype.associatedFunctionScope = function(path) {
113+
var scope = path.scope;
114+
while (scope && n.ArrowFunctionExpression.check(scope.path.node)) {
115+
scope = scope.parent;
116+
}
117+
return scope;
118+
};
65119

66120
/**
67121
* Transform an Esprima AST generated from ES6 by replacing all
@@ -77,7 +131,7 @@ function visitNode(node) {
77131
* @return {Object}
78132
*/
79133
function transform(ast) {
80-
return types.traverse(ast, visitNode);
134+
return types.visit(ast, new ArrowFunctionExpressionVisitor());
81135
}
82136

83137
/**
@@ -87,6 +141,7 @@ function transform(ast) {
87141
* compile('() => 42'); // 'function() { return 42; };'
88142
*
89143
* @param {string} source
144+
* @param {object} mapOptions
90145
* @return {string}
91146
*/
92147
function compile(source, mapOptions) {

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"dependencies": {
1515
"ast-util": "^0.4.1",
1616
"esprima-fb": "^5001.1.0-dev-harmony-fb",
17-
"recast": "~0.5.12",
17+
"recast": "^0.7.0",
1818
"through": "~2.3.4"
1919
},
2020
"devDependencies": {
@@ -25,4 +25,4 @@
2525
},
2626
"author": "Square, Inc.",
2727
"license": "Apache 2"
28-
}
28+
}

0 commit comments

Comments
 (0)