facebook / jscodeshift

A JavaScript codemod toolkit.
https://jscodeshift.com
MIT License
9.11k stars 468 forks source link

cannot replace JSX nodes within render props #565

Open json2d opened 11 months ago

json2d commented 11 months ago

i'm having a problem w/ replacing JSX nodes within render props:

https://astexplorer.net/#/gist/870412de2b8bc7bfd6bfa04cd4600680/381203659283e923155a6b62ed4739df11ba0c2d

input:

const MyController = (props) => (
  <Controller
    render={() => (
      <div id="inner" />
    )} 
  />
)

expected output:

const MyController = (props) => (
  <Controller
    render={() => (
     <div id="outer">
        <div id="inner" />
     </div>
    )} 
  />
)

v0.11.0 actual output:

const MyController = (props) => (
  <Controller
    render={() => (
      (<div id="inner" />)
    )} 
  />
)

v0.15.0 actual output: [input was unmodified]

codemod:

module.exports = function (file, api) {
  const j = api.jscodeshift;
  const root = j(file.source);

  // Find all JSX elements with the name 'Controller'
  const controllerPaths = root.findJSXElements("Controller");

  // Process the found JSX elements
  controllerPaths.forEach((path) => {

    const renderAttribute = path.node.openingElement.attributes.find(
      (attr) => attr.name.name === "render"
    );

    if (
      renderAttribute &&
      renderAttribute.value.type === "JSXExpressionContainer" &&
      renderAttribute.value.expression.type === "ArrowFunctionExpression"
    ) {
      // Find the first JSXElement within the ArrowFunctionExpression
      const renderFunctionBodyNode = renderAttribute.value.expression.body;

      if (renderFunctionBodyNode) {

        const idAttribute = j.jsxAttribute(
          j.jsxIdentifier("id"),
          j.jsxExpressionContainer(j.identifier("outer"))
        );
        // Create a new JSXElement with div as its opening element
        const wrappedRenderFunctionBodyNode = j.jsxElement(
          j.jsxOpeningElement(j.jsxIdentifier("div"), [idAttribute]),
          j.jsxClosingElement(j.jsxIdentifier("div")),
          [renderFunctionBodyNode]
        );

        // Replace the existing JSXElement with the new one
        const renderFunctionBodyPaths = j(renderFunctionBodyNode);
        const renderFunctionBodyPath = renderFunctionBodyPaths.get(0);

        renderFunctionBodyPath.replace(wrappedRenderFunctionBodyNode)
      }
    }
  });

  return root.toSource();
};

is the codemod doing something wrong or is this a bug?