Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

babel-plugin-minify-mangle-names

Package Overview
Dependencies
Maintainers
5
Versions
87
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

babel-plugin-minify-mangle-names - npm Package Compare versions

Comparing version 0.5.0-alpha.55276695 to 0.5.0-alpha.18dcfe13

30

lib/bfs-traverse.js
"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

"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,338 @@ 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);
/**
* Same usage as in DCE, whichever runs first
*/
if (!isEvalScopesMarked(mangler.program)) {
markEvalScopes(mangler.program);
}
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);
});
});
},
/**
* 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;
}
/**
* Mangle the scope
* @param {Scope} scope
*/
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
mangleScope(scope) {
const mangler = this;
const scopeTracker = mangler.scopeTracker; // Unsafe Scope
if (mangler.visitedScopes.has(scope)) {
return;
}
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
mangler.visitedScopes.add(scope); // Helpers to generate names
var i = 0;
if (mangler.visitedScopes.has(scope)) {
return;
}
function getNext() {
return mangler.charset.getIdentifier(i++);
}
mangler.visitedScopes.add(scope); // Helpers to generate names
function resetNext() {
i = 0;
}
let i = 0;
var bindings = scopeTracker.bindings.get(scope);
function getNext() {
return mangler.charset.getIdentifier(i++);
}
var names = _toConsumableArray(bindings.keys());
/**
* 1. Iterate through the list of BindingIdentifiers
* 2. Rename each of them in-place
* 3. Update the scope tree.
*/
function resetNext() {
i = 0;
}
const bindings = scopeTracker.bindings.get(scope);
const names = [...bindings.keys()];
/**
* 1. Iterate through the list of BindingIdentifiers
* 2. Rename each of them in-place
* 3. Update the scope tree.
*/
for (var _i = 0; _i < names.length; _i++) {
var oldName = names[_i];
var binding = bindings.get(oldName); // Names which should NOT be mangled
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 ( // 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;
}
var next = void 0;
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
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
resetNext(); // Once we detected a valid `next` Identifier which could be used,
// call the renamer
mangler.rename(scope, binding, oldName, next);
}
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 +393,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 +504,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 +522,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 +527,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.18dcfe13",
"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.18dcfe13"
}
}

@@ -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 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc