Code Monkey home page Code Monkey logo

eslint-plugin-etc's Introduction

eslint-plugin-etc

GitHub License NPM version Downloads Build status dependency status devDependency Status peerDependency Status

This package contains a bunch of general-purpose, TypeScript-related ESLint rules. Essentially, it's a re-implementation of the rules that are in the tslint-etc package.

Some of the rules are rather opinionated and are not included in the recommended configuration. Developers can decide for themselves whether they want to enable opinionated rules.

Install

Install the ESLint TypeScript parser using npm:

npm install @typescript-eslint/parser --save-dev

Install the package using npm:

npm install eslint-plugin-etc --save-dev

Configure the parser and the parserOptions for ESLint. Here, I use a .eslintrc.js file for the configuration:

const { join } = require("path");
module.exports = {
  parser: "@typescript-eslint/parser",
  parserOptions: {
    ecmaVersion: 2019,
    project: join(__dirname, "./tsconfig.json"),
    sourceType: "module"
  },
  plugins: ["etc"],
  extends: [],
  rules: {
    "etc/no-t": "error"
  }
};

Or, using the recommended configuration:

const { join } = require("path");
module.exports = {
  parser: "@typescript-eslint/parser",
  parserOptions: {
    ecmaVersion: 2019,
    project: join(__dirname, "./tsconfig.json"),
    sourceType: "module"
  },
  extends: ["plugin:etc/recommended"],
};

Rules

The package includes the following rules.

Rules marked with ✅ are recommended and rules marked with 🔧 have fixers.

Rule Description
no-assign-mutated-array Forbids the assignment of returned, mutated arrays.
no-commented-out-code Forbids commented-out code.
no-const-enum Forbids the use of const enum. Constant enums are not compatible with isolated modules.
no-deprecated Forbids the use of deprecated APIs.
no-enum Forbids the use of enum.
no-implicit-any-catch Like the no-implicit-any-catch rule in @typescript-eslint/eslint-plugin, but for Promise rejections instead of catch clauses. 🔧
no-internal Forbids the use of internal APIs.
no-misused-generics Forbids type parameters without inference sites and type parameters that don't add type safety to declarations. This is an ESLint port of Wotan's no-misused-generics rule. See also "The Golden Rule of Generics".
no-t Forbids single-character type parameters.
prefer-interface Forbids type aliases where interfaces can be used. 🔧
prefer-less-than Forbids greater-than comparisons. (Yes, this is the rule for Ben Lesh comparisons.) 🔧
throw-error Forbids throwing - or rejecting with - non-Error values.
underscore-internal Forbids internal APIs that are not prefixed with underscores.

eslint-plugin-etc's People

Contributors

cartant avatar danielnixon avatar dependabot[bot] avatar felixfbecker avatar hotell avatar karlhorky avatar

Stargazers

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

Watchers

 avatar  avatar  avatar

eslint-plugin-etc's Issues

no-internal: Ignore internals defined in the same file

export function useSwipeable(): _SwipeableHandlers {
...
}

/**
 * @internal
 */
export interface _SwipeableHandlers {}

Currently no-internal rule reports _SwipeableHandlers in useSwipeable function.
I think it should not, because It is used in the same file = used internally.
This is legal usage.

I think it's enough to check that internal items are not used inside "import" statement.

no-unused-declaration: issues with "import * as"

version: [email protected]
ESlint config:

'etc/no-unused-declaration': ['error', { ignored: { React: true } }],

Repro:

// icons.tsx
import React from 'react'

export const One = () => {
  return <>one</>;
};

export const Two = () => {
  return <>two</>;
};
import React from 'react'

// 🚨error: Unused declarations are forbidden (etc/no-unused-declaration)
import * as Icons from './icons'

export const App = () => {
  return <div>
    <Icons.One/>
    <Icons.Two/>
  </div>
}

no-unused-declaration: outer scope variables used within class

