From b588e597078f95d670df42cb57cb592692992d46 Mon Sep 17 00:00:00 2001 From: evavirseda Date: Tue, 17 Jan 2023 17:15:33 +0100 Subject: [PATCH] feat: add husky precommit and configure linter (#45) * add husky precommit and configure linter * fix: all errors in the project (#46) * fix all errors * fix: fix all eslint warnings Co-authored-by: Arnau Espin --- .eslintrc.js | 122 +++++ .eslintrc.json | 3 - .husky/pre-commit | 4 + components/Layout/Flash.tsx | 19 +- components/Layout/FontanaSVG.tsx | 28 +- components/Layout/Header.tsx | 34 +- components/Layout/NetworkSelector.tsx | 34 +- components/Layout/Wallet.tsx | 26 +- components/Layout/index.ts | 10 +- components/Table/HeaderTable.tsx | 71 ++- components/Table/Row.tsx | 123 +++-- components/Table/Table.tsx | 54 +- components/Table/index.ts | 6 +- lib/create-token.ts | 22 +- lib/spl.ts | 25 +- package.json | 17 +- pages/_app.tsx | 48 +- pages/api/mint.ts | 34 +- pages/api/mongo-get.ts | 5 +- pages/api/mongo-new-token.ts | 10 +- pages/api/transfer.ts | 77 ++- pages/index.tsx | 122 +++-- yarn.lock | 681 +++++++++++++++++++++++++- 23 files changed, 1185 insertions(+), 390 deletions(-) create mode 100644 .eslintrc.js delete mode 100644 .eslintrc.json create mode 100755 .husky/pre-commit diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..80ba6ef --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,122 @@ +const parserOptions = { + ecmaVersion: 6, + sourceType: 'module', +} + +const eslintRules = { + 'arrow-body-style': 'warn', // WARN b/c blocks style allows for readability and ensure scope + 'arrow-spacing': 'error', + 'eol-last': 'error', + 'func-call-spacing': 'error', + 'indent': 'off', // OFF b/c causes problems between Prettier and ESLint + 'linebreak-style': 'off', // OFF b/c Windows (Git) puts CRLF line endings + 'missing-declaration': 'off', // OFF b/c throws errors on imports / require statements + 'multiline-ternary': 'off', // OFF b/c causes problems between Prettier and ESLint + 'no-alert': 'error', + 'no-async-promise-executor': 'error', + 'no-case-declarations': 'error', + 'no-console': ['error', { allow: ['error', 'warn'] }], + 'no-control-regex': 'error', + 'no-dupe-keys': 'error', + 'no-empty': 'error', + 'no-extra-boolean-cast': 'error', + 'no-extra-parens': 'off', + 'no-extra-semi': 'error', + 'no-fallthrough': 'error', + 'no-import-assign': 'error', + 'no-irregular-whitespace': 'error', + 'no-prototype-builtins': 'error', + 'no-return-await': 'error', + 'no-trailing-spaces': 'error', + 'no-useless-escape': 'error', + 'no-undef': 'error', + 'no-underscore-dangle': 'off', // OFF b/c this syntax is used for defining local callback methods + 'no-unreachable': 'error', + 'no-unused-export-let': 'off', // OFF b/c troublesome with some .js files in packages/shared + 'no-unused-vars': 'off', // OFF b/c there are simply too many and they're harmless + 'no-var': 'error', + 'prefer-arrow-callback': 'warn', + 'prefer-const': 'warn', + 'prefer-destructuring': 'off', // OFF b/c it's not really correct + 'quotes': ['error', 'single'], + 'semi': 'off', // OFF b/c we aren't using semicolons + 'space-before-function-paren': 'off', // OFF b/c we aren't using spaces before function parameters / signatures + 'spaced-comment': 'error', +} + +const eslintRulesOnlyTypescript = { + 'no-undef': 'off' // Typescript handles undefined variables better than eslint +} + +const typescriptEslintRules = { + '@typescript-eslint/array-type': 'error', + '@typescript-eslint/await-thenable': 'error', + '@typescript-eslint/ban-types': 'error', + '@typescript-eslint/ban-ts-comment': 'warn', + '@typescript-eslint/explicit-module-boundary-types': 'error', + '@typescript-eslint/no-array-constructor': 'error', + '@typescript-eslint/no-empty-function': 'off', // OFF b/c we use empty functions a lot (esp. for initialization) + '@typescript-eslint/no-explicit-any': 'error', + '@typescript-eslint/no-extra-non-null-assertion': 'error', + '@typescript-eslint/no-extra-semi': 'error', + '@typescript-eslint/no-floating-promises': 'warn', // Warn b/c we have existing code in migration that I don't want to touch to pass new linting rules + '@typescript-eslint/no-implied-eval': 'error', + '@typescript-eslint/no-inferrable-types': 'off', // OFF b/c this errors on some useful code annotations for function signatures + '@typescript-eslint/no-misused-new': 'error', + '@typescript-eslint/no-misused-promises': 'error', + '@typescript-eslint/no-non-null-asserted-optional-chain': 'error', + '@typescript-eslint/no-non-null-assertion': 'error', + '@typescript-eslint/no-this-alias': 'error', + '@typescript-eslint/no-unnecessary-type-assertion': 'error', + '@typescript-eslint/no-unsafe-assignment': 'off', + '@typescript-eslint/no-unsafe-call': 'off', + '@typescript-eslint/no-unsafe-member-access': 'off', // OFF b/c there are simply too many linting errors + '@typescript-eslint/no-unsafe-return': 'off', + '@typescript-eslint/no-unused-vars': 'off', // OFF b/c there are simply too many and they're harmless + '@typescript-eslint/no-var-requires': 'error', + '@typescript-eslint/prefer-regexp-exec': 'error', + '@typescript-eslint/restrict-plus-operands': 'off', // OFF b/c not entirely accurate despite proper typings + '@typescript-eslint/restrict-template-expressions': 'off', // OFF b/c using any is useful in template expressions + '@typescript-eslint/require-await': 'error', + '@typescript-eslint/unbound-method': 'error', +} + + +module.exports = { + env: { + browser: true, + es6: true, + node: true, + }, + extends: ['eslint:recommended', 'plugin:@next/next/recommended'], + overrides: [ + { + files: ['**/*.ts', '**/*.tsx'], + extends: [ + 'plugin:@typescript-eslint/eslint-recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:@typescript-eslint/recommended-requiring-type-checking', + ], + parser: '@typescript-eslint/parser', + parserOptions: { + ...parserOptions, + project: './tsconfig.json', + tsconfigRootDir: './', + }, + plugins: ['@typescript-eslint'], + rules: { + ...eslintRules, + ...eslintRulesOnlyTypescript, + ...typescriptEslintRules, + }, + }, + ], + parser: '@babel/eslint-parser', + parserOptions: { + ...parserOptions, + requireConfigFile: false, + }, + rules: { + ...eslintRules, + }, +} \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index bffb357..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "next/core-web-vitals" -} diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000..abf1181 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +npx lint-staged --allow-empty diff --git a/components/Layout/Flash.tsx b/components/Layout/Flash.tsx index 3f1f4fc..2ff903f 100644 --- a/components/Layout/Flash.tsx +++ b/components/Layout/Flash.tsx @@ -1,8 +1,8 @@ -import { StyledOcticon, Flash } from "@primer/react"; -import { CheckIcon } from "@primer/octicons-react"; -import { useHandleDestroyAnimated } from "hooks"; -import { useEffect, useRef } from "react"; -import { useSuccess, useConnection } from "contexts"; +import { StyledOcticon, Flash } from '@primer/react'; +import { CheckIcon } from '@primer/octicons-react'; +import { useHandleDestroyAnimated } from 'hooks'; +import { useEffect, useRef } from 'react'; +import { useSuccess, useConnection } from 'contexts'; const Toast: React.FC = () => { const flashRef = useRef(null); @@ -14,19 +14,18 @@ const Toast: React.FC = () => { ); useEffect(() => { - if (message === "") return; + if (message === '') return; setSendSuccess(true); - // eslint-disable-next-line react-hooks/exhaustive-deps }, [message]); - const queryParam = url?.includes("devnet") ? "?cluster=devnet-solana" : ""; + const queryParam = url?.includes('devnet') ? '?cluster=devnet-solana' : ''; if (sendSuccess) return (
@@ -34,7 +33,7 @@ const Toast: React.FC = () => { {mint && ( View token diff --git a/components/Layout/FontanaSVG.tsx b/components/Layout/FontanaSVG.tsx index 11226ed..ef44e99 100644 --- a/components/Layout/FontanaSVG.tsx +++ b/components/Layout/FontanaSVG.tsx @@ -1,17 +1,15 @@ -const FontanaSVG: React.FC<{ width: number }> = ({ width }) => { - return ( - - - - ); -}; +const FontanaSVG: React.FC<{ width: number }> = ({ width }) => ( + + + +); export default FontanaSVG; diff --git a/components/Layout/Header.tsx b/components/Layout/Header.tsx index 9608225..ae60153 100644 --- a/components/Layout/Header.tsx +++ b/components/Layout/Header.tsx @@ -1,21 +1,19 @@ -import { FontanaSVG } from "components/Layout"; -import { NetworkSelector, Wallet } from "components/Layout"; +import { FontanaSVG } from 'components/Layout'; +import { NetworkSelector, Wallet } from 'components/Layout'; -const Header: React.FC = () => { - return ( - - ); -}; +const Header: React.FC = () => ( + +); export default Header; diff --git a/components/Layout/NetworkSelector.tsx b/components/Layout/NetworkSelector.tsx index 8a9d37c..960d44c 100644 --- a/components/Layout/NetworkSelector.tsx +++ b/components/Layout/NetworkSelector.tsx @@ -1,18 +1,18 @@ -import { FC, useEffect, useRef } from "react"; -import { WalletAdapterNetwork } from "@solana/wallet-adapter-base"; -import { clusterApiUrl, Connection } from "@solana/web3.js"; -import { Network, useConnection } from "contexts"; +import { FC, useEffect, useRef } from 'react'; +import { WalletAdapterNetwork } from '@solana/wallet-adapter-base'; +import { clusterApiUrl, Connection } from '@solana/web3.js'; +import { Network, useConnection } from 'contexts'; const RPC_API_DEVNET = - process.env.NEXT_PUBLIC_RPC_API_DEVNET || clusterApiUrl("devnet"); + process.env.NEXT_PUBLIC_RPC_API_DEVNET || clusterApiUrl('devnet'); const RPC_API_MAINNET = - process.env.NEXT_PUBLIC_RPC_API_MAINNET || clusterApiUrl("mainnet-beta"); + process.env.NEXT_PUBLIC_RPC_API_MAINNET || clusterApiUrl('mainnet-beta'); function getUrl(network: Network): string { switch (network) { - case "Devnet": + case 'Devnet': return RPC_API_DEVNET; - case "Mainnet": + case 'Mainnet': return RPC_API_MAINNET; default: throw new Error(`Unknown network: ${network}`); @@ -24,23 +24,27 @@ const NetworkSelector: FC = () => { const detailsRef = useRef(null); function hideUl() { - detailsRef.current!.removeAttribute("open"); + detailsRef.current?.removeAttribute('open'); } function changeNetwork(_network: Network) { setNetwork(_network); setConnection(new Connection(getUrl(_network))); setUrl(getUrl(_network)); - localStorage.setItem("network-fontana", _network); + localStorage.setItem('network-fontana', _network); } useEffect(() => { - const _network = localStorage.getItem("network-fontana") as Network | null; - if (_network === "Devnet" || _network === "Mainnet" || _network === "Testnet") { + const _network = localStorage.getItem('network-fontana') as Network | null; + if ( + _network === 'Devnet' || + _network === 'Mainnet' || + _network === 'Testnet' + ) { changeNetwork(_network); return; } - changeNetwork("Devnet"); + changeNetwork('Devnet'); }, []); return ( @@ -51,13 +55,13 @@ const NetworkSelector: FC = () => { ref={detailsRef} > - {network || "Select Network"} + {network || 'Select Network'} {
    {Object.keys(WalletAdapterNetwork) - .filter((e) => e !== "Testnet") + .filter((e) => e !== 'Testnet') .map((_network) => (
  • { - return ( -
    - -
    - ); -}; +const Wallet: FC = () => ( +
    + +
    +); export default Wallet; diff --git a/components/Layout/index.ts b/components/Layout/index.ts index 448adc8..811701d 100644 --- a/components/Layout/index.ts +++ b/components/Layout/index.ts @@ -1,5 +1,5 @@ -export { default as Wallet } from "./Wallet"; -export { default as Header } from "./Header"; -export { default as FontanaSVG } from "./FontanaSVG"; -export { default as NetworkSelector } from "./NetworkSelector"; -export { default as Toast } from "./Flash"; +export { default as Wallet } from './Wallet'; +export { default as Header } from './Header'; +export { default as FontanaSVG } from './FontanaSVG'; +export { default as NetworkSelector } from './NetworkSelector'; +export { default as Toast } from './Flash'; diff --git a/components/Table/HeaderTable.tsx b/components/Table/HeaderTable.tsx index ee88abe..2df0369 100644 --- a/components/Table/HeaderTable.tsx +++ b/components/Table/HeaderTable.tsx @@ -1,10 +1,10 @@ -import { Header, Button, StyledOcticon } from "@primer/react"; -import { CheckIcon, SyncIcon } from "@primer/octicons-react"; +import { Header, Button, StyledOcticon } from '@primer/react'; +import { CheckIcon, SyncIcon } from '@primer/octicons-react'; -import { useWallet } from "@solana/wallet-adapter-react"; -import { useHasMongoUri, useConnection } from "contexts"; -import { HourglassIcon } from "@primer/octicons-react"; -import { useCreateToken } from "hooks"; +import { useWallet } from '@solana/wallet-adapter-react'; +import { useHasMongoUri, useConnection } from 'contexts'; +import { HourglassIcon } from '@primer/octicons-react'; +import { useCreateToken } from 'hooks'; const HeaderTable: React.FC<{ tokensAmount: number }> = ({ tokensAmount = 0, @@ -16,7 +16,7 @@ const HeaderTable: React.FC<{ tokensAmount: number }> = ({ const canCreateToken = () => { if (publicKey) return true; - if (!publicKey && hasMongoUri && network === "Devnet") return true; + if (!publicKey && hasMongoUri && network === 'Devnet') return true; return false; }; @@ -24,30 +24,29 @@ const HeaderTable: React.FC<{ tokensAmount: number }> = ({
    @@ -57,16 +56,16 @@ const HeaderTable: React.FC<{ tokensAmount: number }> = ({ Available In wallet @@ -74,18 +73,18 @@ const HeaderTable: React.FC<{ tokensAmount: number }> = ({ {canCreateToken() && (