diff --git a/packages/babel-plugin-minify-mangle-names/src/index.js b/packages/babel-plugin-minify-mangle-names/src/index.js index 0705010a0..d9c298294 100644 --- a/packages/babel-plugin-minify-mangle-names/src/index.js +++ b/packages/babel-plugin-minify-mangle-names/src/index.js @@ -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 @@ -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); } }