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

babel-plugin-transform-react-pug

Package Overview
Dependencies
Maintainers
1
Versions
29
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

babel-plugin-transform-react-pug - npm Package Compare versions

Comparing version 2.0.3 to 3.0.0

589

lib/code-gen.js

@@ -9,2 +9,3 @@ 'use strict';

var
// Mising "types"

@@ -17,3 +18,3 @@ // - Comment

// - Each(object)
// - Mixin
babel = _ref.babel;

@@ -24,22 +25,175 @@ var parse = _ref.parse;

var path = _ref.path;
var code = _ref.code;
var t = babel.types;
var nodes = t.identifier('pug_nodes');
function withString(node, stringLiteral) {
t.assertStringLiteral(stringLiteral);
if (t.isStringLiteral(node)) {
return t.stringLiteral(node.value + stringLiteral.value);
}
if (t.isBinaryExpression(node, { operator: '+' }) && t.isStringLiteral(node.right)) {
return t.binaryExpression('+', node.left, withString(node.right, stringLiteral));
}
return t.binaryExpression('+', node, stringLiteral);
}
var staticBlockID = 0;
var compiler = {
unwrap: function unwrap(node) {
if (t.isJSXExpressionContainer(node)) {
return node.expression;
} else if (t.isJSXText(node)) {
return t.stringLiteral(node.value);
} else {
return node;
baseBlock: function baseBlock() {
return {
_id: 'base',
getKey: function getKey(fn) {
fn(t.stringLiteral('pug'));
},
handleAttributes: function handleAttributes(attrs) {},
end: function end() {}
};
},
staticBlock: function staticBlock(parent) {
var enabled = false;
var parentEnabled = false;
var key = null;
var pending = [];
var index = 0;
parent.getKey(function (parentKey) {
parentEnabled = true;
key = withString(parentKey, t.stringLiteral(':' + staticBlockID++));
onUpdate();
});
function onUpdate() {
if (enabled && parentEnabled) {
while (pending.length) {
pending.shift()(withString(key, t.stringLiteral(':' + index++)));
}
}
}
return {
getKey: function getKey(fn) {
if (pending.indexOf(fn) === -1) {
pending.push(fn);
}
onUpdate();
},
handleAttributes: function handleAttributes(attrs) {
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = attrs[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var attr = _step.value;
if (t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name, { name: 'key' })) {
return;
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
this.getKey(function (key) {
if (t.isStringLiteral(key)) key = key; // t.jSXText(key.value);
else key = t.jSXExpressionContainer(key);
attrs.push(t.jSXAttribute(t.jSXIdentifier('key'), key));
});
},
end: function end() {
enabled = true;
onUpdate();
}
};
},
unwrapExpressionToStatements: function unwrapExpressionToStatements(node) {
if (t.isDoExpression(node)) {
return node.body.body;
} else if (t.isJSXExpressionContainer(node)) {
return this.unwrapExpressionToStatements(node.expression);
} else {
return [t.expressionStatement(this.unwrap(node))];
dynamicBlock: function dynamicBlock(parent, lineNumber) {
var enabled = false;
var localKey = null;
var parentEnabled = false;
var parentKey = null;
var pending = [];
var index = 0;
function join(left, right) {
return t.binaryExpression('+', left, right);
}
parent.getKey(function (_parentKey) {
parentEnabled = true;
parentKey = withString(_parentKey, t.stringLiteral(':'));
onUpdate();
});
function onUpdate() {
if (enabled && parentEnabled && localKey) {
while (pending.length) {
pending.shift()(withString(join(parentKey, localKey), t.stringLiteral(':' + index++)));
}
} else if (enabled && parentEnabled) {
var err = (0, _pugError2.default)('MISSING_KEY', 'You must specify a key for the first item in any loops.', {
line: path.node.loc.start.line + lineNumber,
filename: 'pug',
src: code
});
throw err;
}
}
return {
getKey: function getKey(fn) {
if (pending.indexOf(fn) === -1) {
pending.push(fn);
}
onUpdate();
},
handleAttributes: function handleAttributes(attrs) {
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = attrs[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var attr = _step2.value;
if (t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name, { name: 'key' })) {
if (localKey === null && t.isJSXExpressionContainer(attr.value) && attr.value.expression) {
localKey = attr.value.expression;
onUpdate();
// remove the attribute and replace with the properly nested version
attrs.splice(attrs.indexOf(attr), 1);
} else {
return;
}
}
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
this.getKey(function (key) {
if (t.isStringLiteral(key)) key = key; // t.jSXText(key.value);
else key = t.jSXExpressionContainer(key);
attrs.push(t.jSXAttribute(t.jSXIdentifier('key'), key));
});
},
end: function end() {
enabled = true;
onUpdate();
}
};
},

@@ -56,5 +210,138 @@ parseExpression: function parseExpression(src) {

},
visit: function visit(node) {
wrapExpressionInJSX: function wrapExpressionInJSX(value) {
// returns ["JSX"]
if (t.isJSX(value)) {
return value;
} else if (t.isStringLiteral(value)) {
return t.jSXText(value.value);
} else {
return t.jSXExpressionContainer(value);
}
},
joinJsExpressions: function joinJsExpressions(values) {
var vals = [];
// flatten one level
var _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
try {
for (var _iterator3 = values[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var val = _step3.value;
if (t.isArrayExpression(val)) {
var _iteratorNormalCompletion4 = true;
var _didIteratorError4 = false;
var _iteratorError4 = undefined;
try {
for (var _iterator4 = val.entries[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
var e = _step4.value;
vals.push(e);
}
} catch (err) {
_didIteratorError4 = true;
_iteratorError4 = err;
} finally {
try {
if (!_iteratorNormalCompletion4 && _iterator4.return) {
_iterator4.return();
}
} finally {
if (_didIteratorError4) {
throw _iteratorError4;
}
}
}
} else {
vals.push(val);
}
}
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3.return) {
_iterator3.return();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
}
if (vals.length === 0) {
return t.nullLiteral();
} else if (vals.length === 1) {
return vals[0];
} else {
return t.arrayExpression(vals);
}
},
joinJsStatements: function joinJsStatements(values) {
var vals = [];
// flatten one level
var _iteratorNormalCompletion5 = true;
var _didIteratorError5 = false;
var _iteratorError5 = undefined;
try {
for (var _iterator5 = values[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
var val = _step5.value;
if (Array.isArray(val)) {
var _iteratorNormalCompletion6 = true;
var _didIteratorError6 = false;
var _iteratorError6 = undefined;
try {
for (var _iterator6 = val[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
var e = _step6.value;
vals.push(e);
}
} catch (err) {
_didIteratorError6 = true;
_iteratorError6 = err;
} finally {
try {
if (!_iteratorNormalCompletion6 && _iterator6.return) {
_iterator6.return();
}
} finally {
if (_didIteratorError6) {
throw _iteratorError6;
}
}
}
} else {
vals.push(val);
}
}
} catch (err) {
_didIteratorError5 = true;
_iteratorError5 = err;
} finally {
try {
if (!_iteratorNormalCompletion5 && _iterator5.return) {
_iterator5.return();
}
} finally {
if (_didIteratorError5) {
throw _iteratorError5;
}
}
}
return vals;
},
getPushStatement: function getPushStatement(arg) {
return t.expressionStatement(t.callExpression(t.memberExpression(nodes, t.identifier('push')), [arg]));
},
visit: function visit(node, mode, block) {
if (typeof this['visit' + node.type] === 'function') {
return this['visit' + node.type](node);
return this['visit' + node.type](node, mode, block);
} else {

@@ -64,10 +351,5 @@ throw new Error(node.type + ' is not yet supported');

},
visitBlock: function visitBlock(node) {
var _this = this;
return node.nodes.map(function (node) {
return _this.visit(node);
}).filter(Boolean);
},
visitCase: function visitCase(node) {
// [ "JSXExpressionContainer", "ConditionalExpression", "IfStatement" ]
visitCase: function visitCase(node, mode, block) {
// compile the case/when into a series of conditionals then compile that down to jsx

@@ -94,99 +376,126 @@ var base = { alternate: null };

parent.alternate = defaultValue;
return this.visit(base.alternate);
return this.visit(base.alternate, mode, block);
},
visitCode: function visitCode(node) {
if (node.buffer) {
if (!node.mustEscape) {
throw new Error('Unescaped, buffered code is not supported in react-pug');
}
return t.jSXExpressionContainer(this.parseExpression(node.val));
} else {
// TODO: hoist and rename `const` and `let` variables
return t.jSXExpressionContainer(t.doExpression(t.blockStatement([this.parseStatement(node.val), t.expressionStatement(t.nullLiteral())])));
// [ "JSX", "Expression", "Statement" ]
visitCode: function visitCode(node, mode, block) {
if (node.buffer && !node.mustEscape) {
throw new Error('Unescaped, buffered code is not supported in react-pug');
}
switch (mode) {
case 'jsx':
return this.wrapExpressionInJSX(this.visitCode(node, 'jsExpression', block));
case 'jsExpression':
if (node.buffer) {
return this.parseExpression(node.val);
}
// TODO: hoist and rename `const` and `let` variables
return t.doExpression(t.blockStatement([this.parseStatement(node.val), t.expressionStatement(t.nullLiteral())]));
case 'jsStatements':
if (node.buffer) {
return [this.getPushStatement(this.parseExpression(node.val))];
}
return [this.parseStatement(node.val)];
default:
throw new Error('Unexpected mode "' + mode + '" should be "jsx", "jsStatement" or "jsExpression"');
}
},
simplifyBlock: function simplifyBlock(nodes) {
var id = path.scope.generateUidIdentifier('SimpleBlock').name;
nodes = nodes.map(this.unwrap);
if (nodes.length === 1) {
return nodes[0];
// [ "JSXExpressionContainer", "ConditionalExpression", "IfStatement" ]
visitConditional: function visitConditional(node, mode, block) {
var _this = this;
if (node.alternate && node.alternate.type === 'Conditional') {
node.alternate = { nodes: [node.alternate] };
}
nodes.forEach(function (node, i) {
if (t.isJSXElement(node) && !node.openingElement.attributes.some(function (attr) {
return t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name, { name: 'key' });
})) {
var attr = t.jSXAttribute(t.jSXIdentifier('key'), t.stringLiteral(id + '_' + i));
attr.pugSimpleBlockAttr = true;
node.openingElement.attributes.push(attr);
}
});
return t.arrayExpression(nodes);
},
visitConditional: function visitConditional(node) {
var test = this.parseExpression(node.test);
var consequent = this.simplifyBlock(this.visitBlock(node.consequent));
var alternate = !node.alternate ? t.nullLiteral() : node.alternate.type === 'Block' ? this.simplifyBlock(this.visitBlock(node.alternate)) : this.unwrap(this.visitConditional(node.alternate));
return t.jSXExpressionContainer(t.conditionalExpression(test, consequent, alternate));
switch (mode) {
case 'jsx':
return t.jSXExpressionContainer(this.visitConditional(node, 'jsExpression', block));
case 'jsExpression':
var condConsequentBlock = this.staticBlock(block);
var condConsequent = this.joinJsExpressions(node.consequent.nodes.map(function (node) {
return _this.visit(node, 'jsExpression', condConsequentBlock);
}).filter(Boolean));
condConsequentBlock.end();
var condAlternate = t.nullLiteral();
if (node.alternate) {
(function () {
var alternateBlock = _this.staticBlock(block);
condAlternate = _this.joinJsExpressions(node.alternate.nodes.map(function (node) {
return _this.visit(node, 'jsExpression', alternateBlock);
}).filter(Boolean));
alternateBlock.end();
})();
}
return t.conditionalExpression(test, condConsequent, condAlternate);
case 'jsStatements':
var ifConsequentBlock = this.staticBlock(block);
var ifConsequent = t.blockStatement(this.joinJsStatements(node.consequent.nodes.map(function (node) {
return _this.visit(node, 'jsStatements', ifConsequentBlock);
})));
ifConsequentBlock.end();
var ifAlternate = null;
if (node.alternate) {
(function () {
var alternateBlock = _this.staticBlock(block);
ifAlternate = t.blockStatement(_this.joinJsStatements(node.alternate.nodes.map(function (node) {
return _this.visit(node, 'jsStatements', alternateBlock);
})));
alternateBlock.end();
})();
}
return [t.ifStatement(test, ifConsequent, ifAlternate)];
default:
throw new Error('Unexpected mode "' + mode + '" should be "jsx", "jsStatement" or "jsExpression"');
}
},
simplifyDynamicBlock: function simplifyDynamicBlock(block /* Array<Expression> */, nodesIdentifier) {
// [ "JSXExpressionContainer", "DoExpression", "WhileStatement" ]
visitEach: function visitEach(node, mode, block) {
var _this2 = this;
if (!nodesIdentifier) {
throw new Error('Simplify Dynamic Block requires an identifier');
if (mode === 'jsx') {
return this.wrapExpressionInJSX(this.visitEach(node, 'jsExpression', block));
}
var body = [];
var key = null;
block.map(function (exp) {
return _this2.unwrapExpressionToStatements(exp);
}).forEach(function (statements, i) {
var last = statements.pop();
statements.forEach(function (s) {
return body.push(s);
});
t.assertExpressionStatement(last);
if (!t.isNullLiteral(last.expression)) {
if (t.isJSXElement(last.expression)) {
last.expression.openingElement.attributes = last.expression.openingElement.attributes.filter(function (attr) {
return !attr.pugSimpleBlockAttr;
});
var hasKey = last.expression.openingElement.attributes.some(function (attr) {
if (t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name, { name: 'key' })) {
if (t.isJSXExpressionContainer(attr.value) && key === null) {
key = attr.value.expression;
}
return true;
}
return false;
});
if (!hasKey && key) {
last.expression.openingElement.attributes.push(t.jSXAttribute(t.jSXIdentifier('key'), t.jSXExpressionContainer(t.binaryExpression('+', key, t.stringLiteral('_' + i)))));
}
}
body.push(t.expressionStatement(t.callExpression(t.memberExpression(nodesIdentifier, t.identifier('push')), [last.expression])));
}
});
return body;
if (mode === 'jsExpression') {
var _variableDeclaration = t.variableDeclaration('const', [t.variableDeclarator(nodes, t.arrayExpression([]))]);
var end = t.expressionStatement(nodes);
return t.doExpression(t.blockStatement([_variableDeclaration].concat(_toConsumableArray(this.visitEach(node, 'jsStatements', block)), [end])));
}
var childBlock = this.dynamicBlock(block, node.line);
var obj = path.scope.generateUidIdentifier('pug_arr');
var objLength = t.memberExpression(obj, t.identifier('length'));
var variableDeclaration = t.variableDeclaration('const', [t.variableDeclarator(obj, this.parseExpression(node.obj))]);
var index = node.key ? t.identifier(node.key) : path.scope.generateUidIdentifier('pug_index');
var init = t.variableDeclaration('let', [t.variableDeclarator(index, t.numericLiteral(0))]);
var test = t.binaryExpression('<', index, objLength);
var update = t.updateExpression('++', index);
var loop = t.forStatement(init, test, update, t.blockStatement([t.variableDeclaration('const', [t.variableDeclarator(t.identifier(node.val), t.memberExpression(obj, index, true))])].concat(this.joinJsStatements(node.block.nodes.map(function (node) {
return _this2.visit(node, 'jsStatements', childBlock);
})))));
childBlock.end();
if (node.alternate) {
(function () {
var alternateBlock = _this2.staticBlock(block);
var alternate = t.blockStatement(_this2.joinJsStatements(node.alternate.nodes.map(function (node) {
return _this2.visit(node, 'jsStatements', alternateBlock);
}).filter(Boolean)));
alternateBlock.end();
loop = t.ifStatement(objLength, loop, alternate);
})();
}
return [variableDeclaration, t.ifStatement(t.callExpression(t.memberExpression(t.identifier('Array'), t.identifier('isArray')), [obj]), t.blockStatement([loop]), t.blockStatement([t.throwStatement(t.newExpression(t.identifier('TypeError'), [t.stringLiteral('Expected ' + node.obj + ' to be an array.')]))]))];
},
visitEach: function visitEach(node) {
var nodes = path.scope.generateUidIdentifier('EachBlock');
var arr = this.parseExpression(node.obj);
var params = [this.parseExpression(node.val)];
if (node.key) params.push(this.parseExpression(node.key));
var block = this.visitBlock(node.block);
var body = block.length === 1 ? [t.returnStatement(this.unwrap(block[0]))] : [t.variableDeclaration('var', [t.variableDeclarator(nodes, t.arrayExpression([]))])].concat(this.simplifyDynamicBlock(block, nodes)).concat([t.returnStatement(nodes)]);
var mapCall = t.callExpression(t.memberExpression(arr, t.identifier('map')), [t.functionExpression(null, params, t.blockStatement(body)), t.thisExpression()]);
var condition = t.logicalExpression('&&', arr, t.memberExpression(arr, t.identifier('length')));
var flatBody = body.length === 1 ? mapCall : t.callExpression(helpers.flatten, [mapCall]);
var alternate = node.alternate ? this.simplifyBlock(this.visitBlock(node.alternate)) : t.nullLiteral();
var loop = t.jSXExpressionContainer(t.conditionalExpression(condition, flatBody, alternate));
return loop;
},
visitTag: function visitTag(node) {
// returns ["JSXElement", "JSXElement", "PushStatement"] ]
visitTag: function visitTag(node, mode, block) {
var _this3 = this;
var name = t.jSXIdentifier(node.name);
var children = (node.code ? [this.visitCode(node.code)] : []).concat(this.visitBlock(node.block)).reduce(function (a, b) {
return a.concat(Array.isArray(b) ? b : [b]);
}, []);
var children = (node.code ? [this.visitCode(node.code, 'jsx', this.baseBlock())] : []).concat(node.block.nodes.map(function (node) {
return _this3.visit(node, 'jsx', _this3.baseBlock());
}).filter(Boolean))
// TODO: assert that these are all valid jsx things
;

@@ -239,23 +548,57 @@ if (node.attributeBlocks.length) {

}
block.handleAttributes(attrs);
var open = t.jSXOpeningElement(name, attrs, // Array<JSXAttribute | JSXSpreadAttribute>
children.length === 0);
var close = children.length === 0 ? null : t.jSXClosingElement(name);
return t.jSXElement(open, close, children, // ["StringLiteral","JSXExpressionContainer","JSXElement"]
var element = t.jSXElement(open, close, children, // ["StringLiteral","JSXExpressionContainer","JSXElement"]
children.length === 0);
switch (mode) {
case 'jsx':
case 'jsExpression':
return element;
case 'jsStatements':
return [this.getPushStatement(element)];
default:
throw new Error('Unexpected mode "' + mode + '" should be "jsx", "jsStatement" or "jsExpression"');
}
},
visitText: function visitText(node) {
return t.jSXText(node.val);
// [ "JSXText", "StringLiteral", "PushStatement"]
visitText: function visitText(node, mode, block) {
switch (mode) {
case 'jsx':
return t.jSXText(node.val);
case 'jsExpression':
return t.stringLiteral(node.val);
case 'jsStatements':
return [this.getPushStatement(this.visitText(node, 'jsExpression', block))];
default:
throw new Error('Unexpected mode "' + mode + '" should be "jsx", "jsStatement" or "jsExpression"');
}
},
visitWhile: function visitWhile(node) {
var nodes = path.scope.generateUidIdentifier('WhileBlock');
var statements = [];
statements.push(t.variableDeclaration('var', [t.variableDeclarator(nodes, t.arrayExpression([]))]));
// [ "JSXExpressionContainer", "DoExpression", "WhileStatement" ]
visitWhile: function visitWhile(node, mode, block) {
var _this4 = this;
if (mode === 'jsx') {
return this.wrapExpressionInJSX(this.visitWhile(node, 'jsExpression', block));
}
if (mode === 'jsExpression') {
var variableDeclaration = t.variableDeclaration('const', [t.variableDeclarator(nodes, t.arrayExpression([]))]);
var end = t.expressionStatement(nodes);
return t.doExpression(t.blockStatement([variableDeclaration].concat(_toConsumableArray(this.visitWhile(node, 'jsStatements', block)), [end])));
}
var childBlock = this.dynamicBlock(block, node.line);
var test = this.parseExpression(node.test);
var body = this.simplifyDynamicBlock(this.visitBlock(node.block), nodes);
statements.push(t.whileStatement(test, t.blockStatement(body)));
statements.push(t.expressionStatement(nodes));
return t.jSXExpressionContainer(t.doExpression(t.blockStatement(statements)));
var body = t.blockStatement(this.joinJsStatements(node.block.nodes.map(function (node) {
return _this4.visit(node, 'jsStatements', childBlock);
}).filter(Boolean)));
childBlock.end();
return [t.whileStatement(test, body)];
}
};
return compiler.visit(ast);
return ast.nodes.map(function (node) {
return compiler.visit(node, 'jsExpression', compiler.baseBlock());
});
};

@@ -267,2 +610,8 @@

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var _pugError = require('pug-error');
var _pugError2 = _interopRequireDefault(_pugError);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }

@@ -44,9 +44,4 @@ 'use strict';

ast: ast,
path: path
}).map(function (node) {
if (t.isJSXExpressionContainer(node)) {
return node.expression;
} else {
return node;
}
path: path,
code: _this.file.code
});

@@ -53,0 +48,0 @@

{
"name": "babel-plugin-transform-react-pug",
"version": "2.0.3",
"version": "3.0.0",
"description": "A plugin for transpiling pug templates to jsx",

@@ -15,2 +15,3 @@ "main": "lib/index.js",

"babylon": "^6.4.2",
"pug-error": "^1.3.0",
"pug-filters": "^1.1.1",

@@ -17,0 +18,0 @@ "pug-lexer": "^1.0.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