Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

eslint import/order breaks down when using typescript aliases

I am using typescript path aliases in a large react project. So for example, in my tsconfig.json, I specify some path aliases:

{
  "baseUrl": "./src",
    "paths": {
      "@common/*": ["common/*"],
      "@settings/*": ["settings/*"],
      // other paths
    }
}

Ok great, now when I want to import some modules I can uses these aliases. We are also using eslint, and one of the rules we use is the import/order. It enforces that anything coming out of node_modules should be imported before any local modules. I like this rule. Without using my aliases:

import React, { useEffect } from "react";
import SomeDistantComponent from '../../common/components/SomeDistantComponent'
import { Formik } from 'formik'

This will throw a linting error, but formatting the file according to eslint will automatically move the formik import above the SomeComponent import, which is what I want.

However, when using my aliases, this does not throw an error:

import React, { useEffect } from "react";
import SomeComponent from '@common/components/SomeComponent'
import { Formik } from 'formik'

Its as if the typescript path aliases have tricked the import/order rule, and that rule now breaks down.

Question: How can my linter still recognize these aliases paths? Can I configure the groups or pathGroups options of this plugin to properly group and order my aliased local modules to come after my node modules?

Bonus question:

I don't necessarily need to distinguish between aliased modules and non-aliased modules in terms of order. For example

import React, { useEffect } from "react";
import SomeCloseComponent from './SomeCloseComponent'
import SomeComponent from '@common/components/SomeComponent'
import AnotherCloseComponent from './AnotherCloseComponent'
import { Formik } from 'formik'

I'd be fine leaving the import order of SomeCloseComponent, SomeComponent, and AnotherCloseComponent, so long as the formik import goes before all of them. Is it possible to put aliased imports at the same 'priority' level of grouping as non-aliased imports, meaning they won't be reordered among themselves, but will all come after node_module imports?

Edit - making another attempt:

Based on Aviv Hadar's answer, I've tried this in my eslint file:

"import/order": [
      "warn",
      {
        pathGroups: [
          {
            pattern: "@material-ui/**",
            group: "external",
            position: "after"
          },
          {
            pattern: "@/**",
            group: "internal",
            position: "after"
          }
        ],
        pathGroupsExcludedImportTypes: ["internal", "external", "builtins"],
        groups: [
          "builtin",
          "external",
          "unknown",
          ["internal", "sibling", "parent"],
          "index",
          "object",
          "type"
        ]
      }
    ],

This works in that it treats all imports that begin with @ as internal, and keeps them on the same level as other internal imports. So this won't cause a warning:

import { Something } from "@some/aliased/path";
import LocalComponent from "../../local";
import { SomethingElse } from "@another/aliased/path";

Which is what I want - all internal modules at the same level of grouping, regardless of aliased vs not, or parent/sibling. However, this should show a warning, but it doesn't:

import { Something } from "@some/aliased/path";
import LocalComponent from "../../local";
import { SomethingElse } from "@another/aliased/path";
import { makestyles } from '@material-ui/styles'

The last import does begin with @, but it is a node_module, and should be at the top of the list. In retrospect I wish we would have chosen a different alias prefix, but we didn't. Is there a way to tweak my config to properly interperet node_modules as external, and keep them in that group for ordering?

like image 697
Seth Lutske Avatar asked May 14 '21 22:05

Seth Lutske


1 Answers

You need to use the pathGroups property to indicate to eslint that the correct position of paths that starts with @/ is before/after one of the other groups.

I choose to use the ~ symbol instead of @ to create a difference between importing from my src folder and importing from an external library that starts with @, like this one for example:

import {makeStyles} from '@material-ui/core/styles';
import Bar from '~/components/Bar'

So now my file looks like this:

import React from 'react'; // external
import Bar from '~/components/Bar'; // SPECIAL
import {ReactComponent as Logo} from '../logo.svg'; // parent
import './App.css'; // sibling

And this is my definition of the import/order rule in my .eslintrc file:

    "import/order": [
      "error",
      {
        "pathGroups": [
          {
            "pattern": "~/**",
            "group": "external",
            "position": "after"
          }
        ],
        "groups": [
          "builtin",
          "external",
          "internal",
          "unknown",
          "parent",
          "sibling",
          "index",
          "object",
          "type"
        ]
      }
    ]
  }

As you can see, I'm using the pathGroups to tell eslint that the proper position of any import that starts with ~/ is after the external group.

Because each import here belongs to a different group, any change that I will do to this order will result in an ESLint error.

P.S. I had issues at some point with the imports themselves that were resolved after I updated the eslint-plugin-import to version 2.23.4

like image 106
Aviv Hadar Avatar answered Sep 19 '22 12:09

Aviv Hadar