babel-plugin-minify-mangle-names
Advanced tools
Comparing version 0.5.0-alpha.55276695 to 0.5.0-alpha.01eac1c3
"use strict"; | ||
module.exports = function bfsTraverseCreator(_ref) { | ||
var t = _ref.types, | ||
traverse = _ref.traverse; | ||
module.exports = function bfsTraverseCreator({ | ||
types: t, | ||
traverse | ||
}) { | ||
function getFields(path) { | ||
@@ -16,5 +16,5 @@ return t.VISITOR_KEYS[path.type]; | ||
var visitor = traverse.explode(_visitor); | ||
var queue = [path]; | ||
var current; | ||
const visitor = traverse.explode(_visitor); | ||
const queue = [path]; | ||
let current; | ||
@@ -25,3 +25,3 @@ while (queue.length > 0) { | ||
if (visitor && visitor[current.type] && Array.isArray(visitor[current.type].enter)) { | ||
var fns = visitor[current.type].enter; | ||
const fns = visitor[current.type].enter; | ||
var _iteratorNormalCompletion = true; | ||
@@ -33,4 +33,4 @@ var _didIteratorError = false; | ||
for (var _iterator = fns[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | ||
var _fn = _step.value; | ||
if (typeof _fn === "function") _fn(current); | ||
const fn = _step.value; | ||
if (typeof fn === "function") fn(current); | ||
} | ||
@@ -53,3 +53,3 @@ } catch (err) { | ||
var fields = getFields(current); | ||
const fields = getFields(current); | ||
var _iteratorNormalCompletion2 = true; | ||
@@ -61,4 +61,4 @@ var _didIteratorError2 = false; | ||
for (var _iterator2 = fields[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { | ||
var _field = _step2.value; | ||
var child = current.get(_field); | ||
const field = _step2.value; | ||
const child = current.get(field); | ||
@@ -73,4 +73,4 @@ if (Array.isArray(child)) { | ||
for (var _iterator3 = child[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { | ||
var _c = _step3.value; | ||
if (_c.node) queue.push(_c); | ||
const c = _step3.value; | ||
if (c.node) queue.push(c); | ||
} | ||
@@ -77,0 +77,0 @@ } catch (err) { |
"use strict"; | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } | ||
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } | ||
var CHARSET = ("abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ$_").split(""); | ||
module.exports = | ||
/*#__PURE__*/ | ||
function () { | ||
function Charset(shouldConsider) { | ||
var _this = this; | ||
_classCallCheck(this, Charset); | ||
const CHARSET = ("abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ$_").split(""); | ||
module.exports = class Charset { | ||
constructor(shouldConsider) { | ||
this.shouldConsider = shouldConsider; | ||
this.chars = CHARSET.slice(); | ||
this.frequency = {}; | ||
this.chars.forEach(function (c) { | ||
_this.frequency[c] = 0; | ||
this.chars.forEach(c => { | ||
this.frequency[c] = 0; | ||
}); | ||
@@ -28,51 +15,39 @@ this.finalized = false; | ||
_createClass(Charset, [{ | ||
key: "consider", | ||
value: function consider(str) { | ||
var _this2 = this; | ||
consider(str) { | ||
if (!this.shouldConsider) { | ||
return; | ||
} | ||
if (!this.shouldConsider) { | ||
return; | ||
str.split("").forEach(c => { | ||
if (this.frequency[c] != null) { | ||
this.frequency[c]++; | ||
} | ||
}); | ||
} | ||
str.split("").forEach(function (c) { | ||
if (_this2.frequency[c] != null) { | ||
_this2.frequency[c]++; | ||
} | ||
}); | ||
sort() { | ||
if (this.shouldConsider) { | ||
this.chars = this.chars.sort((a, b) => this.frequency[b] - this.frequency[a]); | ||
} | ||
}, { | ||
key: "sort", | ||
value: function sort() { | ||
var _this3 = this; | ||
if (this.shouldConsider) { | ||
this.chars = this.chars.sort(function (a, b) { | ||
return _this3.frequency[b] - _this3.frequency[a]; | ||
}); | ||
} | ||
this.finalized = true; | ||
} | ||
this.finalized = true; | ||
getIdentifier(num) { | ||
if (!this.finalized) { | ||
throw new Error("Should sort first"); | ||
} | ||
}, { | ||
key: "getIdentifier", | ||
value: function getIdentifier(num) { | ||
if (!this.finalized) { | ||
throw new Error("Should sort first"); | ||
} | ||
var ret = ""; | ||
num++; | ||
let ret = ""; | ||
num++; | ||
do { | ||
num--; | ||
ret += this.chars[num % this.chars.length]; | ||
num = Math.floor(num / this.chars.length); | ||
} while (num > 0); | ||
do { | ||
num--; | ||
ret += this.chars[num % this.chars.length]; | ||
num = Math.floor(num / this.chars.length); | ||
} while (num > 0); | ||
return ret; | ||
} | ||
}]); | ||
return ret; | ||
} | ||
return Charset; | ||
}(); | ||
}; |
@@ -1,16 +0,6 @@ | ||
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } | ||
"use strict"; | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } | ||
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } | ||
// Set that counts | ||
module.exports = | ||
/*#__PURE__*/ | ||
function () { | ||
function CountedSet() { | ||
_classCallCheck(this, CountedSet); | ||
module.exports = class CountedSet { | ||
constructor() { | ||
// because you can't simply extend Builtins yet | ||
@@ -20,36 +10,29 @@ this.map = new Map(); | ||
_createClass(CountedSet, [{ | ||
key: "keys", | ||
value: function keys() { | ||
return _toConsumableArray(this.map.keys()); | ||
} | ||
}, { | ||
key: "has", | ||
value: function has(value) { | ||
return this.map.has(value); | ||
} | ||
}, { | ||
key: "add", | ||
value: function add(value) { | ||
if (!this.has(value)) { | ||
this.map.set(value, 0); | ||
} | ||
keys() { | ||
return [...this.map.keys()]; | ||
} | ||
this.map.set(value, this.map.get(value) + 1); | ||
has(value) { | ||
return this.map.has(value); | ||
} | ||
add(value) { | ||
if (!this.has(value)) { | ||
this.map.set(value, 0); | ||
} | ||
}, { | ||
key: "delete", | ||
value: function _delete(value) { | ||
if (!this.has(value)) return; | ||
var count = this.map.get(value); | ||
if (count <= 1) { | ||
this.map.delete(value); | ||
} else { | ||
this.map.set(value, count - 1); | ||
} | ||
this.map.set(value, this.map.get(value) + 1); | ||
} | ||
delete(value) { | ||
if (!this.has(value)) return; | ||
const count = this.map.get(value); | ||
if (count <= 1) { | ||
this.map.delete(value); | ||
} else { | ||
this.map.set(value, count - 1); | ||
} | ||
}]); | ||
} | ||
return CountedSet; | ||
}(); | ||
}; |
@@ -0,1 +1,3 @@ | ||
"use strict"; | ||
// this fixes a bug where converting let to var | ||
@@ -11,10 +13,10 @@ // doesn't change the binding's scope to function scope | ||
var fnScope = path.scope.getFunctionParent() || path.scope.getProgramParent(); | ||
var bindingIds = path.getOuterBindingIdentifierPaths(); | ||
const fnScope = path.scope.getFunctionParent() || path.scope.getProgramParent(); | ||
const bindingIds = path.getOuterBindingIdentifierPaths(); | ||
for (var name in bindingIds) { | ||
var binding = path.scope.getBinding(name); // var isn't hoisted to fnScope | ||
for (const name in bindingIds) { | ||
const binding = path.scope.getBinding(name); // var isn't hoisted to fnScope | ||
if (binding.scope !== fnScope) { | ||
var existingBinding = fnScope.bindings[name]; // make sure we are clear that the fnScope doesn't already have | ||
const existingBinding = fnScope.bindings[name]; // make sure we are clear that the fnScope doesn't already have | ||
// an existing binding | ||
@@ -33,3 +35,3 @@ | ||
// binding.scope and fnScope | ||
var newName = fnScope.generateUid(binding.scope.generateUid(name)); // rename binding in the original scope | ||
const newName = fnScope.generateUid(binding.scope.generateUid(name)); // rename binding in the original scope | ||
@@ -36,0 +38,0 @@ mangler.rename(binding.scope, binding, name, newName); // move binding to fnScope as newName |
821
lib/index.js
"use strict"; | ||
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } | ||
const Charset = require("./charset"); | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
const ScopeTracker = require("./scope-tracker"); | ||
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } | ||
const isLabelIdentifier = require("./is-label-identifier"); | ||
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } | ||
const bfsTraverseCreator = require("./bfs-traverse"); | ||
var Charset = require("./charset"); | ||
const fixupVarScoping = require("./fixup-var-scoping"); | ||
var ScopeTracker = require("./scope-tracker"); | ||
const _require = require("babel-helper-mark-eval-scopes"), | ||
markEvalScopes = _require.markEvalScopes, | ||
isEvalScopesMarked = _require.isMarked, | ||
hasEval = _require.hasEval; | ||
var isLabelIdentifier = require("./is-label-identifier"); | ||
const newIssueUrl = "https://github.com/babel/minify/issues/new"; | ||
var bfsTraverseCreator = require("./bfs-traverse"); | ||
module.exports = babel => { | ||
const t = babel.types, | ||
traverse = babel.traverse; | ||
const bfsTraverse = bfsTraverseCreator(babel); | ||
const hop = Object.prototype.hasOwnProperty; | ||
var fixupVarScoping = require("./fixup-var-scoping"); | ||
var _require = require("babel-helper-mark-eval-scopes"), | ||
markEvalScopes = _require.markEvalScopes, | ||
isEvalScopesMarked = _require.isMarked, | ||
hasEval = _require.hasEval; | ||
var newIssueUrl = "https://github.com/babel/minify/issues/new"; | ||
module.exports = function (babel) { | ||
var t = babel.types, | ||
traverse = babel.traverse; | ||
var bfsTraverse = bfsTraverseCreator(babel); | ||
var hop = Object.prototype.hasOwnProperty; | ||
var Mangler = | ||
/*#__PURE__*/ | ||
function () { | ||
function Mangler(charset, program) { | ||
var _ref = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, | ||
_ref$exclude = _ref.exclude, | ||
exclude = _ref$exclude === void 0 ? {} : _ref$exclude, | ||
_ref$keepFnName = _ref.keepFnName, | ||
keepFnName = _ref$keepFnName === void 0 ? false : _ref$keepFnName, | ||
_ref$keepClassName = _ref.keepClassName, | ||
keepClassName = _ref$keepClassName === void 0 ? false : _ref$keepClassName, | ||
_ref$eval = _ref.eval, | ||
_eval = _ref$eval === void 0 ? false : _ref$eval, | ||
_ref$topLevel = _ref.topLevel, | ||
topLevel = _ref$topLevel === void 0 ? false : _ref$topLevel; | ||
_classCallCheck(this, Mangler); | ||
class Mangler { | ||
constructor(charset, program, { | ||
exclude = {}, | ||
keepFnName = false, | ||
keepClassName = false, | ||
eval: _eval = false, | ||
topLevel = false | ||
} = {}) { | ||
this.charset = charset; | ||
@@ -70,348 +52,391 @@ this.program = program; // user passed options | ||
_createClass(Mangler, [{ | ||
key: "run", | ||
value: function run() { | ||
this.crawlScope(); | ||
this.collect(); | ||
this.fixup(); | ||
this.charset.sort(); | ||
this.mangle(); | ||
} | ||
/** | ||
* Tells if a variable name is excluded | ||
* @param {String} name | ||
*/ | ||
run() { | ||
this.crawlScope(); | ||
this.collect(); | ||
this.fixup(); | ||
this.charset.sort(); | ||
this.mangle(); | ||
} | ||
/** | ||
* Tells if a variable name is excluded | ||
* @param {String} name | ||
*/ | ||
}, { | ||
key: "isExcluded", | ||
value: function isExcluded(name) { | ||
return hop.call(this.exclude, name) && this.exclude[name]; | ||
} | ||
/** | ||
* Clears traverse cache and recrawls the AST | ||
* | ||
* to recompute the bindings, references, other scope information | ||
* and paths because the other transformations in the same pipeline | ||
* (other plugins and presets) changes the AST and does NOT update | ||
* the scope objects | ||
*/ | ||
}, { | ||
key: "crawlScope", | ||
value: function crawlScope() { | ||
(traverse.clearCache || traverse.cache.clear)(); | ||
this.program.scope.crawl(); | ||
} | ||
isExcluded(name) { | ||
return hop.call(this.exclude, name) && this.exclude[name]; | ||
} | ||
/** | ||
* Clears traverse cache and recrawls the AST | ||
* | ||
* to recompute the bindings, references, other scope information | ||
* and paths because the other transformations in the same pipeline | ||
* (other plugins and presets) changes the AST and does NOT update | ||
* the scope objects | ||
*/ | ||
crawlScope() { | ||
(traverse.clearCache || traverse.cache.clear)(); | ||
this.program.scope.crawl(); | ||
} | ||
/** | ||
* Re-crawling comes with a side-effect that let->var conversion | ||
* reverts the update of the binding information (block to fn scope). | ||
* This function takes care of it by updating it again. | ||
* | ||
* TODO: This is unnecessary work and needs to be fixed in babel. | ||
* https://github.com/babel/babel/issues/4818 | ||
* | ||
* When this is removed, remember to remove fixup's dependency in | ||
* ScopeTracker | ||
*/ | ||
fixup() { | ||
fixupVarScoping(this); | ||
} | ||
/** | ||
* A single pass through the AST to collect info for | ||
* | ||
* 1. Scope Tracker | ||
* 2. Unsafe Scopes (direct eval scopes) | ||
* 3. Charset considerations for better gzip compression | ||
* | ||
* Traversed in the same fashion(BFS) the mangling is done | ||
*/ | ||
collect() { | ||
const mangler = this; | ||
const scopeTracker = mangler.scopeTracker; | ||
scopeTracker.addScope(this.program.scope); | ||
/** | ||
* Re-crawling comes with a side-effect that let->var conversion | ||
* reverts the update of the binding information (block to fn scope). | ||
* This function takes care of it by updating it again. | ||
* | ||
* TODO: This is unnecessary work and needs to be fixed in babel. | ||
* https://github.com/babel/babel/issues/4818 | ||
* | ||
* When this is removed, remember to remove fixup's dependency in | ||
* ScopeTracker | ||
* Same usage as in DCE, whichever runs first | ||
*/ | ||
}, { | ||
key: "fixup", | ||
value: function fixup() { | ||
fixupVarScoping(this); | ||
if (!isEvalScopesMarked(mangler.program)) { | ||
markEvalScopes(mangler.program); | ||
} | ||
/** | ||
* A single pass through the AST to collect info for | ||
* The visitors to be used in traversal. | ||
* | ||
* 1. Scope Tracker | ||
* 2. Unsafe Scopes (direct eval scopes) | ||
* 3. Charset considerations for better gzip compression | ||
* Note: BFS traversal supports only the `enter` handlers, `exit` | ||
* handlers are simply dropped without Errors | ||
* | ||
* Traversed in the same fashion(BFS) the mangling is done | ||
* Collects items defined in the ScopeTracker | ||
*/ | ||
}, { | ||
key: "collect", | ||
value: function collect() { | ||
var mangler = this; | ||
var scopeTracker = mangler.scopeTracker; | ||
scopeTracker.addScope(this.program.scope); | ||
const collectVisitor = { | ||
Scopable({ | ||
scope | ||
}) { | ||
scopeTracker.addScope(scope); // Collect bindings defined in the scope | ||
Object.keys(scope.bindings).forEach(name => { | ||
scopeTracker.addBinding(scope.bindings[name]); // add all constant violations as references | ||
scope.bindings[name].constantViolations.forEach(() => { | ||
scopeTracker.addReference(scope, scope.bindings[name], name); | ||
}); | ||
}); | ||
}, | ||
/** | ||
* Same usage as in DCE, whichever runs first | ||
* This is required because after function name transformation | ||
* plugin (part of es2015), the function name is NOT added to the | ||
* scope's bindings. So to fix this issue, we simply add a hack to | ||
* handle that case - fix it to the scope tree. | ||
* | ||
* Related: | ||
* - https://github.com/babel/minify/issues/829 | ||
*/ | ||
BindingIdentifier(path) { | ||
if ( // the parent has this id as the name | ||
(path.parentPath.isFunctionExpression({ | ||
id: path.node | ||
}) || path.parentPath.isClassExpression({ | ||
id: path.node | ||
})) && // and the id isn't yet added to the scope | ||
!hop.call(path.parentPath.scope.bindings, path.node.name)) { | ||
path.parentPath.scope.registerBinding("local", path.parentPath); | ||
} | ||
}, | ||
if (!isEvalScopesMarked(mangler.program)) { | ||
markEvalScopes(mangler.program); | ||
} | ||
/** | ||
* The visitors to be used in traversal. | ||
* This is necessary because, in Babel, the scope.references | ||
* does NOT contain the references in that scope. Only the program | ||
* scope (top most level) contains all the references. | ||
* | ||
* Note: BFS traversal supports only the `enter` handlers, `exit` | ||
* handlers are simply dropped without Errors | ||
* | ||
* Collects items defined in the ScopeTracker | ||
* We collect the references in a fashion where all the scopes between | ||
* and including the referenced scope and scope where it is declared | ||
* is considered as scope referencing that identifier | ||
*/ | ||
ReferencedIdentifier(path) { | ||
if (isLabelIdentifier(path)) { | ||
return; | ||
} | ||
const scope = path.scope, | ||
name = path.node.name; | ||
const binding = scope.getBinding(name); | ||
var collectVisitor = { | ||
Scopable(_ref2) { | ||
var scope = _ref2.scope; | ||
scopeTracker.addScope(scope); // Collect bindings defined in the scope | ||
if (!binding) { | ||
// Do not collect globals as they are already available via | ||
// babel's API | ||
if (scope.hasGlobal(name)) { | ||
return; | ||
} // This should NOT happen ultimately. Panic if this code block is | ||
// reached | ||
Object.keys(scope.bindings).forEach(function (name) { | ||
scopeTracker.addBinding(scope.bindings[name]); | ||
}); | ||
}, | ||
/** | ||
* This is necessary because, in Babel, the scope.references | ||
* does NOT contain the references in that scope. Only the program | ||
* scope (top most level) contains all the references. | ||
* | ||
* We collect the references in a fashion where all the scopes between | ||
* and including the referenced scope and scope where it is declared | ||
* is considered as scope referencing that identifier | ||
*/ | ||
ReferencedIdentifier(path) { | ||
if (isLabelIdentifier(path)) { | ||
return; | ||
} | ||
throw new Error(`Binding not found for ReferencedIdentifier "${name}" ` + `present in "${path.parentPath.type}". ` + `Please report this at ${newIssueUrl}`); | ||
} else { | ||
// Add it to our scope tracker if everything is fine | ||
scopeTracker.addReference(scope, binding, name); | ||
} | ||
} | ||
var scope = path.scope, | ||
name = path.node.name; | ||
var binding = scope.getBinding(name); | ||
}; | ||
/** | ||
* These visitors are for collecting the Characters used in the program | ||
* to measure the frequency and generate variable names for mangling so | ||
* as to improve the gzip compression - as gzip likes repetition | ||
*/ | ||
if (!binding) { | ||
// Do not collect globals as they are already available via | ||
// babel's API | ||
if (scope.hasGlobal(name)) { | ||
return; | ||
} // This should NOT happen ultimately. Panic if this code block is | ||
// reached | ||
if (this.charset.shouldConsider) { | ||
collectVisitor.Identifier = function Identifer(path) { | ||
const node = path.node; // We don't mangle properties, so we collect them as they contribute | ||
// to the frequency of characters | ||
throw new Error(`Binding not found for ReferencedIdentifier "${name}" ` + `present in "${path.parentPath.type}". ` + `Please report this at ${newIssueUrl}`); | ||
} else { | ||
// Add it to our scope tracker if everything is fine | ||
scopeTracker.addReference(scope, binding, name); | ||
} | ||
if (path.parentPath.isMemberExpression({ | ||
property: node | ||
}) || path.parentPath.isObjectProperty({ | ||
key: node | ||
})) { | ||
mangler.charset.consider(node.name); | ||
} | ||
}; | ||
collectVisitor.Literal = function Literal({ | ||
node | ||
}) { | ||
mangler.charset.consider(String(node.value)); | ||
}; | ||
/** | ||
* These visitors are for collecting the Characters used in the program | ||
* to measure the frequency and generate variable names for mangling so | ||
* as to improve the gzip compression - as gzip likes repetition | ||
*/ | ||
} // Traverse the AST | ||
if (this.charset.shouldConsider) { | ||
collectVisitor.Identifier = function Identifer(path) { | ||
var node = path.node; // We don't mangle properties, so we collect them as they contribute | ||
// to the frequency of characters | ||
if (path.parentPath.isMemberExpression({ | ||
property: node | ||
}) || path.parentPath.isObjectProperty({ | ||
key: node | ||
})) { | ||
mangler.charset.consider(node.name); | ||
} | ||
}; | ||
bfsTraverse(mangler.program, collectVisitor); | ||
} | ||
/** | ||
* Tells if a binding is exported as a NamedExport - so as to NOT mangle | ||
* | ||
* Babel treats NamedExports as a binding referenced by this NamedExport decl | ||
* @param {Binding} binding | ||
*/ | ||
collectVisitor.Literal = function Literal(_ref3) { | ||
var node = _ref3.node; | ||
mangler.charset.consider(String(node.value)); | ||
}; | ||
} // Traverse the AST | ||
bfsTraverse(mangler.program, collectVisitor); | ||
isExportedWithName(binding) { | ||
// short circuit | ||
if (!this.topLevel) { | ||
return false; | ||
} | ||
/** | ||
* Tells if a binding is exported as a NamedExport - so as to NOT mangle | ||
* | ||
* Babel treats NamedExports as a binding referenced by this NamedExport decl | ||
* @param {Binding} binding | ||
*/ | ||
}, { | ||
key: "isExportedWithName", | ||
value: function isExportedWithName(binding) { | ||
// short circuit | ||
if (!this.topLevel) { | ||
return false; | ||
} | ||
const refs = binding.referencePaths; | ||
var _iteratorNormalCompletion = true; | ||
var _didIteratorError = false; | ||
var _iteratorError = undefined; | ||
var refs = binding.referencePaths; | ||
var _iteratorNormalCompletion = true; | ||
var _didIteratorError = false; | ||
var _iteratorError = undefined; | ||
try { | ||
for (var _iterator = refs[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | ||
const ref = _step.value; | ||
if (ref.isExportNamedDeclaration()) { | ||
return true; | ||
} | ||
} // default | ||
} catch (err) { | ||
_didIteratorError = true; | ||
_iteratorError = err; | ||
} finally { | ||
try { | ||
for (var _iterator = refs[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | ||
var _ref4 = _step.value; | ||
if (_ref4.isExportNamedDeclaration()) { | ||
return true; | ||
} | ||
} // default | ||
} catch (err) { | ||
_didIteratorError = true; | ||
_iteratorError = err; | ||
if (!_iteratorNormalCompletion && _iterator.return != null) { | ||
_iterator.return(); | ||
} | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion && _iterator.return != null) { | ||
_iterator.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError) { | ||
throw _iteratorError; | ||
} | ||
if (_didIteratorError) { | ||
throw _iteratorError; | ||
} | ||
} | ||
return false; | ||
} | ||
/** | ||
* Mangle the scope | ||
* @param {Scope} scope | ||
*/ | ||
}, { | ||
key: "mangleScope", | ||
value: function mangleScope(scope) { | ||
var mangler = this; | ||
var scopeTracker = mangler.scopeTracker; // Unsafe Scope | ||
return false; | ||
} | ||
/** | ||
* Tells if the name can be mangled in the current observed scope with | ||
* the input binding | ||
* | ||
* @param {string} oldName the old name that needs to be mangled | ||
* @param {Binding} binding Binding of the name | ||
* @param {Scope} scope The current scope the mangler is run | ||
*/ | ||
if (!mangler.eval && hasEval(scope)) { | ||
return; | ||
} // Already visited | ||
// This is because for a function, in Babel, the function and | ||
// the function body's BlockStatement has the same scope, and will | ||
// be visited twice by the Scopable handler, and we want to mangle | ||
// it only once | ||
canMangle(oldName, binding, scope) { | ||
const cannotMangle = // arguments - for non-strict mode | ||
oldName === "arguments" || // labels | ||
binding.path.isLabeledStatement() || // ClassDeclaration has binding in two scopes | ||
// 1. The scope in which it is declared | ||
// 2. The class's own scope | ||
binding.path.isClassDeclaration() && binding.path === scope.path || // excluded | ||
this.isExcluded(oldName) || ( // function names | ||
this.keepFnName ? isFunction(binding.path) : false) || ( // class names | ||
this.keepClassName ? isClass(binding.path) : false) || // named export | ||
this.isExportedWithName(binding); | ||
return !cannotMangle; | ||
} | ||
/** | ||
* Tells if the newName can be used as a valid name for the input binding | ||
* in the input scope | ||
* | ||
* @param {string} newName the old name that needs to be mangled | ||
* @param {Binding} binding Binding of the name that this new name will replace | ||
* @param {Scope} scope The current scope the mangler is run | ||
*/ | ||
if (mangler.visitedScopes.has(scope)) { | ||
return; | ||
} | ||
mangler.visitedScopes.add(scope); // Helpers to generate names | ||
isValidName(newName, binding, scope) { | ||
return t.isValidIdentifier(newName) && !this.scopeTracker.hasBinding(scope, newName) && !scope.hasGlobal(newName) && !this.scopeTracker.hasReference(scope, newName) && this.scopeTracker.canUseInReferencedScopes(binding, newName); | ||
} | ||
/** | ||
* Mangle the scope | ||
* @param {Scope} scope | ||
*/ | ||
var i = 0; | ||
function getNext() { | ||
return mangler.charset.getIdentifier(i++); | ||
} | ||
mangleScope(scope) { | ||
const mangler = this; | ||
const scopeTracker = mangler.scopeTracker; // Unsafe Scope | ||
function resetNext() { | ||
i = 0; | ||
} | ||
if (!mangler.eval && hasEval(scope)) { | ||
return; | ||
} // Already visited | ||
// This is because for a function, in Babel, the function and | ||
// the function body's BlockStatement has the same scope, and will | ||
// be visited twice by the Scopable handler, and we want to mangle | ||
// it only once | ||
var bindings = scopeTracker.bindings.get(scope); | ||
var names = _toConsumableArray(bindings.keys()); | ||
/** | ||
* 1. Iterate through the list of BindingIdentifiers | ||
* 2. Rename each of them in-place | ||
* 3. Update the scope tree. | ||
*/ | ||
if (mangler.visitedScopes.has(scope)) { | ||
return; | ||
} | ||
mangler.visitedScopes.add(scope); | ||
const bindings = scopeTracker.bindings.get(scope); | ||
const names = [...bindings.keys()]; // A counter to generate names and reset | ||
// so we can reuse removed names | ||
for (var _i = 0; _i < names.length; _i++) { | ||
var oldName = names[_i]; | ||
var binding = bindings.get(oldName); // Names which should NOT be mangled | ||
let counter = 0; | ||
/** | ||
* 1. Iterate through the list of BindingIdentifiers | ||
* 2. Rename each of them in-place | ||
* 3. Update the scope tree. | ||
* | ||
* We cannot use a for..of loop over bindings.keys() | ||
* because (2) we rename in place and update the bindings | ||
* as we traverse through the keys | ||
*/ | ||
if ( // arguments - for non-strict mode | ||
oldName === "arguments" || // labels | ||
binding.path.isLabeledStatement() || // ClassDeclaration has binding in two scopes | ||
// 1. The scope in which it is declared | ||
// 2. The class's own scope | ||
binding.path.isClassDeclaration() && binding.path === scope.path || // excluded | ||
mangler.isExcluded(oldName) || ( // function names | ||
mangler.keepFnName ? isFunction(binding.path) : false) || ( // class names | ||
mangler.keepClassName ? isClass(binding.path) : false) || // named export | ||
mangler.isExportedWithName(binding)) { | ||
continue; | ||
} | ||
for (var _i = 0; _i < names.length; _i++) { | ||
const oldName = names[_i]; | ||
const binding = bindings.get(oldName); | ||
var next = void 0; | ||
if (mangler.canMangle(oldName, binding, scope)) { | ||
let next; | ||
do { | ||
next = getNext(); | ||
} while (!t.isValidIdentifier(next) || scopeTracker.hasBinding(scope, next) || scope.hasGlobal(next) || scopeTracker.hasReference(scope, next) || !scopeTracker.canUseInReferencedScopes(binding, next)); // Reset so variables which are removed can be reused | ||
next = mangler.charset.getIdentifier(counter++); | ||
} while (!mangler.isValidName(next, binding, scope)); // Reset so variables which are removed can be reused | ||
// | ||
// the following is an assumtion (for perf) | ||
// the length 3 is an assumption that if the oldName isn't | ||
// 1 or 2 characters, then probably we are not going to find | ||
// a name - because for almost all usecases we have 1 or 2 | ||
// character new names only. And for the edge cases where | ||
// one scope has lots and lots of variables, it's okay to | ||
// name something with 3 characters instead of 1 | ||
resetNext(); // Once we detected a valid `next` Identifier which could be used, | ||
if (oldName.length < 3) { | ||
counter = 0; | ||
} // Once we detected a valid `next` Identifier which could be used, | ||
// call the renamer | ||
mangler.rename(scope, binding, oldName, next); | ||
} | ||
} | ||
/** | ||
* The mangle function that traverses through all the Scopes in a BFS | ||
* fashion - calls mangleScope | ||
*/ | ||
} | ||
/** | ||
* The mangle function that traverses through all the Scopes in a BFS | ||
* fashion - calls mangleScope | ||
*/ | ||
}, { | ||
key: "mangle", | ||
value: function mangle() { | ||
var mangler = this; | ||
bfsTraverse(this.program, { | ||
Scopable(path) { | ||
if (!path.isProgram() || mangler.topLevel) mangler.mangleScope(path.scope); | ||
} | ||
}); | ||
} | ||
/** | ||
* Given a NodePath, collects all the Identifiers which are BindingIdentifiers | ||
* and replaces them with the new name | ||
* | ||
* For example, | ||
* var a = 1, { b } = c; // a and b are BindingIdentifiers | ||
* | ||
* @param {NodePath} path | ||
* @param {String} oldName | ||
* @param {String} newName | ||
* @param {Function} predicate | ||
*/ | ||
mangle() { | ||
const mangler = this; | ||
bfsTraverse(this.program, { | ||
Scopable(path) { | ||
if (!path.isProgram() || mangler.topLevel) mangler.mangleScope(path.scope); | ||
} | ||
}, { | ||
key: "renameBindingIds", | ||
value: function renameBindingIds(path, oldName, newName) { | ||
var predicate = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : function () { | ||
return true; | ||
}; | ||
var bindingIds = path.getBindingIdentifierPaths(true, false); | ||
}); | ||
} | ||
/** | ||
* Given a NodePath, collects all the Identifiers which are BindingIdentifiers | ||
* and replaces them with the new name | ||
* | ||
* For example, | ||
* var a = 1, { b } = c; // a and b are BindingIdentifiers | ||
* | ||
* @param {NodePath} path | ||
* @param {String} oldName | ||
* @param {String} newName | ||
* @param {Function} predicate | ||
*/ | ||
for (var name in bindingIds) { | ||
if (name !== oldName) continue; | ||
var _iteratorNormalCompletion2 = true; | ||
var _didIteratorError2 = false; | ||
var _iteratorError2 = undefined; | ||
try { | ||
for (var _iterator2 = bindingIds[name][Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { | ||
var _idPath = _step2.value; | ||
renameBindingIds(path, oldName, newName, predicate = () => true) { | ||
const bindingIds = path.getBindingIdentifierPaths(true, false); | ||
if (predicate(_idPath)) { | ||
_idPath.node.name = newName; // babel-7 don't requeue | ||
// idPath.replaceWith(t.identifier(newName)); | ||
for (const name in bindingIds) { | ||
if (name !== oldName) continue; | ||
var _iteratorNormalCompletion2 = true; | ||
var _didIteratorError2 = false; | ||
var _iteratorError2 = undefined; | ||
this.renamedNodes.add(_idPath.node); | ||
} | ||
try { | ||
for (var _iterator2 = bindingIds[name][Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { | ||
const idPath = _step2.value; | ||
if (predicate(idPath)) { | ||
idPath.node.name = newName; // babel-7 don't requeue | ||
// idPath.replaceWith(t.identifier(newName)); | ||
this.renamedNodes.add(idPath.node); | ||
} | ||
} catch (err) { | ||
_didIteratorError2 = true; | ||
_iteratorError2 = err; | ||
} | ||
} catch (err) { | ||
_didIteratorError2 = true; | ||
_iteratorError2 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion2 && _iterator2.return != null) { | ||
_iterator2.return(); | ||
} | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion2 && _iterator2.return != null) { | ||
_iterator2.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError2) { | ||
throw _iteratorError2; | ||
} | ||
if (_didIteratorError2) { | ||
throw _iteratorError2; | ||
} | ||
@@ -421,104 +446,100 @@ } | ||
} | ||
/** | ||
* The Renamer: | ||
* Renames the following for one Binding in a Scope | ||
* | ||
* 1. Binding in that Scope | ||
* 2. All the Binding's constant violations | ||
* 3. All its References | ||
* 4. Updates mangler.scopeTracker | ||
* 5. Updates Babel's Scope tracking | ||
* | ||
* @param {Scope} scope | ||
* @param {Binding} binding | ||
* @param {String} oldName | ||
* @param {String} newName | ||
*/ | ||
} | ||
/** | ||
* The Renamer: | ||
* Renames the following for one Binding in a Scope | ||
* | ||
* 1. Binding in that Scope | ||
* 2. All the Binding's constant violations | ||
* 3. All its References | ||
* 4. Updates mangler.scopeTracker | ||
* 5. Updates Babel's Scope tracking | ||
* | ||
* @param {Scope} scope | ||
* @param {Binding} binding | ||
* @param {String} oldName | ||
* @param {String} newName | ||
*/ | ||
}, { | ||
key: "rename", | ||
value: function rename(scope, binding, oldName, newName) { | ||
var mangler = this; | ||
var scopeTracker = mangler.scopeTracker; // rename at the declaration level | ||
this.renameBindingIds(binding.path, oldName, newName, function (idPath) { | ||
return idPath.node === binding.identifier; | ||
}); // update mangler's ScopeTracker | ||
rename(scope, binding, oldName, newName) { | ||
const mangler = this; | ||
const scopeTracker = mangler.scopeTracker; // rename at the declaration level | ||
scopeTracker.renameBinding(scope, oldName, newName); // update all constant violations | ||
this.renameBindingIds(binding.path, oldName, newName, idPath => idPath.node === binding.identifier); // update mangler's ScopeTracker | ||
var violations = binding.constantViolations; | ||
scopeTracker.renameBinding(scope, oldName, newName); // update all constant violations | ||
for (var i = 0; i < violations.length; i++) { | ||
if (violations[i].isLabeledStatement()) continue; | ||
this.renameBindingIds(violations[i], oldName, newName); | ||
scopeTracker.updateReference(violations[i].scope, binding, oldName, newName); | ||
} // update all referenced places | ||
const violations = binding.constantViolations; | ||
for (let i = 0; i < violations.length; i++) { | ||
if (violations[i].isLabeledStatement()) continue; | ||
this.renameBindingIds(violations[i], oldName, newName); | ||
scopeTracker.updateReference(violations[i].scope, binding, oldName, newName); | ||
} // update all referenced places | ||
var refs = binding.referencePaths; | ||
for (var _i2 = 0; _i2 < refs.length; _i2++) { | ||
var path = refs[_i2]; | ||
var node = path.node; | ||
const refs = binding.referencePaths; | ||
if (!path.isIdentifier()) { | ||
// Ideally, this should not happen | ||
// it happens in these places now - | ||
// case 1: Export Statements | ||
// This is a bug in babel | ||
// https://github.com/babel/babel/pull/3629 | ||
// case 2: Replacements in other plugins | ||
// eg: https://github.com/babel/minify/issues/122 | ||
// replacement in dce from `x` to `!x` gives referencePath as `!x` | ||
path.traverse({ | ||
ReferencedIdentifier(refPath) { | ||
if (refPath.node.name !== oldName) { | ||
return; | ||
} | ||
for (let i = 0; i < refs.length; i++) { | ||
const path = refs[i]; | ||
const node = path.node; | ||
var actualBinding = refPath.scope.getBinding(oldName); | ||
if (!path.isIdentifier()) { | ||
// Ideally, this should not happen | ||
// it happens in these places now - | ||
// case 1: Export Statements | ||
// This is a bug in babel | ||
// https://github.com/babel/babel/pull/3629 | ||
// case 2: Replacements in other plugins | ||
// eg: https://github.com/babel/minify/issues/122 | ||
// replacement in dce from `x` to `!x` gives referencePath as `!x` | ||
path.traverse({ | ||
ReferencedIdentifier(refPath) { | ||
if (refPath.node.name !== oldName) { | ||
return; | ||
} | ||
if (actualBinding !== binding) { | ||
return; | ||
} | ||
const actualBinding = refPath.scope.getBinding(oldName); | ||
refPath.node.name = newName; // babel-7 don't requeue | ||
// refPath.replaceWith(t.identifier(newName)); | ||
mangler.renamedNodes.add(refPath.node); | ||
scopeTracker.updateReference(refPath.scope, binding, oldName, newName); | ||
if (actualBinding !== binding) { | ||
return; | ||
} | ||
}); | ||
} else if (!isLabelIdentifier(path)) { | ||
if (path.node.name === oldName) { | ||
path.node.name = newName; // babel-7 don't requeue | ||
// path.replaceWith(t.identifier(newName)); | ||
refPath.node.name = newName; // babel-7 don't requeue | ||
// refPath.replaceWith(t.identifier(newName)); | ||
mangler.renamedNodes.add(path.node); | ||
scopeTracker.updateReference(path.scope, binding, oldName, newName); | ||
} else if (mangler.renamedNodes.has(path.node)) { | ||
// already renamed, | ||
// just update the references | ||
scopeTracker.updateReference(path.scope, binding, oldName, newName); | ||
} else { | ||
throw new Error(`Unexpected Rename Error: ` + `Trying to replace "${node.name}": from "${oldName}" to "${newName}". ` + `Please report it at ${newIssueUrl}`); | ||
mangler.renamedNodes.add(refPath.node); | ||
scopeTracker.updateReference(refPath.scope, binding, oldName, newName); | ||
} | ||
} // else label identifier - silently ignore | ||
} // update babel's internal tracking | ||
}); | ||
} else if (!isLabelIdentifier(path)) { | ||
if (path.node.name === oldName) { | ||
path.node.name = newName; // babel-7 don't requeue | ||
// path.replaceWith(t.identifier(newName)); | ||
mangler.renamedNodes.add(path.node); | ||
scopeTracker.updateReference(path.scope, binding, oldName, newName); | ||
} else if (mangler.renamedNodes.has(path.node)) { | ||
// already renamed, | ||
// just update the references | ||
scopeTracker.updateReference(path.scope, binding, oldName, newName); | ||
} else { | ||
throw new Error(`Unexpected Rename Error: ` + `Trying to replace "${node.name}": from "${oldName}" to "${newName}". ` + `Please report it at ${newIssueUrl}`); | ||
} | ||
} // else label identifier - silently ignore | ||
binding.identifier.name = newName; // update babel's internal scope tracking | ||
} // update babel's internal tracking | ||
var bindings = scope.bindings; | ||
bindings[newName] = binding; | ||
delete bindings[oldName]; | ||
} | ||
}]); | ||
return Mangler; | ||
}(); | ||
binding.identifier.name = newName; // update babel's internal scope tracking | ||
const bindings = scope.bindings; | ||
bindings[newName] = binding; | ||
delete bindings[oldName]; | ||
} | ||
} | ||
return { | ||
@@ -536,5 +557,5 @@ name: "minify-mangle-names", | ||
// sorting on the character set. Currently the number is pretty arbitrary. | ||
var shouldConsiderSource = path.getSource().length > 70000; | ||
var charset = new Charset(shouldConsiderSource); | ||
var mangler = new Mangler(charset, path, this.opts); | ||
const shouldConsiderSource = path.getSource().length > 70000; | ||
const charset = new Charset(shouldConsiderSource); | ||
const mangler = new Mangler(charset, path, this.opts); | ||
mangler.run(); | ||
@@ -554,5 +575,5 @@ } | ||
var map = {}; | ||
const map = {}; | ||
for (var i = 0; i < value.length; i++) { | ||
for (let i = 0; i < value.length; i++) { | ||
map[value[i]] = true; | ||
@@ -559,0 +580,0 @@ } |
@@ -0,5 +1,7 @@ | ||
"use strict"; | ||
module.exports = isLabelIdentifier; | ||
function isLabelIdentifier(path) { | ||
var node = path.node; | ||
const node = path.node; | ||
return path.parentPath.isLabeledStatement({ | ||
@@ -6,0 +8,0 @@ label: node |
"use strict"; | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
const CountedSet = require("./counted-set"); | ||
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } | ||
const isLabelIdentifier = require("./is-label-identifier"); | ||
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } | ||
var CountedSet = require("./counted-set"); | ||
var isLabelIdentifier = require("./is-label-identifier"); | ||
var newIssueUrl = "https://github.com/babel/minify/issues/new"; | ||
const newIssueUrl = "https://github.com/babel/minify/issues/new"; | ||
/** | ||
@@ -20,8 +14,4 @@ * ScopeTracker | ||
module.exports = | ||
/*#__PURE__*/ | ||
function () { | ||
function ScopeTracker() { | ||
_classCallCheck(this, ScopeTracker); | ||
module.exports = class ScopeTracker { | ||
constructor() { | ||
this.references = new Map(); | ||
@@ -36,146 +26,138 @@ this.bindings = new Map(); | ||
_createClass(ScopeTracker, [{ | ||
key: "addScope", | ||
value: function addScope(scope) { | ||
if (!this.references.has(scope)) { | ||
this.references.set(scope, new CountedSet()); | ||
} | ||
addScope(scope) { | ||
if (!this.references.has(scope)) { | ||
this.references.set(scope, new CountedSet()); | ||
} | ||
if (!this.bindings.has(scope)) { | ||
this.bindings.set(scope, new Map()); | ||
} | ||
if (!this.bindings.has(scope)) { | ||
this.bindings.set(scope, new Map()); | ||
} | ||
/** | ||
* Add reference to all Scopes between and including the ReferencedScope | ||
* and Binding's Scope | ||
* @param {Scope} scope | ||
* @param {Binding} binding | ||
* @param {String} name | ||
*/ | ||
} | ||
/** | ||
* Add reference to all Scopes between and including the ReferencedScope | ||
* and Binding's Scope | ||
* @param {Scope} scope | ||
* @param {Binding} binding | ||
* @param {String} name | ||
*/ | ||
}, { | ||
key: "addReference", | ||
value: function addReference(scope, binding, name) { | ||
var parent = scope; | ||
do { | ||
this.references.get(parent).add(name); | ||
addReference(scope, binding, name) { | ||
let parent = scope; | ||
if (!binding) { | ||
throw new Error(`Binding Not Found for ${name} during scopeTracker.addRefernce. ` + `Please report at ${newIssueUrl}`); | ||
} | ||
do { | ||
this.references.get(parent).add(name); | ||
if (binding.scope === parent) break; | ||
} while (parent = parent.parent); | ||
} | ||
/** | ||
* has a Reference in the given {Scope} or a child Scope | ||
* | ||
* Refer {addReference} to know why the following call will be valid | ||
* for detecting references in child Scopes | ||
* | ||
* @param {Scope} scope | ||
* @param {String} name | ||
*/ | ||
if (!binding) { | ||
throw new Error(`Binding Not Found for ${name} during scopeTracker.addRefernce. ` + `Please report at ${newIssueUrl}`); | ||
} | ||
}, { | ||
key: "hasReference", | ||
value: function hasReference(scope, name) { | ||
return this.references.get(scope).has(name); | ||
} | ||
/** | ||
* Update reference count in all scopes between and including the | ||
* Referenced Scope and the Binding's Scope | ||
* | ||
* @param {Scope} scope | ||
* @param {Binding} binding | ||
* @param {String} oldName | ||
* @param {String} newName | ||
*/ | ||
if (binding.scope === parent) break; | ||
} while (parent = parent.parent); | ||
} | ||
/** | ||
* has a Reference in the given {Scope} or a child Scope | ||
* | ||
* Refer {addReference} to know why the following call will be valid | ||
* for detecting references in child Scopes | ||
* | ||
* @param {Scope} scope | ||
* @param {String} name | ||
*/ | ||
}, { | ||
key: "updateReference", | ||
value: function updateReference(scope, binding, oldName, newName) { | ||
var parent = scope; | ||
do { | ||
var ref = this.references.get(parent); | ||
ref.delete(oldName); | ||
ref.add(newName); | ||
hasReference(scope, name) { | ||
return this.references.get(scope).has(name); | ||
} | ||
/** | ||
* Update reference count in all scopes between and including the | ||
* Referenced Scope and the Binding's Scope | ||
* | ||
* @param {Scope} scope | ||
* @param {Binding} binding | ||
* @param {String} oldName | ||
* @param {String} newName | ||
*/ | ||
if (!binding) { | ||
// Something went wrong - panic | ||
throw new Error("Binding Not Found during scopeTracker.updateRefernce " + `while updating "${oldName}" to "${newName}". ` + `Please report at ${newIssueUrl}`); | ||
} | ||
if (binding.scope === parent) break; | ||
} while (parent = parent.parent); | ||
} | ||
/** | ||
* has either a Binding or a Reference | ||
* @param {Scope} scope | ||
* @param {Binding} binding | ||
* @param {String} name | ||
*/ | ||
updateReference(scope, binding, oldName, newName) { | ||
let parent = scope; | ||
}, { | ||
key: "hasBindingOrReference", | ||
value: function hasBindingOrReference(scope, binding, name) { | ||
return this.hasReference(scope, name) || this.hasBinding(scope, name); | ||
} | ||
/** | ||
* For a Binding visit all places where the Binding is used and detect | ||
* if the newName {next} can be used in all these places | ||
* | ||
* 1. binding's own scope | ||
* 2. constant violations' scopes | ||
* 3. referencePaths' scopes | ||
* | ||
* @param {Binding} binding | ||
* @param {String} next | ||
*/ | ||
do { | ||
const ref = this.references.get(parent); | ||
ref.delete(oldName); | ||
ref.add(newName); | ||
}, { | ||
key: "canUseInReferencedScopes", | ||
value: function canUseInReferencedScopes(binding, next) { | ||
var tracker = this; | ||
if (!binding) { | ||
// Something went wrong - panic | ||
throw new Error("Binding Not Found during scopeTracker.updateRefernce " + `while updating "${oldName}" to "${newName}". ` + `Please report at ${newIssueUrl}`); | ||
} | ||
if (tracker.hasBindingOrReference(binding.scope, binding, next)) { | ||
return false; | ||
} // Safari raises a syntax error for a `let` or `const` declaration in a | ||
// `for` loop initialization that shadows a parent function's parameter. | ||
// https://github.com/babel/minify/issues/559 | ||
// https://bugs.webkit.org/show_bug.cgi?id=171041 | ||
// https://trac.webkit.org/changeset/217200/webkit/trunk/Source | ||
if (binding.scope === parent) break; | ||
} while (parent = parent.parent); | ||
} | ||
/** | ||
* has either a Binding or a Reference | ||
* @param {Scope} scope | ||
* @param {Binding} binding | ||
* @param {String} name | ||
*/ | ||
var maybeDecl = binding.path.parentPath; | ||
var isBlockScoped = maybeDecl.isVariableDeclaration({ | ||
kind: "let" | ||
}) || maybeDecl.isVariableDeclaration({ | ||
kind: "const" | ||
hasBindingOrReference(scope, binding, name) { | ||
return this.hasReference(scope, name) || this.hasBinding(scope, name); | ||
} | ||
/** | ||
* For a Binding visit all places where the Binding is used and detect | ||
* if the newName {next} can be used in all these places | ||
* | ||
* 1. binding's own scope | ||
* 2. constant violations' scopes | ||
* 3. referencePaths' scopes | ||
* | ||
* @param {Binding} binding | ||
* @param {String} next | ||
*/ | ||
canUseInReferencedScopes(binding, next) { | ||
const tracker = this; | ||
if (tracker.hasBindingOrReference(binding.scope, binding, next)) { | ||
return false; | ||
} // Safari raises a syntax error for a `let` or `const` declaration in a | ||
// `for` loop initialization that shadows a parent function's parameter. | ||
// https://github.com/babel/minify/issues/559 | ||
// https://bugs.webkit.org/show_bug.cgi?id=171041 | ||
// https://trac.webkit.org/changeset/217200/webkit/trunk/Source | ||
const maybeDecl = binding.path.parentPath; | ||
const isBlockScoped = maybeDecl.isVariableDeclaration({ | ||
kind: "let" | ||
}) || maybeDecl.isVariableDeclaration({ | ||
kind: "const" | ||
}); | ||
if (isBlockScoped) { | ||
const maybeFor = maybeDecl.parentPath; | ||
const isForLoopBinding = maybeFor.isForStatement({ | ||
init: maybeDecl.node | ||
}) || maybeFor.isForXStatement({ | ||
left: maybeDecl.node | ||
}); | ||
if (isBlockScoped) { | ||
var maybeFor = maybeDecl.parentPath; | ||
var isForLoopBinding = maybeFor.isForStatement({ | ||
init: maybeDecl.node | ||
}) || maybeFor.isForXStatement({ | ||
left: maybeDecl.node | ||
}); | ||
if (isForLoopBinding) { | ||
const fnParent = getFunctionParent(maybeFor); | ||
if (isForLoopBinding) { | ||
var fnParent = getFunctionParent(maybeFor); | ||
if (fnParent.isFunction({ | ||
body: maybeFor.parent | ||
})) { | ||
const parentFunctionBinding = this.bindings.get(fnParent.scope).get(next); | ||
if (fnParent.isFunction({ | ||
body: maybeFor.parent | ||
})) { | ||
var parentFunctionBinding = this.bindings.get(fnParent.scope).get(next); | ||
if (parentFunctionBinding) { | ||
const parentFunctionHasParamBinding = parentFunctionBinding.kind === "param"; | ||
if (parentFunctionBinding) { | ||
var parentFunctionHasParamBinding = parentFunctionBinding.kind === "param"; | ||
if (parentFunctionHasParamBinding) { | ||
return false; | ||
} | ||
if (parentFunctionHasParamBinding) { | ||
return false; | ||
} | ||
@@ -185,104 +167,99 @@ } | ||
} | ||
} | ||
for (var i = 0; i < binding.constantViolations.length; i++) { | ||
var violation = binding.constantViolations[i]; | ||
for (let i = 0; i < binding.constantViolations.length; i++) { | ||
const violation = binding.constantViolations[i]; | ||
if (tracker.hasBindingOrReference(violation.scope, binding, next)) { | ||
return false; | ||
} | ||
if (tracker.hasBindingOrReference(violation.scope, binding, next)) { | ||
return false; | ||
} | ||
} | ||
for (var _i = 0; _i < binding.referencePaths.length; _i++) { | ||
var ref = binding.referencePaths[_i]; | ||
for (let i = 0; i < binding.referencePaths.length; i++) { | ||
const ref = binding.referencePaths[i]; | ||
if (!ref.isIdentifier()) { | ||
var canUse = true; | ||
ref.traverse({ | ||
ReferencedIdentifier(path) { | ||
if (path.node.name !== next) return; | ||
if (!ref.isIdentifier()) { | ||
let canUse = true; | ||
ref.traverse({ | ||
ReferencedIdentifier(path) { | ||
if (path.node.name !== next) return; | ||
if (tracker.hasBindingOrReference(path.scope, binding, next)) { | ||
canUse = false; | ||
} | ||
if (tracker.hasBindingOrReference(path.scope, binding, next)) { | ||
canUse = false; | ||
} | ||
} | ||
}); | ||
}); | ||
if (!canUse) { | ||
return canUse; | ||
} | ||
} else if (!isLabelIdentifier(ref)) { | ||
if (tracker.hasBindingOrReference(ref.scope, binding, next)) { | ||
return false; | ||
} | ||
if (!canUse) { | ||
return canUse; | ||
} | ||
} else if (!isLabelIdentifier(ref)) { | ||
if (tracker.hasBindingOrReference(ref.scope, binding, next)) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
/** | ||
* Add a binding to Tracker in binding's own Scope | ||
* @param {Binding} binding | ||
*/ | ||
}, { | ||
key: "addBinding", | ||
value: function addBinding(binding) { | ||
if (!binding) { | ||
return; | ||
} | ||
return true; | ||
} | ||
/** | ||
* Add a binding to Tracker in binding's own Scope | ||
* @param {Binding} binding | ||
*/ | ||
var bindings = this.bindings.get(binding.scope); | ||
var existingBinding = bindings.get(binding.identifier.name); | ||
if (existingBinding && existingBinding !== binding) { | ||
throw new Error(`scopeTracker.addBinding: ` + `Binding "${existingBinding.identifier.name}" already exists. ` + `Trying to add "${binding.identifier.name}" again.`); | ||
} | ||
bindings.set(binding.identifier.name, binding); | ||
addBinding(binding) { | ||
if (!binding) { | ||
return; | ||
} | ||
/** | ||
* Moves Binding from it's own Scope to {@param toScope} | ||
* | ||
* required for fixup-var-scope | ||
* | ||
* @param {Binding} binding | ||
* @param {Scope} toScope | ||
*/ | ||
}, { | ||
key: "moveBinding", | ||
value: function moveBinding(binding, toScope) { | ||
this.bindings.get(binding.scope).delete(binding.identifier.name); | ||
this.bindings.get(toScope).set(binding.identifier.name, binding); | ||
} | ||
/** | ||
* has a Binding in the current {Scope} | ||
* @param {Scope} scope | ||
* @param {String} name | ||
*/ | ||
const bindings = this.bindings.get(binding.scope); | ||
const existingBinding = bindings.get(binding.identifier.name); | ||
}, { | ||
key: "hasBinding", | ||
value: function hasBinding(scope, name) { | ||
return this.bindings.get(scope).has(name); | ||
if (existingBinding && existingBinding !== binding) { | ||
throw new Error(`scopeTracker.addBinding: ` + `Binding "${existingBinding.identifier.name}" already exists. ` + `Trying to add "${binding.identifier.name}" again.`); | ||
} | ||
/** | ||
* Update the ScopeTracker on rename | ||
* @param {Scope} scope | ||
* @param {String} oldName | ||
* @param {String} newName | ||
*/ | ||
}, { | ||
key: "renameBinding", | ||
value: function renameBinding(scope, oldName, newName) { | ||
var bindings = this.bindings.get(scope); | ||
bindings.set(newName, bindings.get(oldName)); | ||
bindings.delete(oldName); | ||
} | ||
}]); | ||
bindings.set(binding.identifier.name, binding); | ||
} | ||
/** | ||
* Moves Binding from it's own Scope to {@param toScope} | ||
* | ||
* required for fixup-var-scope | ||
* | ||
* @param {Binding} binding | ||
* @param {Scope} toScope | ||
*/ | ||
return ScopeTracker; | ||
}(); | ||
moveBinding(binding, toScope) { | ||
this.bindings.get(binding.scope).delete(binding.identifier.name); | ||
this.bindings.get(toScope).set(binding.identifier.name, binding); | ||
} | ||
/** | ||
* has a Binding in the current {Scope} | ||
* @param {Scope} scope | ||
* @param {String} name | ||
*/ | ||
hasBinding(scope, name) { | ||
return this.bindings.get(scope).has(name); | ||
} | ||
/** | ||
* Update the ScopeTracker on rename | ||
* @param {Scope} scope | ||
* @param {String} oldName | ||
* @param {String} newName | ||
*/ | ||
renameBinding(scope, oldName, newName) { | ||
const bindings = this.bindings.get(scope); | ||
bindings.set(newName, bindings.get(oldName)); | ||
bindings.delete(oldName); | ||
} | ||
}; | ||
/** | ||
@@ -293,5 +270,4 @@ * Babel-7 returns null if there is no function parent | ||
function getFunctionParent(path) { | ||
return (path.scope.getFunctionParent() || path.scope.getProgramParent()).path; | ||
} |
{ | ||
"name": "babel-plugin-minify-mangle-names", | ||
"version": "0.5.0-alpha.55276695", | ||
"version": "0.5.0-alpha.01eac1c3", | ||
"description": "", | ||
@@ -15,4 +15,4 @@ "keywords": [ | ||
"dependencies": { | ||
"babel-helper-mark-eval-scopes": "^0.5.0-alpha.55276695" | ||
"babel-helper-mark-eval-scopes": "^0.5.0-alpha.01eac1c3" | ||
} | ||
} |
@@ -34,3 +34,3 @@ # babel-plugin-minify-mangle-names | ||
```sh | ||
npm install babel-plugin-minify-mangle-names | ||
npm install babel-plugin-minify-mangle-names --save-dev | ||
``` | ||
@@ -37,0 +37,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
34775
915