chore(ws): show ESLint errors from local rules on IDE (#439)
Signed-off-by: Guilherme Caponetto <638737+caponetto@users.noreply.github.com>
This commit is contained in:
parent
c0b8f7b300
commit
eb8d3acb93
|
|
@ -1,5 +1,3 @@
|
||||||
const noReactHookNamespace = require('./eslint-local-rules/no-react-hook-namespace');
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
parser: '@typescript-eslint/parser',
|
parser: '@typescript-eslint/parser',
|
||||||
env: {
|
env: {
|
||||||
|
|
@ -13,7 +11,7 @@ module.exports = {
|
||||||
js: true,
|
js: true,
|
||||||
useJSXTextNode: true,
|
useJSXTextNode: true,
|
||||||
project: './tsconfig.json',
|
project: './tsconfig.json',
|
||||||
tsconfigRootDir: '.',
|
tsconfigRootDir: __dirname,
|
||||||
},
|
},
|
||||||
// includes the typescript specific rules found here: https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin#supported-rules
|
// includes the typescript specific rules found here: https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin#supported-rules
|
||||||
plugins: [
|
plugins: [
|
||||||
|
|
@ -24,6 +22,7 @@ module.exports = {
|
||||||
'no-only-tests',
|
'no-only-tests',
|
||||||
'no-relative-import-paths',
|
'no-relative-import-paths',
|
||||||
'prettier',
|
'prettier',
|
||||||
|
'local-rules',
|
||||||
],
|
],
|
||||||
extends: [
|
extends: [
|
||||||
'eslint:recommended',
|
'eslint:recommended',
|
||||||
|
|
@ -200,6 +199,17 @@ module.exports = {
|
||||||
'no-lone-blocks': 'error',
|
'no-lone-blocks': 'error',
|
||||||
'no-lonely-if': 'error',
|
'no-lonely-if': 'error',
|
||||||
'no-promise-executor-return': 'error',
|
'no-promise-executor-return': 'error',
|
||||||
|
'no-restricted-imports': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
paths: [
|
||||||
|
{
|
||||||
|
name: 'react-router',
|
||||||
|
message: 'Use react-router-dom instead.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
'no-restricted-globals': [
|
'no-restricted-globals': [
|
||||||
'error',
|
'error',
|
||||||
{
|
{
|
||||||
|
|
@ -221,7 +231,8 @@ module.exports = {
|
||||||
'symbol-description': 'error',
|
'symbol-description': 'error',
|
||||||
yoda: 'error',
|
yoda: 'error',
|
||||||
'func-names': 'warn',
|
'func-names': 'warn',
|
||||||
'no-react-hook-namespace': 'error',
|
'local-rules/no-react-hook-namespace': 'error',
|
||||||
|
'local-rules/no-raw-react-router-hook': 'error',
|
||||||
},
|
},
|
||||||
overrides: [
|
overrides: [
|
||||||
{
|
{
|
||||||
|
|
@ -270,7 +281,20 @@ module.exports = {
|
||||||
{
|
{
|
||||||
files: ['**/*.{js,jsx,ts,tsx}'],
|
files: ['**/*.{js,jsx,ts,tsx}'],
|
||||||
rules: {
|
rules: {
|
||||||
'no-react-hook-namespace': 'error',
|
'local-rules/no-react-hook-namespace': 'error',
|
||||||
|
'local-rules/no-raw-react-router-hook': 'error',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['.eslintrc.js'],
|
||||||
|
parserOptions: {
|
||||||
|
project: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: ['eslint-local-rules/**/*.js'],
|
||||||
|
rules: {
|
||||||
|
'@typescript-eslint/no-require-imports': 'off',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
module.exports = {
|
||||||
|
'no-react-hook-namespace': require('./no-react-hook-namespace'),
|
||||||
|
'no-raw-react-router-hook': require('./no-raw-react-router-hook'),
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
module.exports = {
|
||||||
|
meta: {
|
||||||
|
type: 'problem',
|
||||||
|
docs: {
|
||||||
|
description: 'Disallow use of raw react-router-dom hooks. Use typed wrappers instead.',
|
||||||
|
},
|
||||||
|
messages: {
|
||||||
|
avoidRawHook:
|
||||||
|
'Use "{{typedHook}}" from `~/app/routerHelper` instead of raw React Router hook "{{rawHook}}".',
|
||||||
|
},
|
||||||
|
schema: [],
|
||||||
|
},
|
||||||
|
|
||||||
|
create(context) {
|
||||||
|
const forbiddenHooks = {
|
||||||
|
useNavigate: 'useTypedNavigate',
|
||||||
|
useParams: 'useTypedParams',
|
||||||
|
useSearchParams: 'useTypedSearchParams',
|
||||||
|
useLocation: 'useTypedLocation',
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
ImportDeclaration(node) {
|
||||||
|
if (node.source.value !== 'react-router-dom') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const specifier of node.specifiers) {
|
||||||
|
if (
|
||||||
|
specifier.type === 'ImportSpecifier' &&
|
||||||
|
Object.prototype.hasOwnProperty.call(forbiddenHooks, specifier.imported.name)
|
||||||
|
) {
|
||||||
|
context.report({
|
||||||
|
node: specifier,
|
||||||
|
messageId: 'avoidRawHook',
|
||||||
|
data: {
|
||||||
|
rawHook: specifier.imported.name,
|
||||||
|
typedHook: forbiddenHooks[specifier.imported.name],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
"@patternfly/react-tokens": "^6.2.0",
|
"@patternfly/react-tokens": "^6.2.0",
|
||||||
"@types/js-yaml": "^4.0.9",
|
"@types/js-yaml": "^4.0.9",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
|
"eslint-plugin-local-rules": "^3.0.2",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
|
|
@ -9966,6 +9967,11 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"node_modules/eslint-plugin-local-rules": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/eslint-plugin-local-rules/-/eslint-plugin-local-rules-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-IWME7GIYHXogTkFsToLdBCQVJ0U4kbSuVyDT+nKoR4UgtnVrrVeNWuAZkdEu1nxkvi9nsPccGehEEF6dgA28IQ=="
|
||||||
|
},
|
||||||
"node_modules/eslint-plugin-no-only-tests": {
|
"node_modules/eslint-plugin-no-only-tests": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-plugin-no-only-tests/-/eslint-plugin-no-only-tests-3.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-plugin-no-only-tests/-/eslint-plugin-no-only-tests-3.3.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,8 @@
|
||||||
"test:unit": "npm run test:jest -- --silent",
|
"test:unit": "npm run test:jest -- --silent",
|
||||||
"test:watch": "jest --watch",
|
"test:watch": "jest --watch",
|
||||||
"test:coverage": "jest --coverage",
|
"test:coverage": "jest --coverage",
|
||||||
"test:fix": "eslint --rulesdir eslint-local-rules --ext .js,.ts,.jsx,.tsx ./src --fix",
|
"test:fix": "eslint --ext .js,.ts,.jsx,.tsx ./src --fix",
|
||||||
"test:lint": "eslint --rulesdir eslint-local-rules --max-warnings 0 --ext .js,.ts,.jsx,.tsx ./src",
|
"test:lint": "eslint --max-warnings 0 --ext .js,.ts,.jsx,.tsx ./src",
|
||||||
"cypress:open": "cypress open --project src/__tests__/cypress",
|
"cypress:open": "cypress open --project src/__tests__/cypress",
|
||||||
"cypress:open:mock": "CY_MOCK=1 CY_WS_PORT=9002 npm run cypress:open -- ",
|
"cypress:open:mock": "CY_MOCK=1 CY_WS_PORT=9002 npm run cypress:open -- ",
|
||||||
"cypress:run": "cypress run -b chrome --project src/__tests__/cypress",
|
"cypress:run": "cypress run -b chrome --project src/__tests__/cypress",
|
||||||
|
|
@ -108,6 +108,7 @@
|
||||||
"@patternfly/react-tokens": "^6.2.0",
|
"@patternfly/react-tokens": "^6.2.0",
|
||||||
"@types/js-yaml": "^4.0.9",
|
"@types/js-yaml": "^4.0.9",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
|
"eslint-plugin-local-rules": "^3.0.2",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { NavLink, useLocation } from 'react-router-dom';
|
import { NavLink } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
Brand,
|
Brand,
|
||||||
Nav,
|
Nav,
|
||||||
|
|
@ -9,11 +9,12 @@ import {
|
||||||
PageSidebar,
|
PageSidebar,
|
||||||
PageSidebarBody,
|
PageSidebarBody,
|
||||||
} from '@patternfly/react-core';
|
} from '@patternfly/react-core';
|
||||||
|
import { useTypedLocation } from '~/app/routerHelper';
|
||||||
import { useNavData, isNavDataGroup, NavDataHref, NavDataGroup } from './AppRoutes';
|
import { useNavData, isNavDataGroup, NavDataHref, NavDataGroup } from './AppRoutes';
|
||||||
import { isMUITheme, LOGO_LIGHT } from './const';
|
import { isMUITheme, LOGO_LIGHT } from './const';
|
||||||
|
|
||||||
const NavHref: React.FC<{ item: NavDataHref }> = ({ item }) => {
|
const NavHref: React.FC<{ item: NavDataHref }> = ({ item }) => {
|
||||||
const location = useLocation();
|
const location = useTypedLocation();
|
||||||
|
|
||||||
// With the redirect in place, we can now use a simple path comparison.
|
// With the redirect in place, we can now use a simple path comparison.
|
||||||
const isActive = location.pathname === item.path;
|
const isActive = location.pathname === item.path;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import { useLocation, matchPath } from 'react-router-dom';
|
import { matchPath } from 'react-router-dom';
|
||||||
import { AppRouteKey, AppRoutePaths } from '~/app/routes';
|
import { AppRouteKey, AppRoutePaths } from '~/app/routes';
|
||||||
|
import { useTypedLocation } from '~/app/routerHelper';
|
||||||
|
|
||||||
export function useCurrentRouteKey(): AppRouteKey | undefined {
|
export function useCurrentRouteKey(): AppRouteKey | undefined {
|
||||||
const location = useLocation();
|
const location = useTypedLocation();
|
||||||
const { pathname } = location;
|
const { pathname } = location;
|
||||||
|
|
||||||
const matchEntries = Object.entries(AppRoutePaths) as [AppRouteKey, string][];
|
const matchEntries = Object.entries(AppRoutePaths) as [AppRouteKey, string][];
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
/* eslint-disable local-rules/no-raw-react-router-hook */
|
||||||
|
|
||||||
import {
|
import {
|
||||||
generatePath,
|
generatePath,
|
||||||
Location,
|
Location,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue