babel-plugin-closure-elimination
Advanced tools
Comparing version 1.1.16 to 1.2.0
124
lib/index.js
@@ -54,13 +54,30 @@ 'use strict'; | ||
function _ref9(_ref8) { | ||
var path = _ref8.path; | ||
return !path.isProgram() || path.node.sourceType === 'module'; | ||
} | ||
function getNearestScopeWithLocalUsedVars(path) { | ||
var disableRoot = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; | ||
function getAllParentScopes(scope) { | ||
var scopes = []; | ||
while (scope = scope.parent) { | ||
scopes.push(scope); | ||
var scope = path.scope, | ||
rootScope = path.scope.getProgramParent(); | ||
function _ref8(refPath) { | ||
return refPath.isDescendant(path); | ||
} | ||
return scopes; | ||
do { | ||
scope = scope.parent; | ||
var bindings = scope.getAllBindings(); | ||
for (var id in bindings) { | ||
if (!scope.hasOwnBinding(id)) { | ||
break; | ||
} | ||
var references = [].concat(bindings[id].referencePaths).concat(bindings[id].constantViolations), | ||
usedReferences = references.filter(_ref8); | ||
if (usedReferences.length) { | ||
return scope; | ||
} | ||
} | ||
if (disableRoot && scope.parent === rootScope) { | ||
return scope; | ||
} | ||
} while (scope.parent); | ||
return scope; | ||
} | ||
@@ -88,36 +105,44 @@ | ||
function _exit(path) { | ||
var node = path.node; | ||
if (path.node._hoisted) { | ||
return; | ||
} | ||
if (path.isClassMethod() || path.isObjectMethod() || node[$boundArrowFunction] || node[$usedEval]) { | ||
return; | ||
} | ||
if (path.findParent(_ref2)) { | ||
path.skip(); | ||
return; | ||
} | ||
var bestParentScope = getHighestCompatibleHoistedScope(path); | ||
if (bestParentScope) { | ||
var attachPath = getAttachmentPosition(bestParentScope.path, path); | ||
if (attachPath) { | ||
// _logAllProgram(path, 'before');// debug | ||
moveToNewPosition(path, attachPath); | ||
// _logAllProgram(path, 'after');// debug | ||
} | ||
} | ||
} | ||
return { | ||
visitor: { | ||
Function: { | ||
Program: { | ||
exit: function exit(path) { | ||
var node = path.node; | ||
path.scope.crawl(); // sibling plugins may not update scope of auto-generated functions | ||
if (path.node._hoisted) { | ||
return; | ||
} | ||
if (path.isClassMethod() || path.isObjectMethod() || node[$boundArrowFunction] || node[$usedEval]) { | ||
return; | ||
} | ||
if (path.findParent(_ref2)) { | ||
path.skip(); | ||
return; | ||
} | ||
var bestParentScope = getHighestCompatibleHoistedScope(path); | ||
if (bestParentScope) { | ||
var attachPath = getAttachmentPosition(bestParentScope.path, path); | ||
if (attachPath) { | ||
// _logAllProgram(path, 'before');// debug | ||
moveToNewPosition(path, attachPath); | ||
// _logAllProgram(path, 'after');// debug | ||
babel.traverse.clearCache(); | ||
path.scope.crawl(); | ||
path.traverse({ | ||
Function: { | ||
exit: _exit | ||
}, | ||
ThisExpression: { | ||
enter: _enter | ||
}, | ||
Identifier: { | ||
enter: _enter2 | ||
} | ||
} | ||
}); | ||
} | ||
}, | ||
ThisExpression: { | ||
enter: _enter | ||
}, | ||
Identifier: { | ||
enter: _enter2 | ||
} | ||
@@ -128,25 +153,6 @@ } | ||
function getHighestCompatibleHoistedScope(path) { | ||
var parentScopes = getAllParentScopes(path.scope), | ||
parentBindings = path.scope.parent.getAllBindings(); | ||
for (var id in parentBindings) { | ||
var parentBinding = parentBindings[id], | ||
idx = parentScopes.indexOf(parentBinding.scope); | ||
if (idx === -1) { | ||
continue; | ||
} | ||
var hasUsageOfBinding = [].concat(parentBinding.referencePaths).concat(parentBinding.constantViolations).some(hasInPath); | ||
if (hasUsageOfBinding) { | ||
parentScopes.splice(idx + 1, Infinity); | ||
} | ||
var scope = getNearestScopeWithLocalUsedVars(path, path.scope.getProgramParent().path.node.sourceType !== 'module'); | ||
if (scope !== path.scope.parent) { | ||
return scope; | ||
} | ||
return parentScopes.filter(_ref9).filter(function (scope) { | ||
return scope !== path.scope.parent; | ||
}).pop(); | ||
function hasInPath(subPath) { | ||
while (subPath = subPath.parentPath) { | ||
if (subPath === path) { | ||
return true; | ||
} | ||
} | ||
} | ||
}function moveToNewPosition(path, attachPath) { | ||
@@ -153,0 +159,0 @@ var node = path.node, |
{ | ||
"name": "babel-plugin-closure-elimination", | ||
"version": "1.1.16", | ||
"version": "1.2.0", | ||
"description": "Removes closures from your JavaScript in the name of performance.", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
158
src/index.js
@@ -14,49 +14,56 @@ import 'babel-polyfill'; | ||
visitor: { | ||
Function: { | ||
exit (path) { | ||
const {node} = path; | ||
path.scope.crawl();// sibling plugins may not update scope of auto-generated functions | ||
if (path.node._hoisted) { | ||
return; | ||
} | ||
if (path.isClassMethod() || path.isObjectMethod() || node[$boundArrowFunction] || node[$usedEval]) { | ||
return; | ||
} | ||
if (path.findParent(({node}) => node._generated || node._compact)) { | ||
path.skip(); | ||
return; | ||
} | ||
const bestParentScope = getHighestCompatibleHoistedScope(path); | ||
if (bestParentScope) { | ||
const attachPath = getAttachmentPosition(bestParentScope.path, path); | ||
if (attachPath) { | ||
// _logAllProgram(path, 'before');// debug | ||
moveToNewPosition(path, attachPath); | ||
// _logAllProgram(path, 'after');// debug | ||
Program: { | ||
exit(path) { | ||
babel.traverse.clearCache() | ||
path.scope.crawl(); | ||
path.traverse({ | ||
Function: { | ||
exit (path) { | ||
const {node} = path; | ||
if (path.node._hoisted) { | ||
return; | ||
} | ||
if (path.isClassMethod() || path.isObjectMethod() || node[$boundArrowFunction] || node[$usedEval]) { | ||
return; | ||
} | ||
if (path.findParent(({node}) => node._generated || node._compact)) { | ||
path.skip(); | ||
return; | ||
} | ||
const bestParentScope = getHighestCompatibleHoistedScope(path); | ||
if (bestParentScope) { | ||
const attachPath = getAttachmentPosition(bestParentScope.path, path); | ||
if (attachPath) { | ||
// _logAllProgram(path, 'before');// debug | ||
moveToNewPosition(path, attachPath); | ||
// _logAllProgram(path, 'after');// debug | ||
} | ||
} | ||
} | ||
}, | ||
ThisExpression: { | ||
enter(path) { | ||
var parentFunctions = path.getAncestry() | ||
.filter(path=>path.isFunction()), | ||
nearestNoArrowFunction = parentFunctions | ||
.findIndex(path => path.type !== 'ArrowFunctionExpression'); | ||
parentFunctions.slice(0, nearestNoArrowFunction) | ||
.forEach(parentArrow => { | ||
parentArrow.node[$boundArrowFunction] = true; | ||
}); | ||
} | ||
}, | ||
Identifier: { | ||
enter(path) { | ||
if (path.node.name === 'eval' && path.parentPath.type === 'CallExpression') { | ||
path.getAncestry() | ||
.filter(path=>path.isFunction()) | ||
.forEach(parentArrow => { | ||
parentArrow.node[$usedEval] = true; | ||
}); | ||
} | ||
} | ||
} | ||
} | ||
}); | ||
} | ||
}, | ||
ThisExpression: { | ||
enter(path) { | ||
var parentFunctions = path.getAncestry() | ||
.filter(path=>path.isFunction()), | ||
nearestNoArrowFunction = parentFunctions | ||
.findIndex(path => path.type !== 'ArrowFunctionExpression'); | ||
parentFunctions.slice(0, nearestNoArrowFunction) | ||
.forEach(parentArrow => { | ||
parentArrow.node[$boundArrowFunction] = true; | ||
}); | ||
} | ||
}, | ||
Identifier: { | ||
enter(path) { | ||
if (path.node.name === 'eval' && path.parentPath.type === 'CallExpression') { | ||
path.getAncestry() | ||
.filter(path=>path.isFunction()) | ||
.forEach(parentArrow => { | ||
parentArrow.node[$usedEval] = true; | ||
}); | ||
} | ||
} | ||
} | ||
@@ -67,39 +74,36 @@ } | ||
function getHighestCompatibleHoistedScope(path) { | ||
const parentScopes = getAllParentScopes(path.scope), | ||
parentBindings = path.scope.parent.getAllBindings(); | ||
for (let id in parentBindings) { | ||
const parentBinding = parentBindings[id], | ||
idx = parentScopes.indexOf(parentBinding.scope); | ||
if (idx === -1) { | ||
continue; | ||
} | ||
let hasUsageOfBinding = [] | ||
.concat(parentBinding.referencePaths) | ||
.concat(parentBinding.constantViolations) | ||
.some(hasInPath); | ||
if (hasUsageOfBinding) { | ||
parentScopes.splice(idx + 1, Infinity); | ||
} | ||
const scope = getNearestScopeWithLocalUsedVars(path, path.scope.getProgramParent().path.node.sourceType !== 'module'); | ||
if(scope !== path.scope.parent) { | ||
return scope; | ||
} | ||
return parentScopes | ||
.filter(({path}) => !path.isProgram() || path.node.sourceType === 'module') | ||
.filter(scope => scope !== path.scope.parent) | ||
.pop(); | ||
function hasInPath(subPath) { | ||
while (subPath = subPath.parentPath) { | ||
if (subPath === path) { | ||
return true; | ||
} | ||
function getNearestScopeWithLocalUsedVars(path, disableRoot = false) { | ||
let scope = path.scope, | ||
rootScope = path.scope.getProgramParent(); | ||
do { | ||
scope = scope.parent; | ||
const bindings = scope.getAllBindings(); | ||
for(const id in bindings) { | ||
if(!scope.hasOwnBinding(id)) { | ||
break; | ||
} | ||
const references = [] | ||
.concat(bindings[id].referencePaths) | ||
.concat(bindings[id].constantViolations), | ||
usedReferences = references | ||
.filter((refPath) => refPath.isDescendant(path)) | ||
; | ||
if(usedReferences.length) { | ||
return scope; | ||
} | ||
} | ||
} | ||
if(disableRoot && scope.parent === rootScope) { | ||
return scope; | ||
} | ||
} while(scope.parent); | ||
return scope; | ||
} | ||
function getAllParentScopes(scope) { | ||
var scopes = []; | ||
while (scope = scope.parent) { | ||
scopes.push(scope); | ||
} | ||
return scopes; | ||
} | ||
function getAttachmentPosition(bestParent, prevPath) { | ||
@@ -106,0 +110,0 @@ const prevParents = prevPath.getAncestry(), |
15840
348