Skip to content

Commit

Permalink
Browserify-specific fix for #29 (#36)
Browse files Browse the repository at this point in the history
* 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.

* Added optimization of browserify modules.

* Refactor of browserify and webpack logic.
  • Loading branch information
aickin authored and nolanlawson committed Nov 2, 2016
1 parent 33d691e commit 0cba85e
Show file tree
Hide file tree
Showing 11 changed files with 182 additions and 13 deletions.
57 changes: 44 additions & 13 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ function optimizeJs (jsString, opts) {
return false
}

function isNumeric (str) {
return /^[0-9]+$/.test(str)
}

function isCallExpression (node) {
return node && node.type === 'CallExpression'
}
Expand All @@ -49,37 +53,62 @@ function optimizeJs (jsString, opts) {
return node && node.type === 'ArrayExpression'
}

function isElementOfArray (node) {
return isArrayExpression(node.parent()) &&
node.parent().elements.indexOf(node) !== -1
}

// returns true iff node is an argument to a function call expression.
function isArgumentToFunctionCall (node) {
return isCallExpression(node.parent()) &&
node.parent().arguments.length &&
node.parent().arguments.indexOf(node) !== -1
}

// returns true iff node is an element of an array literal which is in turn
// an argument to a function call expression.
function isElementOfArrayArgumentToFunctionCall (node) {
return isArrayExpression(node.parent()) &&
node.parent().elements.indexOf(node) !== -1 &&
isArgumentToFunctionCall(node.parent())
function isValueOfObjectLiteralWithNumericName (node) {
return node &&
node.parent() &&
node.parent().type === 'Property' &&
node.parent().key &&
node.parent().key.type === 'Literal' &&
node.parent().key.raw &&
isNumeric(node.parent().key.raw) &&
node.parent().value === node &&
node.parent().parent() &&
node.parent().parent().type === 'ObjectExpression'
}

// returns true iff node is an IIFE.
function isIIFE (node) {
return isCallExpression(node.parent()) &&
return node &&
node.type === 'FunctionExpression' &&
isCallExpression(node.parent()) &&
node.parent().callee === node
}

// returns true iff this is an IIFE call expression
function isIIFECall (node) {
return node &&
isCallExpression(node) &&
node.callee &&
node.callee.type === 'FunctionExpression'
}

// tries to divine if this function is a webpack module wrapper.
// returns true iff node is an element of an array literal which is in turn
// an argument to a function call expression, and that function call
// expression is an IIFE.
function isProbablyWebpackModule (node) {
return isElementOfArrayArgumentToFunctionCall(node) &&
node.parent() && // array literal
node.parent().parent() && // CallExpression
node.parent().parent().callee && // function that is being called
node.parent().parent().callee.type === 'FunctionExpression'
return isElementOfArray(node) &&
isArgumentToFunctionCall(node.parent()) &&
isIIFECall(node.parent().parent())
}

function isProbablyBrowserifyModule (node) {
return isElementOfArray(node) &&
isValueOfObjectLiteralWithNumericName(node.parent()) &&
isArgumentToFunctionCall(node.parent().parent().parent()) &&
isIIFECall(node.parent().parent().parent().parent())
}

if (node.type === 'FunctionExpression') {
Expand All @@ -88,7 +117,9 @@ function optimizeJs (jsString, opts) {
var postChar = jsString.charAt(node.end)
var postPostChar = jsString.charAt(node.end + 1)

if (isArgumentToFunctionCall(node) || isProbablyWebpackModule(node)) {
if (isArgumentToFunctionCall(node) ||
isProbablyWebpackModule(node) ||
isProbablyBrowserifyModule(node)) {
// function passed in to another function, either as an argument, or as an element
// of an array argument. these are almost _always_ executed, e.g. webpack bundles,
// UMD bundles, Browserify bundles, Node-style errbacks, Promise then()s and catch()s, etc.
Expand Down
9 changes: 9 additions & 0 deletions test/cases/browserify-object-literal-in-global-scope/input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// a browserify-style object literal that isn't a parameter shouldn't be optimized.
var a = {
1:[
function(o,r,t){
console.log("browserify style!");
},
{}
]
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// a browserify-style object literal that isn't a parameter shouldn't be optimized.
var a = {
1:[
function(o,r,t){
console.log("browserify style!");
},
{}
]
};
24 changes: 24 additions & 0 deletions test/cases/browserify-style-multi-element/input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
!function(o){
return o[0]();
}(
{
1:[
function(o,r,t){
console.log("browserify style!");
},
{}
],
2:[
function(o,r,t){
console.log("browserify style!");
},
{}
],
3:[
function(o,r,t){
console.log("browserify style!");
},
{}
]
}
);
24 changes: 24 additions & 0 deletions test/cases/browserify-style-multi-element/output.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
!(function(o){
return o[0]();
})(
{
1:[
(function(o,r,t){
console.log("browserify style!");
}),
{}
],
2:[
(function(o,r,t){
console.log("browserify style!");
}),
{}
],
3:[
(function(o,r,t){
console.log("browserify style!");
}),
{}
]
}
);
10 changes: 10 additions & 0 deletions test/cases/browserify-style-non-iife/input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
foo(
{
1:[
function(o,r,t){
console.log("browserify style!");
},
{}
]
}
);
10 changes: 10 additions & 0 deletions test/cases/browserify-style-non-iife/output.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
foo(
{
1:[
function(o,r,t){
console.log("browserify style!");
},
{}
]
}
);
12 changes: 12 additions & 0 deletions test/cases/browserify-style-one-element/input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
!function(o){
return o[0]();
}(
{
1:[
function(o,r,t){
console.log("browserify style!");
},
{}
]
}
);
12 changes: 12 additions & 0 deletions test/cases/browserify-style-one-element/output.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
!(function(o){
return o[0]();
})(
{
1:[
(function(o,r,t){
console.log("browserify style!");
}),
{}
]
}
);
14 changes: 14 additions & 0 deletions test/cases/browserify-style-with-non-numeric-propname/input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// a browserify-style function call that has non-numeric object indices shouldn't
// be optimized.
!function(o){
return o[0]();
}(
{
a:[
function(o,r,t){
console.log("browserify style!");
},
{}
]
}
);
14 changes: 14 additions & 0 deletions test/cases/browserify-style-with-non-numeric-propname/output.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// a browserify-style function call that has non-numeric object indices shouldn't
// be optimized.
!(function(o){
return o[0]();
})(
{
a:[
function(o,r,t){
console.log("browserify style!");
},
{}
]
}
);

0 comments on commit 0cba85e

Please sign in to comment.