Skip to content

Commit

Permalink
use estree-walker to properly parse es6 (#31)
Browse files Browse the repository at this point in the history
remove comment
  • Loading branch information
lattsi authored and nolanlawson committed Oct 31, 2016
1 parent fc35e88 commit 604e862
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 50 deletions.
95 changes: 46 additions & 49 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,69 +1,66 @@
'use strict'

var acorn = require('acorn')
var walk = require('walk-ast')
var MagicString = require('magic-string')
var walk = require('estree-walker').walk

function optimizeJs (jsString, opts) {
opts = opts || {}
var ast = acorn.parse(jsString)
var magicString = new MagicString(jsString)

function walkIt (node) {
if (node.type === 'FunctionExpression') {
handleFunctionExpression(node)
}
}

function handleFunctionExpression (node) {
var prePreChar = jsString.charAt(node.start - 2)
var preChar = jsString.charAt(node.start - 1)
var postChar = jsString.charAt(node.end)
var postPostChar = jsString.charAt(node.end + 1)

// assuming this node is an argument to a function, return true if it itself
// is already padded with parentheses
function isPaddedArgument (node) {
var idx = node.parentNode.arguments.indexOf(node)
if (idx === 0) { // first arg
if (prePreChar === '(' && preChar === '(' && postChar === ')') { // already padded
return true
}
} else if (idx === node.parentNode.arguments.length - 1) { // last arg
if (preChar === '(' && postChar === ')' && postPostChar === ')') { // already padded
return true
}
} else { // middle arg
if (preChar === '(' && postChar === ')') { // already padded
return true
walk(ast, {
enter: function (node, parent) {
// assuming this node is an argument to a function, return true if it itself
// is already padded with parentheses
function isPaddedArgument (node) {
var idx = parent.arguments.indexOf(node)
if (idx === 0) { // first arg
if (prePreChar === '(' && preChar === '(' && postChar === ')') { // already padded
return true
}
} else if (idx === parent.arguments.length - 1) { // last arg
if (preChar === '(' && postChar === ')' && postPostChar === ')') { // already padded
return true
}
} else { // middle arg
if (preChar === '(' && postChar === ')') { // already padded
return true
}
}
return false
}
return false
}

if (node.parentNode && node.parentNode.type === 'CallExpression') {
// this function is getting called itself or
// it is getting passed in to another call expression
// the else statement is strictly never hit, but I think the code is easier to read this way
/* istanbul ignore else */
if (node.parentNode.arguments.length && node.parentNode.arguments.indexOf(node) !== -1) {
// function passed in to another function. these are almost _always_ executed, e.g.
// UMD bundles, Browserify bundles, Node-style errbacks, Promise then()s and catch()s, etc.
if (!isPaddedArgument(node)) { // don't double-pad
magicString = magicString.insertLeft(node.start, '(')
.insertRight(node.end, ')')
}
} else if (node.parentNode.callee === node) {
// this function is getting immediately invoked, e.g. function(){}()
if (preChar !== '(') {
magicString.insertLeft(node.start, '(')
.insertRight(node.end, ')')
if (node.type === 'FunctionExpression') {
var prePreChar = jsString.charAt(node.start - 2)
var preChar = jsString.charAt(node.start - 1)
var postChar = jsString.charAt(node.end)
var postPostChar = jsString.charAt(node.end + 1)

if (parent && parent.type === 'CallExpression') {
// this function is getting called itself or
// it is getting passed in to another call expression
// the else statement is strictly never hit, but I think the code is easier to read this way
/* istanbul ignore else */
if (parent.arguments && parent.arguments.indexOf(node) !== -1) {
// function passed in to another function. these are almost _always_ executed, e.g.
// UMD bundles, Browserify bundles, Node-style errbacks, Promise then()s and catch()s, etc.
if (!isPaddedArgument(node)) { // don't double-pad
magicString = magicString.insertLeft(node.start, '(')
.insertRight(node.end, ')')
}
} else if (parent.callee === node) {
// this function is getting immediately invoked, e.g. function(){}()
if (preChar !== '(') {
magicString.insertLeft(node.start, '(')
.insertRight(node.end, ')')
}
}
}
}
}
}
})

walk(ast, walkIt)
var out = magicString.toString()
if (opts.sourceMap) {
out += '\n//# sourceMappingURL=' + magicString.generateMap().toUrl()
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
"dependencies": {
"acorn": "^3.3.0",
"concat-stream": "^1.5.1",
"estree-walker": "^0.3.0",
"magic-string": "^0.16.0",
"walk-ast": "^0.0.2",
"yargs": "^4.8.1"
},
"devDependencies": {
Expand Down
7 changes: 7 additions & 0 deletions test/cases/arrow-function-no-error/input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// examples taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
var func = x => x * x;
var func = (x, y) => { return x + y; };

setInterval(() => {
this.age++;
}, 1000);
7 changes: 7 additions & 0 deletions test/cases/arrow-function-no-error/output.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// examples taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
var func = x => x * x;
var func = (x, y) => { return x + y; };

setInterval(() => {
this.age++;
}, 1000);

0 comments on commit 604e862

Please sign in to comment.