babel-plugin-react-component-trace-data-attr
Version:
Adds data- attribute to html elements showing the trace of React components names that led to this element creation.
115 lines (92 loc) • 4.39 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = function (babel) {
var t = babel.types;
return {
visitor: {
CallExpression: _styledComponents.CallExpression,
TaggedTemplateExpression: _styledComponents.TaggedTemplateExpression,
JSXOpeningElement: function JSXOpeningElement(path, state) {
var options = (0, _options2.default)(state);
handleOpeningElement(t, path, options);
}
}
};
};
var _options = require('./options');
var _options2 = _interopRequireDefault(_options);
var _styledComponents = require('./styled-components');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var findOrAddDataProperty = function findOrAddDataProperty(t, properties, identifier, attribute) {
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = properties[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var prop = _step.value;
if (prop.key && prop.key.type === 'StringLiteral' && prop.key.value === attribute) {
return prop.value;
}
}
// It's better to unshift instead of push, because last property may be a RestElement (...restProps).
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
properties.unshift(t.objectProperty(t.stringLiteral(attribute), identifier, true, false));
return identifier;
};
var getParentDataAttrExpression = function getParentDataAttrExpression(t, path, options, functionParent) {
// It is safe to handle nodes with only one param, because probably it is props splitting
if (functionParent.node.params.length !== 1) {
return;
}
if (functionParent.node.params[0].type === 'ObjectPattern') {
return findOrAddDataProperty(t, functionParent.node.params[0].properties, path.scope.generateUidIdentifier(), options.attribute);
} else {
var elementDataAttr = t.memberExpression(functionParent.node.params[0], t.stringLiteral(options.attribute), true);
// functionParent.node.params[0] must be defined
return t.logicalExpression("&&", functionParent.node.params[0], elementDataAttr);
}
};
var handleOpeningElement = function handleOpeningElement(t, path, options) {
var functionParent = path.getFunctionParent();
var componentName = void 0;
var parentDataAttrExpression = void 0;
if (!functionParent) return;
if (functionParent.type == "ClassMethod" || functionParent.parent.type == "ClassProperty") {
// Class components
componentName = functionParent.findParent(function (path) {
return path.isClassExpression() || path.isClassDeclaration();
}).node.id.name;
parentDataAttrExpression = t.memberExpression(t.memberExpression(t.thisExpression(), t.identifier('props')), t.stringLiteral(options.attribute), true);
} else if (functionParent.parent.type === "VariableDeclarator") {
// Function components without decorators
componentName = functionParent.parent.id.name;
parentDataAttrExpression = getParentDataAttrExpression(t, path, options, functionParent);
} else if (functionParent.parent.type === "CallExpression" && (functionParent.parent.callee.name === 'memo' || functionParent.parent.callee.property && functionParent.parent.callee.property.name === 'memo')) {
// Function components with memo
var parentPath = functionParent.parentPath;
// Looking for variable declaration.
while (parentPath.parent.type !== 'VariableDeclarator') {
parentPath = parentPath.parentPath;
}
componentName = parentPath.parent.id.name;
parentDataAttrExpression = getParentDataAttrExpression(t, path, options, functionParent);
} else {
return;
}
path.node.attributes.push(t.jSXAttribute(t.jSXIdentifier(options.attribute), t.jSXExpressionContainer(t.binaryExpression("+", parentDataAttrExpression ? t.logicalExpression("||", parentDataAttrExpression, t.stringLiteral("")) : t.stringLiteral(""), t.stringLiteral(options.separator + options.format(componentName))))));
};