Skip to content
This repository has been archived by the owner on Nov 3, 2020. It is now read-only.

Convert to use ESLint for linting TypeScript, etc. (WIP) #174

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Convert to use ESLint for linting TypeScript, etc.
  • Loading branch information
beausmith committed Apr 8, 2019
commit 4b59f826272c164ac7e29c50f2f4ec478c8ba34a
30 changes: 0 additions & 30 deletions .eslintrc

This file was deleted.

53 changes: 53 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
module.exports = {
env: {
browser: true,
node: true,
'cypress/globals': true,
'jest/globals': true,
},
parser: '@typescript-eslint/parser', // Specifies the ESLint parser
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended', // Uses the recommended rules from @typescript-eslint/eslint-plugin
'plugin:cypress/recommended',
'plugin:prettier/recommended', // Enables eslint-plugin-prettier and displays prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array.
'plugin:react/recommended', // Uses the recommended rules from @eslint-plugin-react
'prettier',
'prettier/@typescript-eslint', // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier
],
parserOptions: {
ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features
ecmaFeatures: {
jsx: true, // Allows for the parsing of JSX
},
project: './tsconfig.json',
sourceType: 'module', // Allows for the use of imports
},
plugins: [
'@typescript-eslint',
'eslint-plugin-cypress',
'jest',
'no-null',
'react',
],
settings: {
react: {
version: 'detect', // Tells eslint-plugin-react to automatically detect the version of React to use
},
},
rules: {
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-null-keyword': 'on',
'@typescript-eslint/explicit-function-return-type': 'off', // Want to use it, but it requires return types for all built-in React lifecycle methods.
'no-null/no-null': 2, // TypeScript with strictNullChecks
'react/jsx-boolean-value': [2, 'never'],
camelcase: 'error',
'spaced-comment': [
'error',
'always',
{
markers: ['/'],
},
],
},
}
5 changes: 1 addition & 4 deletions .lintstagedrc
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
{
"linters": {
"*.+(ts|tsx)": [
"tslint"
],
"*.+(js|jsx)": [
"*.+(js|jsx|ts|tsx)": [
"eslint"
],
"*.+(js|jsx|json|yml|yaml|css|less|scss|ts|tsx|md|graphql|mdx)": [
Expand Down
30 changes: 26 additions & 4 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,30 @@
{
"editor.formatOnSave": true,
"editor.tabSize": 2,
"files.autoSave": "onFocusChange",
"eslint.packageManager": "yarn",
"prettier.eslintIntegration": true,
"prettier.tslintIntegration": true
"eslint.autoFixOnSave": true,
"eslint.validate": [
"javascript",
"javascriptreact",
{
"language": "typescript",
"autoFix": true
},
{
"language": "typescriptreact",
"autoFix": true
}
],
"editor.formatOnSave": true,
"[javascript]": {
"editor.formatOnSave": false
},
"[javascriptreact]": {
"editor.formatOnSave": false
},
"[typescript]": {
"editor.formatOnSave": false
},
"[typescriptreact]": {
"editor.formatOnSave": false
}
}
25 changes: 12 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,21 +91,19 @@ help you contribute.
1. Add features, fix bugs, etc. and then use `git` to commit your changes in
logical commits.

There is a pre-commit hook which will run linting and code formatting
scripts. You can run these manually with these three commands which are found
in the `package.json` scripts:
There is a pre-commit hook (see `.lintstangedrc` file) which will run linting
and code formatting scripts. You can run these manually with these three
commands which are found in the `package.json` scripts:

```
yarn eslint:base
tslint:base
yarn prettier:write
```

**Using Visual Studio Code?** Autorun linting and code formatting by
installing/enabling the following plugins (which will pick up their
respective config files in this project):
installing/enabling the following plugins (which will pick up the respective
config files in this project):

- `TSLint` for TypeScript linting
- `ESLint` for (ECMAScript) JavaScript linting
- `Prettier - Code formatter` for code formatting

Expand Down Expand Up @@ -140,9 +138,10 @@ See `package.json` for all available scripts.
This project was bootstrapped with
[Create React App](https://github.com/facebook/create-react-app) for TypeScript.
It uses [Styled Components](https://www.styled-components.com/docs/) for styles
(and some `css` files too). [ESLint](https://eslint.org/),
[TSLint](https://palantir.github.io/tslint/), and
[Prettier](https://prettier.io/) are used to maintain clean code.
[Jest](https://jestjs.io/), [dom-testing-library](https://testing-library.com)
and [react-testing-library](https://github.com/kentcdodds/react-testing-library)
are used to test components and end-to-end user flows.
(and some `css` files too). [ESLint](https://eslint.org/) is configured to lint
Javascript and TypeScript files, and format code using
[Prettier](https://prettier.io/). [Jest](https://jestjs.io/),
[dom-testing-library](https://testing-library.com),
[react-testing-library](https://github.com/kentcdodds/react-testing-library),
and [Cypress](https://www.cypress.io/) are used to test components and
end-to-end user flows.
18 changes: 7 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@
"pretest:e2e:ci": "yarn build",
"test:e2e:ci": "start-server-and-test start http://localhost:3000 cypress:run",
"test:e2e:watch": "start-server-and-test start http://localhost:3000 cypress:open",
"eslint:base": "eslint 'src/**/*.+(js|jsx)'",
"eslint:base": "eslint --ext .js,.jsx,.ts,.tsx src/ test/ cypress/tests/",
"prettier:base": "prettier '**/*.+(js|jsx|json|yml|yaml|css|less|scss|ts|tsx|md|graphql|mdx)'",
"prettier:check": "yarn prettier:base --list-different",
"prettier:write": "yarn prettier:base --write",
"tslint:base": "tslint -c tslint.json 'src/**/*.+(ts,tsx)'"
"prettier:write": "yarn prettier:base --write"
},
"husky": {
"hooks": {
Expand Down Expand Up @@ -77,31 +76,28 @@
"react-scripts": "2.1.8",
"react-simple-keyboard": "^1.21.3",
"styled-components": "^4.2.0",
"tslint-react-hooks": "^2.0.0",
"typescript": "3.2.2"
},
"devDependencies": {
"babel-eslint": "^10.0.1",
"@typescript-eslint/eslint-plugin": "^1.5.0",
"@typescript-eslint/parser": "^1.5.0",
"cypress": "^3.1.5",
"cypress-testing-library": "^2.3.6",
"eslint": "^5.16.0",
"eslint-config-airbnb": "^17.1.0",
"eslint-config-prettier": "^4.1.0",
"eslint-plugin-cypress": "^2.2.1",
"eslint-plugin-import": "^2.16.0",
"eslint-plugin-jest": "^22.4.1",
"eslint-plugin-jsx-a11y": "^6.2.1",
"eslint-plugin-no-null": "^1.0.2",
"eslint-plugin-prettier": "^3.0.1",
"eslint-plugin-react": "^7.12.4",
"husky": "^1.3.1",
"is-ci-cli": "^1.1.1",
"jest-styled-components": "^6.3.1",
"lint-staged": "^8.1.5",
"prettier": "^1.15.3",
"prettier": "^1.16.4",
"react-testing-library": "^5.4.4",
"start-server-and-test": "^1.7.13",
"tslint": "^5.15.0",
"tslint-config-prettier": "^1.18.0",
"tslint-react": "^4.0.0"
"start-server-and-test": "^1.7.11"
}
}
2 changes: 1 addition & 1 deletion src/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ describe('loads election', () => {
})

it(`end to end: election can be uploaded, voter can vote and print`, async () => {
/* tslint:disable-next-line */
/* eslint-disable-next-line */
const eventListenerCallbacksDictionary: any = {}
window.addEventListener = jest.fn((event, cb) => {
eventListenerCallbacksDictionary[event] = cb
Expand Down
2 changes: 1 addition & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import electionSample from './data/electionSample.json'
export const mergeWithDefaults = (
election: Election,
defaults: ElectionDefaults = electionDefaults
) => ({ ...defaults, ...election })
): Election => ({ ...defaults, ...election })

import Ballot from './components/Ballot'
import Screen from './components/Screen'
Expand Down
8 changes: 4 additions & 4 deletions src/components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ export interface ButtonInterface<T> {
}

interface Props
extends React.PropsWithoutRef<JSX.IntrinsicElements['button']> {}
interface Props extends ButtonInterface<{}> {}
extends ButtonInterface<{}>,
React.PropsWithoutRef<JSX.IntrinsicElements['button']> {}

const Button = styled('button').attrs((props: Attrs) => ({
type: props.type || 'button',
const Button = styled('button').attrs((attrs: Attrs) => ({
type: attrs.type || 'button',
}))<Props>`
box-sizing: border-box;
cursor: pointer;
Expand Down
4 changes: 2 additions & 2 deletions src/components/CandidateContest.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { CandidateContest as CandidateContestInterface } from '../config/types'

import CandidateContest from './CandidateContest'

const contest = {
const contest: CandidateContestInterface = {
allowWriteIns: false,
candidates: [0, 1, 2].map(i => ({
id: `name-${i}`,
Expand All @@ -17,7 +17,7 @@ const contest = {
section: 'City',
title: 'Mayor',
type: 'candidate',
} as CandidateContestInterface
}
const candidate0 = contest.candidates[0]
const candidate1 = contest.candidates[1]
const candidate2 = contest.candidates[2]
Expand Down
13 changes: 4 additions & 9 deletions src/components/CandidateContest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -219,14 +219,9 @@ const initialState = {

class CandidateContest extends React.Component<Props, State> {
public static contextType = BallotContext
private keyboard: React.RefObject<Keyboard>
private contestChoices: React.RefObject<HTMLDivElement>
constructor(props: Props) {
super(props)
this.state = initialState
this.keyboard = React.createRef()
this.contestChoices = React.createRef()
}
private keyboard = React.createRef<Keyboard>()
private contestChoices = React.createRef<HTMLDivElement>()
public state: State = initialState

public componentDidMount() {
this.updateContestChoicesScrollStates()
Expand Down Expand Up @@ -263,7 +258,7 @@ class CandidateContest extends React.Component<Props, State> {
const { vote } = this.props
const id = (event.target as HTMLInputElement).value
const candidate = this.findCandidateById(vote, id)
if (!!candidate) {
if (candidate) {
if (candidate.isWriteIn) {
this.setState({ candidatePendingRemoval: candidate })
} else {
Expand Down
3 changes: 1 addition & 2 deletions src/components/DataDebugger.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
import React from 'react'

interface Props {
/* tslint:disable-next-line */
data: any
data: any // eslint-disable-line @typescript-eslint/no-explicit-any
hide?: boolean
}

Expand Down
12 changes: 6 additions & 6 deletions src/components/LinkButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import Button, { ButtonInterface } from '../components/Button'
import { ButtonEvent } from '../config/types'

interface Props
extends React.PropsWithoutRef<JSX.IntrinsicElements['button']> {}
interface Props extends RouteComponentProps<{}> {}
interface Props extends ButtonInterface<{}> {}
extends ButtonInterface<{}>,
RouteComponentProps<{}>,
React.PropsWithoutRef<JSX.IntrinsicElements['button']> {}

interface Props {
goBack?: boolean
Expand All @@ -19,10 +19,10 @@ const LinkButton = (props: Props) => {
const {
goBack,
history,
location,
match,
location, // eslint-disable-line @typescript-eslint/no-unused-vars
match, // eslint-disable-line @typescript-eslint/no-unused-vars
onClick,
staticContext,
staticContext, // eslint-disable-line @typescript-eslint/no-unused-vars
to,
// ⬆ filtering out props that `button` doesn’t know what to do with.
...rest
Expand Down
12 changes: 6 additions & 6 deletions src/components/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,22 @@ const ModalContent = styled('div')<ModalContentInterface>`
`

interface Props {
isOpen: boolean
actions?: ReactNode
ariaLabel?: string
content?: ReactNode
centerContent?: boolean
actions?: ReactNode
content?: ReactNode
isOpen: boolean
onAfterOpen?: () => void
}

const Modal: React.FC<Props> = ({
actions,
content,
centerContent,
ariaLabel = 'Alert Modal',
centerContent,
content,
isOpen,
onAfterOpen,
}) => (
}: Props) => (
<ReactModal
appElement={document.getElementById('root')!}
ariaHideApp={process.env.NODE_ENV !== 'test'}
Expand Down
5 changes: 1 addition & 4 deletions src/components/UploadConfig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,7 @@ const initialState = {
}

class UploadConfig extends React.Component<Props, State> {
constructor(props: Props) {
super(props)
this.state = initialState
}
public state: State = initialState

public setErrorMessage = (
errorMessage: string = 'Only files that end in ".json" are accepted. Try again.'
Expand Down
2 changes: 1 addition & 1 deletion src/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export interface ElectionDefaults {
readonly bmdConfig: BMDConfig
}
export interface Election {
readonly contests: Array<CandidateContest | YesNoContest>
readonly contests: (CandidateContest | YesNoContest)[]
readonly county: string
readonly date: string
readonly seal: string
Expand Down
Loading