version: [email protected]
ESlint config:

'etc/no-unused-declaration': ['error', { ignored: { React: true } }],

Repro:

// 🚨Unused declarations are forbidden.eslint(etc/no-unused-declaration)
let draging = false;
// 🚨Unused declarations are forbidden.eslint(etc/no-unused-declaration)
let popupVisible = false;

class DragAndDropStore {
  isDraging = () => draging;
  isPopupVisible = () => popupVisible;
}

`no-commented-out-code`: Unexpectedly triggered for some magic Webpack comments

Hi there! We're having a project where we're using code splitting and noticed that the no-commented-out-code was triggered for some Webpack Magic comments we can use in these code split import functions. For example webpackChunkName:

const Component = lazy(() => import(/* webpackChunkName: 'my_component' */ './component'))

For some magic comments it didn't trigger, for example /* webpackMode: true */, /* webpackPreload: true */, /* webpackPrefetch: true */.

It looks like it only triggers for magic comments with a string, array or regex as a value for the webpack option.

`no-assign-mutated-array` should allow mutation+assignment on arrays created with Array(), Array.of() and Array.from()

Problem

Currently a mutation + assignment of arrays created with Array(x), Array.of(x) or Array.from(x) is forbidden:

Example

// NOK
const x = Array(10).fill(0);
const y = Array.from(someArray).reverse();
const z = Array.of(x, y, z).sort();

// OK
const x = Array(10).map(e => e).fill(0);
const y = [...someArray].reverse();
const z = [x, y, z].sort();

Proposal

I propose to allow such cases as there is no error potential and it already works with slightly different syntax.

Edit: I just stumbled across another case. I think an exception could also be added for other functions that create arrays, like string.split(",").

no-unused-declaration false positive for global augmentations

You can tell TypeScript that you'd like to globally augment window via declare global:

declare global {
  interface Window {
    myGlobalVar?: string;
  }
}

This makes the following code type safe:

declare global {
  interface Window {
    myGlobalVar?: string;
  }
}

window.myGlobalVar  // type is string | undefined

The no-unused-declaration rule flags this as unused, however:

declare global {
  interface Window {
    //      ~~~~~~ "Window" is not used; unused declarations are forbidden.
    //             eslintetc/no-unused-declaration
    myGlobalVar?: string;
  }
}

no-unused-declaration false positive for namespace import

$ npm install firebase-admin
import admin from 'firebase-admin';
//     ~~~~~ "admin" is not used; unused declarations are forbidden.
//           eslintetc/no-unused-declaration

export async function getToken(
  firebaseAuth: admin.auth.Auth,
) {
  return firebaseAuth.createCustomToken('user');
}

The import is clearly used, so it should not be flagged as an unused import.

The workaround isn't too bad, just import auth instead of the whole namespace:

import {auth} from 'firebase-admin';

export async function getToken(
  firebaseAuth: auth.Auth,
) {
  return firebaseAuth.createCustomToken('user');
}

If it's relevant, I have "esModuleInterop": true set in my tsconfig.json.

allowLocal should support export statements

The allowLocal option in no-const-enum and prefer-interface should support export statements. For example:

type Person = { name: string; };
export { Person };

Also, with prefer-interface, allowLocal should deem indirectly exported types to be non-local, as the whole point of the rule is to avoid inlining.

False positive for no-unused-declaration with typescript-eslint 4.0

I encountered a false positive with the rule no-unused-declaration after updating to @typescript-eslint/parser 4.0.0
The issue happens when I type a callback function which contains arguments:

export const myFunction = (
    args: any,
    cb: (arg: string) => void,
          ~~~                       "arg" is not used; unused declarations are forbidden.
): void => cb('value');

I guess the expected behavior should be as it was with older version of typescript-eslint: no unused error for typed functions arguments

deprecation rule is incorrect

