GithubHelp home page GithubHelp logo

react-codemod's Introduction

react-codemod Build Status

This repository contains a collection of codemod scripts for use with JSCodeshift that help update React APIs.

Usage

npx react-codemod <transform> <path> [...options]

  • transform - name of transform, see available transforms below.
  • path - files or directory to transform
  • use the --dry option for a dry-run and use --print to print the output for comparison

This will start an interactive wizard, and then run the specified transform.

Included Transforms

create-element-to-jsx

Converts calls to React.createElement into JSX elements.

npx react-codemod create-element-to-jsx <path>

error-boundaries

Renames the experimental unstable_handleError lifecycle hook to componentDidCatch.

npx react-codemod error-boundaries <path>

findDOMNode

Updates this.getDOMNode() or this.refs.foo.getDOMNode() calls inside of React.createClass components to React.findDOMNode(foo). Note that it will only look at code inside of React.createClass calls and only update calls on the component instance or its refs. You can use this script to update most calls to getDOMNode and then manually go through the remaining calls.

npx react-codemod findDOMNode <path>

manual-bind-to-arrow

Converts manual function bindings in a class (e.g., this.f = this.f.bind(this)) to arrow property initializer functions (e.g., f = () => {}).

npx react-codemod manual-bind-to-arrow <path>

pure-component

Converts ES6 classes that only have a render method, only have safe properties (statics and props), and do not have refs to Functional Components.

The wizard will ask for 2 options -

  • Use arrow functions?: converts to arrow function. Converts to function by default.
  • Destructure props?: will destructure props in the argument where it is safe to do so.
npx react-codemod pure-component <path>

pure-render-mixin

Removes PureRenderMixin and inlines shouldComponentUpdate so that the ES2015 class transform can pick up the React component and turn it into an ES2015 class. NOTE: This currently only works if you are using the master version (>0.13.1) of React as it is using React.addons.shallowCompare

npx react-codemod pure-render-mixin <path>
  • The wizard will ask to optionally override mixin-name, and look for it instead of PureRenderMixin. Note that it is not possible to use a namespaced name for the mixin. mixins: [React.addons.PureRenderMixin] will not currently work.

React-PropTypes-to-prop-types

Replaces React.PropTypes references with prop-types and adds the appropriate import or require statement. This codemod is intended for React 15.5+.

npx react-codemod React-PropTypes-to-prop-types <path>
  • In addition to running the above codemod you will also need to install the prop-types NPM package.

rename-unsafe-lifecycles

