babel-plugin-react-svg
Version:
Babel plugin to transform svg to react component
130 lines (105 loc) • 4.34 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = _default;
var _cssToObj = _interopRequireDefault(require("./css-to-obj"));
var _camelize = require("./camelize");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _default(babel) {
const t = babel.types;
const restElement = t.restElement ? t.restElement : t.restProperty;
const createClass = className => t.logicalExpression("||", t.memberExpression(
/* object = */
t.identifier("styles"),
/* property = */
t.stringLiteral(className),
/* computed = */
true), t.stringLiteral(className));
const attrVisitor = {
JSXAttribute(path) {
const name = path.get("name");
const value = path.get("value");
if (name.isJSXNamespacedName()) {
// converts
// <svg xmlns:xlink="asdf">
// to
// <svg xmlnsXlink="asdf">
name.replaceWith(t.jSXIdentifier((0, _camelize.namespaceToCamel)(path.node.name.namespace.name, path.node.name.name.name)));
} else if (name.isJSXIdentifier()) {
if (name.node.name === "class") {
// converts
// <tag class="blah blah1"/>
// to
// <tag className="blah blah1"/>
name.replaceWith(t.jSXIdentifier("className")); // converts
// className="foo bar"
// to
// className={(styles["foo"] || "foo") + " " + (styles["bar"] || "bar")}
let classes = value.node.value.split(/\s/);
if (classes.length > 0) {
let expr = createClass(classes[0]);
for (let i = 1; i < classes.length; i++) {
expr = t.binaryExpression("+", // (props.styles["foo"] || "foo") + " "
t.binaryExpression("+", expr, t.stringLiteral(" ")), // (props.styles["bar"] || "bar")
createClass(classes[i]));
}
value.replaceWith(t.jSXExpressionContainer(expr));
}
} // converts
// <tag style="text-align: center; width: 50px">
// to
// <tag style={{textAlign: 'center', width: '50px'}}>
if (name.node.name === "style") {
let csso = (0, _cssToObj.default)(value.node.value);
let properties = Object.keys(csso).map(prop => t.objectProperty(t.identifier((0, _camelize.hyphenToCamel)(prop)), t.stringLiteral(csso[prop])));
value.replaceWith(t.jSXExpressionContainer(t.objectExpression(properties)));
} // converts
// <svg stroke-width="5" data-x="0" aria-label="foo">
// to
// <svg strokeWidth="5" data-x="0" aria-label="foo">
if (!/^(data-|aria-)/.test(name.node.name)) {
name.replaceWith(t.jSXIdentifier((0, _camelize.hyphenToCamel)(path.node.name.name)));
}
}
}
}; // returns
// export default (props) => ${input_svg_node}
const getExport = function (svg) {
return t.exportDefaultDeclaration(t.arrowFunctionExpression([t.objectPattern([t.objectProperty(t.identifier("styles"), t.assignmentPattern(t.identifier("styles"), t.objectExpression([])), false, true), restElement(t.identifier("props"))])], svg));
}; // converts
// <svg>
// to
// <svg {this.props}>
// after passing through attributes visitors
const svgVisitor = {
JSXOpeningElement(path) {
if (path.node.name.name.toLowerCase() === "svg") {
// add spread props
path.node.attributes.push(t.jSXSpreadAttribute(t.identifier("props")));
}
}
}; // converts
// <svg/>
// to
// import React from 'react';
// export default props => <svg {...props}/>;
// after passing through other visitors
const svgExpressionVisitor = {
ExpressionStatement(path) {
if (!path.get("expression").isJSXElement()) return;
if (path.get("expression.openingElement.name").node.name !== "svg") return;
path.replaceWith(getExport(path.get("expression").node));
}
};
const programVisitor = {
Program(path) {
// add import react statement
path.node.body.unshift(t.importDeclaration([t.importDefaultSpecifier(t.identifier("React"))], t.stringLiteral("react")));
}
};
return {
visitor: Object.assign({}, programVisitor, svgExpressionVisitor, svgVisitor, attrVisitor)
};
}
module.exports = exports.default;