The deprecation rule flags deprecated declarations as lint failures. This is totally wrong; it should be flagging usage of the deprecated declarations as failures. 🤦‍♂

etc/no-deprecated: support detection within `exports`

would it be possible to add export detection capabilities ?

context:
Currently we are using https://github.com/gund/eslint-plugin-deprecation which is terribly slow. etc plugin provides much better performance, but in order to be able to migrate we would need to add this functionality.

Current Behaviour

// src/foo.ts

// @deprecated use world instead
export const hello = 'world'

export const world = 'this one'

// src/index.ts

// 🚨 No error
export {hello} from './foo'
// ✅ No error
export {world} from './foo'

New Behaviour

// src/foo.ts

// @deprecated use world instead
export const hello = 'world'

export const world = 'this one'

// src/index.ts

// ✅ etc/no-deprecated Errors
export {hello} from './foo'
// ✅ No error
export {world} from './foo'

throw-error: allow eventEmitter.on('error', reject)

A pattern I commonly have is rejecting a Promise when a Node event emitter emits an error event. These are usually Error, but really on the type level any. Therefor I would expect throw-error to allow .on('error', reject) as any is assignable to Error.

Example:

export const watchIntegrationAssets = gulp.series(
    copyIntegrationAssets,
    async function watchIntegrationAssets(): Promise<void> {
        await new Promise<never>((_, reject) => {
            gulp.watch(INTEGRATION_FILES, copyIntegrationAssets).on('error', reject)
        })
    }
)

no-commented-out-code misses JSX comments and one line comments in some occasions

Expected: to have the entire snippet marked as a comment block.

Actual: only the lines stated are identified as such.

       {/*{options.map((button: { label: string }, index: any) => (*/}
       {/*  <div*/}
       {/*    className={`toggle toggle__opt--${button.label}`}*/}
       {/*    key={`${button.label}-${index}`}*/}
       {/*    onClick={() =>*/}
       {/*      handleButtonsChange(button.label)*/} <------- THIS IS COMMENTED CODE 
       {/*    }*/}
       {/*  >*/}
       {/*    {button.label.toUpperCase()}*/} <------- THIS IS COMMENTED CODE 
       {/*  </div>*/}
       {/*))}*/}

This happens because all comments in JSX are multiline and we only combine single-line comments to a block. Combining multiline comments, at least inside JSX, shuld resolve it.

Do you want me to submit a PR for that?

no-unused-declaration false positives, not respecting disabling declarations

In my .eslintrc:

    'etc/no-unused-declaration': [
      'error',
      {
        imports: true,
        declarations: false,
      }
    ]

In my code:

interface Foo {
  //      ~~~ "Foo" is not used; unused declarations are forbidden.eslintetc/no-unused-declaration
  x: string;
}

export interface Bar extends Foo {
  y: string;
}

First of all, this isn't an unused declaration since Bar extends Foo.
Second, why doesn't setting declarations: false disable this error?

Even these settings produce errors, which is surprising to me:

    'etc/no-unused-declaration': [
      'error',
      {
        imports: false,
        declarations: false,
      }
    ]

Version: 0.0.1-beta.41

no-unused-declaration: doesn't work with types

There might be an issue with resolving declaration tokens via import

Config:

'etc/no-unused-declaration': ['error',{ignored:{'React': true}}]

Example:

✅No error

type Greeter = { who: string; greeting: string }

const greeter: Greeter = { who: 'John', greeting: 'Hello!' }

console.log(greeter)

🚨Error

// error: Unused declarations are forbidden (etc/no-unused-declaration)
import {Greeter} from './types';

const greeter: Greeter = { who: 'John', greeting: 'Hello!' }

console.log(greeter)

Expected behaviour:

  • non of those should be error

Upgrade eslint-etc to v5.2.1

I'm getting the following warning when installing eslint-plugin-etc with Typescript 5+.

