Skip to content

Commit f6e23e3

Browse files
committed
backported ES6 parser support
1 parent ee1b8c4 commit f6e23e3

File tree

2 files changed

+153
-50
lines changed

2 files changed

+153
-50
lines changed

lib/Parser.js

Lines changed: 152 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
MIT License http://www.opensource.org/licenses/mit-license.php
33
Author Tobias Koppers @sokra
44
*/
5-
var esprima = require("esprima");
5+
var acorn = require("acorn");
66
var Tapable = require("tapable");
77
var BasicEvaluatedExpression = require("./BasicEvaluatedExpression");
88

@@ -39,28 +39,34 @@ Parser.prototype.initializeEvaluating = function() {
3939
return new BasicEvaluatedExpression().setRegExp(expr.value).setRange(expr.range);
4040
});
4141
this.plugin("evaluate LogicalExpression", function(expr) {
42+
var left;
43+
var leftAsBool;
44+
var right;
4245
if(expr.operator === "&&") {
43-
var left = this.evaluateExpression(expr.left);
44-
var leftAsBool = left && left.asBool();
46+
left = this.evaluateExpression(expr.left);
47+
leftAsBool = left && left.asBool();
4548
if(leftAsBool === false) return left.setRange(expr.range);
4649
if(leftAsBool !== true) return;
47-
var right = this.evaluateExpression(expr.right);
50+
right = this.evaluateExpression(expr.right);
4851
return right.setRange(expr.range);
4952
} else if(expr.operator === "||") {
50-
var left = this.evaluateExpression(expr.left);
51-
var leftAsBool = left && left.asBool();
53+
left = this.evaluateExpression(expr.left);
54+
leftAsBool = left && left.asBool();
5255
if(leftAsBool === true) return left.setRange(expr.range);
5356
if(leftAsBool !== false) return;
54-
var right = this.evaluateExpression(expr.right);
57+
right = this.evaluateExpression(expr.right);
5558
return right.setRange(expr.range);
5659
}
5760
});
5861
this.plugin("evaluate BinaryExpression", function(expr) {
62+
var left;
63+
var right;
64+
var res;
5965
if(expr.operator === "+") {
60-
var left = this.evaluateExpression(expr.left);
61-
var right = this.evaluateExpression(expr.right);
66+
left = this.evaluateExpression(expr.left);
67+
right = this.evaluateExpression(expr.right);
6268
if(!left || !right) return;
63-
var res = new BasicEvaluatedExpression();
69+
res = new BasicEvaluatedExpression();
6470
if(left.isString()) {
6571
if(right.isString()) {
6672
res.setString(left.string + right.string);
@@ -112,37 +118,37 @@ Parser.prototype.initializeEvaluating = function() {
112118
res.setRange(expr.range);
113119
return res;
114120
} else if(expr.operator === "-") {
115-
var left = this.evaluateExpression(expr.left);
116-
var right = this.evaluateExpression(expr.right);
121+
left = this.evaluateExpression(expr.left);
122+
right = this.evaluateExpression(expr.right);
117123
if(!left || !right) return;
118124
if(!left.isNumber() || !right.isNumber()) return;
119-
var res = new BasicEvaluatedExpression();
125+
res = new BasicEvaluatedExpression();
120126
res.setNumber(left.number - right.number);
121127
res.setRange(expr.range);
122128
return res;
123129
} else if(expr.operator === "*") {
124-
var left = this.evaluateExpression(expr.left);
125-
var right = this.evaluateExpression(expr.right);
130+
left = this.evaluateExpression(expr.left);
131+
right = this.evaluateExpression(expr.right);
126132
if(!left || !right) return;
127133
if(!left.isNumber() || !right.isNumber()) return;
128-
var res = new BasicEvaluatedExpression();
134+
res = new BasicEvaluatedExpression();
129135
res.setNumber(left.number * right.number);
130136
res.setRange(expr.range);
131137
return res;
132138
} else if(expr.operator === "/") {
133-
var left = this.evaluateExpression(expr.left);
134-
var right = this.evaluateExpression(expr.right);
139+
left = this.evaluateExpression(expr.left);
140+
right = this.evaluateExpression(expr.right);
135141
if(!left || !right) return;
136142
if(!left.isNumber() || !right.isNumber()) return;
137-
var res = new BasicEvaluatedExpression();
143+
res = new BasicEvaluatedExpression();
138144
res.setNumber(left.number / right.number);
139145
res.setRange(expr.range);
140146
return res;
141147
} else if(expr.operator === "==" || expr.operator === "===") {
142-
var left = this.evaluateExpression(expr.left);
143-
var right = this.evaluateExpression(expr.right);
148+
left = this.evaluateExpression(expr.left);
149+
right = this.evaluateExpression(expr.right);
144150
if(!left || !right) return;
145-
var res = new BasicEvaluatedExpression();
151+
res = new BasicEvaluatedExpression();
146152
res.setRange(expr.range);
147153
if(left.isString() && right.isString()) {
148154
return res.setBoolean(left.string === right.string);
@@ -152,10 +158,10 @@ Parser.prototype.initializeEvaluating = function() {
152158
return res.setBoolean(left.bool === right.bool);
153159
}
154160
} else if(expr.operator === "!=" || expr.operator === "!==") {
155-
var left = this.evaluateExpression(expr.left);
156-
var right = this.evaluateExpression(expr.right);
161+
left = this.evaluateExpression(expr.left);
162+
right = this.evaluateExpression(expr.right);
157163
if(!left || !right) return;
158-
var res = new BasicEvaluatedExpression();
164+
res = new BasicEvaluatedExpression();
159165
res.setRange(expr.range);
160166
if(left.isString() && right.isString()) {
161167
return res.setBoolean(left.string !== right.string);
@@ -168,10 +174,11 @@ Parser.prototype.initializeEvaluating = function() {
168174
});
169175
this.plugin("evaluate UnaryExpression", function(expr) {
170176
if(expr.operator === "typeof") {
177+
var res;
171178
if(expr.argument.type === "Identifier") {
172179
var name = this.scope.renames["$" + expr.argument.name] || expr.argument.name;
173180
if(this.scope.definitions.indexOf(name) === -1) {
174-
var res = this.applyPluginsBailResult("evaluate typeof " + name, expr);
181+
res = this.applyPluginsBailResult("evaluate typeof " + name, expr);
175182
if(res !== undefined) return res;
176183
}
177184
}
@@ -186,7 +193,7 @@ Parser.prototype.initializeEvaluating = function() {
186193
exprName.unshift(this.scope.renames["$" + expression.name] || expression.name);
187194
if(this.scope.definitions.indexOf(name) === -1) {
188195
exprName = exprName.join(".");
189-
var res = this.applyPluginsBailResult("evaluate typeof " + exprName, expr);
196+
res = this.applyPluginsBailResult("evaluate typeof " + exprName, expr);
190197
if(res !== undefined) return res;
191198
}
192199
}
@@ -267,15 +274,16 @@ Parser.prototype.initializeEvaluating = function() {
267274
["substr", "substring"].forEach(function(fn) {
268275
this.plugin("evaluate CallExpression ." + fn, function(expr, param) {
269276
if(!param.isString()) return;
277+
var arg1;
270278
var result, str = param.string;
271279
switch(expr.arguments.length) {
272280
case 1:
273-
var arg1 = this.evaluateExpression(expr.arguments[0]);
281+
arg1 = this.evaluateExpression(expr.arguments[0]);
274282
if(!arg1.isNumber()) return;
275283
result = str[fn](arg1.number);
276284
break;
277285
case 2:
278-
var arg1 = this.evaluateExpression(expr.arguments[0]);
286+
arg1 = this.evaluateExpression(expr.arguments[0]);
279287
var arg2 = this.evaluateExpression(expr.arguments[1]);
280288
if(!arg1.isNumber()) return;
281289
if(!arg2.isNumber()) return;
@@ -302,11 +310,12 @@ Parser.prototype.initializeEvaluating = function() {
302310
this.plugin("evaluate ConditionalExpression", function(expr) {
303311
var condition = this.evaluateExpression(expr.test);
304312
var conditionValue = condition.asBool();
313+
var res;
305314
if(conditionValue === undefined) {
306315
var consequent = this.evaluateExpression(expr.consequent);
307316
var alternate = this.evaluateExpression(expr.alternate);
308317
if(!consequent || !alternate) return;
309-
var res = new BasicEvaluatedExpression();
318+
res = new BasicEvaluatedExpression();
310319
if(consequent.isConditional())
311320
res.setOptions(consequent.options);
312321
else
@@ -316,7 +325,7 @@ Parser.prototype.initializeEvaluating = function() {
316325
else
317326
res.addOptions([alternate]);
318327
} else {
319-
var res = this.evaluateExpression(conditionValue ? expr.consequent : expr.alternate);
328+
res = this.evaluateExpression(conditionValue ? expr.consequent : expr.alternate);
320329
}
321330
res.setRange(expr.range);
322331
return res;
@@ -339,6 +348,24 @@ Parser.prototype.getRenameIdentifier = function getRenameIdentifier(expr) {
339348
return;
340349
};
341350

351+
Parser.prototype.walkClass = function walkClass(classy) {
352+
if(classy.superClass)
353+
this.walkExpression(classy.superClass);
354+
if(classy.body && classy.body.type === "ClassBody") {
355+
classy.body.body.forEach(function(methodDefinition) {
356+
if(methodDefinition.type === "MethodDefinition")
357+
this.walkMethodDefinition(methodDefinition);
358+
}, this);
359+
}
360+
};
361+
362+
Parser.prototype.walkMethodDefinition = function walkMethodDefinition(methodDefinition) {
363+
if(methodDefinition.computed && methodDefinition.key)
364+
this.walkExpression(methodDefinition.key);
365+
if(methodDefinition.value)
366+
this.walkExpression(methodDefinition.value);
367+
};
368+
342369
Parser.prototype.walkStatements = function walkStatements(statements) {
343370
statements.forEach(function(statement) {
344371
this.walkStatement(statement);
@@ -405,7 +432,8 @@ Parser.prototype.walkTryStatement = function walkTryStatement(statement) {
405432
this.walkStatement(statement.block);
406433
this.scope.inTry = false;
407434
}
408-
this.walkCatchClauses(statement.handlers);
435+
if(statement.handler)
436+
this.walkCatchClause(statement.handler);
409437
if(statement.finalizer)
410438
this.walkStatement(statement.finalizer);
411439
};
@@ -439,6 +467,15 @@ Parser.prototype.walkForInStatement = function walkForInStatement(statement) {
439467
this.walkStatement(statement.body);
440468
};
441469

470+
Parser.prototype.walkForOfStatement = function walkForOfStatement(statement) {
471+
if(statement.left.type === "VariableDeclaration")
472+
this.walkStatement(statement.left);
473+
else
474+
this.walkExpression(statement.left);
475+
this.walkExpression(statement.right);
476+
this.walkStatement(statement.body);
477+
};
478+
442479
// Declarations
443480
Parser.prototype.walkFunctionDeclaration = function walkFunctionDeclaration(statement) {
444481
this.scope.renames["$" + statement.id.name] = undefined;
@@ -456,6 +493,10 @@ Parser.prototype.walkVariableDeclaration = function walkVariableDeclaration(stat
456493
this.walkVariableDeclarators(statement.declarations);
457494
};
458495

496+
Parser.prototype.walkClassDeclaration = function walkClassDeclaration(statement) {
497+
this.walkClass(statement);
498+
};
499+
459500
Parser.prototype.walkSwitchCases = function walkSwitchCases(switchCases) {
460501
switchCases.forEach(function(switchCase) {
461502
if(switchCase.test)
@@ -464,14 +505,12 @@ Parser.prototype.walkSwitchCases = function walkSwitchCases(switchCases) {
464505
}, this);
465506
};
466507

467-
Parser.prototype.walkCatchClauses = function walkCatchClauses(catchClauses) {
468-
catchClauses.forEach(function(catchClause) {
469-
if(catchClause.guard)
470-
this.walkExpression(catchClause.guard);
471-
this.inScope([catchClause.param], function() {
472-
this.walkStatement(catchClause.body);
473-
}.bind(this));
474-
}, this);
508+
Parser.prototype.walkCatchClause = function walkCatchClause(catchClause) {
509+
if(catchClause.guard)
510+
this.walkExpression(catchClause.guard);
511+
this.inScope([catchClause.param], function() {
512+
this.walkStatement(catchClause.body);
513+
}.bind(this));
475514
};
476515

477516
Parser.prototype.walkVariableDeclarators = function walkVariableDeclarators(declarators) {
@@ -518,8 +557,15 @@ Parser.prototype.walkArrayExpression = function walkArrayExpression(expression)
518557
this.walkExpressions(expression.elements);
519558
};
520559

560+
Parser.prototype.walkSpreadElement = function walkSpreadElement(expression) {
561+
if(expression.argument)
562+
this.walkExpression(expression.argument);
563+
};
564+
521565
Parser.prototype.walkObjectExpression = function walkObjectExpression(expression) {
522566
expression.properties.forEach(function(prop) {
567+
if(prop.computed)
568+
this.walkExpression(prop.key)
523569
this.walkExpression(prop.value);
524570
}, this);
525571
};
@@ -533,6 +579,15 @@ Parser.prototype.walkFunctionExpression = function walkFunctionExpression(expres
533579
}.bind(this));
534580
};
535581

582+
Parser.prototype.walkArrowFunctionExpression = function walkArrowFunctionExpression(expression) {
583+
this.inScope(expression.params, function() {
584+
if(expression.body.type === "BlockStatement")
585+
this.walkStatement(expression.body);
586+
else
587+
this.walkExpression(expression.body);
588+
}.bind(this));
589+
};
590+
536591
Parser.prototype.walkSequenceExpression = function walkSequenceExpression(expression) {
537592
if(expression.expressions)
538593
this.walkExpressions(expression.expressions);
@@ -612,6 +667,27 @@ Parser.prototype.walkNewExpression = function walkNewExpression(expression) {
612667
this.walkExpressions(expression.arguments);
613668
};
614669

670+
Parser.prototype.walkYieldExpression = function walkYieldExpression(expression) {
671+
if(expression.argument)
672+
this.walkExpression(expression.argument);
673+
};
674+
675+
Parser.prototype.walkTemplateLiteral = function walkTemplateLiteral(expression) {
676+
if(expression.expressions)
677+
this.walkExpressions(expression.expressions);
678+
};
679+
680+
Parser.prototype.walkTaggedTemplateExpression = function walkTaggedTemplateExpression(expression) {
681+
if(expression.tag)
682+
this.walkExpression(expression.tag);
683+
if(expression.quasi && expression.quasi.expressions)
684+
this.walkExpressions(expression.quasi.expressions);
685+
};
686+
687+
Parser.prototype.walkClassExpression = function walkClassExpression(expression) {
688+
this.walkClass(expression);
689+
};
690+
615691
Parser.prototype.walkCallExpression = function walkCallExpression(expression) {
616692
function walkIIFE(functionExpression, args) {
617693
var params = functionExpression.params;
@@ -798,12 +874,38 @@ Parser.prototype.parseCalculatedString = function parseCalculatedString(expressi
798874
};
799875
});
800876

877+
var POSSIBLE_AST_OPTIONS = [{
878+
ranges: true,
879+
locations: true,
880+
ecmaVersion: 6,
881+
sourceType: "module"
882+
}, {
883+
ranges: true,
884+
locations: true,
885+
ecmaVersion: 6,
886+
sourceType: "script"
887+
}]
888+
801889
Parser.prototype.parse = function parse(source, initialState) {
802-
var ast = esprima.parse(source, {
803-
range: true,
804-
loc: true,
805-
raw: true
806-
});
890+
var ast;
891+
for(var i = 0; i < POSSIBLE_AST_OPTIONS.length; i++) {
892+
if(!ast) {
893+
try {
894+
ast = acorn.parse(source, POSSIBLE_AST_OPTIONS[i]);
895+
} catch(e) {
896+
// ignore the error
897+
}
898+
}
899+
}
900+
if(!ast) {
901+
// for the error
902+
ast = acorn.parse(source, {
903+
ranges: true,
904+
locations: true,
905+
ecmaVersion: 6,
906+
sourceType: "module"
907+
});
908+
}
807909
if(!ast || typeof ast !== "object")
808910
throw new Error("Source couldn't be parsed");
809911
var oldScope = this.scope;
@@ -822,10 +924,11 @@ Parser.prototype.parse = function parse(source, initialState) {
822924
};
823925

824926
Parser.prototype.evaluate = function evaluate(source) {
825-
var ast = esprima.parse("(" + source + ")", {
826-
range: true,
827-
loc: true,
828-
raw: true
927+
var ast = acorn.parse("(" + source + ")", {
928+
ranges: true,
929+
locations: true,
930+
ecmaVersion: 6,
931+
sourceType: "module"
829932
});
830933
if(!ast || typeof ast !== "object" || ast.type !== "Program")
831934
throw new Error("evaluate: Source couldn't be parsed");

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"async": "^1.3.0",
88
"clone": "^1.0.2",
99
"enhanced-resolve": "~0.9.0",
10-
"esprima": "^2.5.0",
10+
"acorn": "^3.0.0",
1111
"interpret": "^0.6.4",
1212
"loader-utils": "^0.2.11",
1313
"memory-fs": "~0.3.0",

0 commit comments

Comments
 (0)