facebook / jscodeshift

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

Extra parens inserted around JSX after attributes are added #534

Open michaldudak opened 1 year ago

michaldudak commented 1 year ago

We're encountering an issue that prevents us from upgrading from 0.13.1 to 0.14.0 (https://github.com/mui/material-ui/pull/34680).

It seems that when a codemod adds a JSX attribute, the whole JSX block in a return statement gets wrapped in extra parens (even if it had parens before).

I managed to create a minimal repro:

index.js

const fs = require("fs");
const j = require("jscodeshift");

const source = fs.readFileSync("source.js", "utf8");

function addAttribute(path) {
  const attributes = path.node.openingElement.attributes;

  attributes.push(
    j.jsxAttribute(j.jsxIdentifier("data-foo"), j.literal("bar"))
  );
}

const result = j(source).find(j.JSXElement).forEach(addAttribute).toSource();
console.log(result);

source.js

import * as React from "react";

export default function WrappedLink(props) {
  return (
    <div>
      <a {...props} />
    </div>
  );
}

expected output

import * as React from "react";

export default function WrappedLink(props) {
  return (
    <div data-foo="bar">
      <a {...props} data-foo="bar" />
    </div>
  );
}

actual output

import * as React from "react";

export default function WrappedLink(props) {
  return (
    (<div data-foo="bar">
      <a {...props} data-foo="bar" />
    </div>)
  );
}
ElonVolo commented 1 year ago

This is something that is happening at the recast level. I temporarily downgraded jscodeshift's version of recast to 0.20.4 and the extra parentheses went away.

Do the extra parentheses actually introduce bugs/regressions into migrated mui projects, or is it more of an aesthetics thing?

ElonVolo commented 1 year ago

Here's what I believe is the issue.

The JSX node itself is node.extra.parenthesized, and the printer always adds parens when returning a multiline JSX node as a return statement.

This might be one of those tricky recast bugs that crosses the border into philosophical debate on what the "correct" behavior might be and how exceptions to rules to exceptions to rules might have to be calculated. You might be better off adding a prettier rule that strips the extra parentheses and running code through prettier after the migration.

https://github.com/benjamn/recast/pull/1068#issuecomment-1112298542

michaldudak commented 1 year ago

Do the extra parentheses actually introduce bugs/regressions into migrated mui projects, or is it more of an aesthetics thing?

It's impossible to say right now. We haven't released our codemods with updated jscodeshift, so we don't know what our users may run into. The way our tests fail makes me believe it's only a matter of aesthetics.

This might be one of those tricky recast bugs that crosses the border into philosophical debate on what the "correct" behavior might be and how exceptions to rules to exceptions to rules might have to be calculated. You might be better off adding a prettier rule that strips the extra parentheses and running code through prettier after the migration.

Thanks a lot for investigating it! I don't think we can use any formatter as a part of the codemod, as we don't know the code style settings of codebases the codemods will run on.

I temporarily downgraded jscodeshift's version of recast to 0.20.4 and the extra parentheses went away

Does jscodeshift 0.14.0 work with recast 0.20.4 without any code changes? Perhaps pinning the recast version using yarn resolutions could be the way to go for us.

Daniel15 commented 1 year ago

You might be better off adding a prettier rule that strips the extra parentheses and running code through prettier after the migration.

FWIW this is generally the approach I take these days, across a few languages. Rather than ensuring all codemods output properly formatted code, I instead use a tool like Prettier to auto-format the resulting code. At Meta, we have auto-formatters for the most commonly-used languages, and for example our Hacklang codemods don't even try to properly format the resulting code as they just rely on hackfmt to format the code.

The issue with that is that not every codebase uses auto-formatting...

michaelfaith commented 1 year ago

This impacts us as well, in the same way. Unit tests doing input/output comparisons (similar to what MUI's codemod test suite does) are breaking after the update, and it yields files now that don't conform to prettier rules.

phitattoo commented 1 year ago

Phiphuket99

michaelfaith commented 1 month ago

There appears to be a PR in recast to address this https://github.com/benjamn/recast/pull/1406

ElonVolo commented 1 month ago

Aye, there is. And it may get merged into recast's main branch in the next year or two.