warning "eslint-plugin-etc > [email protected]" has incorrect peer dependency "typescript@^4.0.0".
warning "eslint-plugin-etc > eslint-etc > [email protected]" has incorrect peer dependency "typescript@^4.0.0".

This plugin has a dependency on [email protected], which doesn't official support Typescript 5. The latest release of eslint-etc does officially support Typescript 5.

This issue is requesting that eslint-etc be bumped to version 5.2.1, which should solve the above warning.

False positive for no-misused-generics

Here's a generic function:

/**
 * Call two functions with the same args, e.g.
 *
 * onClick={both(action('click'), setState)}
 */
export function both<
  Args extends unknown[],
  CB1 extends (...args: Args) => void,
  CB2 extends (...args: Args) => void
>(fn1: CB1, fn2: CB2): (...args: Args) => void {
  // See https://stackoverflow.com/a/62093430/388951
  return function (...args: Args) {
    fn1(...args);
    fn2(...args);
  };
}

I think this is fine in terms of every type parameter appearing multiple times. Args only appears in other extends clauses, but it can still be inferred. Here's an example of it being used, with all the parameters inferred:

const noop = () => {};
const fnWithCallback = (cb: (a: string, b: number) => void) => _.noop;

fnWithCallback(
  both((a, b) => {
    a;  // type is string
    b;  // type is number
    return a * b;  // error
  }, _.noop),
);

Mousing over both in that example, I see:

const both: <[string, number], (a: string, b: number) => number, (...args: any[]) => void>(fn1: (a: string, b: number) => number, fn2: (...args: any[]) => void) => (a: string, b: number) => void

In other words, Args is very much inferred.

Feature request, prefer-less-than rule: Add exception for literals, be consistent with eslint native "yoda" rule

I just added this rule and getting warning for a code like this:

if (datetime.create(datetimeString).hours() >= 12) ...

According to the rule I should rewrite it as:

if (12  <= datetime.create(datetimeString).hours()) ...

However in this case I get warning from "yoda" rule:

image

I think in this case yoda approach is better.

Is it possible to add exception for literal (yodaStyleForLiterals option):

if (x > 3) ...

Also there might be separate option for ranges (outsideOrderForRanges option):

if (1 < x && x > 3)

or insideOrderForRanges

if (x > 1 && 3 < x)

ESLint 8 support

subj.

npm ERR! Could not resolve dependency:
npm ERR! peer eslint@"^5.0.0 || ^6.0.0 || ^ 7.0.0" from [email protected]
npm ERR! node_modules/eslint-plugin-etc
npm ERR!   dev eslint-plugin-etc@"^1.5.4" from the root project

--fix for prefer-interface breaks the code, generics get erased

Input:

export type MyType<
  Foo = Record<string, string>,
  Bar = Record<string, string>,
  Baz = Record<string, string>
> = {
  foo: Foo;
  bar: Bar;
  baz: Baz;
};

Expected output:

export interface MyType<
  Foo = Record<string, string>,
  Bar = Record<string, string>,
  Baz = Record<string, string>
> {
  foo: Foo;
  bar: Bar;
  baz: Baz;
}

Actual output:

export interface MyType {
  foo: Foo;
  bar: Bar;
  baz: Baz;
}

`prefer-interface`: detect intersection types

I expected an error for B:

interface A {
  x: number;
}
type B = A & {
  y: string;
};

This should be written as:

interface A {
  x: number;
}
interface B extends A {
  y: string;
}

WDYT?

Support node 12

When I updated from 0.0.2-beta.45 → 1.0.1, I got the following error:

/home/circleci/project/ts/node_modules/eslint-plugin-etc/dist/rules/no-foreach.js:35
        const types = config?.types ?? ["Array", "Map", "NodeList", "Set"];

I'm on node 12.16.3, which I assume does not support optional chains. Do you want to downlevel the distributed version a bit?

"no-misused-generics" warns about code that seems correct?

