Giter Site home page Giter Site logo

tslint-import-group-ordering's Introduction

TSLint import group ordering rule

npm GitHub stars GitHub license Build Status

Demo

  • enforces imports groups ordering

  • highly configurable

    Use regular expressions to configure which import statements go into which import group.

  • support for determining package.json dependencies (or reading all the dependencies from node_modules)

  • has an auto-fixer

    • preserves comments

    • preserves non-import statements that appear in-between import statements

      Even though it is allowed in the ECMAScript Modules specification, the rule discourages mixing regular statements with import declarations.

Usage

Install this library as a devDependency:

npm install tslint-import-group-ordering --save-dev

Modify tslint.json (add extends and the rule configuration to rules):

{
  "extends": ["tslint-import-group-ordering"],
  "rules": {
    "import-group-ordering": {
      "severity": "warning",
      "options": {
        "imports-groups": [
          {
            "name": "dependencies"
          },
          {
            "name": "common"
          },
          {
            "name": "product"
          },
          {
            "name": "other"
          }
        ],
        "matching-rules": [
          {
            "type": "project",
            "matches": "^(common)",
            "imports-group": "common"
          },
          {
            "type": "project",
            "matches": "^(product)",
            "imports-group": "product"
          },
          {
            "type": "dependencies",
            "imports-group": "dependencies",
            "disable-native-nodejs-modules": true,
            "from-package.json": true
          },
          {
            "type": "project",
            "matches": ".*",
            "imports-group": "other"
          }
        ]
      }
    }
  }
}

The above configuration would enforce the following import group order:

  • dependencies from node_modules (but not NodeJS native modules - this is configured by setting disable-native-nodejs-modules)
  • anything that starts with common
  • anything that starts wtih products
  • other imports

For example, the following order of imports would be incorrect:

import React, { Component } from 'react';

import { ITableHeaderProps, ITableHeaderState } from './interfaces';
import ActionGroup from 'common/components/action-button-group';
import { FilterBar, FilterDock } from 'common/components/filters';
import { SearchInput } from 'common/components/inputs';

because ./interfaces is imported too early.

Testing

The project uses 3 types of tests. To run the automated tests, run

npm run test

This will build the project and run the tests. Alternatively, to only run the tests without building the project run

npm run test:only

Automated lint tests

These use the TSLint command to test whether the actual errors match the expected ones.

First, build the rule using npm run build and then run:

npm run test:only:lint

to run the lint tests.

See TSLint's docs for more information.

Automated autofix tests

There apply the TSLint's autofix and compare the results with the expected ones.

First, build the rule using npm run build and then run:

npm run test:only:automated-fix

to run the autofix tests.

Manual tests

Open the test/manual directory to perform manual tests, e.g. use your IDE or the tslint CLI directly.

Author

The author of this rule is Grzegorz Rozdzialik.

tslint-import-group-ordering's People

Contributors

dependabot[bot] avatar gelio avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

dtmp am1fat32

tslint-import-group-ordering's Issues

Update docs & error messages

First of all, nice linter rule :)

I had some trouble configuring the rule though, here are my insights:

  • you could describe in README that the order of matching-groups serves as priorities from top to bottom, I found that out via trial-and-error and later from closed issues.
  • what does the type: 'project' | 'dependencies' option do in matching-groups? It's not mentioned in the README, my guess would be that the matching group selects only in-project imports and external deps, but my rules didn't work until I removed the type fields completely. It may have to do with @-prefixed imports -- I have two kinds of those: external ones, like @nestjs/common, and internal ones like @utils/something, configured through aliases in tsconfig-paths (or module-alias for a non-TS version).
  • the error messages for the errors with @-prefixed imports was confusing: it said This import declaration should appear in an earlier group ("namespaced-deps", number 2), while the import on the mentioned line was indeed a namespaced-deps import and it was indeed group 2 in the file. Maybe it would be useful to also describe in the message, to which group was the import actually matched?

Fix duplicating comments in auto-fix

When applying the auto-fix, the comments before the first import statement is duplicated and the comment after the last import statement does not follow the import statement.

E.g.

// @ts-ignore
import {} from 'utils';
import {} from './correct-order.ts.lint';

after the auto-fix is

// @ts-ignore
// @ts-ignore
import {} from 'utils';

import {} from './correct-order.ts.lint';

I reckon using getFullStart and getFullEnd has to be used when creating Lint.Replacement

Add group matching priority as a configuration option

A proposal for fixing #7

I suggest adding a new option for each import group in the configuration - the priority.

Matching an import statement to import groups will follow the groups according to their priority.

This way, a group of third-party dependencies may be matched before a group of project imports even though third-party dependencies may come later than project imports, e.g. in the following scenario:

// project imports from some part of the code that should be a dependency

// third-party imports

// the rest of the project imports

Adding a priority option seems to be the most versatile solution that allows the users to fully configure the rule.

[feature request] special match type for side-effect imports

I'd like to enforce side-effect-only imports to be in a separate import group at the top of the file -- the ones without the from keyword, e.g. import 'reflect-metadata' or import 'tsconfig-paths/register'.

