eslint-plugin-reblend
Version:
Reblend specific linting rules for ESLint
2,064 lines (2,041 loc) • 200 kB
JavaScript
/**
* @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