Adds UNSAFE_ prefix for deprecated lifecycle hooks. (For more information about this codemod, see React RFC #6)

npx react-codemod rename-unsafe-lifecycles <path>

react-to-react-dom

Updates code for the split of the react and react-dom packages (e.g., React.render to ReactDOM.render). It looks for require('react') and replaces the appropriate property accesses using require('react-dom'). It does not support ES6 modules or other non-CommonJS systems. We recommend performing the findDOMNode conversion first.

npx react-codemod react-to-react-dom <path>
  • After running the automated codemod, you may want to run a regex-based find-and-replace to remove extra whitespace between the added requires, such as codemod.py -m -d src --extensions js '(var React\s*=\s*require\(.react.\);)\n\n(\s*var ReactDOM)' '\1\n\2' using https://github.com/facebook/codemod.

React-DOM-to-react-dom-factories

Converts calls like React.DOM.div(...) to React.createElement('div', ...).

npx react-codemod React-DOM-to-react-dom-factories <path>

ReactNative-View-propTypes

Replaces View.propTypes references with ViewPropTypes and adds the appropriate import or require statement. This codemod is intended for ReactNative 44+.

npx react-codemod ReactNative-View-propTypes <path>

sort-comp

Reorders React component methods to match the ESLint react/sort-comp rule. (Defaults to ordering of the Airbnb style guide.

npx react-codemod sort-comp <path>

update-react-imports

As of Babel 7.9.0, when using runtime: automatic in @babel/preset-react or @babel/plugin-transform-react-jsx, you will not need to explicitly import React for compiling jsx. This codemod removes the redundant import statements. It also converts default imports (import React from 'react') to named imports (e.g. import { useState } from 'react').

The wizard will ask for 1 option -

  • Destructure namespace imports as well?: If chosen, namespace imports like import * as React will also be converted. By default, it's false, so only default imports (import React) are converted.
npx react-codemod update-react-imports <path>

Explanation of the new ES2015 class transform with property initializers

  1. Determine if mixins are convertible. We only transform a createClass call to an ES6 class component when:
  • There are no mixins on the class, or
  • options['pure-component'] is true, the mixins property is an array and it only contains pure render mixin (the specific module name can be specified using options['mixin-module-name'], which defaults to react-addons-pure-render-mixin)
  1. Ignore components that:
  • Call deprecated APIs. This is very defensive, if the script finds any identifiers called isMounted, getDOMNode, replaceProps, replaceState or setProps it will skip the component
  • Explicitly call this.getInitialState() and/or this.getDefaultProps() since an ES6 class component will no longer have these methods
  • Use arguments in methods since arrow functions don't have arguments. Also please notice that arguments should be very carefully used and it's generally better to switch to spread (...args) instead
  • Have inconvertible getInitialState(). Specifically if you have variable declarations like var props = ... and the right hand side is not this.props then we can't inline the state initialization in the constructor due to variable shadowing issues
  • Have non-primitive right hand side values (like foo: getStuff()) in the class spec
  1. Transform it to an ES6 class component
  2. Replace var A = React.createClass(spec) with class A extends React.Component {spec}. If a component uses pure render mixin and passes the mixins test (as described above), it will extend React.PureComponent instead - Remove the require/import statement that imports pure render mixin when it's no longer being referenced
  3. Pull out all statics defined on statics plus the few special cased statics like childContextTypes, contextTypes, displayName, getDefaultProps(), and propTypes and transform them to static properties (static propTypes = {...};) - If getDefaultProps() is simple (i.e. it only contains a return statement that returns something) it will be converted to a simple assignment (static defaultProps = ...;). Otherwise an IIFE (immediately-invoked function expression) will be created (static defaultProps = function() { ... }();). Note that this means that the function will be executed only a single time per app-lifetime. In practice this hasn't caused any issues — getDefaultProps should not contain any side-effects
  4. Transform getInitialState() - If there's no getInitialState() or the getInitialState() function is simple (i.e., it only contains a return statement that returns something) then we don't need a constructor; state will be lifted to a property initializer (state = ...;)
    • However, if the RHS of return contains references to this other than this.props and/or this.context, we can't be sure about what you'll need from this. We need to ensure that our property initializers' evaluation order is safe, so we defer state's initialization by moving it all the way down until all other property initializers have been initialized - If getInitialState() is not simple, we create a constructor and convert getInitialState() to an assignment to this.state
    • constructor always have props as the first parameter
    • We only put context as the second parameter when (one of) the following things happen in getInitialState():
      • It accesses this.context, or
      • There's a direct method call this.x(), or
      • this is referenced alone
    • Rewrite accesses to this.props to props and accesses to this.context to context since the values will be passed as constructor arguments
      • Remove simple variable declarations like var props = this.props; and var context = this.context
    • Rewrite top-level return statements (return {...};) to this.state = {...}
      • Add return; after the assignment when the return statement is part of a control flow statement (not a direct child of getInitialState()'s body) and not in an inner function declaration
  5. Transform all non-lifecycle methods and fields to class property initializers (like onClick = () => {};). All your Flow annotations will be preserved - It's actually not necessary to transform all methods to arrow functions (i.e., to bind them), but this behavior is the same as createClass() and we can make sure that we won't accidentally break stuff
  6. Generate Flow annotations from propTypes and put it on the class (this only happens when there's /* @flow */ in your code and options['flow'] is true)
  • Flow actually understands propTypes in createClass calls but not ES6 class components. Here the transformation logic is identical to how Flow treats propTypes
  • Notice that Flow treats an optional propType as non-nullable
    • For example, foo: React.PropTypes.number is valid when you pass {}, {foo: null}, or {foo: undefined} as props at runtime. However, when Flow infers type from a createClass call, only {} and {foo: undefined} are valid; {foo: null} is not. Thus the equivalent type annotation in Flow is actually {foo?: number}. The question mark on the left hand side indicates {} and {foo: undefined} are fine, but when foo is present it must be a number
  • For propTypes fields that can't be recognized by Flow, $FlowFixMe will be used
  1. React.createClass is no longer present in React 16. So, if a createClass call cannot be converted to a plain class, the script will fallback to using the create-react-class package.
  • Replaces React.createClass with ReactCreateClass.
  • Adds a require or import statement for create-react-class. The import style is inferred from the import style of the react import. The default module name can be overridden with the --create-class-module-name option.
  • Prunes the react import if there are no more references to it.

Usage

npx react-codemod class <path>

jscodeshift options

To pass more options directly to jscodeshift, use --jscodeshift="...". For example:

npx react-codemod --jscodeshift="--run-in-band --verbose=2"

See all available options here.

Recast Options

Options to recast's printer can be provided through jscodeshift's printOptions command line argument

npx react-codemod <transform> <path> --jscodeshift="--printOptions='{\"quote\":\"double\"}'"

explicit-require=false

If you're not explicitly importing React in your files (eg: if you're loading React with a script tag), you should add --explicit-require=false.

Support and Contributing

The scripts in this repository are provided in the hope that they are useful, but they are not officially maintained, and we generally will not fix community-reported issues. They are a collection of scripts that were previously used internally within Facebook or were contributed by the community, and we rely on community contributions to fix any issues discovered or make any improvements. If you want to contribute, you're welcome to submit a pull request.

License

react-codemod is MIT licensed.

react-codemod's People

Contributors

acdlite avatar alangpierce avatar bt4r9 avatar bvaughn avatar cpojer avatar dependabot[bot] avatar doytch avatar exkazuu avatar flarnie avatar gaearon avatar jsnmoon avatar keyz avatar lencioni avatar lunaruan avatar manixate avatar mkozmin avatar oliviertassinari avatar olosegres avatar paramaggarwal avatar piperchester avatar realetive avatar reergymerej avatar relekang avatar simison avatar skovy avatar stephen avatar thesavior avatar threepointone avatar turadg avatar zpao avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-codemod's Issues

Class transform fails for files with non-ASCII chars in template literals

Transforming a file with a template literal that contains a non-ASCII character will not work.

Any two-byte character seems to bump rightBackTickPos (determined by node.loc.end) 1 higher than what this assert in recast's util.fixTemplateLiteral expects.

The parser seems to count the position of multibyte chars several times in a way that recast doesn't handle properly. At first sight the parser chosen by jscodeshift seems to be babel-core.

Example of a file that won't be transformed:

import React from 'react';

const Foo = React.createClass({
  render() {
    return <div>{`æøåß`}</div>;
  },
});

export default Foo;

Output:

$ jscodeshift -t react-codemod/transforms/class.js ../Foo.js 
Processing 1 files... 
Spawning 1 workers...
Sending 1 files to free worker...
 ERR ../Foo.js Transformation error
AssertionError: 'd' === '`'
    at fixTemplateLiteral (/home/misund/.nvm/versions/node/v7.9.0/lib/node_modules/jscodeshift/node_modules/recast/lib/util.js:257:10)
    at Object.util.fixFaultyLocations (/home/misund/.nvm/versions/node/v7.9.0/lib/node_modules/jscodeshift/node_modules/recast/lib/util.js:167:5)
    at TreeCopier.TCp.copy (/home/misund/.nvm/versions/node/v7.9.0/lib/node_modules/jscodeshift/node_modules/recast/lib/parser.js:108:8)
    at TreeCopier.TCp.copy (/home/misund/.nvm/versions/node/v7.9.0/lib/node_modules/jscodeshift/node_modules/recast/lib/parser.js:151:24)
    at Array.map (native)
    at TreeCopier.TCp.copy (/home/misund/.nvm/versions/node/v7.9.0/lib/node_modules/jscodeshift/node_modules/recast/lib/parser.js:101:17)
    at TreeCopier.TCp.copy (/home/misund/.nvm/versions/node/v7.9.0/lib/node_modules/jscodeshift/node_modules/recast/lib/parser.js:151:24)
    at TreeCopier.TCp.copy (/home/misund/.nvm/versions/node/v7.9.0/lib/node_modules/jscodeshift/node_modules/recast/lib/parser.js:151:24)
    at Array.map (native)
    at TreeCopier.TCp.copy (/home/misund/.nvm/versions/node/v7.9.0/lib/node_modules/jscodeshift/node_modules/recast/lib/parser.js:101:17)
All done. 
Results: 
1 errors
0 unmodified
0 skipped
0 ok
Time elapsed: 1.376seconds 

I'm probably not going to bother to look any more at this, especially if I'm the only one having this issue.

If you're reading this in search for a quick and dirty fix, you can try to comment out the assert in recast and hope for the best. (That worked out just fine for me, but your milage may vary.)

ReactUtils can't detect the component if it extends different component extending React.Component

Let's take this scenario into consideration:

(...)
class AwesomeComponent extends React.Component {
(...)
}

(...)
class Test extends AwesomeComponent {
(...)
}

When using e.g. sort-comp it skips components extending other component extending React.Component.

My temporary fix was changing:

  // Finds all classes that extend React.Component
  const findReactES6ClassDeclaration = path => {
    const componentImport = findReactComponentName(path);
    const selector = componentImport
      ? {
        superClass: {
          type: 'Identifier',
          name: componentImport,
        },
      }
      : {
        superClass: {
          type: 'MemberExpression',
          object: {
            type: 'Identifier',
            name: 'React',
          },
          property: {
            type: 'Identifier',
            name: 'Component',
          },
        },
      };

    return path
     .find(j.ClassDeclaration, selector);
  };

to

  // Finds all classes that extend React.Component
  const findReactES6ClassDeclaration = path => {
    const componentImport = findReactComponentName(path);
    const selector = componentImport
      ? {
        superClass: {
          type: 'Identifier',
          name: componentImport,
        },
      }
      : {
        superClass: {
          type: 'Identifier',
          name: 'AwesomeComponent',
        },
      };

    return path
     .find(j.ClassDeclaration, selector);
  };

Class mod breaks on arrow function getInitialState

Hi!

So I ran into an issue where running the mod when getInitialState is an arrow
function it breaks (code has been changed due to corporate IP...):

export default React.createClass({
  displayName: 'MyComponent',

  getInitialState: () => ({
    stateThingOne: 'beep',
    stateThingTwo: 'boop'
  }),

  // ...
})

The error message would be this:

TypeError: Cannot read property 'map' of undefined
    at inlineGetInitialState (class.js:294:12)
    at createConstructor (class.js:343:15)
    at createESClass (class.js:362:11)
    at updateToClass (class.js:477:7)
    at NodePath.<anonymous> (class.js:507:31)
    at __paths.forEach (/Users/ricard.sole/.nvm/versions/node/v6.2.2/lib/node_modules/jscodeshift/dist/Collection.js:71:36)
    at Array.forEach (native)
    at Collection.forEach (/Users/ricard.sole/.nvm/versions/node/v6.2.2/lib/node_modules/jscodeshift/dist/Collection.js:70:18)
    at apply (class.js:507:10)
    at module.exports (class.js:514:7)

React-PropTypes-to-prop-types.js has a transformation error

When trying to upgrade to the latest version of React (migrating prop types and create class stuff), I found a LOT of errors with this particular codemod when running it on my codebase:

~/Developer/paypal/p2pnodeweb (pr/upgrade-react)
🚀  $ jscodeshift -t react-codemod/transforms/React-PropTypes-to-prop-types.js public/js/
Processing 691 files... 
Spawning 7 workers...
Sending 50 files to free worker...
Sending 50 files to free worker...
Sending 50 files to free worker...
Sending 50 files to free worker...
Sending 50 files to free worker...
Sending 50 files to free worker...
Sending 50 files to free worker...

...

 ERR public/js/calculator/containers/to-country/__tests__/skip-to-xoom.js Transformation error
TypeError: Cannot read property 'name' of undefined
    at /Users/kdodds/Developer/paypal/p2pnodeweb/react-codemod/transforms/React-PropTypes-to-prop-types.js:91:7
    at Array.filter (native)
    at Collection.filter (/Users/kdodds/.nvm/versions/node/v4.7.0/lib/node_modules/jscodeshift/dist/Collection.js:65:46)
    at removeDestructuredPropTypeStatements (/Users/kdodds/Developer/paypal/p2pnodeweb/react-codemod/transforms/React-PropTypes-to-prop-types.js:90:6)
    at module.exports (/Users/kdodds/Developer/paypal/p2pnodeweb/react-codemod/transforms/React-PropTypes-to-prop-types.js:163:22)

...

All done. 
Results: 
168 errors
0 unmodified
436 skipped
87 ok

Here's what that __tests__/skip-to-xoom.js file looks like:

import React from 'react'
import { render } from 'enzyme'
import SkipToXoom from '../skip-to-xoom'

test('the rendered button redirects to the proper URL when clicked', () => {
	const toCountryData = { countryName: 'India', countryCode: 'IN' }
	const div = renderToDiv({ toCountryData, userLang: 'en_us' })
	const { attribs: { href } } = div.find('a')[0]
	expect(href).toBe('#')
})

/**
 * Render the <SkipToXoom /> component to a div with the given props
 * We have to do some fancy footwork with the Router component to get
 * the Link component in our SkipToXoom component to render out the href
 * @param {Object} props - the props to apply to the component
 * @returns {Element} - the div that contains the element
 */
function renderToDiv(props = {}) {
	return render(<SkipToXoom {...props} dispatch={() => {}} userLang="en" />)
}

Any ideas?

react-to-react-dom does not work with es6 import

Files are simply skipped due this code:

        root
            .find(j.CallExpression)
            .filter(p => isRequire(p, coreModuleName))

In order to workaround es6 imports there should be something like:

        root
            .find(j.ImportDeclaration)

I'm dumb in AST, so I can't make a PR.

Add support for ES6 classes in sort-comp

In a comment in sort-comp, I noticed the following comment:

NOTE: only works on React.createClass() syntax, not ES6 class.

It would be really lovely if this worked with ES6 classes. 😺

sort-comp is not working

Hi, when I run sort-comp transformation, it throws following error:

Error: Cannot read config package: eslint-config-airbnb
Error: Cannot find module 'eslint-config-airbnb'
Referenced from: C:\projects\client\.eslintrc
    at Function.Module._resolveFilename (module.js:440:15)
    at Function.Module._load (module.js:388:25)
    at Module.require (module.js:468:17)
    at require (internal/module.js:20:19)
    at loadPackage (C:\projects\react-codemod\node_modules\eslint\lib\config\config-file.js:168:16)
    at loadConfigFile (C:\projects\react-codemod\node_modules\eslint\lib\config\config-file.js:212:1
8)
    at load (C:\projects\react-codemod\node_modules\eslint\lib\config\config-file.js:385:18)
    at C:\projects\react-codemod\node_modules\eslint\lib\config\config-file.js:326:36
    at Array.reduceRight (native)
    at applyExtends (C:\projects\react-codemod\node_modules\eslint\lib\config\config-file.js:309:28)

I've run npm install within react-codemod directory but it didn't help.

The script skips files that have mixins in them

When running this command:
jscodeshift -t ~/react-codemod/transforms/class.js filename.jsx

For the files that have mixins in them I get:
filename.jsx: 'Component' was skipped because of mixins.

although the component is defined with React.createClass() and the script should create ES2015 classes.

How should I still use the script and work around mixins?

"Unknown encoding" error

I've tried saving my files in various encodings both in Sublime Text and by converting using iconv but I get encoding errors every time:

MacOS 10.11.3, node 5.7.0, npm 3.6.0

jscodeshift path/to/my/file.jsx -v 2 -t ~/react-codemod/transforms/sort-comp.js 
Processing 1 files... 
Spawning 1 workers...
Sending 1 files to free worker...
buffer.js:638
          throw new TypeError('Unknown encoding: ' + encoding);
          ^
TypeError: Unknown encoding: path/to/my/file.jsx
    at Buffer.write (buffer.js:638:17)
    at fromString (buffer.js:113:26)
    at new Buffer (buffer.js:58:12)
    at createWriteReq (net.js:723:38)
    at WriteStream.Socket._writeGeneric (net.js:675:11)
    at WriteStream.Socket._write (net.js:698:8)
    at doWrite (_stream_writable.js:301:12)
    at writeOrBuffer (_stream_writable.js:287:5)
    at WriteStream.Writable.write (_stream_writable.js:215:11)
    at WriteStream.Socket.write (net.js:624:40)

findDOMNode script

Why does the script convert
this.refs.injectButton.getDOMNode().focus();

into
React.findDOMNode(this.refs.injectButton).focus()

My understanding of the changes to refs is that the original code could be transformed to
this.refs.injectButton.focus()
which if equivalent functionally seems like a better solution. In the docs it even mentions that using findDOMNode() will be unnecessary in most cases.

Was there a specific case you are guarding against to take the more conservative approach?

Thanks

Cannot read property 'name' of undefined, prop-types transformation

I have many components for which prop-types transformation happening correctly, but for other components its failing with following error

TypeError: Cannot read property 'name' of undefined
    at /Users/z013mrq/react-codemod/transforms/React-PropTypes-to-prop-types.js:91:7
    at Array.filter (native)
    at Collection.filter (/Users/z013mrq/.nvm/versions/node/v6.9.0/lib/node_modules/jscodeshift/dist/Collection.js:65:46)
    at removeDestructuredPropTypeStatements (/Users/z013mrq/react-codemod/transforms/React-PropTypes-to-prop-types.js:90:6)
    at module.exports (/Users/z013mrq/react-codemod/transforms/React-PropTypes-to-prop-types.js:163:22)

One such component code example is

import React from 'react';
import Link from '../Link';

const Breadcrumb = ({ links }) => (
  <div itemType="http://schema.org/breadcrumb" itemProp="breadcrumb">
    ......
  </div>
);

Breadcrumb.propTypes = {
  links: React.PropTypes.arrayOf(
    React.PropTypes.shape({
      title: React.PropTypes.string.isRequired,
      href: React.PropTypes.string
    })
  ).isRequired
};

export default Breadcrumb;

Help!

class.js complains about deprecated API calls that are not used with React

Did a dry run and got the following message:

Overview was skipped because of deprecated API calls. Remove calls to getDOMNode, isMounted, replaceProps, replaceState, setProps in your React component and re-run this script.

I do indeed use replaceState, but the context is history.replaceState (https://developer.mozilla.org/en-US/docs/Web/API/History_API), not React, so I believe there should not be a problem, the function names just happen to be the same.

Debugging skipped files

Is there a way I can see why my files are being skipped on the create-element-to-jsx transform? I tried using astexplorer but had no luck. Any advice would be appreciated. Thanks.

The output shows:

Processing 1 files...
Spawning 1 workers...
Sending 1 files to free worker...
All done.
Results:
0 errors
0 unmodified
1 skipped
0 ok

react-codemod/transforms/class.js has a issues

I'm just here to say thanks for this! I'm really amazed at how well it works and I was pleasantly surprised that it doesn't just force an import of create-react-class but it will actually codemod to ES6 class (with public class fields) if it's not using mixins 🎉 thanks!

Results: 
0 errors
495 unmodified
0 skipped
196 ok

Fix tests

Right now they don't pass. Looks like trailing commas are getting added by the printer.

`prop-types` codemod fails for explicit, nested destructuring

Given a PropTypes import that looks like this:

const {
    PropTypes: {
        shape,
        bool,
        number,
        string
    }
} = React

the prop-types codemod will insert import PropTypes from 'prop-types'; but will remove the destructuring assignment completely, leaving shape, bool, number, and string undefined.

Publish current version to NPM?

The current version on NPM is 2.0.0 and the repo is at 4.0.0. Can we publish the current version? I want to use the sort-comp transform without having to clone and build the repo.

getInitialState isn't transformed into constructor this.state

Before:

let WorkspaceItemView = React.createClass({
  displayName: 'WorkspaceItemView',

  getInitialState() {
    return {
      workspaceItemManager: this._getWorkspaceItemManager(this.props.id),
      workspaceItemStore: this._getWorkspaceItemStore(this.props.id)
    };
  },
});

After: (that's how it works now)

class WorkspaceItemView extends React.Component {
  static displayName = 'WorkspaceItemView';

  state = {
    workspaceItemManager: this._getWorkspaceItemManager(this.props.id),
    workspaceItemStore: this._getWorkspaceItemStore(this.props.id)
  };
}

But it should be this:

class WorkspaceItemView extends React.Component {
  static displayName = 'WorkspaceItemView';

  constructor(props) {
    super(props);

    this.state = {
      workspaceItemManager: this._getWorkspaceItemManager(this.props.id),
      workspaceItemStore: this._getWorkspaceItemStore(this.props.id)
    };
  }
}

pure-render-mixin transformation error

Getting error:

ERR  ./app/node_modules/components/tweet/index.jsx Transformation error
TypeError: Cannot read property 'get' of undefined
    at Collection.get (/Users/eschaefer/.nvm/versions/node/v4.2.1/lib/node_modules/jscodeshift/dist/Collection.js:205:18)
    at deletePureRenderMixin (/Users/eschaefer/Projects/react-codemod/transforms/pure-render-mixin.js:159:54)
    at removePureRenderMixin (/Users/eschaefer/Projects/react-codemod/transforms/pure-render-mixin.js:180:7)

when running the pure-render-mixin.js on example component:

import React from 'react';
import TranslationsMixin from 'mixins/translations';

var Tweet = React.createClass({

  mixins: [TranslationsMixin],

  render() {

    return (
      <div>
        Stuff
      </div>
    );
  }
});

export default Tweet;

I have tried passing the --mixin-name=TranslationsMixin flag, but no luck. Any idea what could be the issue?

React.DOM.div not transformed

I noticed that this will transform React.createElement, but not React.DOM.div, which is just a convenience wrapper. I have a large codebase that I'm going to need to transform very soon and it's all using React.DOM.div (or span, a, etc). It sounds like it would be easy to make that work the same as React.createElement. What do you think?

pure-component does not check for refs

A component can only be converted into a stateless component if it does not use refs on its children. The current codemod does not check for it.
Still amazed by these codemods, but this could be a great improvement...

"react/sort-comp" regexes are ignored

Looks like regular expression values in this eslint property are ignored. For example:

"react/sort-comp": [2, {
  order: [
    "/^render/",
    "/^on/",
    "everything-else"
  ]
}]

gives the same output as

"react/sort-comp": [2, {
  order: [
    "everything-else"
  ]
}]

Don't skip files conversion because of mixins?

I want to leave mixins and use them with a 3rd party lib.
When i try to convert components that have mixins in them they get skipped, can i somehow ignore that and still convert components to classes?

create-element-to-jsx doesn't preserve comments

It looks like the create-element-to-jsx transform silently throws away comments within React.createElement calls. Ideally, it should find some way to preserve the comments, or maybe give an error so I can fix those files manually, but if that's too hard or out of scope, I think the behavior should at least be documented.

For example, if you run it on this file:

import React from 'react';

const render = () => {
  return React.createElement('div', {
    className: 'foo',  // This is an important comment.
  });
};

It produces this:

import React from 'react';

const render = () => {
  return <div className="foo" />;
};

class transform is broken

From the most recent travis-ci build:

...
     [{kind: method, key: [object Object], value: [object Object], static: false, loc: null, type: MethodDefinition, comments: undefined, computed: false, decorators: null}] does not match field "body": [ClassMethod | ClassProperty] of type ClassBody
      
      at add (node_modules/jscodeshift/node_modules/recast/node_modules/ast-types/lib/types.js:580:31)
      at node_modules/jscodeshift/node_modules/recast/node_modules/ast-types/lib/types.js:593:21
      at Array.forEach (native)
      at Function.Object.defineProperty.value [as classBody] (node_modules/jscodeshift/node_modules/recast/node_modules/ast-types/lib/types.js:592:34)
      at createESClass (transforms/class.js:957:7)
      at NodePath.updateToClass (transforms/class.js:1068:5)
      at node_modules/jscodeshift/dist/Collection.js:76:36
      at Array.forEach (native)
      at Collection.forEach (node_modules/jscodeshift/dist/Collection.js:75:18)
      at apply (transforms/class.js:1137:264)
...

I saw the same failures when running npm install locally.

Looking at the build history it appears that 84ef7b7 is good but 5093980 even though the latter didn't introduce any changes into class.js.

I thought maybe it was an issue with the version of jscodeshift I had so I installed 0.3.28 which is what the last passing build installed. That did not fix the situation so I'm guessing it's some other dependency (or sub-dependency) that's causing the issue.

PureComponent doesn’t work with React.Component destructured

We use

import React, { PropTypes, Component } from 'react';

export default class Foo extends Component {

and the PureComponent transform skips all of these. Even if this doesn't get fixed its worth noting here in case someone else is wondering why all their files are being skipped.

Build Warning

Thanks for updating the test suite. There is currently a build warning:

Running 1 test suite...class-test.js: `MyComponent4` was skipped because of invalid field(s) `foo` on the React component. Remove any right-hand-side expressions that are not simple, like: `componentWillUpdate: createWillUpdate()` or `render: foo ? renderA : renderB`.

Create JSX - file skipped with no explanation

Hi, I'm playing with this for this first time. My test file is being skipped, and nothing is being logged as to why.

jscodeshift -t react-codemod/transforms/create-element-to-jsx.js -p --dry --run-in-band -v 2 output.js 
Processing 1 files...
Running in dry mode, no files will be written!
 SKIP output.js
All done.
Results: 0 errors 0 unmodified 1 skipped 0 ok
Time elapsed: 0.999 seconds

Is there any way I can get more output from it?

Destructuring for pure-component

Would anyone be interested in an option for the pure-component transform that would destructure the props where possible - i.e. when there's no {...this.props} or doStuff(this.props) and only keys of this.props are used?
I would be down to open a PR if so.

So

render() {
  const { prop1 } = this.props;
  return <div prop1={prop1} prop2={this.props.prop2} />;
}

Would become

({ prop1, prop2 }) => <div prop1={prop1} prop2={prop2} />;

create-element-to-jsx: All files being skipped, even though react is included

not sure why but all my code is being skipped over without explantion

this is how I call the command

jscodeshift -t react-codemod/transforms/create-element-to-jsx.js dealroom/assets/jsx/core/Dialog.js

here is a sample of one of my files that is being skipped

import React from 'react';
import ReactDOM from 'react-dom';

var Dialog = React.createClass({
    
    getInitialState(){

        return {
            show: false,
            onConfirm: null,
            onCancel: null,
            onBackgroundClick: null,
        }

    },

    render(){
        return null;
    },

    showDialogBox(onConfirm, onCancel, onBackgroundClick){
        this.setState({
            show: true,
            onConfirm: onConfirm,
            onCancel: onCancel,
            onBackgroundClick: onBackgroundClick,
        })
    },

    hideDialogBox(){
        this.setState({
            show: false,
            onConfirm: null,
            onCancel: null
        })
    },

    _onConfirm(){
        if(this.state.onConfirm){
            this.state.onConfirm()
            
        }
    },

    _onCancel(){
        if(this.state.onCancel){
            this.state.onCancel()
            
        }
    },
    _onBackgroundClick(){
        if(this.state.onBackgroundClick){
            this.state.onBackgroundClick()
        }
    },


    _renderDialog(){

        const {show} = this.state;
        
        var className = 'toast dialog';
        
        

        var btn1_class = 'btn-primary';
        var btn2_class = 'btn-error';
        if(this.props.reverseBtnColor){
            btn1_class = 'btn-error';
            btn2_class = 'btn-primary';
        }

        ReactDOM.render(
            <div className='toast-container dialog-container'>
                {show?
                    <div>
                        <div className={className}>
                            <div>
                                {this.props.children} 
                            </div>
                            <div className='btns'>
                                <a className={'btn ' + btn1_class} onClick={this._onConfirm}>
                                    {this.props.confirm_text}
                                </a>
                                <a className={'btn ' + btn2_class} onClick={this._onCancel}>
                                    {this.props.cancel_text}
                                </a>
                            </div>
                                
                        </div>
                        <div className='waiting-overlay' onClick={this._onBackgroundClick}/>
                    </div>
                    :null
                }
                
             </div>
        , this.portal)
    },

    componentDidMount(){
        this.portal = document.createElement('toast');
        document.getElementById('portal').appendChild(this.portal);


        this._renderDialog(this.props);
        
    },

    componentWillReceiveProps(new_props){
        this._renderDialog(new_props);
    },
    componentDidUpdate(){
        this._renderDialog(); 
    },

    componentWillUnmount(){
        ReactDOM.unmountComponentAtNode(this.portal);
    }
});

export default Dialog

output

Processing 1 files...
Spawning 1 workers...
Sending 1 files to free worker...
All done.
Results:
0 errors
0 unmodified
1 skipped
0 ok

create-element-to-jsx doesn't handle props objects

The create-element-to-jsx transform attempts to make replacements like this:

-    return React.createElement(Constructor, props);
+    return <Constructor />;

Expected:

-    return React.createElement(Constructor, props);
+    return <Constructor {...props} />;

Destructured parameters throws error in PropTypes transform

Hey there!

So our codebase has a component like this

import React, { PropTypes } from 'react';
import classnames from 'classnames';

const propTypes = {
  children: PropTypes.node,
  className: PropTypes.string,
};

const Accordion = ({ children, className, ...other }) => {
  const classNames = classnames('bx--accordion', className);
  return (
    <ul className={classNames} {...other}>
      {children}
    </ul>
  );
};

Accordion.propTypes = propTypes;

export default Accordion;

The codemod currently breaks on the destructured parameter - if I swap it over to

const Accordion = props => {
  const { children, className, ...other } = props;

  const classNames = classnames('bx--accordion', className);
  return (
    <ul className={classNames} {...other}>
      {children}
    </ul>
  );
};

The codemod runs perfectly fine and outputs as expected.

The error is below

 ERR components/Accordion.js Transformation error
TypeError: Cannot read property 'name' of undefined
    at /Users/chrisdhanaraj/bluemix/react-codemod/transforms/React-PropTypes-to-prop-types.js:103:7
    at Array.filter (native)
    at Collection.filter (/Users/chrisdhanaraj/.config/yarn/global/node_modules/jscodeshift/dist/Collection.js:65:46)
    at removeDestructuredPropTypeStatements (/Users/chrisdhanaraj/bluemix/react-codemod/transforms/React-PropTypes-to-prop-types.js:102:6)
    at module.exports (/Users/chrisdhanaraj/bluemix/react-codemod/transforms/React-PropTypes-to-prop-types.js:175:22)

TypeError: Cannot read property 'parent' of undefined

I have some components that transform incorrectly as blow:

TypeError: Cannot read property 'parent' of undefined
    at addPropTypesImport (/Users/ltchronus/Documents/workspace4air/react-codemod/transforms/React-PropTypes-to-prop-types.js:95:7)
    at module.exports (/Users/ltchronus/Documents/workspace4air/react-codemod/transforms/React-PropTypes-to-prop-types.js:182:5)

My component example is

import React, { Component, PropTypes } from 'react';
import Message from './Message';
if (process.env.BROWSER) {
  require('./style/index.scss'); // eslint-disable-line
}
...

related code

function addPropTypesImport(j, root) {
  if (useImportSyntax(j, root)) {
    const importStatement = j.importDeclaration(
      [j.importDefaultSpecifier(j.identifier('PropTypes'))],
      j.literal('prop-types')
    );

    const path = findImportAfterPropTypes(j, root);

    j(path).insertBefore(importStatement);
  } else {
    const requireStatement = useVar(j, root)
      ? j.template.statement`var PropTypes = require('prop-types');\n`
      : j.template.statement`const PropTypes = require('prop-types');\n`;
    // I have require but don't use require to PropTypes
    const path = findRequireAfterPropTypes(j, root) ;

    j(path.parent.parent).insertBefore(requireStatement);
  }
}

"Cannot read property 'Symbol(Symbol.iterator)' of undefined"

Wasn't sure if I should report this here or with the jscodemod folks. I ran npm -g list jscodeshift which installed the latest jscodeshift, then tried running:

jscodeshift -t ~/Downloads/react-codemod-master/transforms/pure-render-mixin.js ./ -d -p

Which gave me the following error:

No files selected, nothing to do. 
(node:1598) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 3): TypeError: Cannot read property 'Symbol(Symbol.iterator)' of undefined

Possible separate issue (unsure): I tried running it on a specific file, ./Components/App.jsx, instead of a ./ path, and I got the following output:

Processing 1 files... 
Spawning 1 workers...
Running in dry mode, no files will be written! 
Sending 1 files to free worker...
 ERR ./Components/App.jsx Transformation error
Error: You cannot call "get" on a collection with no paths. Instead, check the "length" property first to verify at least 1 path exists.
    at Error (native)
    at Collection.get (/usr/local/lib/node_modules/jscodeshift/dist/Collection.js:190:13)
    at deletePureRenderMixin (/Users/ruben/Downloads/react-codemod-master/transforms/pure-render-mixin.js:159:54)
    at removePureRenderMixin (/Users/ruben/Downloads/react-codemod-master/transforms/pure-render-mixin.js:180:7)
All done. 
Results: 
1 errors
0 unmodified
0 skipped
0 ok
Time elapsed: 1.130seconds

For the record, I'm on node v6.7.0, React 15.3.2, and I just downloaded react-codemod.

React-PropTypes to prop types: Transformation Error: Cannot read property 'name' of undefined

I ran:

jscodeshift -t transforms/React-PropTypes-to-prop-types.js ../project/src/

It this error on almost every file:

 ERR ../project/src/WebAPI/application.js Transformation error
TypeError: Cannot read property 'name' of undefined
    at /home/user/react-codemod/transforms/React-PropTypes-to-prop-types.js:91:7
    at Array.filter (native)
    at Collection.filter (/home/user/.config/yarn/global/node_modules/jscodeshift/dist/Collection.js:65:46)
    at removeDestructuredPropTypeStatements (/home/user/react-codemod/transforms/React-PropTypes-to-prop-types.js:90:6)
    at module.exports (/home/gio/react-codemod/transforms/React-PropTypes-to-prop-types.js:163:22)
// application.js
import { callApi } from './api'

export const details = ({
    ID,
    hash,
    page,
}) => callApi('application_view', {
    params: {
        id,
       hash,
       page,
    },
})

export const add = () => {}
export const search = (params) => callApi('application_search', { params })

results:

74 errors
0 unmodified
292 skipped
0 ok
Time elapsed: 1.808seconds 

I use babel-loader with this config:

['react', 'stage-1', ["env", { "targets": { "chrome": 56 }}]]

but as jscodeshift --babel flag defaults to true, I think this shouldn't be an issue.

PropTypes transformation completely broken?

I'm trying to run the PropTypes transformation on my code base but the only file it (wrongly) updates is a file containing a single propType specification:

- import { PropTypes } from 'react';
+ import 'react';
export const SEPARATOR = 'separator';

export default {
  type: PropTypes.oneOf([SEPARATOR]),
  value: PropTypes.any,
  label: PropTypes.string,
};

All my other files are simply ignored (they are very basic components):

Processing 17 files...
Spawning 3 workers...
Sending 6 files to free worker...
Sending 6 files to free worker...
Sending 5 files to free worker...
All done.
Results:
0 errors
0 unmodified
17 skipped
0 ok

What happens?

sort-comp customization

It would be nice to pick up the specific sort order defined in .eslintrc if not provided through the CLI options before falling back to the airbnb order.

Pure component transform transforms classes with class properties initializers

This class that is using class property initializers

import React, { PropTypes } from 'react';

export default class StringControl extends React.Component {
  handleChange = (e) => {
    this.props.onChange(e.target.value);
  };

  render() {
    return <input type="text" value={this.props.value || ''} onChange={this.handleChange} />;
  }
}

StringControl.propTypes = {
  onChange: PropTypes.func.isRequired,
  value: PropTypes.node,
};

gets transformed to:

import React, { PropTypes } from 'react';

export default function StringControl(props) {
  return <input type="text" value={props.value || ''} onChange={this.handleChange} />;
}

StringControl.propTypes = {
  onChange: PropTypes.func.isRequired,
  value: PropTypes.node,
};

It should not.

Buggy conversion

MyComponent.jsx:

const React = require("react");

const MyComponent = React.createClass({
  render: function () {
    return (
      <label title={(true ? "T1" + "•" : "T2") + "T3"} />
    );
  },
});
jscodeshift -t ../../react-codemod/transforms/class.js MyComponent.jsx

output:

const React = require("react");

class MyComponent extends React.Component {
  render() {
    return (
      <label title={(((true ? "T1" + "•" : : 2") ))+ "T3"} />
    );
  }
}

TypeError: Cannot read property 'start' of null in pure_component.js

Here's the error:

TypeError: Cannot read property 'start' of null
    at reportSkipped (pure-component.js:86:28)
    at pure-component.js:97:9
    at Array.filter (native)
    at Collection.filter (/Users/ritz078/.npm-packages/lib/node_modules/jscodeshift/dist/Collection.js:60:46)
    at module.exports (pure-component.js:94:6)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.