I'm not sure if there's a bug indeed or if I'm just wrong here.

Code:

interface Args<Foo> {
  foo: Foo;
  fooKey: keyof Foo;
}

interface MyFunction {
  <Foo>(args: Args<Foo>): any;
/* ^^^
   Type parameter 'Foo' is not used to enforce a constraint between types and can be replaced with 'unknown'
   etc/no-misused-generics
*/
}

const func: MyFunction = (args) => args.foo[args.fooKey];

func({ foo: { bar: 'bar' }, fooKey: 'bar' }); // No errors
func({ foo: { bar: 'bar' }, fooKey: 'qwerty' }); // Type '"qwerty"' is not assignable to type '"bar"'

Explanation:

Foo is used to enforce a constraint between foo: Foo and fooKey: keyof Foo.
You can see how the constraint allows for type-safety in the last two lines of my example.
I can only pass keys of foo in the fooKey parameter.

But no-misused-generics thinks that is redundant. When I replace <Foo>(args: Args<Foo>): any; with (args: Args<any>): any; or (args: Args<unknown>): any; my code stops working as intended.

Possible fix:

Ignore this rule when a type parameter is used as a nested type parameter in another generic type. If somewhere down the line this type is indeed not used to enforce a constraint, then we'll see this error down the line.

`etc/prefer-interface` conflicts with `@typescript-eslint/prefer-function-type`

// Interface only has a call signature, you should use a function type instead. eslint(@typescript-eslint/prefer-function-type)
interface resultFunction<TOut, TIn> { (_: TIn): TOut; }
interface ContentGetter<TRow> { (row: TRow): Content<TRow>; }
// Type can be declared using an interface. eslint (etc/prefer-interface)
type resultFunction<TOut, TIn> = (_: TIn) => TOut;
type ContentGetter<TRow> = (row: TRow) => Content<TRow>;

"etc/no-assign-mutated-array" not catching all occurrences

