0

I'm working on my company project and there's a 3rd party yarn package called ipfs-http-client I want to use. However, somehow whenever I try to import something from it, eslint's popup shows up when I hover over the package name and say it's unable to resolve to to the module. However, when I press Command (on Mac) and left click the package name it's able to lead me to the source code of the package correctly. When I run yarn start, the following error message is shown.

Failed to compile.
./src/hooks/ipfs/useFileUpload.ts
Module not found: Can't resolve 'ipfs-http-client' in '/Users/stephen/github/cool-project-frontend/src/hooks/ipfs'

I tried to use create-react-app to create a testing project and the import of this package is fine and the code works alright. Does anyone know what's the possible reason of it and the solution for that? My current investigating direction is eslint or typescript compilation configuration, but I don't have many clues right now.

Here is the code I'm having in my company's project.

// useFileUpload.ts

import React, { useState } from 'react';
import { create } from 'ipfs-http-client'
import CID from 'cids';
const client = create({ url: "https://ipfs.infura.io:5001/api/v0" });
export default function useFileUpload() {
    const [file, setFile] = useState({});
    const onFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
        if (e.target.value !== '' && !!e.target.files) {
            setFile(e.target.files[0]);
            const { cid } = await client.add(e.target.files[0]);
            console.log(new CID(cid.toString()).toV1().toString('base32'));
        } else {
            setFile({});
        }
    }
    return {onFileChange};
}

// package.json

{
  "name": "cool-project",
  "version": "0.1.0",
  "private": true,
  "homepage": "./",
  "resolutions": {
    "babel-loader": "8.1.0",
    "react-error-overlay": "6.0.9"
  },
  "dependencies": {
    "@hookform/devtools": "^4.0.2",
    "@hookform/resolvers": "^2.8.8",
    "@mui/material": "^5.6.1",
    "@mui/styled-engine-sc": "^5.6.1",
    "@mui/x-date-pickers": "^5.0.0-alpha.0",
    "@reach/menu-button": "^0.16.2",
    "@reach/rect": "^0.16.0",
    "@types/d3-format": "^3.0.1",
    "@walletconnect/client": "^1.7.1",
    "@walletconnect/types": "^1.7.1",
    "adam-sdk": "file:packages/adam-sdk",
    "boring-avatars": "^1.6.3",
    "classnames": "^2.3.1",
    "crypto-js": "^4.1.1",
    "d3-format": "^3.1.0",
    "date-fns": "^2.28.0",
    "ethers": "^5.5.4",
    "graphql": "^16.3.0",
    "graphql-hooks": "^5.7.1",
    "jotai": "^1.6.0",
    "lodash": "^4.17.21",
    "magic-sdk": "^8.1.0",
    "numeral": "^2.0.6",
    "polished": "^4.1.4",
    "react": "^17.0.2",
    "react-app-polyfill": "^3.0.0",
    "react-dom": "^17.0.2",
    "react-hook-form": "^7.27.1",
    "react-loading": "^2.0.3",
    "react-modal": "^3.14.4",
    "react-router-dom": "^6.2.1",
    "react-scripts": "^4.0.3",
    "styled-components": "^5.3.3",
    "styled-system": "^5.1.5",
    "subscriptions-transport-ws": "^0.11.0",
    "typescript": "^4.5.4",
    "web-vitals": "^2.1.0",
    "yup": "^0.32.11"
  },
  "scripts": {
    "prepare": "npm install --prefix packages/custom-sdk && npm run --prefix packages/custom-sdk build",
    "update-sdk": "yarn prepare && yarn update-sdk-files",
    "update-sdk-files": "cp -R ./packages/custom-sdk ./node_modules",
    "start": "PORT=10002 react-app-rewired start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "bundle-size": "npx cra-bundle-analyzer",
    "storybook": "start-storybook -p 6006 -s public",
    "build-storybook": "build-storybook -s public",
    "prettify": "prettier --write \"src/**/*.{ts,tsx}\"",
    "tsc": "tsc -p tsconfig.json",
    "lint": "yarn prettier --check \"src/**/*.{ts,tsx}\" && yarn eslint \"src/**/*.{ts,tsx}\"",
    "ci": "yarn tsc && yarn lint"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ],
    "overrides": [
      {
        "files": [
          "**/*.stories.*"
        ],
        "rules": {
          "import/no-anonymous-default-export": "off"
        }
      }
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "@babel/eslint-parser": "^7.17.0",
    "@fleekhq/fleek-cli": "^0.1.8",
    "@storybook/addon-actions": "^6.4.19",
    "@storybook/addon-console": "^1.2.3",
    "@storybook/addon-essentials": "^6.4.19",
    "@storybook/addon-interactions": "^6.4.19",
    "@storybook/addon-links": "^6.4.19",
    "@storybook/addon-measure": "^6.4.19",
    "@storybook/addon-outline": "^6.4.19",
    "@storybook/node-logger": "^6.4.19",
    "@storybook/preset-create-react-app": "^3.2.0",
    "@storybook/react": "^6.4.19",
    "@storybook/testing-library": "^0.0.9",
    "@testing-library/jest-dom": "^5.14.1",
    "@testing-library/react": "^12.0.0",
    "@testing-library/user-event": "^13.2.1",
    "@types/crypto-js": "^4.1.0",
    "@types/jest": "^27.4.0",
    "@types/lodash.get": "^4.4.6",
    "@types/node": "^17.0.9",
    "@types/numeral": "^2.0.2",
    "@types/react": "^17.0.38",
    "@types/react-dom": "^17.0.11",
    "@types/react-modal": "^3.13.1",
    "@types/styled-components": "^5.1.20",
    "@types/styled-system": "^5.1.15",
    "@typescript-eslint/eslint-plugin": "^5.18.0",
    "@typescript-eslint/parser": "^5.18.0",
    "babel-plugin-module-resolver": "^4.1.0",
    "babel-plugin-styled-components": "^2.0.7",
    "cra-bundle-analyzer": "^0.1.1",
    "customize-cra": "^1.0.0",
    "eslint-config-airbnb": "^19.0.4",
    "eslint-config-prettier": "^8.5.0",
    "eslint-plugin-module-resolver": "^1.4.0",
    "eslint-plugin-react-hooks": "^4.4.0",
    "jest-mock": "^27.5.1",
    "prettier": "^2.6.2",
    "react-app-rewired": "^2.2.1",
    "react-docgen-typescript": "^2.2.2",
    "storybook-dark-mode": "^1.0.9"
  }
}

