Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 64 additions & 37 deletions packages/babel-plugin-minify-mangle-names/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,54 @@ module.exports = babel => {
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
*/
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
*/
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
Expand Down Expand Up @@ -288,50 +336,29 @@ module.exports = babel => {
* 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
*/
for (let i = 0; i < names.length; i++) {
const oldName = names[i];
const binding = bindings.get(oldName);

// Names which should NOT be mangled
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;
if (mangler.canMangle(oldName, binding, scope)) {
let next;
do {
next = getNext();
} while (!mangler.isValidName(next, binding, scope));

// Reset so variables which are removed can be reused
resetNext();

// Once we detected a valid `next` Identifier which could be used,
// call the renamer
mangler.rename(scope, binding, oldName, next);
}

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
resetNext();

// Once we detected a valid `next` Identifier which could be used,
// call the renamer
mangler.rename(scope, binding, oldName, next);
}
}

Expand Down