Skip to content

Commit cd00deb

Browse files
aickinnolanlawson
authored andcommitted
Webpack-specific fix for issue #7 (#33)
* Fix for #7: Not as optimized for Webpack as it could be. Adds ability to wrap functions that are elements of an array literal in an argument list * Added a few negative case tests in response to code review. * Modified the array literal logic to very specifically track with webpack patterns rather than all array literal function parameters.
1 parent 94a5714 commit cd00deb

File tree

15 files changed

+138
-22
lines changed

15 files changed

+138
-22
lines changed

lib/index.js

Lines changed: 68 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,25 @@ function optimizeJs (jsString, opts) {
1111

1212
walk(ast, {
1313
enter: function (node, parent) {
14-
// assuming this node is an argument to a function, return true if it itself
15-
// is already padded with parentheses
14+
// estree-walker does not automatically add a parent node pointer to nodes,
15+
// which we need for some of our context checks below.
16+
// normally I would just write "node.parentNode = parent" here, but that makes
17+
// estree-walker think that parentNode is a child node of the node, which leads to
18+
// infinite loops as it walks a circular tree. if we make parent a function, though,
19+
// estree-walker does not follow the link.
20+
node.parent = function () {
21+
return parent
22+
}
23+
// assuming this node is an argument to a function or an element in an array,
24+
// return true if it itself is already padded with parentheses
1625
function isPaddedArgument (node) {
17-
var idx = parent.arguments.indexOf(node)
26+
var parentArray = node.parent().arguments ? node.parent().arguments : node.parent().elements
27+
var idx = parentArray.indexOf(node)
1828
if (idx === 0) { // first arg
1929
if (prePreChar === '(' && preChar === '(' && postChar === ')') { // already padded
2030
return true
2131
}
22-
} else if (idx === parent.arguments.length - 1) { // last arg
32+
} else if (idx === parentArray.length - 1) { // last arg
2333
if (preChar === '(' && postChar === ')' && postPostChar === ')') { // already padded
2434
return true
2535
}
@@ -31,30 +41,66 @@ function optimizeJs (jsString, opts) {
3141
return false
3242
}
3343

44+
function isCallExpression (node) {
45+
return node && node.type === 'CallExpression'
46+
}
47+
48+
function isArrayExpression (node) {
49+
return node && node.type === 'ArrayExpression'
50+
}
51+
52+
// returns true iff node is an argument to a function call expression.
53+
function isArgumentToFunctionCall (node) {
54+
return isCallExpression(node.parent()) &&
55+
node.parent().arguments.length &&
56+
node.parent().arguments.indexOf(node) !== -1
57+
}
58+
59+
// returns true iff node is an element of an array literal which is in turn
60+
// an argument to a function call expression.
61+
function isElementOfArrayArgumentToFunctionCall (node) {
62+
return isArrayExpression(node.parent()) &&
63+
node.parent().elements.indexOf(node) !== -1 &&
64+
isArgumentToFunctionCall(node.parent())
65+
}
66+
67+
// returns true iff node is an IIFE.
68+
function isIIFE (node) {
69+
return isCallExpression(node.parent()) &&
70+
node.parent().callee === node
71+
}
72+
73+
// tries to divine if this function is a webpack module wrapper.
74+
// returns true iff node is an element of an array literal which is in turn
75+
// an argument to a function call expression, and that function call
76+
// expression is an IIFE.
77+
function isProbablyWebpackModule (node) {
78+
return isElementOfArrayArgumentToFunctionCall(node) &&
79+
node.parent() && // array literal
80+
node.parent().parent() && // CallExpression
81+
node.parent().parent().callee && // function that is being called
82+
node.parent().parent().callee.type === 'FunctionExpression'
83+
}
84+
3485
if (node.type === 'FunctionExpression') {
3586
var prePreChar = jsString.charAt(node.start - 2)
3687
var preChar = jsString.charAt(node.start - 1)
3788
var postChar = jsString.charAt(node.end)
3889
var postPostChar = jsString.charAt(node.end + 1)
3990

40-
if (parent && parent.type === 'CallExpression') {
41-
// this function is getting called itself or
42-
// it is getting passed in to another call expression
43-
// the else statement is strictly never hit, but I think the code is easier to read this way
44-
/* istanbul ignore else */
45-
if (parent.arguments && parent.arguments.indexOf(node) !== -1) {
46-
// function passed in to another function. these are almost _always_ executed, e.g.
47-
// UMD bundles, Browserify bundles, Node-style errbacks, Promise then()s and catch()s, etc.
48-
if (!isPaddedArgument(node)) { // don't double-pad
49-
magicString = magicString.insertLeft(node.start, '(')
50-
.insertRight(node.end, ')')
51-
}
52-
} else if (parent.callee === node) {
53-
// this function is getting immediately invoked, e.g. function(){}()
54-
if (preChar !== '(') {
55-
magicString.insertLeft(node.start, '(')
56-
.insertRight(node.end, ')')
57-
}
91+
if (isArgumentToFunctionCall(node) || isProbablyWebpackModule(node)) {
92+
// function passed in to another function, either as an argument, or as an element
93+
// of an array argument. these are almost _always_ executed, e.g. webpack bundles,
94+
// UMD bundles, Browserify bundles, Node-style errbacks, Promise then()s and catch()s, etc.
95+
if (!isPaddedArgument(node)) { // don't double-pad
96+
magicString = magicString.insertLeft(node.start, '(')
97+
.insertRight(node.end, ')')
98+
}
99+
} else if (isIIFE(node)) {
100+
// this function is getting immediately invoked, e.g. function(){}()
101+
if (preChar !== '(') {
102+
magicString.insertLeft(node.start, '(')
103+
.insertRight(node.end, ')')
58104
}
59105
}
60106
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
function b() {
2+
var a = [
3+
!function() {return 1;}()
4+
];
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
function b() {
2+
var a = [
3+
!(function() {return 1;})()
4+
];
5+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
var a = [
2+
!function() {return 1;}()
3+
]
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
var a = [
2+
!(function() {return 1;})()
3+
]
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
foo([
2+
function(o,r,t){
3+
console.log("element 0");
4+
}
5+
]);
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
foo([
2+
function(o,r,t){
3+
console.log("element 0");
4+
}
5+
]);
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
function b() {
2+
var a = [
3+
function() {return 1;}
4+
];
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
function b() {
2+
var a = [
3+
function() {return 1;}
4+
];
5+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
var a = [
2+
function() {return 1;}
3+
]
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
var a = [
2+
function() {return 1;}
3+
]
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
!function(o){
2+
return o[0]();
3+
}([function(o,r,t){
4+
console.log("webpack style element 0");
5+
},function(o,r,t){
6+
console.log("webpack style element 1");
7+
},function(o,r,t){
8+
console.log("webpack style element 2");
9+
}]);
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
!(function(o){
2+
return o[0]();
3+
})([(function(o,r,t){
4+
console.log("webpack style element 0");
5+
}),(function(o,r,t){
6+
console.log("webpack style element 1");
7+
}),(function(o,r,t){
8+
console.log("webpack style element 2");
9+
})]);
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
!function(o){
2+
return o[0]();
3+
}([function(o,r,t){
4+
console.log("webpack style!");
5+
}]);
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
!(function(o){
2+
return o[0]();
3+
})([(function(o,r,t){
4+
console.log("webpack style!");
5+
})]);

0 commit comments

Comments
 (0)