Skip to content

Commit

Permalink
feat(eslint): add eslint in addition to tslint (#75)
Browse files Browse the repository at this point in the history
* feat(eslint): add eslint in addition to tslint

integrate eslint with pf recommended rules
update deps
adjust test to be more useful
dark theme for sidenav

* [cleanup] add support for linting js files

be more specific about which rules are disabled
update readme
  • Loading branch information
seanforyou23 committed Nov 13, 2019
1 parent 5404142 commit b245fa4
Show file tree
Hide file tree
Showing 15 changed files with 1,507 additions and 657 deletions.
30 changes: 30 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
// tells eslint to use the TypeScript parser
"parser": "@typescript-eslint/parser",
// tell the TypeScript parser that we want to use JSX syntax
"parserOptions": {
"tsx": true,
"jsx": true,
"js": true,
"useJSXTextNode": true,
"project": "./tsconfig.json",
"tsconfigRootDir": "."
},
// we want to use the recommended rules provided from the typescript plugin
"extends": [
"plugin:@typescript-eslint/recommended",
"plugin:patternfly-react/recommended"
],
// includes the typescript specific rules found here: https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin#supported-rules
"plugins": ["@typescript-eslint", "react-hooks", "eslint-plugin-react-hooks"],
"rules": {
"@typescript-eslint/explicit-function-return-type": "off",
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
"@typescript-eslint/interface-name-prefix": "off",
"prettier/prettier": "off",
"import/no-unresolved": "off",
"import/extensions": "off",
"react/prop-types": "off"
}
}
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,7 @@ body {
* To keep our bundle size in check, we use [webpack-bundle-analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer)
* To keep our code formatting in check, we use [prettier](https://github.com/prettier/prettier)
* To keep our code logic and test coverage in check, we use [jest](https://github.com/facebook/jest)
* To ensure code styles remain consistent, we use [eslint](https://eslint.org/)
### Linter Supper
Currently, eslint and tslint are both supported. This is temporary. We will be removing support for tslint at some point in the near future, given tslint's roadmap to deprecate itself in favor of a more unified developer experience across TypeScript and JavaScript languages.
57 changes: 32 additions & 25 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
"build": "webpack --config webpack.prod.js",
"start": "webpack-dev-server --hot --color --progress --info=true --config webpack.dev.js",
"test": "jest",
"lint": "tslint -c ./tslint.json --project .",
"tslint": "tslint -c ./tslint.json --project .",
"eslint": "eslint --ext .tsx,.js ./src/",
"lint": "yarn tslint && yarn eslint",
"format": "prettier --check --write ./src/**/*.{tsx,ts,js}",
"build:bundle-profile": "webpack --config webpack.prod.js --profile --json > stats.json",
"bundle-profile:analyze": "yarn build:bundle-profile && webpack-bundle-analyzer ./stats.json",
Expand All @@ -20,25 +22,30 @@
"build:storybook": "build-storybook"
},
"devDependencies": {
"@storybook/addon-actions": "^5.2.1",
"@storybook/addon-info": "^5.2.1",
"@storybook/addon-links": "^5.2.1",
"@storybook/addons": "^5.2.1",
"@storybook/react": "^5.2.1",
"@storybook/addon-actions": "^5.2.5",
"@storybook/addon-info": "^5.2.5",
"@storybook/addon-links": "^5.2.5",
"@storybook/addons": "^5.2.5",
"@storybook/react": "^5.2.5",
"@types/enzyme": "^3.10.3",
"@types/enzyme-adapter-react-16": "^1.0.5",
"@types/jest": "^24.0.17",
"@types/node": "^12.7.1",
"@types/react": "^16.8.25",
"@types/react-dom": "^16.8.5",
"@types/react-router-dom": "^4.3.4",
"@types/jest": "^24.0.21",
"@types/node": "^12.12.5",
"@types/react": "^16.9.11",
"@types/react-dom": "^16.9.3",
"@types/react-router-dom": "^5.1.2",
"@types/storybook__react": "^4.0.2",
"@types/victory": "^33.0.0",
"@types/webpack": "^4.32.1",
"@types/webpack": "^4.39.8",
"@typescript-eslint/eslint-plugin": "^2.6.0",
"@typescript-eslint/parser": "^2.6.0",
"css-loader": "^3.2.0",
"enzyme": "^3.7.0",
"enzyme-adapter-react-16": "^1.7.0",
"enzyme-to-json": "^3.4.0",
"enzyme-adapter-react-16": "^1.15.1",
"enzyme-to-json": "^3.4.3",
"eslint": "^6.6.0",
"eslint-plugin-patternfly-react": "^0.2.3",
"eslint-plugin-react-hooks": "^2.2.0",
"file-loader": "^4.2.0",
"html-webpack-plugin": "^3.2.0",
"imagemin": "^7.0.0",
Expand All @@ -48,34 +55,34 @@
"prettier": "^1.15.2",
"prop-types": "^15.6.1",
"raw-loader": "^3.1.0",
"react": "^16.8.4",
"react": "^16.11.0",
"react-axe": "^3.0.2",
"react-docgen-typescript-loader": "^3.2.1",
"react-dom": "^16.6.3",
"react-dom": "^16.11.0",
"regenerator-runtime": "^0.13.3",
"rimraf": "^3.0.0",
"style-loader": "^1.0.0",
"svg-url-loader": "^3.0.0",
"terser-webpack-plugin": "^2.1.0",
"terser-webpack-plugin": "^2.2.1",
"ts-jest": "^24.0.0",
"ts-loader": "^6.0.2",
"ts-loader": "^6.2.1",
"tsconfig-paths-webpack-plugin": "^3.2.0",
"tslint": "^5.13.1",
"tslint-config-prettier": "^1.18.0",
"tslint-eslint-rules": "^5.4.0",
"tslint-react": "^4.0.0",
"tslint-react-hooks": "^2.2.1",
"typescript": "^3.5.3",
"url-loader": "^2.1.0",
"webpack": "^4.39.1",
"webpack-bundle-analyzer": "^3.4.1",
"webpack-cli": "^3.3.6",
"webpack-dev-server": "^3.7.2",
"typescript": "^3.6.4",
"url-loader": "^2.2.0",
"webpack": "^4.41.2",
"webpack-bundle-analyzer": "^3.6.0",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.9.0",
"webpack-merge": "^4.1.4"
},
"dependencies": {
"@patternfly/react-core": "^3.77.2",
"@patternfly/react-icons": "^3.10.15",
"@patternfly/react-icons": "^3.14.18",
"@patternfly/react-styles": "^3.5.7",
"react-router-dom": "^5.0.1",
"react-router-last-location": "^2.0.1"
Expand Down
15 changes: 7 additions & 8 deletions src/app/AppLayout/AppLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,27 +38,26 @@ const AppLayout: React.FunctionComponent<IAppLayout> = ({children}) => {
logo="Patternfly"
logoProps={logoProps}
toolbar="Toolbar"
showNavToggle={true}
showNavToggle
isNavOpen={isNavOpen}
onNavToggle={isMobileView ? onNavToggleMobile : onNavToggle}
/>
);

const Navigation = (
<Nav id="nav-primary-simple">
<NavList id="nav-list-simple" variant={NavVariants.simple}>
{routes.map((route, idx) => {
return route.label && (
<Nav id="nav-primary-simple" theme="dark">
<NavList id="nav-list-simple" variant={NavVariants.default}>
{routes.map((route, idx) => route.label && (
<NavItem key={`${route.label}-${idx}`} id={`${route.label}-${idx}`}>
<NavLink exact={true} to={route.path} activeClassName="pf-m-current">{route.label}</NavLink>
<NavLink exact to={route.path} activeClassName="pf-m-current">{route.label}</NavLink>
</NavItem>
);
})}
))}
</NavList>
</Nav>
);
const Sidebar = (
<PageSidebar
theme="dark"
nav={Navigation}
isNavOpen={isMobileView ? isNavOpenMobile : isNavOpen} />
);
Expand Down
6 changes: 2 additions & 4 deletions src/app/Dashboard/Dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import * as React from 'react';
import { PageSection, Title } from '@patternfly/react-core';

const Dashboard: React.FunctionComponent<any> = (props) => {
return (
const Dashboard: React.FunctionComponent<{}> = () => (
<PageSection>
<Title size="lg">Dashboard Page Title</Title>
</PageSection>
);
}
)

export { Dashboard };
2 changes: 2 additions & 0 deletions src/app/DynamicImport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import * as React from 'react';
import { accessibleRouteChangeHandler } from '@app/utils/utils';

interface IDynamicImport {
/* eslint-disable @typescript-eslint/no-explicit-any */
load: () => Promise<any>;
children: any;
/* eslint-enable @typescript-eslint/no-explicit-any */
focusContentAfterMount: boolean;
}

Expand Down
6 changes: 2 additions & 4 deletions src/app/NotFound/NotFound.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@ import * as React from 'react';
import { NavLink } from 'react-router-dom';
import { Alert, PageSection } from '@patternfly/react-core';

const NotFound: React.FunctionComponent = () => {
return (
const NotFound: React.FunctionComponent = () => (
<PageSection>
<Alert variant="danger" title="404! This view hasn't been created yet." /><br />
<NavLink to="/" className="pf-c-nav__link">Take me home</NavLink>
</PageSection>
);
}
)

export { NotFound };
8 changes: 3 additions & 5 deletions src/app/Support/Support.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,15 @@ export interface ISupportProps {
sampleProp?: string;
}

const Support: React.FunctionComponent<ISupportProps> = (props) => {
return (
const Support: React.FunctionComponent<ISupportProps> = () => (
<PageSection>
<EmptyState variant={EmptyStateVariant.full}>
<EmptyStateIcon icon={CubesIcon} />
<Title headingLevel="h5" size="lg">
Empty State (Stub Support Module)
</Title>
<EmptyStateBody>
This represents an the empty state pattern in Patternfly 4. Hopefully it's simple enough to use but flexible
This represents an the empty state pattern in Patternfly 4. Hopefully it&apos;s simple enough to use but flexible
enough to meet a variety of needs.
</EmptyStateBody>
<Button variant="primary">Primary Action</Button>
Expand All @@ -38,7 +37,6 @@ const Support: React.FunctionComponent<ISupportProps> = (props) => {
</EmptyStateSecondaryActions>
</EmptyState>
</PageSection>
);
}
)

export { Support };
5 changes: 3 additions & 2 deletions src/app/app.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ describe('App tests', () => {
it('should hide the sidebar when clicking the nav-toggle button', () => {
const wrapper = mount(<App />);
const button = wrapper.find('#nav-toggle').hostNodes();
expect(wrapper.find('#page-sidebar').hasClass('pf-m-expanded'));
expect(wrapper.find('#page-sidebar').hasClass('pf-m-expanded')).toBeTruthy();
button.simulate('click');
expect(wrapper.find('#page-sidebar').hasClass('pf-m-collapsed'));
expect(wrapper.find('#page-sidebar').hasClass('pf-m-collapsed')).toBeTruthy();
expect(wrapper.find('#page-sidebar').hasClass('pf-m-expanded')).toBeFalsy();
});
});
4 changes: 1 addition & 3 deletions src/app/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,12 @@ import { AppLayout } from '@app/AppLayout/AppLayout';
import { AppRoutes } from '@app/routes';
import '@app/app.css';

const App: React.FunctionComponent = () => {
return (
const App: React.FunctionComponent = () => (
<Router>
<AppLayout>
<AppRoutes />
</AppLayout>
</Router>
);
};

export { App };
16 changes: 7 additions & 9 deletions src/app/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,16 @@ import { LastLocationProvider, useLastLocation } from 'react-router-last-locatio

let routeFocusTimer: number;

const getSupportModuleAsync = () => {
return () => import(/* webpackChunkName: 'support' */ '@app/Support/Support');
};
const getSupportModuleAsync = () => () => import(/* webpackChunkName: 'support' */ '@app/Support/Support');

const Support = (routeProps: RouteComponentProps) => {
const lastNavigation = useLastLocation();
return (
/* eslint-disable @typescript-eslint/no-explicit-any */
<DynamicImport load={getSupportModuleAsync()} focusContentAfterMount={lastNavigation !== null}>
{(Component: any) => {
let loadedComponent: any;
/* eslint-enable @typescript-eslint/no-explicit-any */
if (Component === null) {
loadedComponent = (
<PageSection aria-label="Loading Content Container">
Expand All @@ -39,8 +39,9 @@ const Support = (routeProps: RouteComponentProps) => {

export interface IAppRoute {
label?: string;
/* eslint-disable @typescript-eslint/no-explicit-any */
component: React.ComponentType<RouteComponentProps<any>> | React.ComponentType<any>;
icon?: any;
/* eslint-enable @typescript-eslint/no-explicit-any */
exact?: boolean;
path: string;
title: string;
Expand All @@ -51,15 +52,13 @@ const routes: IAppRoute[] = [
{
component: Dashboard,
exact: true,
icon: null,
label: 'Dashboard',
path: '/',
title: 'Main Dashboard Title'
},
{
component: Support,
exact: true,
icon: null,
isAsync: true,
label: 'Support',
path: '/support',
Expand Down Expand Up @@ -108,18 +107,17 @@ const PageNotFound = ({ title }: { title: string }) => {
const AppRoutes = () => (
<LastLocationProvider>
<Switch>
{routes.map(({ path, exact, component, title, isAsync, icon }, idx) => (
{routes.map(({ path, exact, component, title, isAsync }, idx) => (
<RouteWithTitleUpdates
path={path}
exact={exact}
component={component}
key={idx}
icon={icon}
title={title}
isAsync={isAsync}
/>
))}
<PageNotFound title={'404 Page Not Found'} />
<PageNotFound title="404 Page Not Found" />
</Switch>
</LastLocationProvider>
);
Expand Down
2 changes: 1 addition & 1 deletion src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { App } from '@app/index';

if (process.env.NODE_ENV !== "production") {
// tslint:disable-next-line
const axe = require("react-axe");
const axe = require("react-axe"); // eslint-disable-line
axe(React, ReactDOM, 1000);
}

Expand Down
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"include": [
"**/*.ts",
"**/*.tsx",
"**/*.jsx"
"**/*.jsx",
"**/*.js"
],
"exclude": ["node_modules"]
}
10 changes: 8 additions & 2 deletions tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,17 @@
],
"linterOptions": {
"exclude": [
"**/node_modules/**/*.ts"
"**/node_modules/**/*.ts",
"**/node_modules/**/*.js",
"**/dist/**/*.js",
"**/coverage/**/*.js",
"**/webpack.*.js",
"**/jest.config.js"
]
},
"rules": {
"ordered-imports": [false],
"react-hooks-nesting": "error"
"react-hooks-nesting": "error",
"jsx-boolean-value": ["never"]
}
}
Loading

0 comments on commit b245fa4

Please sign in to comment.