{
    "componentChunkName": "component---src-templates-post-js",
    "path": "/using-codemods-to-evolve-code/",
    "result": {"data":{"mdx":{"body":"var _excluded = [\"components\"];\n\nfunction _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsxRuntime classic */\n\n/* @jsx mdx */\nvar _frontmatter = {\n  \"title\": \"Evolving components with codemods\",\n  \"date\": \"2020-05-10T00:00:00.000Z\",\n  \"summary\": \"It's certain that the way in which we write software won't be the same tomorrow or a decade from now. AST manipulation can help with just that.\",\n  \"tags\": [\"design\", \"development\", \"css\", \"grid\", \"flexbox\", \"react\"]\n};\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n      props = _objectWithoutProperties(_ref, _excluded);\n\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"p\", null, \"Codemods are a wonderful tool to add to your toolbox. When creating APIs or even moving libraries, we can use codemods to make our lives easier and focus on more important tasks.\"), mdx(\"h2\", {\n    \"id\": \"ast-transformation\"\n  }, \"AST Transformation\"), mdx(\"p\", null, \"Codemods solely work off of AST transformations. These are detailed bits of information that describe all sorts of charaterisittcs of your code like line positions, types, and much more.\"), mdx(\"h2\", {\n    \"id\": \"evolving-apis\"\n  }, \"Evolving APIs\"), mdx(\"p\", null, \"When working through a component library or and sort of code for that matter The difference between good/great API design is thinking about how your code will evolve. You may find that certain component doesn\\u2019t work well or you might want to switch a codebase from using class names to CSS in JS.\"), mdx(\"h2\", {\n    \"id\": \"finding-all-components\"\n  }, \"Finding all components\"), mdx(\"p\", null, \"First, we will map out some expectations of what we need our codemod to do:\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Remove the old component from imports\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Add the new component as an import if it does not exist yet\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Find every place the old component is defined and replace it with the new one\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Update any APIs that may have changed between both solutions\")), mdx(\"h2\", {\n    \"id\": \"handling-imports\"\n  }, \"Handling imports\"), mdx(\"p\", null, \"By visiting the ImportDeclration we can determine what we need to remove and add.\"), mdx(\"h2\", {\n    \"id\": \"swapping-or-adding-props\"\n  }, \"Swapping or adding props\"), mdx(\"p\", null, \"When inspecting the AST you will find that props in JSX are referred to as attributes.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\",\n    \"className\": \"language-js\"\n  }, \"export default function ({ types: t }) {\\n  function matchesComponentName(path, name) {\\n    const [object, property] = name.split('.')\\n    return (\\n      path.node.name.object.name === object &&\\n      path.node.name.property.name === property\\n    )\\n  }\\n  function changeComponentName(path, name) {\\n    path.node.name = t.jSXIdentifier(name)\\n  }\\n  function createAttribute(name, value) {\\n    const literalType =\\n      typeof value === 'number'\\n        ? t.jSXExpressionContainer(literalType(value))\\n        : t.stringLiteral(value)\\n    return t.jSXAttribute(t.jSXIdentifier(name), literalType)\\n  }\\n  return {\\n    visitor: {\\n      JSXOpeningElement(path) {\\n        componentsToChange.forEach(([from, to, propsToConvert]) => {\\n          if (matchesComponentName(path, from.name)) {\\n            changeComponentName(path, to.name)\\n            if (propsToConvert) {\\n              propsToConvert.forEach(([fromName, toName]) => {\\n                path.node.attributes.forEach((attribute, index) => {\\n                  if (attribute.name.name === fromName) {\\n                    attribute.name.name = toName\\n                  }\\n                })\\n              })\\n            }\\n            if (to.props) {\\n              to.props.forEach(([prop, value]) => {\\n                path.node.attributes.unshift(createAttribute(prop, value))\\n              })\\n            }\\n          }\\n        })\\n      },\\n      JSXClosingElement(path) {\\n        componentsToChange.forEach(([from, to]) => {\\n          if (matchesComponentName(path, from.name)) {\\n            changeComponentName(path, to.name)\\n          }\\n        })\\n      },\\n      ImportDeclaration(path) {\\n        if (path.node.source.value !== '@planning-center/ui-kit') {\\n          return\\n        }\\n        path.node.specifiers = path.node.specifiers\\n          .map((specifier) => {\\n            if (oldComponentNames.includes(specifier.imported.name)) {\\n              const [oldComponent, newComponent] = componentsToChange[\\n                oldComponentNames.indexOf(specifier.imported.name)\\n              ]\\n              const hasNewImportAlready = path.node.specifiers.some(\\n                (specifier) => specifier.imported.name === newComponent.name\\n              )\\n              return hasNewImportAlready\\n                ? null\\n                : t.importSpecifier(\\n                    t.identifier(newComponent.name),\\n                    t.identifier(newComponent.name)\\n                  )\\n            } else {\\n              return specifier\\n            }\\n          })\\n          .filter(Boolean)\\n      },\\n    },\\n  }\\n}\\n\")), mdx(\"h2\", {\n    \"id\": \"summary\"\n  }, \"Summary\"), mdx(\"p\", null, \"Codemods are easy to write for JSX and allow component libraries to evolve. If you\\u2019d like to dive further into AST transformation I highly reccommend Kent C Dodds FrontendMasters coures. I hope this gave you a glimpse into the power of codemods and how easy they can be to write.\"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"Codemods are a wonderful tool to add to your toolbox. When creating APIs or even moving libraries, we can use codemods to make our lives…","fields":{"readingTime":{"text":"3 min read"},"slug":"/using-codemods-to-evolve-code/"},"frontmatter":{"date":"05/10/2020","summary":"It's certain that the way in which we write software won't be the same tomorrow or a decade from now. AST manipulation can help with just that.","tags":["design","development","css","grid","flexbox","react"],"title":"Evolving components with codemods","image":null}},"previous":null,"next":null},"pageContext":{"id":"7a8624b9-ac40-56ba-a200-4d36e973867a"}},
    "staticQueryHashes": ["3764592887"]}