codemod-com / codemod

The end-to-end platform for creating, sharing, and running codemods with engines like jscodeshift, ast-grep, ts-morph, and more. Automate code migrations, cleanups, and refactors for you, your team, and the community. AI-powered and CLI-first.
https://go.codemod.com/docs
Apache License 2.0
365 stars 32 forks source link

react/prop-types-typescript removes prop types regardless of the --preserver-prop-types option #498

Closed ml closed 4 months ago

ml commented 4 months ago

I just copy-pasted the components, but I believe their content is irrelevant. The problem is that the codemod doesn't respect passed options.

Faulty codemod

react-prop-types-typescript doesn't respect the preserver-prop-types (in both VS Code and CLI).

Code before transformation

import { styled } from '@mui/material/styles'
import PropTypes from 'prop-types'

const Root = styled('button', {
  name: 'SegmentedTab',
  shouldForwardProp: (property) => property !== 'isSelected',
  // @ts-expect-error TS(2339): Property 'isSelected' does not exist on type 'MUIS... Remove this comment to see the full error message
})(({ isSelected, theme }) => ({
  '&:hover': {
    // @ts-expect-error TS(2339): Property 'neutral' does not exist on type 'Palette... Remove this comment to see the full error message
    background: theme.palette.neutral[200],
  },
  alignItems: 'center',
  background: 'transparent',
  border: 'none',
  borderRadius: 999,
  color: theme.palette.text.primary,
  cursor: 'pointer',
  display: 'flex',
  flex: 1,
  fontFamily: theme.typography.fontFamily,
  fontSize: 'inherit',
  fontWeight: 'inherit',
  justifyContent: 'center',
  padding: 0,
  textDecoration: 'none',
  ...(isSelected && {
    '&, &:hover': {
      background: 'white',
    },
  }),
}))

export default function SegmentedTab({
  component = 'button',
  isSelected,
  ...other
}: any) {
  const Component = Root.withComponent(component)

  return <Component isSelected={isSelected} {...other} />
}

SegmentedTab.propTypes = {
  /** React component or HTML element name that is used to render tabs. */
  component: PropTypes.elementType,
  /** Marks the tab as a selected. */
  isSelected: PropTypes.bool.isRequired,
}

Expected code after transformation

import { styled } from '@mui/material/styles'

const Root = styled('button', {
  name: 'SegmentedTab',
  shouldForwardProp: (property) => property !== 'isSelected',
  // @ts-expect-error TS(2339): Property 'isSelected' does not exist on type 'MUIS... Remove this comment to see the full error message
})(({ isSelected, theme }) => ({
  '&:hover': {
    // @ts-expect-error TS(2339): Property 'neutral' does not exist on type 'Palette... Remove this comment to see the full error message
    background: theme.palette.neutral[200],
  },
  alignItems: 'center',
  background: 'transparent',
  border: 'none',
  borderRadius: 999,
  color: theme.palette.text.primary,
  cursor: 'pointer',
  display: 'flex',
  flex: 1,
  fontFamily: theme.typography.fontFamily,
  fontSize: 'inherit',
  fontWeight: 'inherit',
  justifyContent: 'center',
  padding: 0,
  textDecoration: 'none',
  ...(isSelected && {
    '&, &:hover': {
      background: 'white',
    },
  }),
}))

interface SegmentedTabProps {
  /** React component or HTML element name that is used to render tabs. */
  component?: React.ElementType;
  /** Marks the tab as a selected. */
  isSelected: boolean;
}

export default function SegmentedTab({
  component = 'button',
  isSelected,
  ...other
}: SegmentedTabProps) {
  const Component = Root.withComponent(component)

  return <Component isSelected={isSelected} {...other} />
}

SegmentedTab.propTypes = {
  /** React component or HTML element name that is used to render tabs. */
  component: PropTypes.elementType,
  /** Marks the tab as a selected. */
  isSelected: PropTypes.bool.isRequired,
}

Actual code after transformation

import { styled } from '@mui/material/styles'

const Root = styled('button', {
  name: 'SegmentedTab',
  shouldForwardProp: (property) => property !== 'isSelected',
  // @ts-expect-error TS(2339): Property 'isSelected' does not exist on type 'MUIS... Remove this comment to see the full error message
})(({ isSelected, theme }) => ({
  '&:hover': {
    // @ts-expect-error TS(2339): Property 'neutral' does not exist on type 'Palette... Remove this comment to see the full error message
    background: theme.palette.neutral[200],
  },
  alignItems: 'center',
  background: 'transparent',
  border: 'none',
  borderRadius: 999,
  color: theme.palette.text.primary,
  cursor: 'pointer',
  display: 'flex',
  flex: 1,
  fontFamily: theme.typography.fontFamily,
  fontSize: 'inherit',
  fontWeight: 'inherit',
  justifyContent: 'center',
  padding: 0,
  textDecoration: 'none',
  ...(isSelected && {
    '&, &:hover': {
      background: 'white',
    },
  }),
}))

interface SegmentedTabProps {
  /** React component or HTML element name that is used to render tabs. */
  component?: React.ElementType;
  /** Marks the tab as a selected. */
  isSelected: boolean;
}

export default function SegmentedTab({
  component = 'button',
  isSelected,
  ...other
}: SegmentedTabProps) {
  const Component = Root.withComponent(component)

  return <Component isSelected={isSelected} {...other} />
}

Estimated severity

It's quite a sever issue to us. We have hundreds of components we want to add types to, preserving prop types. Doing it manually is not an option.

Looks like a trivial issue to fix. The codemod doesn't pick up the preserver-prop-types option, neither from VS Code nor from the console. Feels like a typo to me, but I couldn't find a way to debug it myself.

Environment:

mohab-sameh commented 4 months ago

@arybitskiy can you check this please.

@all-contributors add @ml for bug

allcontributors[bot] commented 4 months ago

@mohab-sameh

I've put up a pull request to add @ml! :tada:

DmytroHryshyn commented 4 months ago

Thanks for reporting @ml, the issue will be fixed in the next CLI release.

DmytroHryshyn commented 4 months ago

@ml issue if fixed in codemod@0.10.20. codemod react/prop-types-typescript --preserve-prop-types='all' should preserve prop types definitions.