/**
* @fileoverview Prevent multiple component definition per file
* @author Yannick Croissant
*/
'use strict';
// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------
const RuleTester = require('../../helpers/ruleTester');
const rule = require('../../../lib/rules/no-multi-comp');
const parsers = require('../../helpers/parsers');
const parserOptions = {
ecmaVersion: 2018,
sourceType: 'module',
ecmaFeatures: {
jsx: true,
},
};
// ------------------------------------------------------------------------------
// Tests
// ------------------------------------------------------------------------------
const ruleTester = new RuleTester({ parserOptions });
ruleTester.run('no-multi-comp', rule, {
valid: parsers.all([
{
code: `
var Hello = require('./components/Hello');
var HelloJohn = createReactClass({
render: function() {
return ;
}
});
`,
},
{
code: `
class Hello extends React.Component {
render() {
return
Hello {this.props.name}
;
}
}
`,
},
{
code: `
var Heading = createReactClass({
render: function() {
return (
{this.props.buttons.map(function(button, index) {
return
);
}
});
`,
},
{
code: `
function Hello(props) {
return Hello {props.name}
;
}
function HelloAgain(props) {
return Hello again {props.name}
;
}
`,
options: [{ ignoreStateless: true }],
},
{
code: `
function Hello(props) {
return Hello {props.name}
;
}
class HelloJohn extends React.Component {
render() {
return ;
}
}
`,
options: [{ ignoreStateless: true }],
},
{
// multiple non-components
code: `
import React, { createElement } from "react"
const helperFoo = () => {
return true;
};
function helperBar() {
return false;
};
function RealComponent() {
return createElement("img");
};
`,
parserOptions: Object.assign({ sourceType: 'module' }, parserOptions),
},
{
code: `
const Hello = React.memo(function(props) {
return Hello {props.name}
;
});
class HelloJohn extends React.Component {
render() {
return ;
}
}
`,
options: [{ ignoreStateless: true }],
},
{
code: `
class StoreListItem extends React.PureComponent {
// A bunch of stuff here
}
export default React.forwardRef((props, ref) => );
`,
options: [{ ignoreStateless: false }],
},
{
code: `
class StoreListItem extends React.PureComponent {
// A bunch of stuff here
}
export default React.forwardRef((props, ref) => {
return
});
`,
options: [{ ignoreStateless: false }],
},
{
code: `
const HelloComponent = (props) => {
return ;
}
export default React.forwardRef((props, ref) => );
`,
options: [{ ignoreStateless: false }],
},
{
code: `
class StoreListItem extends React.PureComponent {
// A bunch of stuff here
}
export default React.forwardRef(
function myFunction(props, ref) {
return ;
}
);
`,
options: [{ ignoreStateless: true }],
},
{
code: `
const HelloComponent = (props) => {
return ;
}
class StoreListItem extends React.PureComponent {
// A bunch of stuff here
}
export default React.forwardRef(
function myFunction(props, ref) {
return ;
}
);
`,
options: [{ ignoreStateless: true }],
},
{
code: `
const HelloComponent = (props) => {
return ;
}
export default React.memo((props, ref) => );
`,
options: [{ ignoreStateless: true }],
},
{
code: `
import React from 'react';
function memo() {
var outOfScope = "hello"
return null;
}
class ComponentY extends React.Component {
memoCities = memo((cities) => cities.map((v) => ({ label: v })));
render() {
return (
);
}
}
`,
features: ['class fields'],
},
{
code: `
const MenuList = forwardRef(({onClose, ...props}, ref) => {
const {t} = useTranslation();
const handleLogout = useLogoutHandler();
const onLogout = useCallback(() => {
onClose();
handleLogout();
}, [onClose, handleLogout]);
return (
{t('global-logout')}
);
});
MenuList.displayName = 'MenuList';
MenuList.propTypes = {
onClose: PropTypes.func,
};
MenuList.defaultProps = {
onClose: () => null,
};
export default MenuList;
`,
},
{
code: `
const MenuList = forwardRef(({ onClose, ...props }, ref) => {
const onLogout = useCallback(() => {
onClose()
}, [onClose])
return (
Logout
)
})
MenuList.displayName = 'MenuList'
MenuList.propTypes = {
onClose: PropTypes.func
}
MenuList.defaultProps = {
onClose: () => null
}
export default MenuList
`,
},
]),
invalid: parsers.all([
{
code: `
var Hello = createReactClass({
render: function() {
return Hello {this.props.name}
;
}
});
var HelloJohn = createReactClass({
render: function() {
return ;
}
});
`.split('\n').join('\r'),
errors: [
{
messageId: 'onlyOneComponent',
line: 7,
},
],
},
{
code: `
class Hello extends React.Component {
render() {
return Hello {this.props.name}
;
}
}
class HelloJohn extends React.Component {
render() {
return ;
}
}
class HelloJohnny extends React.Component {
render() {
return ;
}
}
`.split('\n').join('\r'),
errors: [
{
messageId: 'onlyOneComponent',
line: 7,
},
{
messageId: 'onlyOneComponent',
line: 12,
},
],
},
{
code: `
function Hello(props) {
return Hello {props.name}
;
}
function HelloAgain(props) {
return Hello again {props.name}
;
}
`,
errors: [
{
messageId: 'onlyOneComponent',
line: 5,
},
],
},
{
code: `
function Hello(props) {
return Hello {props.name}
;
}
class HelloJohn extends React.Component {
render() {
return ;
}
}
`,
errors: [
{
messageId: 'onlyOneComponent',
line: 5,
},
],
},
{
code: `
export default {
RenderHello(props) {
let {name} = props;
return {name}
;
},
RenderHello2(props) {
let {name} = props;
return {name}
;
}
};
`,
errors: [
{
messageId: 'onlyOneComponent',
line: 7,
},
],
},
{
code: `
exports.Foo = function Foo() {
return <>>
}
exports.createSomeComponent = function createSomeComponent(opts) {
return function Foo() {
return <>{opts.a}>
}
}
`,
features: ['fragment'],
errors: [
{
messageId: 'onlyOneComponent',
line: 7,
},
],
},
{
code: `
class StoreListItem extends React.PureComponent {
// A bunch of stuff here
}
export default React.forwardRef((props, ref) =>
);
`,
options: [{ ignoreStateless: false }],
errors: [
{
messageId: 'onlyOneComponent',
line: 5,
},
],
},
{
code: `
const HelloComponent = (props) => {
return ;
}
const HelloComponent2 = React.forwardRef((props, ref) => );
`,
options: [{ ignoreStateless: false }],
errors: [
{
messageId: 'onlyOneComponent',
line: 5,
},
],
},
{
code: `
const HelloComponent = (0, (props) => {
return ;
});
const HelloComponent2 = React.forwardRef((props, ref) => <>>);
`,
options: [{ ignoreStateless: false }],
features: ['fragment'],
errors: [
{
messageId: 'onlyOneComponent',
line: 5,
},
],
},
{
code: `
const forwardRef = React.forwardRef;
const HelloComponent = (0, (props) => {
return ;
});
const HelloComponent2 = forwardRef((props, ref) => );
`,
options: [{ ignoreStateless: false }],
errors: [
{
messageId: 'onlyOneComponent',
line: 6,
},
],
},
{
code: `
const memo = React.memo;
const HelloComponent = (props) => {
return ;
};
const HelloComponent2 = memo((props) => );
`,
options: [{ ignoreStateless: false }],
errors: [
{
messageId: 'onlyOneComponent',
line: 6,
},
],
},
{
code: `
const {forwardRef} = React;
const HelloComponent = (0, (props) => {
return ;
});
const HelloComponent2 = forwardRef((props, ref) => );
`,
options: [{ ignoreStateless: false }],
errors: [
{
messageId: 'onlyOneComponent',
line: 6,
},
],
},
{
code: `
const {memo} = React;
const HelloComponent = (0, (props) => {
return ;
});
const HelloComponent2 = memo((props) => );
`,
options: [{ ignoreStateless: false }],
errors: [
{
messageId: 'onlyOneComponent',
line: 6,
},
],
},
{
code: `
import React, { memo } from 'react';
const HelloComponent = (0, (props) => {
return ;
});
const HelloComponent2 = memo((props) => );
`,
options: [{ ignoreStateless: false }],
errors: [
{
messageId: 'onlyOneComponent',
line: 6,
},
],
},
{
code: `
import {forwardRef} from 'react';
const HelloComponent = (0, (props) => {
return ;
});
const HelloComponent2 = forwardRef((props, ref) => );
`,
options: [{ ignoreStateless: false }],
errors: [
{
messageId: 'onlyOneComponent',
line: 6,
},
],
},
{
code: `
const { memo } = require('react');
const HelloComponent = (0, (props) => {
return ;
});
const HelloComponent2 = memo((props) => );
`,
options: [{ ignoreStateless: false }],
errors: [
{
messageId: 'onlyOneComponent',
line: 6,
},
],
},
{
code: `
const {forwardRef} = require('react');
const HelloComponent = (0, (props) => {
return ;
});
const HelloComponent2 = forwardRef((props, ref) => );
`,
options: [{ ignoreStateless: false }],
errors: [
{
messageId: 'onlyOneComponent',
line: 6,
},
],
},
{
code: `
const forwardRef = require('react').forwardRef;
const HelloComponent = (0, (props) => {
return ;
});
const HelloComponent2 = forwardRef((props, ref) => );
`,
options: [{ ignoreStateless: false }],
errors: [
{
messageId: 'onlyOneComponent',
line: 6,
},
],
},
{
code: `
const memo = require('react').memo;
const HelloComponent = (0, (props) => {
return ;
});
const HelloComponent2 = memo((props) => );
`,
options: [{ ignoreStateless: false }],
errors: [
{
messageId: 'onlyOneComponent',
line: 6,
},
],
},
{
code: `
import Foo, { memo, forwardRef } from 'foo';
const Text = forwardRef(({ text }, ref) => {
return {text}
;
})
const Label = memo(() => );
`,
settings: {
react: {
pragma: 'Foo',
},
},
errors: [{ messageId: 'onlyOneComponent' }],
},
]),
});