// .eslintrc.js

module.exports = {
  env: {
    browser: true,
    es6: true,
  },
  extends: [
    'airbnb',
    'plugin:@typescript-eslint/recommended',
    'plugin:import/errors',
    'plugin:import/warnings',
    'plugin:react-hooks/recommended',
    'prettier',
  ],
  settings: {
    'import/resolver': {
      node: {
        paths: ['src'],
        extensions: ['.js', '.jsx', '.ts', '.tsx'],
        moduleDirectory: ['node_modules/', 'src/', 'packages/'],
      },
    },
  },
  globals: {
    Atomics: 'readonly',
    SharedArrayBuffer: 'readonly',
  },
  parser: '@typescript-eslint/parser',
  parserOptions: {
    project: './tsconfig.json',
    tsconfigRootDir: __dirname,
    ecmaFeatures: {
      jsx: true,
    },
    ecmaVersion: 2018,
    sourceType: 'module',
    requireConfigFile: false,
  },
  plugins: ['@typescript-eslint', 'module-resolver', 'react'],
  rules: {
    // We use prettier to check indent instead
    indent: 0,
    semi: 2,
    curly: ['error', 'all'],
    eqeqeq: [
      1,
      'always',
      {
        null: 'always',
      },
    ],
    'consistent-return': 0,
    'default-case': 0,
    'implicit-arrow-linebreak': 0,
    'comma-dangle': 0,
    'class-methods-use-this': 0,
    'arrow-body-style': 1,
    'prefer-destructuring': 1,
    'object-curly-newline': 0,
    'dot-notation': 0,
    'prefer-template': 0,
    'eol-last': 2,
    'no-param-reassign': 0,
    'no-alert': 0,
    'no-nested-ternary': 0,
    'no-unused-vars': 0,
    'no-trailing-spaces': 0,
    'no-shadow': 0,
    'no-await-in-loop': 0,
    'no-plusplus': 0,
    'no-use-before-define': 0,
    'no-extra-boolean-cast': 0,
    'no-restricted-syntax': [
      'error',
      {
        selector: 'ForInStatement',
        message:
          'for..in loops iterate over the entire prototype chain, which is virtually never what you want. Use Object.{keys,values,entries}, and iterate over the resulting array.',
      },
      {
        selector: 'LabeledStatement',
        message: 'Labels are a form of GOTO; using them makes code confusing and hard to maintain and understand.',
      },
      {
        selector: 'WithStatement',
        message: '`with` is disallowed in strict mode because it makes code impossible to predict and optimize.',
      },
    ],
    'import/order': [
      2,
      {
        groups: ['external', 'builtin', 'internal', 'sibling', 'parent', 'index'],
        alphabetize: {
          order: 'asc',
          caseInsensitive: true,
        },
      },
    ],
    'import/extensions': [
      'error',
      'ignorePackages',
      {
        js: 'never',
        jsx: 'never',
        ts: 'never',
        tsx: 'never',
      },
    ],
    'import/prefer-default-export': 0,
    'import/no-extraneous-dependencies': [
      'error',
      {
        devDependencies: ['**/*.stories.*', '**/.storybook/**/*.*', '**/*.test.*', 'src/setupTests.ts'],
        peerDependencies: true,
      },
    ],
    'react/jsx-indent': 0,
    'react/jsx-indent-props': 0,
    'react/forbid-prop-types': 0,
    'react/button-has-type': 0,
    'react/jsx-fragments': 0,
    'react/no-did-update-set-state': 0,
    'react/prop-types': 0,
    'react/destructuring-assignment': 0,
    'react/prefer-stateless-function': 0,
    'react/no-array-index-key': 0,
    'react/jsx-props-no-spreading': 0,
    'react/jsx-filename-extension': [1, { extensions: ['.ts', '.tsx', '.js', '.jsx'] }],
    'react/react-in-jsx-scope': 0,
    'react/require-default-props': 0,
    'react/function-component-definition': [
      0,
      {
        namedComponents: 'arrow-function' | 'function-declaration' | 'function-expression',
        unnamedComponents: 'arrow-function' | 'function-expression',
      },
    ],
    'react/no-unused-prop-types': 0,
    'jsx-a11y/label-has-for': [
      2,
      {
        required: {
          every: ['id'],
        },
      },
    ],
    'jsx-a11y/alt-text': 0,
    'jsx-a11y/label-has-associated-control': 0,
    'jsx-a11y/anchor-is-valid': 0,
    '@typescript-eslint/no-empty-function': 0,
    '@typescript-eslint/no-empty-interface': 0,
    '@typescript-eslint/no-unused-vars': ['error'],
    '@typescript-eslint/no-shadow': ['error'],
    '@typescript-eslint/no-use-before-define': ['error'],
    '@typescript-eslint/no-inferrable-types': 0,
    '@typescript-eslint/explicit-function-return-type': [
      'warn',
      {
        allowExpressions: true,
      },
    ],
    '@typescript-eslint/switch-exhaustiveness-check': 2,
  },
};

// tsconfig.json

{
  "compilerOptions": {
    "baseUrl": "src",
    "target": "es5",
    "lib": [
    "dom",
    "dom.iterable",
    "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx"
  },
  "include": [
    "src"
  ]
}

UPDATE:

I figure out whenever I remove airbnb and plugin:import/errors, the error goes away. Does anyone know the reason for that and how do I keep both rules while keeping the error message away?

Stephen Fong
  • 697
  • 5
  • 17

0 Answers0