Not sure exact why but etc/no-assign-mutated-array is not catching all occurrences of assigning the result of an array mutation method. It found one but I did a search =\s\w+\.sort\( around the app and found many occurrences it missed. The one occurrence it actually caught was similar to the ones it missed, I don't see a difference other than the fact it didn't catch them.

example of some occurrences it didn't catch:

someLetVariable = someLetVariable.sort((b, a) => a - b)

const sortedData = dataFromProps.sort((b, a) => a - b)

const someSortedData = this.props.data.sort((b, a) => a - b)

but other "etc" rules catch issues in the same file so I am sure the file itself is being linted but this rule doesnt seem to catch things.

no-deprecated rule makes eslint to fail with an exception

When I run eslint, I get this error:

Oops! Something went wrong! :(

ESLint: 7.31.0

TypeError: tag.trim is not a function

The stack trace goes like this (personal info censored):

Occurred while linting ***
    at Identifier (***/node_modules/eslint-plugin-etc/dist/rules/no-deprecated.js:86:46)
    at ***/node_modules/eslint/lib/linter/safe-emitter.js:45:58
    at Array.forEach (<anonymous>)
    at Object.emit (***/node_modules/eslint/lib/linter/safe-emitter.js:45:38)
    at NodeEventGenerator.applySelector (***/node_modules/eslint/lib/linter/node-event-generator.js:293:26)
    at NodeEventGenerator.applySelectors (***/node_modules/eslint/lib/linter/node-event-generator.js:322:22)
    at NodeEventGenerator.enterNode (***/node_modules/eslint/lib/linter/node-event-generator.js:336:14)
    at CodePathAnalyzer.enterNode (***/node_modules/eslint/lib/linter/code-path-analysis/code-path-analyzer.js:711:23)
    at ***/node_modules/eslint/lib/linter/linter.js:960:32
    at Array.forEach (<anonymous>)

When I disable this rule, the analysis ends as expected.

deprecations rule extremely slow

Over two different projects in https://github.com/sourcegraph/sourcegraph etc/deprecation took by far the most of the time, it feels likely there might be potential for a speed-up.

cd ./shared && yarn -s run eslint

Rule                                    | Time (ms) | Relative
:---------------------------------------|----------:|--------:
etc/deprecation                         |  4957.662 |    31.7%
@typescript-eslint/no-misused-promises  |  2000.343 |    12.8%
@typescript-eslint/no-floating-promises |  1864.392 |    11.9%
import/extensions                       |   841.693 |     5.4%
react/prefer-stateless-function         |   757.892 |     4.8%
react/no-deprecated                     |   454.456 |     2.9%
react/void-dom-elements-no-children     |   365.701 |     2.3%
react/jsx-no-bind                       |   309.506 |     2.0%
import/no-self-import                   |   241.475 |     1.5%
react/no-string-refs                    |   224.624 |     1.4%

cd ./web && yarn -s run eslint

Rule                                    | Time (ms) | Relative
:---------------------------------------|----------:|--------:
etc/deprecation                         | 11145.833 |    38.0%
@typescript-eslint/no-misused-promises  |  3269.386 |    11.2%
import/extensions                       |  1803.290 |     6.2%
@typescript-eslint/no-floating-promises |  1746.615 |     6.0%
react/prefer-stateless-function         |  1361.724 |     4.6%
react/no-deprecated                     |   881.264 |     3.0%
react/jsx-no-bind                       |   757.441 |     2.6%
react/void-dom-elements-no-children     |   636.453 |     2.2%
@typescript-eslint/unbound-method       |   549.754 |     1.9%
react/no-string-refs                    |   491.509 |     1.7%

Latest update fails

On the upgrade in sourcegraph/eslint-config#92 all ESLint steps fail with this error:


+ cd web
--
  | + eval ' yarn -s run eslint --quiet'
  | ++ yarn -s run eslint --quiet
  |  
  | Oops! Something went wrong! :(
  |  
  | ESLint: 7.0.0
  |  
  | Error: Error while loading rule 'etc/throw-error': This rule requires you to use `@typescript-eslint/parser` and to specify a `project` in `parserOptions`.
  | Occurred while linting /tmp/tmp.bIj3sqdKWo/web/src/Layout.tsx
  | at Object.getParserServices (/tmp/tmp.bIj3sqdKWo/node_modules/eslint-plugin-etc/node_modules/eslint-etc/get-parser-services.js:9:15)
  | at Object.create (/tmp/tmp.bIj3sqdKWo/node_modules/eslint-plugin-etc/dist/rules/throw-error.js:18:65)
  | at createRuleListeners (/tmp/tmp.bIj3sqdKWo/node_modules/eslint/lib/linter/linter.js:758:21)
  | at /tmp/tmp.bIj3sqdKWo/node_modules/eslint/lib/linter/linter.js:928:31
  | at Array.forEach (<anonymous>)
  | at runRules (/tmp/tmp.bIj3sqdKWo/node_modules/eslint/lib/linter/linter.js:873:34)
  | at Linter._verifyWithoutProcessors (/tmp/tmp.bIj3sqdKWo/node_modules/eslint/lib/linter/linter.js:1169:31)
  | at Linter._verifyWithConfigArray (/tmp/tmp.bIj3sqdKWo/node_modules/eslint/lib/linter/linter.js:1267:21)
  | at Linter.verify (/tmp/tmp.bIj3sqdKWo/node_modules/eslint/lib/linter/linter.js:1222:25)
  | at Linter.verifyAndFix (/tmp/tmp.bIj3sqdKWo/node_modules/eslint/lib/linter/linter.js:1412:29)

It seems it cannot find our project, even though we have it configured:
https://github.com/sourcegraph/sourcegraph/blob/71c4313c3f6535b791513708841c836962d793b8/web/.eslintrc.js#L6

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.