From 0d65ca6d3f7045fb99adb4e599bd2e90b825338b Mon Sep 17 00:00:00 2001 From: Jannis Mattheis Date: Fri, 11 Sep 2020 22:17:50 +0200 Subject: [PATCH] TSLint -> ESLint --- ui/.eslintignore | 2 + ui/.eslintrc.yml | 87 +++++++++++++++++++++++ ui/package.json | 13 ++-- ui/src/CurrentUser.ts | 68 +++++++++--------- ui/src/index.tsx | 2 +- ui/src/inject.tsx | 20 +++--- ui/src/message/extras.ts | 2 +- ui/src/registerServiceWorker.ts | 1 - ui/src/tests/client.test.ts | 2 +- ui/src/tests/message.test.ts | 4 +- ui/src/types.ts | 2 +- ui/tslint.json | 120 -------------------------------- ui/yarn.lock | 5 -- 13 files changed, 148 insertions(+), 180 deletions(-) create mode 100644 ui/.eslintignore create mode 100644 ui/.eslintrc.yml delete mode 100644 ui/tslint.json diff --git a/ui/.eslintignore b/ui/.eslintignore new file mode 100644 index 000000000..a0eb34e9c --- /dev/null +++ b/ui/.eslintignore @@ -0,0 +1,2 @@ +src/setupTests.ts +src/registerServiceWorker.ts diff --git a/ui/.eslintrc.yml b/ui/.eslintrc.yml new file mode 100644 index 000000000..7d1c033d9 --- /dev/null +++ b/ui/.eslintrc.yml @@ -0,0 +1,87 @@ +--- +extends: + - eslint:recommended + - plugin:@typescript-eslint/eslint-recommended + - plugin:@typescript-eslint/recommended + - plugin:@typescript-eslint/recommended-requiring-type-checking + - plugin:react/recommended + - plugin:import/errors + - plugin:import/typescript + - plugin:jest/recommended + - prettier +env: + browser: true + es6: true + node: true +parser: "@typescript-eslint/parser" +parserOptions: + project: tsconfig.json + sourceType: module +plugins: + - "@typescript-eslint" + - react + - import + - unicorn +settings: + react: + version: detect +rules: + consistent-return: error + default-case: error + default-param-last: error + no-loop-func: off + arrow-body-style: [error, as-needed] + + import/no-useless-path-segments: error + import/group-exports: off + import/extensions: [error, never] + import/no-duplicates: error + import/first: error + import/no-unused-modules: error + + unicorn/no-abusive-eslint-disable: error + unicorn/no-array-instanceof: error + unicorn/no-unreadable-array-destructuring: error + unicorn/no-zero-fractions: error + + react/jsx-key: error + react/jsx-pascal-case: error + react/destructuring-assignment: warn + react/function-component-definition: [error, {namedComponents: arrow-function, unnamedComponents: arrow-function}] + react/no-array-index-key: error + react/no-deprecated: error + react/no-string-refs: error + react/no-this-in-sfc: error + react/no-typos: error + react/no-unknown-property: error + react/prefer-stateless-function: error + react/prop-types: off + + jest/expect-expect: off + jest/no-jasmine-globals: off + "@typescript-eslint/require-await": off + + "@typescript-eslint/array-type": [error, {default: array-simple}] + "@typescript-eslint/await-thenable": error + "@typescript-eslint/no-unused-vars": error + "@typescript-eslint/no-use-before-define": off + "@typescript-eslint/consistent-type-assertions": [error, {assertionStyle: as}] + + "@typescript-eslint/no-extra-non-null-assertion": error + "@typescript-eslint/no-inferrable-types": error + "@typescript-eslint/no-this-alias": error + "@typescript-eslint/no-throw-literal": error + "@typescript-eslint/prefer-nullish-coalescing": error + "@typescript-eslint/prefer-optional-chain": error + "@typescript-eslint/prefer-readonly": error + "@typescript-eslint/unbound-method": error + "@typescript-eslint/no-empty-function": off + "@typescript-eslint/explicit-module-boundary-types": off + "@typescript-eslint/no-floating-promises": off + "@typescript-eslint/no-unsafe-member-access": off + "@typescript-eslint/no-unsafe-return": off + "@typescript-eslint/no-unsafe-assignment": off + "@typescript-eslint/restrict-plus-operands": off + "@typescript-eslint/no-misused-promises": off + + "@typescript-eslint/no-explicit-any": error diff --git a/ui/package.json b/ui/package.json index 8fdcd15c4..6224d5330 100644 --- a/ui/package.json +++ b/ui/package.json @@ -32,8 +32,7 @@ "build": "react-scripts build", "test": "react-scripts test --env=node", "eject": "react-scripts eject", - "lint": "tslint --project .", - "lintfix": "tslint --fix --project .", + "lint": "eslint \"src/*.{ts,tsx}\"", "format": "prettier \"src/**/*.{ts,tsx}\" --write", "testformat": "prettier \"src/**/*.{ts,tsx}\" --list-different" }, @@ -52,14 +51,20 @@ "@types/react-router-dom": "^5.1.5", "@types/remove-markdown": "^0.1.1", "@types/rimraf": "^3.0.0", + "@typescript-eslint/eslint-plugin": "^4.1.0", + "@typescript-eslint/parser": "^4.1.0", + "eslint-config-prettier": "^6.11.0", + "eslint-plugin-import": "^2.22.0", + "eslint-plugin-jest": "^24.0.0", + "eslint-plugin-prefer-arrow": "^1.2.2", + "eslint-plugin-react": "^7.20.6", + "eslint-plugin-unicorn": "^21.0.0", "get-port": "^5.1.1", "prettier": "^2.1.1", "puppeteer": "^5.3.0", "react-scripts": "^3.4.3", "rimraf": "^3.0.2", "tree-kill": "^1.2.0", - "tslint": "^6.1.2", - "tslint-sonarts": "^1.7.0", "typescript": "4.0.2", "wait-on": "^5.2.0" }, diff --git a/ui/src/CurrentUser.ts b/ui/src/CurrentUser.ts index 59d5a559b..3016cc60d 100644 --- a/ui/src/CurrentUser.ts +++ b/ui/src/CurrentUser.ts @@ -37,7 +37,7 @@ export class CurrentUser { return ''; }; - private setToken = (token: string) => { + private readonly setToken = (token: string) => { this.tokenCache = token; window.localStorage.setItem(tokenKey, token); }; @@ -53,6 +53,7 @@ export class CurrentUser { url: config.get('url') + 'client', method: 'POST', data: {name}, + // eslint-disable-next-line @typescript-eslint/naming-convention headers: {Authorization: 'Basic ' + Base64.encode(username + ':' + password)}, }) .then((resp: AxiosResponse) => { @@ -81,36 +82,39 @@ export class CurrentUser { return Promise.reject(); } - return axios - .create() - .get(config.get('url') + 'current/user', {headers: {'X-Gotify-Key': this.token()}}) - .then((passThrough) => { - this.user = passThrough.data; - this.loggedIn = true; - this.connectionErrorMessage = null; - this.reconnectTime = 7500; - return passThrough; - }) - .catch((error: AxiosError) => { - if (!error || !error.response) { - this.connectionError('No network connection or server unavailable.'); - return Promise.reject(error); - } - - if (error.response.status >= 500) { - this.connectionError( - `${error.response.statusText} (code: ${error.response.status}).` - ); - return Promise.reject(error); - } + return ( + axios + .create() + // eslint-disable-next-line @typescript-eslint/naming-convention + .get(config.get('url') + 'current/user', {headers: {'X-Gotify-Key': this.token()}}) + .then((passThrough) => { + this.user = passThrough.data; + this.loggedIn = true; + this.connectionErrorMessage = null; + this.reconnectTime = 7500; + return passThrough; + }) + .catch((error: AxiosError) => { + if (!error || !error.response) { + this.connectionError('No network connection or server unavailable.'); + return Promise.reject(error); + } + + if (error.response.status >= 500) { + this.connectionError( + `${error.response.statusText} (code: ${error.response.status}).` + ); + return Promise.reject(error); + } - this.connectionErrorMessage = null; + this.connectionErrorMessage = null; - if (error.response.status >= 400 && error.response.status < 500) { - this.logout(); - } - return Promise.reject(error); - }); + if (error.response.status >= 400 && error.response.status < 500) { + this.logout(); + } + return Promise.reject(error); + }) + ); }; public logout = async () => { @@ -119,9 +123,7 @@ export class CurrentUser { .then((resp: AxiosResponse) => { resp.data .filter((client) => client.token === this.tokenCache) - .forEach((client) => { - return axios.delete(config.get('url') + 'client/' + client.id); - }); + .forEach((client) => axios.delete(config.get('url') + 'client/' + client.id)); }) .catch(() => Promise.resolve()); window.localStorage.removeItem(tokenKey); @@ -143,7 +145,7 @@ export class CurrentUser { }); }; - private connectionError = (message: string) => { + private readonly connectionError = (message: string) => { this.connectionErrorMessage = message; if (this.reconnectTimeoutId !== null) { window.clearTimeout(this.reconnectTimeoutId); diff --git a/ui/src/index.tsx b/ui/src/index.tsx index b47f96529..061b07620 100644 --- a/ui/src/index.tsx +++ b/ui/src/index.tsx @@ -30,8 +30,8 @@ const defaultProdConfig = { url: urlWithSlash, }; +// eslint-disable-next-line @typescript-eslint/no-unused-vars declare global { - // tslint:disable-next-line interface Window { config: config.IConfig; } diff --git a/ui/src/inject.tsx b/ui/src/inject.tsx index d25fc9f39..aa51db769 100644 --- a/ui/src/inject.tsx +++ b/ui/src/inject.tsx @@ -23,15 +23,13 @@ export interface StoreMapping { export type AllStores = Extract; export type Stores = Pick; -export const inject = (...stores: I[]) => { - return

( - node: React.ComponentType

- ): React.ComponentType>> => { - // tslint:disable-next-line:no-any - return mobxInject(...stores)(node) as any; - }; -}; +// eslint-disable-next-line @typescript-eslint/ban-types +export const inject = (...stores: I[]) =>

( + node: React.ComponentType

+): React.ComponentType>> => + // eslint-disable-next-line @typescript-eslint/no-explicit-any + mobxInject(...stores)(node) as any; -export const InjectProvider: React.SFC<{stores: StoreMapping}> = ({children, stores}) => { - return {children}; -}; +export const InjectProvider: React.SFC<{stores: StoreMapping}> = ({children, stores}) => ( + {children} +); diff --git a/ui/src/message/extras.ts b/ui/src/message/extras.ts index ec0b0c416..724a74401 100644 --- a/ui/src/message/extras.ts +++ b/ui/src/message/extras.ts @@ -13,7 +13,7 @@ export const contentType = (extras?: IMessageExtras): RenderMode => { return valid ? type : RenderMode.Plain; }; -// tslint:disable-next-line:no-any +// eslint-disable-next-line @typescript-eslint/no-explicit-any const extract = (extras: IMessageExtras | undefined, key: string, path: string): any => { if (!extras) { return null; diff --git a/ui/src/registerServiceWorker.ts b/ui/src/registerServiceWorker.ts index 6b0242e93..eabfe4791 100644 --- a/ui/src/registerServiceWorker.ts +++ b/ui/src/registerServiceWorker.ts @@ -1,4 +1,3 @@ -// tslint:disable // In production, we register a service worker to serve assets from local cache. // This lets the app load faster on subsequent visits in production, and gives diff --git a/ui/src/tests/client.test.ts b/ui/src/tests/client.test.ts index cc8488002..129d25d79 100644 --- a/ui/src/tests/client.test.ts +++ b/ui/src/tests/client.test.ts @@ -94,7 +94,7 @@ describe('Client', () => { expect(await count(page, $table.rows())).toBe(2); }); - // tslint:disable-next-line:no-identical-functions + // eslint-disable-next-line it('deletes own client', async () => { await page.click($table.cell(1, Col.Delete, '.delete')); diff --git a/ui/src/tests/message.test.ts b/ui/src/tests/message.test.ts index 16b47b5c4..89a53c95c 100644 --- a/ui/src/tests/message.test.ts +++ b/ui/src/tests/message.test.ts @@ -16,7 +16,7 @@ beforeAll(async () => { afterAll(async () => await gotify.close()); -// tslint:disable-next-line +// eslint-disable-next-line const axiosAuth = {auth: {username: 'admin', password: 'admin'}}; let windowsServerToken: string; @@ -35,7 +35,7 @@ const navigate = async (appName: string) => { await waitForExists(page, selector.heading(), appName); }; -// tslint:disable-next-line +// eslint-disable-next-line describe('Messages', () => { it('does login', async () => await auth.login(page)); it('is on messages', async () => { diff --git a/ui/src/types.ts b/ui/src/types.ts index 537d0970e..ad22775d2 100644 --- a/ui/src/types.ts +++ b/ui/src/types.ts @@ -37,7 +37,7 @@ export interface IMessage { } export interface IMessageExtras { - [key: string]: any; // tslint:disable-line no-any + [key: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any } export interface IPagedMessages { diff --git a/ui/tslint.json b/ui/tslint.json deleted file mode 100644 index 5d6419355..000000000 --- a/ui/tslint.json +++ /dev/null @@ -1,120 +0,0 @@ -{ - "extends": "tslint-sonarts", - "rules": { - "array-type": [true, "array-simple"], - "arrow-return-shorthand": true, - "ban": [true, - {"name": "Array", "message": "tsstyle#array-constructor"} - ], - "ban-types": [true, - ["Object", "Use {} instead."], - ["String", "Use 'string' instead."], - ["Number", "Use 'number' instead."], - ["Boolean", "Use 'boolean' instead."], - ["Function", "Use (..) => .. instead."] - ], - "class-name": true, - "curly": [true], - "forin": true, - "label-position": true, - "member-access": [true], - "new-parens": true, - "no-angle-bracket-type-assertion": true, - "no-any": true, - "no-arg": true, - "no-conditional-assignment": true, - "no-construct": true, - "no-shadowed-variable": true, - "prefer-object-spread": true, - "no-debugger": true, - "no-default-export": false, - "no-duplicate-variable": true, - "no-inferrable-types": true, - "no-namespace": [true, "allow-declarations"], - "cyclomatic-complexity": [true, 13], - "no-duplicate-imports": true, - "no-reference": true, - "no-string-throw": true, - "no-unused-expression": true, - "no-var-keyword": true, - "object-literal-shorthand": true, - "only-arrow-functions": [true, "allow-declarations", "allow-named-functions"], - "prefer-const": true, - "radix": true, - "semicolon": [true, "always", "ignore-bound-class-methods"], - "switch-default": true, - "triple-equals": [true, "allow-null-check"], - "use-isnan": true, - "variable-name": [ - true, - "check-format", - "ban-keywords", - "allow-pascal-case", - "allow-leading-underscore", - "allow-trailing-underscore" - ], - - "cognitive-complexity": true, - "mccabe-complexity": true, - "no-duplicate-string": false, - "no-nested-incdec": false, - "no-all-duplicated-branches": true, - "consecutive-overloads": true, - "max-union-size": false, - "no-accessor-field-mismatch": true, - "no-array-delete": true, - "no-big-function": true, - "no-case-with-or": true, - "no-collection-size-mischeck": true, - "no-commented-code": true, - "no-dead-store": true, - "no-duplicate-in-composite": true, - "no-duplicated-branches": true, - "no-element-overwrite": true, - "no-empty-destructuring": true, - "no-empty-nested-blocks": true, - "no-extra-semicolon": true, - "no-gratuitous-expressions": true, - "no-hardcoded-credentials": true, - "no-identical-conditions": true, - "no-identical-expressions": true, - "no-identical-functions": true, - "no-ignored-initial-value": true, - "no-ignored-return": true, - "no-in-misuse": true, - "no-inconsistent-return": true, - "no-misleading-array-reverse": true, - "no-misspelled-operator": true, - "no-multiline-string-literals": true, - "no-nested-template-literals": true, - "no-redundant-boolean": true, - "no-redundant-jump": true, - "no-redundant-parentheses": true, - "no-return-type-any": true, - "no-same-line-conditional": true, - "no-self-assignment": true, - "no-small-switch": true, - "no-statements-same-line": true, - "no-unconditional-jump": true, - "no-undefined-argument": true, - "no-unenclosed-multiline-block": true, - "no-unthrown-error": true, - "no-unused-array": true, - "no-use-of-empty-return-value": true, - "no-useless-cast": true, - "no-useless-increment": true, - "no-useless-intersection": true, - "no-variable-usage-before-declaration": true, - "parameters-max-number": true, - "prefer-default-last": true, - "prefer-immediate-return": true, - "prefer-promise-shorthand": true, - "use-primitive-type": true, - "use-type-alias": true - }, - "linterOptions": { - "exclude": [ - "node_modules/**/*.ts" - ] - } -} \ No newline at end of file diff --git a/ui/yarn.lock b/ui/yarn.lock index 31deadf7d..0d648ca00 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -4689,11 +4689,6 @@ eslint-plugin-prefer-arrow@^1.2.2: resolved "https://registry.yarnpkg.com/eslint-plugin-prefer-arrow/-/eslint-plugin-prefer-arrow-1.2.2.tgz#0c6d25a6b94cb3e0110a23d129760af5860edb6e" integrity sha512-C8YMhL+r8RMeMdYAw/rQtE6xNdMulj+zGWud/qIGnlmomiPRaLDGLMeskZ3alN6uMBojmooRimtdrXebLN4svQ== -eslint-plugin-promise@^4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz#845fd8b2260ad8f82564c1222fce44ad71d9418a" - integrity sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw== - eslint-plugin-react-hooks@^1.6.1: version "1.7.0" resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-1.7.0.tgz#6210b6d5a37205f0b92858f895a4e827020a7d04"