Typescript Import Sorter, the vscode plugin that I use for sorting imports, allows to do this through a type: 'importMember' param (though the name is not really descriptive for me, but it's explained below).

Maybe would it be possible to add such a matching type to your linter rule too?

Preserve the order of comments

Currently, the comments between import statements are removed.

They should be preserved.

Since there are multiple solutions how to handle this I reckon the one that makes the most sense is the following:

  • if a comment is a tslint-disable or ts-ignore for the next line, glue it to the next import statement
  • if a comment is a tslint-disable for the same line, glue it to that import statement
  • move any other statements (comments, lines of code) after all the import groups

This way the developer will be able to put back the comments (e.g. some general comments) into place themselves.

Import by alphabetical order in a group

Hello,

Do you know if it possible to expose an option which will be able to order the import of classes and packages inside a group? For example, you have two groups : Angular imports and classes imports

import { ActivatedRoute } from '@angular/router';
import { Component, OnInit, OnDestroy } from '@angular/core';

import { SomeClass, CoolClass, UsefulClass } from './myclasses';
import { RouteClass, AbstractClass, OtherClass } from './some.other.classes';

Can be changed on :

import { Component, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

import { CoolClass, UsefulClass, SomeClass } from './myclasses';
import { AbstractClass, OtherClass, RouteClass } from './some.other.classes';

Can be quite hard to order @angular (caused by the '@') but for the classes and regular packages imports it can be quite useful. What do you think of it ? :)

Thanks,

Read library names from package.json

To avoid listing all the directories in node_modules the rule could read the package names from package.json.

This could improve the performance because reading and parsing a JSON file could be faster than listing the files in a directory, especially since the node_modules could be flattened (which could end up in false-positives, e.g. importing from a package not included as a dependency).

In order not to break the current behavior this option could be provided in the configuration. This also matters because some projects may use an external solution for package management and keep package.json empty.

Fix internal comparison order for import groups

The current implementation first tries to match the module specifier against node_modules and Node's standard library.

Only then the module specifier is matched against the rule's configuration.

It results in improper behavior when having a module specifier whose prefix is a library's name, e.g. utils/some-project-file would be determined to be a module specifier from Node's utils instead of project files.

In order to fix it and preserve the old behavior, I suggest adding a configuration option to decide whether the module specifiers should first be matched against node_modules and Node's standard libraries or the rule's configuration for project files.

grouping is incorrect

import { monitor } from "@colyseus/monitor"
import { Server } from "colyseus"
import express from "express"
import * as bodyParser from "body-parser"
import cors from "cors"

import http from "http"
import path from "path"
import { PusoyTable } from "./rooms/PusoyTable"
import { rooms } from "./routes/rooms"
import { users } from "./routes/users"
import { sequelize } from "./sequelize"

why http and path is in the second group?

this is my config

    "import-group-ordering": {
      "severity": "warning",
      "options": {
        "imports-groups": [
          {
            "name": "dependencies"
          },
          {
            "name": "common"
          },
          {
            "name": "product"
          },
          {
            "name": "other"
          }
        ],
        "matching-rules": [
          {
            "type": "project",
            "matches": "^(common)",
            "imports-group": "common"
          },
          {
            "type": "project",
            "matches": "^(product)",
            "imports-group": "product"
          },
          {
            "type": "dependencies",
            "imports-group": "dependencies",
            "disable-native-nodejs-modules": true,
            "from-package.json": true
          },
          {
            "type": "project",
            "matches": ".*",
            "imports-group": "other"
          }
        ]
      }
    }

Improve the performance

One idea I had is to use a TRIE tree for checking whether the import statement refers to a module from node_modules.

This may have an increased startup cost due to having to construct a tree (yet currently many regexps are constructed, so the cost may stay the same - need to benchmark), yet further checking would be proportional to the longest module's name instead of the number of modules.

Benchmarking the performance would be great

wrong result or wrong config

With this config:

        "import-group-ordering": {
            "severity": "warning",
            "options": {
                "imports-groups": [
                    {
                        "name": "first"
                    },
                    {
                        "name": "external"
                    },
                    {
                        "name": "app"
                    },
                    {
                        "name": "other"
                    }
                ],
                "matching-rules": [
                    {
                        "type": "project",
                        "imports-group": "first",
                        "matches": "^app/chat/helpers/sdk$"
                    },
                    {
                        "type": "project",
                        "imports-group": "app",
                        "matches": "^(app)"
                    },
                    {
                        "type": "dependencies",
                        "imports-group": "external",
                        "from-package.json": true,
                        "disable-native-nodejs-modules": true
                    },
                    {
                        "type": "project",
                        "imports-group": "other",
                        "matches": ".*"
                    }
                ]
            }
        }

I end up with this order of imports:

import { storiesOf } from '@storybook/react-native'
import React from 'react'
import { Text } from 'react-native'

import R from 'ramda'
import SizeScrollFlatList from './SizeScrollFlatList'

Why is ramda down there?
Shouldn't it be with the group above?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.