lydell / eslint-plugin-simple-import-sort

Easy autofixable import sorting.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Support tsconfig.json paths and baseUrl automatically

lydell opened this issue · comments

It would be cool if the plugin could automatically detect tsconfig.json to see which first party imports you have, such as web/components/foo. Then those could be moved into the “absolute imports” group, instead of ending up in the (third party) “packages” group.

One idea is to use https://github.com/dividab/tsconfig-paths. Need to check that it actually supports baseUrl, though.

Another idea is to try to require("typescript"). If it’s there, use it to resolve the config.

The initial idea comes from: #18 (comment)

Edit: This is a light-weight package that can do the hard work for us: https://github.com/privatenumber/get-tsconfig

You can also take a look to import/order that can differentiate external and internal modules.

// 2. "external" modules
import _ from 'lodash';
import chalk from 'chalk';
// 3. "internal" modules
// (if you have configured your path or webpack to handle your internal paths differently)
import foo from 'src/foo';

I had terrible experience (in terms of performance/caching) so far with plugins/rules that relies on external "effects" (config files, files other than the file being linted etc.). import plugin is a notorious example which has been (and probably still is) part of most create-x-app bootstrappers.

I've never implemented a plugin myself, so my knowledge on why that's happening is very limited. I can only imagine cache invalidation being hard and once users report that as a bug, maintainers are more prone to not using a cache and to doing file I/O during linting.

I'd feel very sorry if the same happened to this awesome tool.

@anilanar Thanks for sharing your experience! That’ll be kept in mind if this is implemented. I think the added custom grouping option works well enough and is easy enough to use that this might not even be needed.

any progress with baseUrl support 👀?

No, I’m not so interested in this anymore because (1) I don’t need this myself and (2) the custom grouping option seems easy enough to set up (and is possibly more performant).

Would you like to tell us more about your use case that you’d want this for and how much configuration you’d avoid not having to set up custom grouping?

I have a create-react-app typescript project, and I've set the project to use non-relative modules.
my tsconfig:

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

so now I can use

import CommonService from 'services/commonService';

insted of

import CommonService from '../../../services/commonService';

and now when I'm using the auto-fix import
my imports look like that

import { Col, Row } from 'antd'; // third party
import Dictionary from 'dictionary/dictionary'; // my module
import React, { useState } from 'react'; // third party
import { RouteComponentProps } from 'react-router-dom'; // third party
import CommonService from 'services/commonService'; // my module
import TreatmentService from 'services/treatmentService';// my module

which makes no sense

@israelKusayev Thanks! Have you tried the custom grouping option? How did it go?

I don't know that custom grouping is the right solution here–won't it require you to write a regex pattern that explicitly matches every file/directory in your baseUrl?

Yes it does! By showing me how complex that would be, that would motivate adding the automatic tsconfig.json thing.

Sorry for the late response

I tried to use groups and I ended up with:

"overrides": [
    {
      "files": ["*.tsx", "*.ts"],
      "rules": {
        "simple-import-sort/sort": [
          "error",
          {
            "groups": [
              // Packages. `react` related packages come first.
              // Things that start with a letter (or digit or underscore), or `@` followed by a letter.
              ["^react", "^@?\\w"],
              // Absolute imports and Relative imports.
              ["^(utils|services|hooks|hoc|types|contexts|dictionary|components)(/.*|$)", "^\\."],
              // for scss imports.
              ["^[^.]"]
            ]
          }
        ]
      }
    }
  ]

I think it is great for me, except for one thing, If I'll add more folders to my project under src folder, I'll need to update eslint file

But I'm not adding a new folder (under src) every day, so it is OK (i think..)

@israelKusayev Thanks!

If you use .eslintrc.js, maybe you can use fs.readdirSync("src") if you don’t want to update the config every time you add a folder to src/?

Yesss great idea, works perfect

const fs = require('fs');

const foldersUnderSrc = fs
  .readdirSync('src', { withFileTypes: true })
  .filter(dirent => dirent.isDirectory())
  .map(dirent => dirent.name);

and then

// Absolute imports and Relative imports.
[`^(${foldersUnderSrc.join('|')})(/.*|$)`, '^\\.'],

You can see here the config file https://github.com/reflexology/client-V2/blob/master/.eslintrc.js

Cool! If you’re paranoid you could regex-escape the directory names as well :)

The import plugin solves this using custom resolvers, which works well with eslint-import-resolver-typescript. See the issue in their repository here. Could be a reasonable extension here to omit double configuration.

@iwan933 Can you show what your double configuration looks like?

@lydell double configuration is maybe the wrong wording here. But actually if i would use the simple-import-sort and import plugin, it would be nice to have a single solution for handling tsconfigs baseUrl. Personally i prefer the resolver over the groups solution. But i might miss some points here as i have not developed a plugin yet.

Ok! It still helps to see some real config, though. Having examples to look at always help me.

Can we reuse resolver plugins of eslint-plugin-import?

@JounQin Last time I looked at the resolvers of eslint-plugin-import I came to the conslusion that we can’t reuse them. Things might have changed though and I might have overlooked something, so feel free to look into it!

But do keep in mind that:

  • I would like to see a really compelling real-world use case before we add complexity to this plugin.
  • I really like how this plugin is very small and has no dependencies. My bar is pretty high when it comes to changes to that. So don’t spend lots of time on a PR or something without talking about it first, or not knowing that it might not be merged.

@lydell

AFAI, the resolver is very simple to be reused, they just share same signature resolve(source, file, config) => { found: Boolean, path: String? }, doc. So I'm not sure why you came to the conslusion that we can’t reuse them.

Take eslint-import-resolver-typescript for example.

I really like how this plugin is very small and has no dependencies.

The resolvers are installed by users manually, so this is not a problem?

The resolvers are installed by users manually, so this is not a problem?

We clearly have different opinions here. I don’t think anything good will come out of a discussion about it.

Hmm … I realized something. Thank you for pushing me to think about this!

I think that the only solution I will find acceptable is:

  • Adding no dependencies to this plugin. We could support like require("typescript") but it would be up to the user to install typescript.
  • The code added to this plugin must be less than the size of the source code now.
  • It must be fast.

require("typescript") is not even used in eslint-import-resolver-typescript, it is using tsconfig-paths inside.

I was thinking to reuse import/resolver's settings, so I don't know what you want here.

I’ve made up my mind. This is complexity I don’t want. I’m updating the readme: #93