Skip to content

Commit

Permalink
fix(rw-eslint): Implement more specific checking on Routes (#10404)
Browse files Browse the repository at this point in the history
  • Loading branch information
dac09 authored Apr 3, 2024
1 parent f99e3b3 commit 7864b72
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 15 deletions.
3 changes: 3 additions & 0 deletions .changesets/10404.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
- fix(rw-eslint): Implement more specific checking on Routes (#10404) by @dac09

Fixes: If you use any other elements outside the Route tree it should not throw a linting error or warning
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,30 @@ ruleTester.run('unsupported-route-components', unsupportedRouteComponents, {
)
}`.replace(/ +/g, ' '),
},
{
code: `
const AnotherThing = <Bazinga><p>Hello</p></Bazinga>
const Routes = () => {
return (
<Router>
<PrivateSet whileLoadingAuth={AnotherThing}>
<Route path="/contacts" page={ContactsPage} name="contacts" />
</PrivateSet>
</Router>
)
}`.replace(/ +/g, ' '),
},
],
invalid: [
{
code: 'const Routes = () => <Router><div><Route path="/" page={HomePage} name="home" /></div></Router>',
errors: [{ messageId: 'unexpected' }],
},
// block statement style
{
code: 'const Routes = () => { return (<Router><div><Route path="/" page={HomePage} name="home" /></div></Router>) }',
errors: [{ messageId: 'unexpected' }],
},
{
code: `
const Routes = () => {
Expand Down
69 changes: 54 additions & 15 deletions packages/eslint-plugin/src/unsupported-route-components.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import type { TSESTree } from '@typescript-eslint/utils'
import { ESLintUtils } from '@typescript-eslint/utils'
import type { RuleContext } from '@typescript-eslint/utils/ts-eslint'

const createRule = ESLintUtils.RuleCreator.withoutDocs

Expand All @@ -7,6 +9,29 @@ function isAllowedElement(name: string) {
return allowedElements.includes(name)
}

function checkNodes(
nodesToCheck: TSESTree.JSXElement | TSESTree.JSXChild,
context: RuleContext<'unexpected', []>,
) {
if (nodesToCheck.type === 'JSXElement') {
const name =
nodesToCheck.openingElement.name.type === 'JSXIdentifier'
? nodesToCheck.openingElement.name.name
: null
if (name && !isAllowedElement(name)) {
context.report({
node: nodesToCheck,
messageId: 'unexpected',
data: { name },
})
}

if (nodesToCheck.children) {
nodesToCheck.children.forEach((node) => checkNodes(node, context))
}
}
}

export const unsupportedRouteComponents = createRule({
meta: {
type: 'problem',
Expand All @@ -16,30 +41,44 @@ export const unsupportedRouteComponents = createRule({
},
messages: {
unexpected:
'Unexpected JSX element <{{name}}>. Only <Router>, <Route>, <Set>, <PrivateSet> and <Private> are allowed in the Routes file.',
'Unexpected JSX element <{{name}}>. Only <Router>, <Route>, <Set>, <PrivateSet> and <Private> are allowed in the Routes component.',
},
schema: [], // No additional configuration needed
schema: [],
},
defaultOptions: [],
create(context) {
return {
JSXOpeningElement: function (node) {
let name = ''
VariableDeclaration(node) {
if (isRoutesRenderBlock(node.declarations[0])) {
const routesDeclaration = node.declarations[0].init

if (node.name.type === 'JSXIdentifier') {
name = node.name.name
} else {
return
}
if (routesDeclaration?.type === 'ArrowFunctionExpression') {
if (routesDeclaration.body.type === 'JSXElement') {
// Routes = () => <Router>...</Router>
checkNodes(routesDeclaration.body, context)
} else if (routesDeclaration.body.type === 'BlockStatement') {
// For when Routes = () => { return (<Router>...</Router>) }
if (
routesDeclaration.body.body[0].type === 'ReturnStatement' &&
routesDeclaration.body.body[0].argument?.type === 'JSXElement'
) {
const routesReturnStatement =
routesDeclaration.body.body[0].argument

if (!isAllowedElement(name)) {
context.report({
node,
messageId: 'unexpected',
data: { name },
})
checkNodes(routesReturnStatement, context)
}
}
}
}
},
}
},
})

function isRoutesRenderBlock(node?: TSESTree.VariableDeclarator) {
return (
node?.type === 'VariableDeclarator' &&
node?.id.type === 'Identifier' &&
node?.id.name === 'Routes'
)
}

0 comments on commit 7864b72

Please sign in to comment.