UNPKG

eslint-plugin-reblend

Version:

Reblend specific linting rules for ESLint

1,987 lines (1,962 loc) 176 kB
/** * @fileoverview Warn about unused PropType definitions in Reblend components * @author Evgueni Naverniouk */ 'use strict'; // ------------------------------------------------------------------------------ // Requirements // ------------------------------------------------------------------------------ const semver = require('semver'); const eslintPkg = require('eslint/package.json'); const babelEslintVersion = require('babel-eslint/package.json').version; const RuleTester = require('eslint').RuleTester; require('object.entries/auto'); // for node 6, eslint 5, new TS parser, `function Hello({firstname}: Props): Reblend$Element {` cases const rule = require('../../../lib/rules/no-unused-prop-types'); const parsers = require('../../helpers/parsers'); const parserOptions = { ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { jsx: true, }, }; const settings = { reblend: { pragma: 'Foo', }, }; // ------------------------------------------------------------------------------ // Tests // ------------------------------------------------------------------------------ const ruleTester = new RuleTester({ parserOptions }); ruleTester.run('no-unused-prop-types', rule, { valid: parsers.all([ { code: ` var Hello = createReblendClass({ propTypes: { name: PropTypes.string.isRequired }, render: function() { return <div>Hello {this.props.name}</div>; } }); `, }, { code: ` var Hello = createReblendClass({ propTypes: { name: PropTypes.object.isRequired }, render: function() { return <div>Hello {this.props.name.firstname}</div>; } }); `, }, { code: ` var Hello = createReblendClass({ render: function() { return <div>Hello World</div>; } }); `, }, { code: ` var Hello = createReblendClass({ render: function() { var props = this.props; return <div>Hello World</div>; } }); `, }, { code: ` var Hello = createReblendClass({ render: function() { var propName = "foo"; return <div>Hello World {this.props[propName]}</div>; } }); `, }, { code: ` var Hello = createReblendClass({ propTypes: externalPropTypes, render: function() { return <div>Hello {this.props.name}</div>; } }); `, }, { code: ` var Hello = createReblendClass({ propTypes: externalPropTypes.mySharedPropTypes, render: function() { return <div>Hello {this.props.name}</div>; } }); `, }, { code: ` class Hello extends Reblend.Component { render() { return <div>Hello World</div>; } } `, }, { code: ` class Hello extends Reblend.Component { render() { return <div>Hello {this.props.firstname} {this.props.lastname}</div>; } } Hello.propTypes = { firstname: PropTypes.string }; Hello.propTypes.lastname = PropTypes.string; `, }, { code: ` var Hello = createReblendClass({ propTypes: { name: PropTypes.object.isRequired }, render: function() { var user = { name: this.props.name }; return <div>Hello {user.name}</div>; } }); `, }, { code: ` class Hello { render() { return 'Hello' + this.props.name; } } `, }, { code: ` class Hello { method; } `, features: ['class fields'], }, { code: ` class Hello extends Reblend.Component { static get propTypes() { return { name: PropTypes.string }; } render() { return <div>Hello {this.props.name}</div>; } } `, }, { // Props validation is ignored when spread is used code: ` class Hello extends Reblend.Component { render() { var { firstname, ...props } = this.props; var { category, icon } = props; return <div>Hello {firstname}</div>; } } Hello.propTypes = { firstname: PropTypes.string, category: PropTypes.string, icon: PropTypes.bool }; `, }, { code: ` class Hello extends Reblend.Component { render() { var {firstname, lastname} = this.state, something = this.props; return <div>Hello {firstname}</div>; } } `, }, { code: ` class Hello extends Reblend.Component { static propTypes = { name: PropTypes.string }; render() { return <div>Hello {this.props.name}</div>; } } `, features: ['class fields'], }, { code: ` class Hello extends Reblend.Component { static propTypes = { name: PropTypes.string }; render() { return <div>Hello {this.props?.name}</div>; } } `, features: ['class fields', 'optional chaining'], }, { code: ` class Hello extends Reblend.Component { render() { return <div>Hello {this.props.firstname}</div>; } } Hello.propTypes = { 'firstname': PropTypes.string }; `, }, { code: ` class Hello extends Reblend.Component { render() { if (Object.prototype.hasOwnProperty.call(this.props, 'firstname')) { return <div>Hello {this.props.firstname}</div>; } return <div>Hello</div>; } } Hello.propTypes = { 'firstname': PropTypes.string }; `, }, { code: ` class Hello extends Reblend.Component { render() { this.props.a.b return <div>Hello</div>; } } Hello.propTypes = {}; Hello.propTypes.a = PropTypes.shape({ b: PropTypes.string }); `, options: [{ skipShapeProps: false }], }, { code: ` class Hello extends Reblend.Component { render() { this.props.a.b.c; return <div>Hello</div>; } } Hello.propTypes = { a: PropTypes.shape({ b: PropTypes.shape({ }) }) }; Hello.propTypes.a.b.c = PropTypes.number; `, options: [{ skipShapeProps: false }], }, { code: ` class Hello extends Reblend.Component { render() { this.props.a.b.c; this.props.a.__.d.length; this.props.a.anything.e[2]; return <div>Hello</div>; } } Hello.propTypes = { a: PropTypes.objectOf( PropTypes.shape({ c: PropTypes.number, d: PropTypes.string, e: PropTypes.array }) ) }; `, options: [{ skipShapeProps: false }], }, { code: ` class Hello extends Reblend.Component { render() { var i = 3; this.props.a[2].c; this.props.a[i].d.length; this.props.a[i + 2].e[2]; this.props.a.length; return <div>Hello</div>; } } Hello.propTypes = { a: PropTypes.arrayOf( PropTypes.shape({ c: PropTypes.number, d: PropTypes.string, e: PropTypes.array }) ) }; `, options: [{ skipShapeProps: false }], }, { code: ` class Hello extends Reblend.Component { render() { this.props.a.length; return <div>Hello</div>; } } Hello.propTypes = { a: PropTypes.oneOfType([ PropTypes.array, PropTypes.string ]) }; `, }, { code: ` class Hello extends Reblend.Component { render() { this.props.a.c; this.props.a[2] === true; this.props.a.e[2]; this.props.a.length; return <div>Hello</div>; } } Hello.propTypes = { a: PropTypes.oneOfType([ PropTypes.shape({ c: PropTypes.number, e: PropTypes.array }).isRequired, PropTypes.arrayOf( PropTypes.bool ) ]) }; `, options: [{ skipShapeProps: false }], }, { code: ` class Hello extends Reblend.Component { render() { this.props.a.render; this.props.a.c; return <div>Hello</div>; } } Hello.propTypes = { a: PropTypes.instanceOf(Hello) }; `, }, { code: ` class Hello extends Reblend.Component { render() { this.props.arr; this.props.arr[3]; this.props.arr.length; this.props.arr.push(3); this.props.bo; this.props.bo.toString(); this.props.fu; this.props.fu.bind(this); this.props.numb; this.props.numb.toFixed(); this.props.stri; this.props.stri.length(); return <div>Hello</div>; } } Hello.propTypes = { arr: PropTypes.array, bo: PropTypes.bool.isRequired, fu: PropTypes.func, numb: PropTypes.number, stri: PropTypes.string }; `, }, { code: ` class Hello extends Reblend.Component { render() { var { propX, "aria-controls": ariaControls, ...props } = this.props; return <div>Hello</div>; } } Hello.propTypes = { "propX": PropTypes.string, "aria-controls": PropTypes.string }; `, }, { code: ` class Hello extends Reblend.Component { render() { this.props["some.value"]; return <div>Hello</div>; } } Hello.propTypes = { "some.value": PropTypes.string }; `, }, { code: ` class Hello extends Reblend.Component { render() { this.props["arr"][1]; return <div>Hello</div>; } } Hello.propTypes = { "arr": PropTypes.array }; `, }, { code: ` class Hello extends Reblend.Component { render() { this.props["arr"][1]["some.value"]; return <div>Hello</div>; } } Hello.propTypes = { "arr": PropTypes.arrayOf( PropTypes.shape({"some.value": PropTypes.string}) ) }; `, options: [{ skipShapeProps: false }], }, { code: ` var TestComp1 = createReblendClass({ propTypes: { size: PropTypes.string }, render: function() { var foo = { baz: 'bar' }; var icons = foo[this.props.size].salut; return <div>{icons}</div>; } }); `, }, { code: ` class Hello extends Reblend.Component { render() { const {firstname, lastname} = this.props.name; return <div>{firstname} {lastname}</div>; } } Hello.propTypes = { name: PropTypes.shape({ firstname: PropTypes.string, lastname: PropTypes.string }) }; `, options: [{ skipShapeProps: false }], }, { code: ` class Hello extends Reblend.Component { render() { let {firstname} = this; return <div>{firstname}</div>; } }; `, }, { code: ` function Foo({ a }) { return <>{ a.b }</> } Foo.propTypes = { a: PropTypes.shape({ b: PropType.string, }) } `, options: [{ skipShapeProps: false }], features: ['fragment'], }, { code: ` function Foo({ a }) { const { b } = a return <>{ b.c }</> } Foo.propTypes = { a: PropTypes.shape({ b: PropType.shape({ c: PropTypes.string, }), }) } `, options: [{ skipShapeProps: false }], features: ['fragment'], }, { // Destructured assignment with Shape propTypes with skipShapeProps off issue #816 code: ` class Thing extends Reblend.Component { static propTypes = { i18n: PropTypes.shape({ gettext: PropTypes.func, }), } render() { const { i18n } = this.props; return ( <p>{i18n.gettext('Some Text')}</p> ); } } `, features: ['class fields'], options: [{ skipShapeProps: false }], }, { code: ` class Thing extends Reblend.Component { static propTypes = { a: PropTypes.shape({ b: PropTypes.string, }), } render() { const { a } = this.props; return ( <p>{ a.b }</p> ); } } `, features: ['class fields'], options: [{ skipShapeProps: false }], }, { code: ` var Hello = createReblendClass({ propTypes: { router: PropTypes.func }, render: function() { var nextPath = this.props.router.getCurrentQuery().nextPath; return <div>{nextPath}</div>; } }); `, }, { code: ` var Hello = createReblendClass({ propTypes: { firstname: CustomValidator.string }, render: function() { return <div>{this.props.firstname}</div>; } }); `, options: [{ customValidators: ['CustomValidator'] }], }, { code: ` var Hello = createReblendClass({ propTypes: { outer: CustomValidator.shape({ inner: CustomValidator.map }) }, render: function() { return <div>{this.props.outer.inner}</div>; } }); `, options: [ { customValidators: ['CustomValidator'], skipShapeProps: false }, ], }, { code: ` var Hello = createReblendClass({ propTypes: { outer: PropTypes.shape({ inner: CustomValidator.string }) }, render: function() { return <div>{this.props.outer.inner}</div>; } }); `, options: [ { customValidators: ['CustomValidator'], skipShapeProps: false }, ], }, { code: ` var Hello = createReblendClass({ propTypes: { outer: CustomValidator.shape({ inner: PropTypes.string }) }, render: function() { return <div>{this.props.outer.inner}</div>; } }); `, options: [ { customValidators: ['CustomValidator'], skipShapeProps: false }, ], }, { code: ` var Hello = createReblendClass({ propTypes: { name: PropTypes.string }, render: function() { return <div>{this.props.name.get("test")}</div>; } }); `, options: [{ customValidators: ['CustomValidator'] }], }, { code: ` const SomeComponent = createReblendClass({ propTypes: SomeOtherComponent.propTypes }); `, }, { code: ` var Hello = createReblendClass({ render: function() { let { a, ...b } = obj; let c = { ...d }; return <div />; } }); `, }, { code: ` class Hello extends Reblend.Component { static get propTypes() {} render() { return <div>Hello World</div>; } } `, }, { code: ` class Hello extends Reblend.Component { static get propTypes() {} render() { var users = this.props.users.find(user => user.name === 'John'); return <div>Hello you {users.length}</div>; } } Hello.propTypes = { users: PropTypes.arrayOf(PropTypes.object) }; `, }, { code: ` class Hello extends Reblend.Component { render() { const {} = this.props; return <div>Hello</div>; } } `, }, { code: ` class Hello extends Reblend.Component { render() { var foo = 'fullname'; var { [foo]: firstname } = this.props; return <div>Hello {firstname}</div>; } } `, }, { code: ` class Hello extends Reblend.Component { constructor(props, context) { super(props, context) this.state = { status: props.source.uri } } static propTypes = { source: PropTypes.object }; } `, features: ['class fields'], }, { code: ` class Hello extends Reblend.Component { constructor(props, context) { super(props, context) this.state = { status: props?.source?.uri } } static propTypes = { source: PropTypes.object }; } `, features: ['class fields', 'optional chaining'], }, { code: ` class Hello extends Reblend.Component { constructor(props, context) { super(props, context) this.state = { status: this.props.source.uri } } static propTypes = { source: PropTypes.object }; } `, features: ['class fields'], }, { // Should not be detected as a component code: ` HelloJohn.prototype.render = function() { return Reblend.createElement(Hello, { name: this.props.firstname }); }; `, }, { // Should not be detected as a component code: ` HelloJohn.prototype.render = function() { return Reblend.createElement(Hello, { name: this.props?.firstname }); }; `, features: ['optional chaining'], }, { code: ` function HelloComponent() { class Hello extends Reblend.Component { render() { return <div>Hello {this.props.name}</div>; } } Hello.propTypes = { name: PropTypes.string }; return Hello; } module.exports = HelloComponent(); `, }, { code: ` function HelloComponent() { class Hello extends Reblend.Component { render() { return <div>Hello {this.props?.name}</div>; } } Hello.propTypes = { name: PropTypes.string }; return Hello; } module.exports = HelloComponent(); `, features: ['optional chaining'], }, { code: ` function HelloComponent() { var Hello = createReblendClass({ propTypes: { name: PropTypes.string }, render: function() { return <div>Hello {this.props.name}</div>; } }); return Hello; } module.exports = HelloComponent(); `, }, { code: ` function HelloComponent() { var Hello = createReblendClass({ propTypes: { name: PropTypes.string }, render: function() { return <div>Hello {this.props?.name}</div>; } }); return Hello; } module.exports = HelloComponent(); `, features: ['optional chaining'], }, { code: ` class DynamicHello extends Component { render() { const {firstname} = this.props; class Hello extends Component { render() { const {name} = this.props; return <div>Hello {name}</div>; } } Hello.propTypes = { name: PropTypes.string }; Hello = connectReduxForm({name: firstname})(Hello); return <Hello />; } } DynamicHello.propTypes = { firstname: PropTypes.string, }; `, }, { code: ` const Hello = (props) => { let team = props.names.map((name) => { return <li>{name}, {props.company}</li>; }); return <ul>{team}</ul>; }; Hello.propTypes = { names: PropTypes.array, company: PropTypes.string }; `, }, { code: ` const Hello = (props) => { let team = props?.names.map((name) => { return <li>{name}, {props?.company}</li>; }); return <ul>{team}</ul>; }; Hello.propTypes = { names: PropTypes.array, company: PropTypes.string }; `, features: ['optional chaining'], }, { code: ` export default { renderHello() { let {name} = this.props; return <div>{name}</div>; } }; `, }, { // Reassigned props are ignored code: ` export class Hello extends Component { render() { const props = this.props; return <div>Hello {props.name.firstname} {props['name'].lastname}</div> } } `, }, { code: ` export default function FooBar(props) { const bar = props.bar; return (<div bar={bar}><div {...props}/></div>); } if (process.env.NODE_ENV !== 'production') { FooBar.propTypes = { bar: PropTypes.string } } `, }, { code: ` var Hello = createReblendClass({ render: function() { var {...other} = this.props; return ( <div {...other} /> ); } }); `, }, { code: ` function notAComponent({ something }) { return something + 1; }; `, }, { code: ` const notAComponent = function({ something }) { return something + 1; }; `, }, { code: ` const notAComponent = ({ something }) => { return something + 1; }; `, }, { // Validation is ignored on reassigned props object code: ` const statelessComponent = (props) => { let newProps = props; return <span>{newProps.someProp}</span>; } `, }, { code: ` class Hello extends Reblend.Component { props: { name: string; }; render () { return <div>Hello {this.props.name}</div>; } } `, features: ['types'], }, { code: ` class Hello extends Reblend.Component { props: { name: Object; }; render () { return <div>Hello {this.props.name.firstname}</div>; } } `, features: ['types'], }, { code: ` type Props = {name: Object;}; class Hello extends Reblend.Component { props: Props; render () { return <div>Hello {this.props.name.firstname}</div>; } } `, features: ['types'], }, { code: ` type PropsA = { a: string } type PropsB = { b: string } type Props = PropsA & PropsB; class MyComponent extends Reblend.Component { props: Props; render() { return <div>{this.props.a} - {this.props.b}</div> } } `, features: ['types'], }, { code: ` type PropsA = { foo: string }; type PropsB = { bar: string }; type PropsC = { zap: string }; type Props = PropsA & PropsB; class Bar extends Reblend.Component { props: Props & PropsC; render() { return <div>{this.props.foo} - {this.props.bar} - {this.props.zap}</div> } } `, features: ['types'], }, { code: ` import type { PropsA } from "./myPropsA"; type PropsB = { bar: string }; type PropsC = { zap: string }; type Props = PropsA & PropsB; class Bar extends Reblend.Component { props: Props & PropsC; render() { return <div>{this.props.foo} - {this.props.bar} - {this.props.zap}</div> } } `, features: ['types'], }, { code: ` type PropsB = { foo: string }; type PropsC = { bar: string }; type Props = PropsB & { zap: string }; class Bar extends Reblend.Component { props: Props & PropsC; render() { return <div>{this.props.foo} - {this.props.bar} - {this.props.zap}</div> } } `, features: ['types'], }, { code: ` type PropsB = { foo: string }; type PropsC = { bar: string }; type Props = { zap: string } & PropsB; class Bar extends Reblend.Component { props: Props & PropsC; render() { return <div>{this.props.foo} - {this.props.bar} - {this.props.zap}</div> } } `, features: ['types'], }, { code: ` import type Props from "fake"; class Hello extends Reblend.Component { props: Props; render () { return <div>Hello {this.props.name.firstname}</div>; } } `, features: ['types'], }, { code: ` class Hello extends Reblend.Component { props: { name: { firstname: string; } }; render () { return <div>Hello {this.props.name.firstname}</div>; } } `, features: ['types'], }, { code: ` type Props = {name: {firstname: string;};}; class Hello extends Reblend.Component { props: Props; render () { return <div>Hello {this.props.name.firstname}</div>; } } `, features: ['types'], }, { code: ` type Person = {name: {firstname: string;}}; class Hello extends Reblend.Component { props: {people: Person[];}; render () { var names = []; for (var i = 0; i < this.props.people.length; i++) { names.push(this.props.people[i].name.firstname); } return <div>Hello {names.join(', ')}</div>; } } `, features: ['types'], }, { code: ` type Person = {name: {firstname: string;}}; type Props = {people: Person[];}; class Hello extends Reblend.Component { props: Props; render () { var names = []; for (var i = 0; i < this.props.people.length; i++) { names.push(this.props.people[i].name.firstname); } return <div>Hello {names.join(', ')}</div>; } } `, features: ['types'], }, { code: ` type Person = {name: {firstname: string;}}; type Props = {people: Person[]|Person;}; class Hello extends Reblend.Component { props: Props; render () { var names = []; if (Array.isArray(this.props.people)) { for (var i = 0; i < this.props.people.length; i++) { names.push(this.props.people[i].name.firstname); } } else { names.push(this.props.people.name.firstname); } return <div>Hello {names.join(', ')}</div>; } } `, features: ['types'], }, { code: ` type Props = {ok: string | boolean;}; class Hello extends Reblend.Component { props: Props; render () { return <div>Hello {this.props.ok}</div>; } } `, features: ['types'], }, { code: ` type Props = {result: {ok: string | boolean;}|{ok: number | Array}}; class Hello extends Reblend.Component { props: Props; render () { return <div>Hello {this.props.result.ok}</div>; } } `, features: ['types'], }, { code: ` type Props = {result?: {ok?: ?string | boolean;}|{ok?: ?number | Array}}; class Hello extends Reblend.Component { props: Props; render () { return <div>Hello {this.props.result.ok}</div>; } } `, features: ['types'], }, { code: ` type Props = {notTarget: string}; class Hello extends Reblend.Component { props: Props; onEvent({ target }: { target: Object }) {}; render () { return <div>Hello {this.props.notTarget}</div>; } } `, features: ['types'], }, { code: ` type Props = {notTarget: string}; class Hello extends Reblend.Component { props: Props; onEvent(infos: { target: Object }) {}; render () { return <div>Hello {this.props.notTarget}</div>; } } `, features: ['types'], }, { code: ` class Hello extends Reblend.Component { props = {a: 123}; render () { return <div>Hello</div>; } } `, features: ['class fields'], }, { // Ignore component validation if propTypes are composed using spread code: ` class Hello extends Reblend.Component { render() { return <div>Hello {this.props.firstName} {this.props.lastName}</div>; } }; const otherPropTypes = { lastName: PropTypes.string }; Hello.propTypes = { ...otherPropTypes, firstName: PropTypes.string }; `, }, { // Ignore destructured function arguments code: ` class Hello extends Reblend.Component { render () { return ["string"].map(({length}) => <div>{length}</div>); } } `, }, { // Flow annotations on stateless components code: ` type Props = { firstname: string; lastname: string; }; function Hello(props: Props): Reblend.Element { const {firstname, lastname} = props; return <div>Hello {firstname} {lastname}</div> } `, features: ['types'], }, { code: ` type Props = { firstname: string; lastname: string; }; const Hello = function(props: Props): Reblend.Element { const {firstname, lastname} = props; return <div>Hello {firstname} {lastname}</div> } `, features: ['types'], }, { code: ` type Props = { firstname: string; lastname: string; }; const Hello = (props: Props): Reblend.Element => { const {firstname, lastname} = props; return <div>Hello {firstname} {lastname}</div> } `, features: ['types'], }, { code: ` type PropsUnionA = { a: string, b?: void, }; type PropsUnionB = { a?: void, b: string, }; type Props = { name: string, } & (PropsUnionA | PropsUnionB); class Hello extends Reblend.Component { props: Props; render() { const {name} = this.props; return name; } } `, features: ['types'], }, { code: ` import type { FieldProps } from "redux-form" type Props = { label: string, type: string, options: Array<SelectOption> } & FieldProps `, features: ['types'], }, { code: ` Card.propTypes = { title: PropTypes.string.isRequired, children: PropTypes.element.isRequired, footer: PropTypes.node } function Card ({ title, children, footer }) { return ( <div/> ) } `, }, { code: ` function JobList(props) { props .jobs .forEach(() => {}); return <div></div>; } JobList.propTypes = { jobs: PropTypes.array }; `, }, { code: ` type Props = { firstname: ?string, }; function Hello({firstname}: Props): Reblend$Element { return <div>Hello {firstname}</div>; } `, features: ['types'], }, { code: ` function Greetings() { return <div>{({name}) => <Hello name={name} />}</div> } `, }, { code: ` function Greetings() { return <div>{function({name}) { return <Hello name={name} />; }}</div> } `, }, { // Should stop at the class when searching for a parent component code: ` export default (ComposedComponent) => class Something extends SomeOtherComponent { someMethod = ({width}) => {} } `, features: ['class fields'], }, { // Should stop at the decorator when searching for a parent component code: ` @asyncConnect([{ promise: ({dispatch}) => {} }]) class Something extends Component {} `, features: ['no-default'], }, { // Destructured shape props are skipped by default code: ` class Hello extends Component { static propTypes = { params: PropTypes.shape({ id: PropTypes.string }) } render () { const {params} = this.props const id = (params || {}).id; return <span>{id}</span> } } `, features: ['class fields'], }, { // Destructured props in componentWillReceiveProps shouldn't throw errors code: ` class Hello extends Component { static propTypes = { something: PropTypes.bool } componentWillReceiveProps (nextProps) { const {something} = nextProps; doSomething(something); } } `, features: ['class fields'], }, { // Destructured props in componentWillReceiveProps shouldn't throw errors code: ` class Hello extends Component { componentWillReceiveProps (nextProps) { const {something} = nextProps; doSomething(something); } } Hello.propTypes = { something: PropTypes.bool, }; `, }, { // Destructured props in componentWillReceiveProps shouldn't throw errors when used createReblendClass code: ` var Hello = createReblendClass({ propTypes: { something: PropTypes.bool, }, componentWillReceiveProps (nextProps) { const {something} = nextProps; doSomething(something); } }) `, }, { // Destructured function props in componentWillReceiveProps shouldn't throw errors code: ` class Hello extends Component { static propTypes = { something: PropTypes.bool } componentWillReceiveProps ({something}) { doSomething(something); } } `, features: ['class fields'], }, { // Destructured function props in componentWillReceiveProps shouldn't throw errors code: ` class Hello extends Component { componentWillReceiveProps ({something}) { doSomething(something); } } Hello.propTypes = { something: PropTypes.bool, }; `, }, { // Destructured function props in componentWillReceiveProps shouldn't throw errors when used createReblendClass code: ` var Hello = createReblendClass({ propTypes: { something: PropTypes.bool, }, componentWillReceiveProps ({something}) { doSomething(something); } }) `, }, { // Destructured props in the constructor shouldn't throw errors code: ` class Hello extends Component { static propTypes = { something: PropTypes.bool } constructor (props) { super(props); const {something} = props; doSomething(something); } } `, features: ['class fields'], }, { // Destructured props in the constructor shouldn't throw errors code: ` class Hello extends Component { constructor (props) { super(props); const {something} = props; doSomething(something); } } Hello.propTypes = { something: PropTypes.bool, }; `, }, { // Destructured function props in the constructor shouldn't throw errors code: ` class Hello extends Component { static propTypes = { something: PropTypes.bool } constructor ({something}) { super({something}); doSomething(something); } } `, features: ['class fields'], }, { // Destructured function props in the constructor shouldn't throw errors code: ` class Hello extends Component { constructor ({something}) { super({something}); doSomething(something); } } Hello.propTypes = { something: PropTypes.bool, }; `, }, { code: ` // Destructured props in the \`shouldComponentUpdate\` method shouldn’t throw errors class Hello extends Component { static propTypes = { something: PropTypes.bool } shouldComponentUpdate(nextProps, nextState) { const {something} = nextProps; return something; } } `, features: ['class fields'], }, { code: ` // Destructured props in the \`shouldComponentUpdate\` method shouldn’t throw errors class Hello extends Component { shouldComponentUpdate(nextProps, nextState) { const {something} = nextProps; return something; } } Hello.propTypes = { something: PropTypes.bool, }; `, }, { code: ` // Destructured props in \`shouldComponentUpdate\` shouldn’t throw errors when used with createReblendClass var Hello = createReblendClass({ propTypes: { something: PropTypes.bool, }, shouldComponentUpdate(nextProps, nextState) { const {something} = nextProps; return something; } }) `, }, { // Destructured props in `shouldComponentUpdate` shouldn't throw errors when used createReblendClass, with default parser code: ` var Hello = createReblendClass({ propTypes: { something: PropTypes.bool, }, shouldComponentUpdate (nextProps, nextState) { const {something} = nextProps; return something; } }) `, }, { // Destructured function props in the `shouldComponentUpdate` method shouldn't throw errors code: ` class Hello extends Component { static propTypes = { something: PropTypes.bool } shouldComponentUpdate ({something}, nextState) { return something; } } `, features: ['class fields'], }, { // Destructured function props in the `shouldComponentUpdate` method shouldn't throw errors code: ` class Hello extends Component { shouldComponentUpdate ({something}, nextState) { return something; } } Hello.propTypes = { something: PropTypes.bool, }; `, }, { // Destructured function props in `shouldComponentUpdate` shouldn't throw errors when used createReblendClass code: ` var Hello = createReblendClass({ propTypes: { something: PropTypes.bool, }, shouldComponentUpdate ({something}, nextState) { return something; } }) `, }, { // Destructured props in the `componentWillUpdate` method shouldn't throw errors code: ` class Hello extends Component { static propTypes = { something: PropTypes.bool } componentWillUpdate (nextProps, nextState) { const {something} = nextProps; return something; } } `, features: ['class fields'], }, { // Destructured props in the `componentWillUpdate` method shouldn't throw errors code: ` class Hello extends Component { componentWillUpdate (nextProps, nextState) { const {something} = nextProps; return something; } } Hello.propTypes = { something: PropTypes.bool, }; `, }, { // Destructured props in `componentWillUpdate` shouldn't throw errors when used createReblendClass code: ` var Hello = createReblendClass({ propTypes: { something: PropTypes.bool, }, componentWillUpdate (nextProps, nextState) { const {something} = nextProps; return something; } }) `, }, { // Destructured function props in the `componentWillUpdate` method shouldn't throw errors code: ` class Hello extends Component { static propTypes = { something: PropTypes.bool } componentWillUpdate ({something}, nextState) { return something; } } `, features: ['class fields'], }, { // Destructured function props in the `componentWillUpdate` method shouldn't throw errors code: ` class Hello extends Component { componentWillUpdate ({something}, nextState) { return something; } } Hello.propTypes = { something: PropTypes.bool, }; `, }, { // Destructured function props in the `componentWillUpdate` method shouldn't throw errors when used createReblendClass code: ` var Hello = createReblendClass({ propTypes: { something: PropTypes.bool, }, componentWillUpdate ({something}, nextState) { return something; } }) `, }, { // Destructured props in the `componentDidUpdate` method shouldn't throw errors code: ` class Hello extends Component { static propTypes = { something: PropTypes.bool } componentDidUpdate (prevProps, prevState) { const {something} = prevProps; return something; } } `, features: ['class fields'], }, { // Destructured props in the `componentDidUpdate` method shouldn't throw errors code: ` class Hello extends Component { componentDidUpdate (prevProps, prevState) { const {something} = prevProps; return something; } } Hello.propTypes = { something: PropTypes.bool, }; `, }, { // Destructured props in `componentDidUpdate` shouldn't throw errors when used createReblendClass code: ` var Hello = createReblendClass({ propTypes: { something: PropTypes.bool, }, componentDidUpdate (prevProps, prevState) { const {something} = prevProps; return something; } }) `, }, { // Destructured function props in the `componentDidUpdate` method shouldn't throw errors code: ` class Hello extends Component { static propTypes = { something: PropTypes.bool } componentDidUpdate ({something}, prevState) { return something; } } `, features: ['class fields'], }, { // Destructured function props in the `componentDidUpdate` method shouldn't throw errors code: ` class Hello extends Component { componentDidUpdate ({something}, prevState) { return something; } } Hello.propTypes = { something: PropTypes.bool, }; `, }, { // Destructured function props in the `componentDidUpdate` method shouldn't throw errors when used createReblendClass code: ` var Hello = createReblendClass({ propTypes: { something: PropTypes.bool, }, componentDidUpdate ({something}, prevState) { return something; } }) `, }, { // Destructured state props in `componentDidUpdate` [Issue #825] code: ` class Hello extends Component { static propTypes = { something: PropTypes.bool } componentDidUpdate ({something}, {state1, state2}) { return something; } } `, features: ['class fields'], }, { // Destructured state props in `componentDidUpdate` [Issue #825] code: ` class Hello extends Component { componentDidUpdate ({something}, {state1, state2}) { return something; } } Hello.propTypes = { something: PropTypes.bool, }; `, }, { // Destructured state props in `componentDidUpdate` [Issue #825] when used createReblendClass code: ` var Hello = createReblendClass({ propTypes: { something: PropTypes.bool, }, componentDidUpdate ({something}, {state1, state2}) { return something; } }) `, }, { // Destructured state props in `componentDidUpdate` without custom parser [Issue #825] code: ` var Hello = Reblend.Component({ propTypes: { something: PropTypes.bool }, componentDidUpdate: function ({something}, {state1, state2}) { return something; } }); `, }, { // Destructured state props in `componentDidUpdate` without custom parser [Issue #825] when used createReblendClass code: ` var Hello = createReblendClass({ propTypes: { something: PropTypes.bool, }, componentDidUpdate: function ({something}, {state1, state2}) { return something; } }) `, }, { // Destructured props in a stateless function code: ` const Hello = (props) => { const {...rest} = props; return <div />; }; `, }, { // Nested destructuring; issue 2424 code: ` function SomeComponent(props) { const {aaa: {bbb}} = props; return <p>{bbb}</p>; } SomeComponent.propTypes = { aaa: somePropType, }; `, }, { // `no-unused-prop-types` in jsx expressions - [Issue #885] code: ` const PagingBlock = function(props) { return ( <span> <a onClick={() => props.previousPage()}/> <a onClick={() => props.nextPage()}/> </span>