const boxFlexRule = require('./rules/boxFlexRule');const fetchKeyRule = require('./rules/fetchKeyRule');const withRelayBoundaryRule = require('./rules/withRelayBoundaryRule');module.exports = {rules: {'box-flex': boxFlexRule,'fetch-key': fetchKeyRule,'with-relay-boundary': withRelayBoundaryRule,},};
const { forbidElements } = require('./forbidElements');const { noRestrictedSyntax } = require('./noRestrictedSyntax');module.exports = {parser: '@typescript-eslint/parser',env: {browser: true,node: true,jest: true,es6: true,'cypress/globals': true,serviceworker: true,},plugins: ['react','import','cypress','relay','@typescript-eslint','react-hooks','no-only-tests','testing-library','@woovi',],parserOptions: {ecmaVersion: 10,sourceType: 'module',ecmaFeatures: {modules: true,},},extends: ['eslint:recommended','plugin:react/recommended','plugin:import/errors','plugin:relay/ts-strict','plugin:@typescript-eslint/eslint-recommended','plugin:@typescript-eslint/recommended','prettier','plugin:storybook/recommended','plugin:testing-library/react',],rules: {// ...'@woovi/box-flex': 'error','@woovi/fetch-key': 'error','@woovi/with-relay-boundary': 'error',},};
Add plugin
@typescript-eslint/utils
const { ESLintUtils } = require('@typescript-eslint/utils');const createRule = ESLintUtils.RuleCreator((name) =>`https://dev-docs.woovi.com/docs/category/eslint/rules/eslint-${name}-rule`);module.exports.createRule = createRule;
createRule.js
<Flex />
to <BoxFlex />
const { ESLintUtils, AST_NODE_TYPES } = require('@typescript-eslint/utils');const { createRule } = require('../createRule');const findRootNode = (node) => {if (node.parent !== null) {return findRootNode(node.parent);}return node;};module.exports = createRule({create: (context) => {let flexImport;let flexSpecifier;return {ImportDeclaration: (node) => {if (node.source.value !== 'rebass') {return;}const specifier = node.specifiers.find((specifier) => {if (specifier.type !== AST_NODE_TYPES.ImportSpecifier) {return false;}if (specifier.imported.name !== 'Flex') {return false;}return true;});if (!specifier) {return;}flexImport = node;flexSpecifier = specifier;},JSXElement: (node) => {if (node.openingElement.name.name !== 'Flex') {return;}if (node.closingElement && node.closingElement.name.name !== 'Flex') {return;}context.report({node,messageId: 'preferBoxFlex',*fix(fixer) {const attributes = node.openingElement.attributes.map((attribute) => {if (attribute.type === AST_NODE_TYPES.JSXSpreadAttribute) {return `...${attribute.argument.name}`;}const getValue = () => {if (attribute.value.type === AST_NODE_TYPES.Literal) {return attribute.value.raw;}return attribute.value.expression.raw;};return `${attribute.name.name}: ${getValue()}`;});const sxProp = attributes.length? ` sx={{ ${attributes.join(', ')} }}`: '';yield fixer.replaceText(node.openingElement, `<BoxFlex${sxProp}>`);if (node.closingElement) {yield fixer.replaceText(node.closingElement.name, 'BoxFlex');}if (flexImport && flexSpecifier) {yield fixer.remove(flexImport.specifiers.length > 1 ? flexSpecifier : flexImport);}const rootNode = findRootNode(node);yield fixer.insertTextBefore(rootNode,"import { BoxFlex } from '@woovi/ui';\n");},});},};},name: 'box-flex',meta: {type: 'problem',fixable: 'code',docs: {description:'Rebass Flex is deprecated. Prefer to use BoxFlex from @woovi/ui',recommended: 'error',},messages: {preferBoxFlex: 'Prefer <BoxFlex />',},schema: [],},defaultOptions: [],});
boxFlexRule.js
const { ESLintUtils, AST_NODE_TYPES } = require('@typescript-eslint/utils');const { createRule } = require('../createRule');const findRootNode = (node) => {if (node.parent !== null) {return findRootNode(node.parent);}return node;};module.exports = createRule({create: (context) => {let flexImport;let flexSpecifier;return {ImportDeclaration: (node) => {if (node.source.value !== 'rebass') {return;}const specifier = node.specifiers.find((specifier) => {if (specifier.type !== AST_NODE_TYPES.ImportSpecifier) {return false;}if (specifier.imported.name !== 'Flex') {return false;}return true;});if (!specifier) {return;}flexImport = node;flexSpecifier = specifier;},JSXElement: (node) => {if (node.openingElement.name.name !== 'Flex') {return;}if (node.closingElement && node.closingElement.name.name !== 'Flex') {return;}context.report({node,messageId: 'preferBoxFlex',*fix(fixer) {const attributes = node.openingElement.attributes.map((attribute) => {if (attribute.type === AST_NODE_TYPES.JSXSpreadAttribute) {return `...${attribute.argument.name}`;}const getValue = () => {if (attribute.value.type === AST_NODE_TYPES.Literal) {return attribute.value.raw;}return attribute.value.expression.raw;};return `${attribute.name.name}: ${getValue()}`;});const sxProp = attributes.length? ` sx={{ ${attributes.join(', ')} }}`: '';yield fixer.replaceText(node.openingElement, `<BoxFlex${sxProp}>`);if (node.closingElement) {yield fixer.replaceText(node.closingElement.name, 'BoxFlex');}if (flexImport && flexSpecifier) {yield fixer.remove(flexImport.specifiers.length > 1 ? flexSpecifier : flexImport);}const rootNode = findRootNode(node);yield fixer.insertTextBefore(rootNode,"import { BoxFlex } from '@woovi/ui';\n");},});},};},name: 'box-flex',meta: {type: 'problem',fixable: 'code',docs: {description:'Rebass Flex is deprecated. Prefer to use BoxFlex from @woovi/ui',recommended: 'error',},messages: {preferBoxFlex: 'Prefer <BoxFlex />',},schema: [],},defaultOptions: [],});
Auto fixing
const { ESLintUtils } = require('@typescript-eslint/utils');const boxFlexRule = require('../boxFlexRule');const ruleTester = new ESLintUtils.RuleTester({parser: '@typescript-eslint/parser',parserOptions: {ecmaVersion: 2018,sourceType: 'module',ecmaFeatures: {jsx: true,},},});ruleTester.run('box-flex', boxFlexRule, {valid: [`import { BoxFlex } from '@woovi/ui';const Component = () => {return (<BoxFlex><Typography>something</Typography></BoxFlex>);}`,`import { BoxFlex } from '@woovi/ui';const Component = () => {return (<BoxFlex sx={{ marginTop: '16px', marginBottom: '20px', padding: '8px' }}><Typography>something</Typography></BoxFlex>);};`,`import { Text } from 'rebass';import { BoxFlex } from '@woovi/ui';const Component = () => {return (<BoxFlex sx={{ marginTop: '16px', marginBottom: '20px', padding: '8px' }}><Typography>something</Typography></BoxFlex>);};`,],invalid: [{code: `import { Flex } from 'rebass';const Component = () => {return (<Flex><Typography>something</Typography></Flex>);}`,output: `import { BoxFlex } from '@woovi/ui';const Component = () => {return (<BoxFlex><Typography>something</Typography></BoxFlex>);}`,errors: [{ message: 'Prefer <BoxFlex />' }],},{code: `const Component = () => {return (<Flex><Typography>something</Typography></Flex>);}`,output: `import { BoxFlex } from '@woovi/ui';const Component = () => {return (<BoxFlex><Typography>something</Typography></BoxFlex>);}`,errors: [{ message: 'Prefer <BoxFlex />' }],},{code: `import { Text, Flex } from 'rebass';const Component = () => {return (<Flex><Text>something</Text></Flex>);}`,output: `import { BoxFlex } from '@woovi/ui';import { Text, } from 'rebass';const Component = () => {return (<BoxFlex><Text>something</Text></BoxFlex>);}`,errors: [{ message: 'Prefer <BoxFlex />' }],},{code: `import { Flex } from 'rebass';const Component = () => {return (<Flex marginTop='16px' marginBottom='20px' padding='8px'><Typography>something</Typography></Flex>);}`,output: `import { BoxFlex } from '@woovi/ui';const Component = () => {return (<BoxFlex sx={{ marginTop: '16px', marginBottom: '20px', padding: '8px' }}><Typography>something</Typography></BoxFlex>);}`,errors: [{ message: 'Prefer <BoxFlex />' }],},{code: `import { Flex } from 'rebass';const Component = () => {return (<Flex marginTop={'16px'} marginBottom={'20px'} padding={'8px'}><Typography>something</Typography></Flex>);}`,output: `import { BoxFlex } from '@woovi/ui';const Component = () => {return (<BoxFlex sx={{ marginTop: '16px', marginBottom: '20px', padding: '8px' }}><Typography>something</Typography></BoxFlex>);}`,errors: [{ message: 'Prefer <BoxFlex />' }],},{code: `import { Flex } from 'rebass';const Component = () => {return (<Flex marginTop={'16px'} marginBottom={'20px'} padding={'8px'} {...props}><Typography>something</Typography></Flex>);}`,output: `import { BoxFlex } from '@woovi/ui';const Component = () => {return (<BoxFlex sx={{ marginTop: '16px', marginBottom: '20px', padding: '8px', ...props }}><Typography>something</Typography></BoxFlex>);}`,errors: [{ message: 'Prefer <BoxFlex />' }],},],});
boxFlexRule.spec.js