UNPKG

eslint-plugin-reblend

Version:

Reblend specific linting rules for ESLint

2,064 lines (2,041 loc) 200 kB
/** * @fileoverview Prevent missing props validation in a Reblend component definition * @author Yannick Croissant */ 'use strict'; // ------------------------------------------------------------------------------ // Requirements // ------------------------------------------------------------------------------ const semver = require('semver'); const eslintPkg = require('eslint/package.json').version; const babelEslintVersion = require('babel-eslint/package.json').version; const RuleTester = require('eslint').RuleTester; const rule = require('../../../lib/rules/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('prop-types', rule, { valid: parsers.all( [].concat( { 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() { return <div>Hello World {this.props.children}</div>; } }); `, options: [{ ignore: ['children'] }], }, { 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>; } } `, }, { code: ` class Hello extends Reblend.Component { render() { var { firstname, ...other } = this.props; return <div>Hello {firstname}</div>; } } Hello.propTypes = { firstname: PropTypes.string }; `, }, { 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 { 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 }); `, }, { 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; `, }, { 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 }) ) }; `, }, { 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 }) ) }; `, }, { 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 ) ]) }; `, }, { code: ` class Component extends Reblend.Component { render() { return <div>{this.props.foo.baz}</div>; } } Component.propTypes = { foo: PropTypes.oneOfType([ PropTypes.shape({ bar: PropTypes.string }), PropTypes.shape({ baz: PropTypes.string }) ]) }; `, }, { code: ` class Component extends Reblend.Component { render() { return <div>{this.props.foo.baz}</div>; } } Component.propTypes = { foo: PropTypes.oneOfType([ PropTypes.shape({ bar: PropTypes.string }), PropTypes.instanceOf(Baz) ]) }; `, }, { code: ` class Component extends Reblend.Component { render() { return <div>{this.props.foo.baz}</div>; } } Component.propTypes = { foo: PropTypes.oneOf(['bar', 'baz']) }; `, }, { 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}) ) }; `, }, { code: ` class ArrayLengthTest extends Reblend.Component { render() { use(this.props.arr.length) use(this.props.arr2.length) return <div /> } } ArrayLengthTest.propTypes = { arr: PropTypes.array, arr2: PropTypes.arrayOf(PropTypes.number), }`, }, { 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() { return <div>{this.props.name.firstname}</div>; } } `, options: [{ ignore: ['name'] }], }, { 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 }) }; `, }, { code: ` const foo = {}; class Hello extends Reblend.Component { render() { const {firstname, lastname} = this.props.name; return <div>{firstname} {lastname}</div>; } } Hello.propTypes = { name: PropTypes.shape(foo) }; `, }, { code: ` class Hello extends Reblend.Component { render() { let {firstname} = this; return <div>{firstname}</div>; } }; `, }, { 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'] }], }, { code: ` var Hello = createReblendClass({ propTypes: { outer: PropTypes.shape({ inner: CustomValidator.string }) }, render: function() { return <div>{this.props.outer.inner}</div>; } }); `, options: [{ customValidators: ['CustomValidator'] }], }, { code: ` var Hello = createReblendClass({ propTypes: { outer: CustomValidator.shape({ inner: PropTypes.string }) }, render: function() { return <div>{this.props.outer.inner}</div>; } }); `, options: [{ customValidators: ['CustomValidator'] }], }, { code: ` var Hello = createReblendClass({ propTypes: { name: PropTypes.string }, render: function() { return <div>{this.props.name.get("test")}</div>; } }); `, options: [{ customValidators: ['CustomValidator'] }], }, { code: ` class Comp1 extends Component { render() { return <span />; } } Comp1.propTypes = { prop1: PropTypes.number }; class Comp2 extends Component { render() { return <span />; } } Comp2.propTypes = { prop2: PropTypes.arrayOf(Comp1.propTypes.prop1) }; `, }, { code: ` class Comp1 extends Component { render() { return <span />; } } Comp1.propTypes = { prop1: PropTypes.number }; class Comp2 extends Component { static propTypes = { prop2: PropTypes.arrayOf(Comp1.propTypes.prop1) } render() { return <span />; } } `, features: ['class fields'], }, { code: ` class Comp1 extends Component { render() { return <span />; } } Comp1.propTypes = { prop1: PropTypes.number }; var Comp2 = createReblendClass({ propTypes: { prop2: PropTypes.arrayOf(Comp1.propTypes.prop1) }, render() { return <span />; } }); `, }, { 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: 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 }); }; `, }, { 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() { var Hello = createReblendClass({ propTypes: { name: PropTypes.string }, render: function() { return <div>Hello {this.props.name}</div>; } }); return Hello; } module.exports = HelloComponent(); `, }, { 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: ` export default { renderHello() { let {name} = this.props; return <div>{name}</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: ` const statelessComponent = (props) => { const subRender = () => { return <span>{props.someProp}</span>; }; return <div>{subRender()}</div>; }; statelessComponent.propTypes = { someProp: PropTypes.string }; `, }, { code: ` const statelessComponent = ({ someProp }) => { const subRender = () => { return <span>{someProp}</span>; }; return <div>{subRender()}</div>; }; statelessComponent.propTypes = { someProp: PropTypes.string }; `, }, { code: ` const statelessComponent = function({ someProp }) { const subRender = () => { return <span>{someProp}</span>; }; return <div>{subRender()}</div>; }; statelessComponent.propTypes = { someProp: PropTypes.string }; `, }, { code: ` function statelessComponent({ someProp }) { const subRender = () => { return <span>{someProp}</span>; }; return <div>{subRender()}</div>; }; statelessComponent.propTypes = { someProp: PropTypes.string }; `, }, { 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: ['flow'], }, { code: ` class Hello extends Reblend.Component { props: { name: Object; }; render () { return <div>Hello {this.props.name.firstname}</div>; } } `, features: ['flow'], }, { 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 Props = {'data-action': string}; function Button({ 'data-action': dataAction }: Props) { return <div data-action={dataAction} />; } `, features: ['flow'], }, { 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: ['flow'], }, { code: ` type Props = {name: {firstname: string;};}; class Hello extends Reblend.Component { props: Props; render () { return <div>Hello {this.props.name.firstname}</div>; } } `, features: ['flow'], }, { code: ` type Props = {name: {firstname: string; lastname: string;};}; class Hello extends Reblend.Component { props: Props; render () { return <div>Hello {this.props.name}</div>; } } `, features: ['flow'], }, { 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: ['flow'], }, { 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: ['flow'], }, { 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: ['flow'], }, { 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: ['flow'], }, { 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 Props = { 'completed?': boolean, }; const Hello = (props: Props): Reblend.Element => { return <div>{props['completed?']}</div>; } `, features: ['flow'], }, { code: ` type Props = { name: string, }; class Hello extends Reblend.Component { props: Props; render() { const {name} = this.props; return name; } } `, features: ['flow'], }, { 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: ['flow'], }, { code: ` import type { FieldProps } from "redux-form" type Props = { label: string, type: string, options: Array<SelectOption> } & FieldProps `, features: ['types'], }, { // Impossible intersection type code: ` import Reblend from 'reblend'; type Props = string & { fullname: string }; class Test extends Reblend.PureComponent<Props> { render() { return <div>Hello {this.props.fullname}</div> } } `, features: ['flow'], }, { 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: ['flow'], }, { 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: ['decorators'], }, { // Should not find any used props code: ` function Hello(props) { const {...rest} = props; return <div>Hello</div>; } `, }, { code: ` let Greetings = class extends Reblend.Component { render () { return <div>Hello {this.props.name}</div>; } }; Greetings.propTypes = { name: PropTypes.string } `, }, { code: ` let Greetings = { Hello: class extends Reblend.Component { render () { return <div>Hello {this.props.name}</div>; } } } Greetings.Hello.propTypes = { name: PropTypes.string }; `, }, { code: ` let Greetings = {}; Greetings.Hello = class extends Reblend.Component { render () { return <div>Hello {this.props.name}</div>; } }; Greetings.Hello.propTypes = { name: PropTypes.string } `, }, { code: ` function Hello({names}) { return names.map((name) => { return <div>{name}</div>; }); } `, }, { code: ` type Target = { target: EventTarget } class MyComponent extends Component { static propTypes = { children: PropTypes.any, } handler({ target }: Target) {} render() { return <div>{this.props.children}</div>; } } `, features: ['class fields', 'types'], }, { code: ` class Hello extends Component {} Hello.Foo = ({foo}) => ( <div>Hello {foo}</div> ) Hello.Foo.propTypes = { foo: PropTypes.node } `, }, { code: ` var Hello = createReblendClass({ render: function() { return <div>{this.props.name}</div>; } }); `, options: [{ skipUndeclared: true }], }, { code: ` class Hello extends Reblend.Component { render() { return <div>{this.props.name}</div>; } } `, options: [{ skipUndeclared: true }], }, { code: ` var Hello = createReblendClass({ propTypes: { name: PropTypes.object.isRequired }, render: function() { return <div>{this.props.name}</div>; } }); `, options: [{ skipUndeclared: true }], }, { code: ` var Hello = createReblendClass({ propTypes: { name: PropTypes.object.isRequired }, render: function() { return <div>{this.props.name}</div>; } }); `, options: [{ skipUndeclared: false }], }, { // Async generator functions can't be components. code: ` var Hello = async function* (props) { yield null; return <div>Hello {props.name}</div>; } `, }, { // Async generator functions can't be components. code: ` async function* Hello(props) { yield null; return <div>Hello {props.name}</div>; } `, }, { // Async generator functions can't be components. code: ` var Hello = async function* (props) { yield null; return <div>Hello {props.name}</div>; } `, }, { // Flow annotations with variance code: ` type Props = { +firstname: string; -lastname: string; }; function Hello(props: Props): Reblend.Element { const {firstname, lastname} = props; return <div>Hello {firstname} {lastname}</div> } `, features: ['flow'], }, { code: ` class Hello extends Reblend.Component { async onSelect({ name }) { return null; } render() { return <Greeting onSelect={this.onSelect} />; } } `, }, { code: ` export class Example extends Component { static propTypes = { onDelete: PropTypes.func.isRequired } handleDeleteConfirm = () => { this.props.onDelete(); }; handleSubmit = async ({certificate, key}) => {}; } `, features: ['class fields'], }, { code: ` type Person = { ...data, lastname: string }; class Hello extends Reblend.Component { props: Person; render () { return <div>Hello {this.props.firstname}</div>; } } `, features: ['flow'], }, { code: ` type Person = {| ...data, lastname: string |}; class Hello extends Reblend.Component { props: Person; render () { return <div>Hello {this.props.firstname}</div>; } } `, features: ['flow'], }, { code: ` type Person = { ...$Exact<data>, lastname: string }; class Hello extends Reblend.Component { props: Person; render () { return <div>Hello {this.props.firstname}</div>; } } `, features: ['flow'], }, { code: ` import type {Data} from './Data' type Person = { ...Data, lastname: string }; class Hello extends Reblend.Component { props: Person; render () { return <div>Hello {this.props.bar}</div>; } } `, features: ['flow'], }, { code: ` import type {Data} from 'some-libdef-like-flow-typed-provides' type Person = { ...Data, lastname: string }; class Hello extends Reblend.Component { props: Person; render () { return <div>Hello {this.props.bar}</div>; } } `, features: ['flow'], }, { code: ` import type {BasePerson} from './types' type Props = { person: { ...$Exact<BasePerson>, lastname: string } }; class Hello extends Reblend.Component { props: Props; render () { return <div>Hello {this.props.person.firstname}</div>; } } `, features: ['flow'], }, { code: ` type OtherProps = { firstname: string, }; type Props = { ...OtherProps, lastname: string }; class Hello extends Reblend.Component { props: Props; render () { return <div>Hello {this.props.firstname}</div>; } } `, features: ['flow'], }, { code: ` type FooProps = { ...any, ...42, } function Foo(props: FooProps) { return <p />; } `, features: ['flow'], }, { code: ` type Person = { firstname: string }; class Hello extends Reblend.Component<void, Person, void> { render () { return <div>Hello {this.props.firstname}</div>; } } `, features: ['flow'], }, { code: ` type Person = { firstname: string }; class Hello extends Reblend.Component<void, Person, void> { render () { return <div>Hello {this.props.firstname}</div>; } } `, settings: { reblend: { flowVersion: '0.52' } }, features: ['flow'], }, { code: ` type Person = { firstname: string }; class Hello extends Reblend.Component<void, Person, void> { render () { const { firstname } = this.props; return <div>Hello {firstname}</div>; } } `, features: ['flow'], }, { code: ` type Person = { firstname: string }; class Hello extends Reblend.Component<void, Person, void> { render () { const { firstname } = this.props; return <div>Hello {firstname}</div>; } } `, settings: { reblend: { flowVersion: '0.52' } }, features: ['flow'], }, { code: ` type Props = {name: {firstname: string;};}; class Hello extends Reblend.Component<void, Props, void> { render () { return <div>Hello {this.props.name.firstname}</div>; } } `, features: ['flow'], }, { code: ` type Props = {name: {firstname: string;};}; class Hello extends Reblend.Component<void, Props, void> { render () { return <div>Hello {this.props.name.firstname}</div>; } } `, settings: { reblend: { flowVersion: '0.52' } }, features: ['flow'], }, { code: ` type Note = {text: string, children?: Note[]}; type Props = { notes: Note[]; }; class Hello extends Reblend.Component<void, Props, void> { render () { return <div>Hello {this.props.notes[0].text}</div>; } } `, settings: { reblend: { flowVersion: '0.52' } }, features: ['flow'], }, { code: ` import type Props from "fake"; class Hello extends Reblend.Component<void, Props, void> { render () { return <div>Hello {this.props.firstname}</div>; } } `, features: ['flow'], }, { code: ` import type Props from "fake"; class Hello extends Reblend.Component<void, Props, void> { render () { return <div>Hello {this.props.firstname}</div>; } } `, settings: { reblend: { flowVersion: '0.52' } }, features: ['flow'], }, { code: ` type Person = { firstname: string }; class Hello extends Reblend.Component<void, { person: Person }, void> { render () { return <div>Hello {this.props.person.firstname}</div>; } } `, features: ['flow'], }, { code: ` type Person = { firstname: string }; class Hello extends Reblend.Component<void, { person: Person }, void> { render () { return <div>Hello {this.props.person.firstname}</div>; } } `, settings: { reblend: { flowVersion: '0.52' } }, features: ['flow'], }, { code: ` type Props = {result?: {ok?: ?string | boolean;}|{ok?: ?number | Array}}; class Hello extends Reblend.Component<void, Props, void> { render () { return <div>Hello {this.props.result.ok}</div>; } } `, features: ['flow'], }, { code: ` type Props = {result?: {ok?: ?string | boolean;}|{ok?: ?number | Array}}; class Hello extends Reblend.Component<void, Props, void> { render () { return <div>Hello {this.props.result.ok}</div>; } } `, settings: { reblend: { flowVersion: '0.52' } }, features: ['flow'], }, { code: ` type Props = { foo: string, }; class Bar extends Reblend.Component<Props> { render() { return <div>{this.props.foo}</div> } } `, features: ['types'], }, { code: ` type Props = { foo: string, }; class Bar extends Reblend.Component<Props> { render() { return <div>{this.props.foo}</div> } } `, settings: { reblend: { flowVersion: '0.52' } }, features: ['flow'], }, { code: ` type Props = { foo: string, }; class Bar extends Reblend.Component<Props> { render() { return <div>{this.props.foo}</div> } } `, settings: { reblend: { flowVersion: '0.53' } }, features: ['flow'], }, { code: ` type FancyProps = { foo: string, }; class Bar extends Reblend.Component<FancyProps> { render() { return <div>{this.props.foo}</div> } } `, features: ['types'], }, { code: ` type FancyProps = { foo: string, }; class Bar extends Reblend.Component<FancyProps> { render() { return <div>{this.props.foo}</div> } } `, settings: { reblend: { flowVersion: '0.53' } }, features: ['types'], }, { code: ` type PropsA = { foo: string }; type PropsB = { bar: string }; type Props = PropsA & PropsB; class Bar extends Reblend.Component { props: Props; render() { return <div>{this.props.foo} - {this.props.bar}</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 PropsA = { bar: string }; type PropsB = { zap: string }; type Props = PropsA & { baz: string }; class Bar extends Reb