Coder Social home page Coder Social logo

denoland / deno_lint Goto Github PK

View Code? Open in Web Editor NEW
1.5K 25.0 172.0 2.98 MB

Blazing fast linter for JavaScript and TypeScript written in Rust

Home Page: https://lint.deno.land/

License: MIT License

Rust 97.83% JavaScript 0.22% TypeScript 1.92% CSS 0.04%
deno typescript javascript linters

deno_lint's Introduction

deno_lint

Discord Chat

A Rust crate for writing fast JavaScript and TypeScript linters.

This crate powers deno lint, but is not Deno specific and can be used to write linters for Node as well.

Supports recommended set of rules from ESLint and @typescript-eslint out of the box with no config.

See the roadmap


Supported rules

Visit https://lint.deno.land for the list of available rules.

Performance

Blazing fast, see comparison with ESLint:

[
  {
    "name": "deno_lint",
    "totalMs": 105.3750100000002,
    "runsCount": 5,
    "measuredRunsAvgMs": 21.07500200000004,
    "measuredRunsMs": [
      24.79783199999997,
      19.563640000000078,
      20.759051999999883,
      19.99068000000011,
      20.26380600000016
    ]
  },
  {
    "name": "eslint",
    "totalMs": 11845.073306000002,
    "runsCount": 5,
    "measuredRunsAvgMs": 2369.0146612000003,
    "measuredRunsMs": [
      2686.1039550000005,
      2281.501061,
      2298.6185210000003,
      2279.5962849999996,
      2299.2534840000008
    ]
  }
]

Benchmarks are run during CI on Ubuntu, using the same set of rules for both linters. Test subject is oak server consisting of about 50 files. See ./benchmarks/ directory for more info.

Node.js bindings

If you want to use deno_lint with Node, please refer to @node-rs/deno-lint package which provides programmatic API as well as Webpack loader for deno_lint.

Example

examples/dlint/main.rs provides a minimal standalone binary demonstrating how deno_lint can be used as a crate.

# Build standalone binary
$ cargo build --example dlint --features="docs"

$ ./target/debug/examples/dlint --help

dlint

USAGE:
    dlint <SUBCOMMAND>

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

SUBCOMMANDS:
    help     Prints this message or the help of the given subcommand(s)
    rules
    run

$ ./target/debug/examples/dlint run ../deno/std/http/server.ts ../deno/std/http/file_server.ts
(no-empty) Empty block statement
  --> ../deno/std/http/server.ts:93:14
   |
93 |       } catch {}
   |               ^^
   |
(no-empty) Empty block statement
   --> ../deno/std/http/server.ts:111:44
    |
111 |     while ((await body.read(buf)) !== null) {}
    |                                             ^^
    |
(no-empty) Empty block statement
   --> ../deno/std/http/server.ts:120:41
    |
120 |   constructor(public listener: Listener) {}
    |                                          ^^
    |
(ban-untagged-todo) TODO should be tagged with (@username) or (#issue)
 --> ../deno/std/http/file_server.ts:5:0
  |
5 | // TODO Stream responses instead of reading them into memory.
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
(ban-untagged-todo) TODO should be tagged with (@username) or (#issue)
 --> ../deno/std/http/file_server.ts:6:0
  |
6 | // TODO Add tests like these:
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
(ban-untagged-todo) TODO should be tagged with (@username) or (#issue)
   --> ../deno/std/http/file_server.ts:137:0
    |
137 | // TODO: simplify this after deno.stat and deno.readDir are fixed
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
(no-empty) Empty block statement
   --> ../deno/std/http/file_server.ts:155:16
    |
155 |     } catch (e) {}
    |                 ^^
    |
Found 7 problems

For more concrete implementation visit deno

Developing

Make sure to have latest stable version of Rust installed (1.56.0).

// check version
$ rustc --version
rustc 1.56.0 (09c42c458 2021-10-18)

// build all targets
$ cargo build --all-targets

// test it
$ cargo test

Generating flamegraph (Linux)

Prerequisites:

$ RUSTFLAGS='-g' cargo build --release --all-targets # build target
$ sudo perf record --call-graph dwarf ./target/release/examples/dlint benchmarks/oak/**.ts # create performance profile
$ perf script | stackcollapse-perf | c++filt | flamegraph > flame.svg # generate flamegraph

You can use rust-unmangle or rustfilt instead of c++filt.

These commands can take a few minutes to run.

Contributing

Submitting a Pull Request

Before submitting, please make sure the following is done:

  1. That there is a related issue and it is referenced in the PR text.
  2. There are tests that cover the changes.
  3. Ensure cargo test passes.
  4. Format your code with deno run --allow-run tools/format.ts
  5. Make sure deno run --allow-run --allow-env tools/lint.ts passes.
  6. If you've added a new rule:
    1. Run cargo build --example dlint --all-features
    2. Update docs by running the generated binary with these arguments ./target/debug/examples/dlint rules --json > www/static/docs.json

deno_lint's People

Contributors

ah-yu avatar bartlomieju avatar cknight avatar denobot avatar disizali avatar dmitryromaniuk avatar domparfitt avatar dsherret avatar g-plane avatar hashrock avatar hirasawayuki avatar iuioiua avatar iykekings avatar kamilogorek avatar kdy1 avatar kitsonk avatar littledivy avatar littletof avatar lucacasonato avatar magurotuna avatar marvinhagemeister avatar nathanwhit avatar nayeemrmn avatar not-my-profile avatar petamoriken avatar qkniep avatar ross-weir avatar ry avatar skanehira avatar togami2864 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

deno_lint's Issues

CI cache not working properly

It seems there is problem with caching in CI - pipeline take about 3-4 minutes and installation of crates can be seen during clippy stage.

no-empty rule broken

(no-empty) Empty block statement
  --> ../deno/std/encoding/toml.ts:13:56
   |
13 |   constructor(public type: string, public name: string) {}
   |                                                         ^^
   |

Publish to crates.io and release binaries

CI pipeline should be updated to publish deno_lint to crates.io on tag (similar to Deno).

Built binary artifacts should be uploaded to Github and be downloadable from releases page

no-empty-character-class rule broken

(no-empty-character-class) empty character class in RegExp is not allowed
   --> ../deno/std/encoding/toml.ts:333:27
    |
333 |               line.replace(/\[/g, "").replace(/\]/g, ""))
    |                            ^^^^^
    |
(no-empty-character-class) empty character class in RegExp is not allowed
   --> ../deno/std/encoding/toml.ts:333:46
    |
333 |               line.replace(/\[/g, "").replace(/\]/g, ""))
    |                                               ^^^^^
    |

discussion: plugin support

It would be great if deno_lint had support for plugins that can add rules. Here are my thoughts:

Constraints:

  • Plugins should be written in Rust and use the same infrastructure as the rules that are part of this repository.
  • Plugins should be loaded at runtime, and should not have to be available at compile time. This means they need to be dynamically executed.
  • Plugins should not have to re-parse the source code.
  • Plugins should be sandboxed and safe - they should not be able to access the system directly.

Possible solution:
Most likely the best solution that satisfies these constraints is WebAssembly. You can write your rules in Rust, and then compile them to WebAssembly that deno_lint could load and run. WebAssembly is also safe because it is sandboxed and only gets access to resources that deno_lint exposes to it. Here is how I imagine this would work:

  1. deno_lint should initialise all plugins at startup, by creating a WebAssembly VM for each plugin.
  2. deno_lint parses the source code into a swc AST. This ASTs raw memory is stored in a shared memory area with the WebAssembly plugin.
  3. deno_lint calls a function in WebAssembly to kick off code checking in the plugin. It can now do its analysis and create diagnostics. These diagnostics are stored in a separate section of shared memory between the plugin and deno_lint.
  4. deno_lint extracts these diagnostics and augments them with information about plugin origin and returns them to the user.

This approach would allow you to start multiple instances of the same plugin easily, to paralelnize code analysis for large modules.
There are definitely some issues with this. The biggest one I can see right now is how to pass the AST between plugin and deno_lint. If we only support rust, we might be able to pass the raw backing of the object, but I don't know if this is feasible and how likely it is to break (probably very). Another solution would be to serialise to JSON or protobuf and then deserialise on the plugin side. This is definitely possible, but requires a lot of careful translation of the swc structs into JSON. This might be very complicated and time intensive, and would probably not be great for performance. The third solution would be to have the plugin do the parsing of code itself - this would also slow down the process significantly though.

Prior art:
dprint also makes use of WebAssembly for its plugin system. To the end user looks like this: https://dprint.dev/plugin-dev/ (very clean)

[deno lint] Uncaught Promise Warning

Would it be possible to somehow warn a user when a Promise is not properly handled? Deno will always crash on uncaught errors. I guarantee there will be many users that will have an issue because their project continues to crash because they forgot to catch a promise. Somewhere in the hundreds, thousands, or millions of lines of code for some project someone is gonna forget a promise and have it crash in production.

Any sort of warning system during compile time to say hey u have an unhandled promise, this could crash your project. Please catch it.

Add deno-lint-ignore-file directive

Deno has // deno-fmt-ignore-file directive to skip formatting of a file. Linter should have analogous directive: // deno-lint-ignore-file

empty-block too strict

Doesn't allow this valid code:

(no-empty) Empty block statement
  constructor(readonly r: BufReader) {}
                                     ~~
    at /Users/rld/src/deno/cli/../std/textproto/mod.ts:23:37

Show code snippets in report output

Current report is very minimalistic:

$ target/debug/dlint test.ts
error: `any` type is not allowed (noExplicitAny) at ./test.ts:6:14
error: `var` keyword is not allowed (noVar) at ./test.ts:12:0
error: Variables shouldn't be deleted (noDeleteVar) at ./test.ts:14:0

It should be changed to contain a snippet of code that shows where the problem occurs (much like ESLint):

$ target/debug/dlint test.ts
error: `any` type is not allowed (noExplicitAny) at ./test.ts:6:14
1| function foo(a: any) {
 |                  ^
2|   //
3| }

@disizali had a PR with colorful output at #26, but I'd rather not introduce new crates for the sake of formatting right now.

Instead we should copy-paste color helpers from deno and construct diagnostic messages manually.

I expect that Linter struct will be extended to be able to extract code snippets from from SWC'c source map.

MVP rule ignores

The only way to ignore a lint rule should be inline comment:

// dlint-ignore explicitFunctionReturnType
function fooBar() {
  ...
}

// dlint-ignore noExplicitAny explicitFunctionReturnType
function fizzBuzz(a: any) {
  ...
}

Buggy rules

False positives found while linting deno_std (denoland/deno#6240)

  • no-misued-new
(no-misused-new) Class cannot have method named `new`.
  static create(writer: Writer, size: number = DEFAULT_BUF_SIZE): BufWriter {
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    at ./std/io/bufio.ts:451:2

(no-misused-new) Class cannot have method named `new`.
  static create(
  ~~~~~~~~~~~~~~
    at ./std/io/bufio.ts:540:2
  • no-case-declarations
(no-case-declarations) Unexpected declaration in case
      case "string":
      ~~~~~~~~~~~~~~
    at ./std/fmt/printf.ts:631:6

(no-case-declarations) Unexpected declaration in case
        case OpCode.Close:
        ~~~~~~~~~~~~~~~~~~
    at ./std/ws/mod.ts:265:8
  • no-empty
(no-empty) Empty block statement
        process.on("uncaughtException", (_err: Error) => {});
                                                         ~~
    at ./std/node/process_test.ts:76:57

(no-empty) Empty block statement
    const testFunction = (): void => {};
                                     ~~
    at ./std/node/events_test.ts:134:37
  • no-empty-function
(no-empty-function) Empty functions are not allowed
function callbackify<Arg1T, Arg2T, Arg3T, ResultT>(
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    at ./std/node/_util/_util_callbackify.ts:53:0

(no-empty-function) Empty functions are not allowed
function callbackify<Arg1T, Arg2T, Arg3T, Arg4T, ResultT>(
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    at ./std/node/_util/_util_callbackify.ts:56:0

(no-empty-function) Empty functions are not allowed
function callbackify<Arg1T, Arg2T, Arg3T, Arg4T, Arg5T, ResultT>(
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    at ./std/node/_util/_util_callbackify.ts:70:0

Ignore directives should be linted

deno-lint-ignore <code...> allows to ignore diagnostics matching codes originating on the next line.

Each ignore directive should be checked that it was used, otherwise a new diagnostic should be produced to avoid bit rot.

Add descriptions and examples to each lint rule

All lint rules should be updated to contains some kind of method that returns description as well as some examples of valid and invalid. This feature will be used to generate documentation.

  • adjacent-overload-signatures
  • ban-ts-comment
  • ban-types
  • ban-untagged-ignore
  • ban-untagged-todo
  • constructor-super
  • default-param-last
  • eqeqeq
  • explicit-function-return-type
  • explicit-module-boundary-types
  • for-direction
  • getter-return
  • no-array-constructor
  • no-async-promise-executor
  • no-await-in-loop
  • no-case-declarations
  • no-class-assign
  • no-compare-neg-zero
  • no-cond-assign
  • no-const-assign
  • no-constant-condition
  • no-control-regex
  • no-debugger
  • no-delete-var
  • no-dupe-args
  • no-dupe-class-members
  • no-dupe-else-if
  • no-dupe-keys
  • no-duplicate-case
  • no-empty
  • no-empty-character-class
  • no-empty-interface
  • no-empty-pattern
  • no-eval
  • no-ex-assign
  • no-explicit-any
  • no-extra-boolean-cast
  • no-extra-non-null-assertion
  • no-extra-semi
  • no-fallthrough
  • no-func-assign
  • no-global-assign
  • no-import-assign
  • no-inferrable-types
  • no-inner-declarations
  • no-invalid-regexp
  • no-irregular-whitespace
  • no-misused-new
  • no-mixed-spaces-and-tabs
  • no-namespace
  • no-new-symbol
  • no-non-null-asserted-optional-chain
  • no-non-null-assertion
  • no-obj-calls
  • no-octal
  • no-prototype-builtins
  • no-redeclare
  • no-regex-spaces
  • no-self-assign
  • no-setter-return
  • no-shadow-restricted-names
  • no-sparse-arrays
  • no-this-alias
  • no-this-before-super
  • no-throw-literal
  • no-undef
  • no-unreachable
  • no-unsafe-finally
  • no-unsafe-negation
  • no-unused-labels
  • no-unused-vars
  • no-var
  • no-with
  • prefer-as-const
  • prefer-const
  • prefer-namespace-keyword
  • require-yield
  • single-var-declarator
  • triple-slash-reference
  • use-isnan
  • valid-typeof

Out-of-bounds columns when file contains tabs

So built on master a few hours ago and while it works fine on a bunch of modules in cli/js and std/* I found a case that ends with a crash.

$ RUST_BACKTRACE=1 ../deno/target/debug/deno lint --unstable mod.ts
(explicit-function-return-type) Missing return type on function
function errno(err : Error) {
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    at mod.ts:231:0

(no-explicit-any) `any` type is not allowed
	fds : any[];
	         ~~~
    at mod.ts:292:10

(no-explicit-any) `any` type is not allowed
	exports: { [key: string]: any };
	                             ~~~
    at mod.ts:293:30

(explicit-function-return-type) Missing return type on function
				view.setUint32(argv_buf_size_out, args.reduce(function(acc, arg) {
				                                                          ~~~~~~~~~~~~~~~~~~~~
    at mod.ts:359:62

(explicit-function-return-type) Missing return type on function
				view.setUint32(environ_buf_size_out, entries.reduce(function(acc, [key, value]) {
				                                                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    at mod.ts:390:68

thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', cli/fmt_errors.rs:86:8
stack backtrace:
   0: backtrace::backtrace::libunwind::trace
             at /Users/runner/.cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.44/src/backtrace/libunwind.rs:86
   1: backtrace::backtrace::trace_unsynchronized
             at /Users/runner/.cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.44/src/backtrace/mod.rs:66
   2: std::sys_common::backtrace::_print_fmt
             at src/libstd/sys_common/backtrace.rs:78
   3: <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt
             at src/libstd/sys_common/backtrace.rs:59
   4: core::fmt::write
             at src/libcore/fmt/mod.rs:1063
   5: std::io::Write::write_fmt
             at src/libstd/io/mod.rs:1426
   6: std::sys_common::backtrace::_print
             at src/libstd/sys_common/backtrace.rs:62
   7: std::sys_common::backtrace::print
             at src/libstd/sys_common/backtrace.rs:49
   8: std::panicking::default_hook::{{closure}}
             at src/libstd/panicking.rs:204
   9: std::panicking::default_hook
             at src/libstd/panicking.rs:224
  10: std::panicking::rust_panic_with_hook
             at src/libstd/panicking.rs:470
  11: rust_begin_unwind
             at src/libstd/panicking.rs:378
  12: std::panicking::begin_panic
  13: std::panicking::begin_panic
  14: core::str::traits::<impl core::slice::SliceIndex<str> for core::ops::range::RangeFrom<usize>>::index
  15: deno::fmt_errors::format_maybe_source_line
             at cli/fmt_errors.rs:86
  16: deno::fmt_errors::format_stack
             at cli/fmt_errors.rs:33
  17: deno::lint_command::{{closure}}
             at cli/main.rs:359
  18: <std::future::GenFuture<T> as core::future::future::Future>::poll
             at /rustc/8d69840ab92ea7f4d323420088dd8c9775f180cd/src/libstd/future.rs:44
  19: <rusty_v8::string::WriteOptions as core::ops::bit::BitOr>::bitor
  20: tokio::runtime::basic_scheduler::BasicScheduler<P>::block_on::{{closure}}::{{closure}}
             at /Users/caspervonb/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/runtime/basic_scheduler.rs:131
  21: tokio::coop::with_budget::{{closure}}
             at /Users/caspervonb/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/coop.rs:127
  22: std::thread::local::LocalKey<T>::try_with
             at /rustc/8d69840ab92ea7f4d323420088dd8c9775f180cd/src/libstd/thread/local.rs:262
  23: std::thread::local::LocalKey<T>::with
             at /rustc/8d69840ab92ea7f4d323420088dd8c9775f180cd/src/libstd/thread/local.rs:239
  24: tokio::coop::with_budget
             at /Users/caspervonb/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/coop.rs:120
  25: tokio::coop::budget
             at /Users/caspervonb/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/coop.rs:96
  26: tokio::runtime::basic_scheduler::BasicScheduler<P>::block_on::{{closure}}
             at /Users/caspervonb/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/runtime/basic_scheduler.rs:131
  27: tokio::runtime::basic_scheduler::enter::{{closure}}
             at /Users/caspervonb/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/runtime/basic_scheduler.rs:213
  28: tokio::macros::scoped_tls::ScopedKey<T>::set
             at /Users/caspervonb/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/macros/scoped_tls.rs:63
  29: tokio::runtime::basic_scheduler::enter
             at /Users/caspervonb/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/runtime/basic_scheduler.rs:213
  30: tokio::runtime::basic_scheduler::BasicScheduler<P>::block_on
             at /Users/caspervonb/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/runtime/basic_scheduler.rs:123
  31: tokio::runtime::Runtime::block_on::{{closure}}
             at /Users/caspervonb/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/runtime/mod.rs:444
  32: tokio::runtime::context::enter
             at /Users/caspervonb/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/runtime/context.rs:72
  33: tokio::runtime::handle::Handle::enter
             at /Users/caspervonb/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/runtime/handle.rs:76
  34: tokio::runtime::Runtime::block_on
             at /Users/caspervonb/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.2.21/src/runtime/mod.rs:441
  35: deno::tokio_util::run_basic
             at cli/tokio_util.rs:18
  36: deno::main
             at cli/main.rs:765
  37: std::rt::lang_start::{{closure}}
             at /rustc/8d69840ab92ea7f4d323420088dd8c9775f180cd/src/libstd/rt.rs:67
  38: std::rt::lang_start_internal::{{closure}}
             at src/libstd/rt.rs:52
  39: std::panicking::try::do_call
             at src/libstd/panicking.rs:303
  40: __rust_maybe_catch_panic
             at src/libpanic_unwind/lib.rs:86
  41: std::panicking::try
             at src/libstd/panicking.rs:281
  42: std::panic::catch_unwind
             at src/libstd/panic.rs:394
  43: std::rt::lang_start_internal
             at src/libstd/rt.rs:51
  44: std::rt::lang_start
             at /rustc/8d69840ab92ea7f4d323420088dd8c9775f180cd/src/libstd/rt.rs:67
  45: deno::main
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

See mod.ts

Missing documentation on why not eslint

Would it be possible to document why eslint is not used for linting typescript used in deno ?

I would understand the desire to write a linter in rust if eslint is deemed too slow.

I would also understand that the deno community does not wish to rely on a binary like eslint that only works with node dist/eslint.js ( That being I said i see eslint as a third_party module ( https://github.com/denoland/deno_third_party/tree/4a3ade332261afb8fcb8b364e59d3cca7c975d36/node_modules/eslint ) )

eslint and typescript-eslint ( https://github.com/typescript-eslint/typescript-eslint ) have written a lot of linting rules that can be used to provide linting benefits.

It is possible to "bundle up" eslint into a single standalone CLI linter with no configuration file, for example ( https://github.com/Raynos/tsdocstandard ).

Lint rule code - camelCase or kebab-case

Currently rule codes are written using camelCase:

  • noExplicitAny
  • noEval
  • getterReturn

In ESLint rule codes are written using kebab-case:

  • no-explicit-any
  • no-eval
  • getter-return

Ignore directives are currently spelled out using kebab-case (deno-lint-ignore which follows convention from Deno for formatting ignores - deno-fmt-ignore).

We should make a decision one way or another and unify all codes.

Probably the best to go with kebab-case as it's already established convention in ESLint

Add Deno scripts for formatting and linting Rust code

tools/format.ts should run:

rustfmt --check examples/dlint/main.rs
rustfmt --check src/lib.rs
deno fmt --check benchmarks/benchmarks.ts

tools/lint.ts should run:

cargo clippy --all-targets --release --locked -- -D clippy::all
target/release/examples/dlint benchmarks/benchmarks.ts

CI script should be updated to run those scripts

Add CLI parser

It will take a few more months before linter is good enough to be shipped with deno.

In the meantime we could still use it as a standalone binary for testing purposes. That means we need a CLI parser - we should use clap (because it's already used in Deno).

For starters CLI should only two situations:

  • printing help - dlint -h, dlint --help, dlint help
  • running linter on provided list of files - dlint file.ts, dlint file2.ts

getter-return does not consider flow analysis

Using the following example:

export class Test {
  #internal: string | undefined;

  constructor(testStr?: string) {
    this.#internal = testStr;
  }

  get str(): string {
    if (this.#internal !== undefined) {
      return this.#internal;
    } else {
      return "empty";
    }
  }
}

I get the following output:

(getter-return) Getter requires a return
  get str(): string {
                    ^
    at test.ts:8:20

Found 1 problems

As a side-note, this rule is only useful in JS, as the TypeScript compiler errors with the following if a getter does not return:

A 'get' accessor must return a value. ts(2378)

Is it worth keeping the rule in recommended?

typescript-eslint recommended rules

Tracking issues for recommended rules of typescript-eslint

https://github.com/typescript-eslint/typescript-eslint/blob/v3.1.0/packages/eslint-plugin/src/configs/recommended.ts

  • adjacent-overload-signatures (easy)
  • ban-ts-comment
  • ban-types
  • explicit-module-boundary-types (easy)
  • no-array-constructor
  • no-empty-function (needs to be fixed, false positives for constructors)
  • no-empty-interface
  • no-explicit-any
  • no-extra-non-null-assertion
  • no-extra-semi (easy)
  • no-inferrable-types (easy)
  • no-misused-new
  • no-namespace
  • no-non-null-asserted-optional-chain
  • no-non-null-assertion
  • no-this-alias
  • no-unused-vars (hard, needs scope analysis)
  • no-var-requires
  • prefer-as-const
  • prefer-namespace-keyword
  • triple-slash-reference

Thanks for making this :-)

Not really an issue, just a ๐Ÿ‘ on the push to a self-contained TypeScript toolchain :) I spend a significant amount of time babysitting TypeScript build for vscode extension for rust-analyzer, and I am looking forward to a (distant) future, where I need only deno, and don't have a 100k lockfile just to make sure I use ; consistently.

Add benchmarks

We should have some minimal benchmarks comparing performance with ESlint.

ESLint benchmarks should use same set of rules that are available in deno_lint, with configuration producing same results as deno_lint.

It will require to setup a project with decent number of files.

cargo install deno fails to compile deno_lint v0.1.10 under rustc 1.41.0

Hi & thank you for Deno! I hope I'm not wasting your time with this but here goes.

A few weeks ago, I successfully installed deno with cargo on Fedora 32. Today I was able to use the install-update crate to update deno to v1.1.0 under rustc/cargo 1.44.0. So all good there.

Separately, on another machine, a new Ubuntu Server 20.04 LTS install that thinks rustc 1.41.0 and cargo 1.41.0 are current, I tried cargo install deno and it failed to cope with some of the new hotness of deno_lint:

   Compiling deno_lint v0.1.10
error[E0658]: use of unstable library feature 'matches_macro'
  --> /home/nacho/.cargo/registry/src/github.com-1ecc6299db9ec823/deno_lint-0.1.10/src/rules/eqeqeq.rs:37:23
   |
37 |     Expr::Lit(lit) => matches!(lit, Lit::Null(_)),
   |                       ^^^^^^^
   |
   = note: for more information, see https://github.com/rust-lang/rust/issues/65721

error: aborting due to previous error

For more information about this error, try `rustc --explain E0658`.
error: could not compile `deno_lint`.
warning: build failed, waiting for other jobs to finish...
error: failed to compile `deno v1.1.0`, intermediate artifacts can be found at `/tmp/cargo-install4kXDXS`

Maybe there's nothing for you to do here and I should just wait for Ubuntu to offer Rust 1.44.0.

Or I could re-install Rust and Cargo outside of apt and get quicker updates by doing them myself.

Or I could use one of your other recommended install methods for Deno.

All of these are fine with me, but I thought I would report this anyway in case it's more significant than I think, and affects a lot of people.

Also, I noticed cargo install deno is an install option in the manual at https://deno.land/manual/getting_started/installation but it is not a listed option in the Install instructions linked from the project's README.md at https://github.com/denoland/deno_install

Let me know if I just missed that it's deprecated and I'll use another install method.

ESLint recommended rules

Tracking issue for rules from eslint:recommended set.

  • constructor-super
  • for-direction
  • getter-return
  • no-async-promise-executor
  • no-case-declarations
  • no-class-assign (broken, needs scope analysis)
  • no-compare-neg-zero
  • no-cond-assign
  • no-const-assign (broken, needs scope analysis)
  • no-constant-condition
  • no-control-regex (easy)
  • no-debugger
  • no-delete-var
  • no-dupe-args
  • no-dupe-class-members
  • no-dupe-else-if
  • no-dupe-keys
  • no-duplicate-case
  • no-empty
  • no-empty-character-class
  • no-empty-pattern
  • no-ex-assign (broken, needs scope analysis)
  • no-extra-boolean-cast
  • no-extra-semi
  • no-fallthrough (hard, needs code path analysis)
  • no-func-assign (broken, needs scope analysis)
  • no-global-assign (hard, needs scope analysis)
  • no-import-assign (hard, needs scope analysis)
  • no-inner-declarations
  • no-invalid-regexp(easy hard, need to port additional lib)
  • no-irregular-whitespace (easy)
  • no-misleading-character-class (easy)
  • no-mixed-spaces-and-tabs
  • no-new-symbol
  • no-obj-calls
  • no-octal
  • no-prototype-builtins
  • no-redeclare (hard, needs scope analysis)
  • no-regex-spaces
  • no-self-assign
  • no-setter-return
  • no-shadow-restriced-names
  • no-sparse-arrays
  • no-this-before-super
  • no-undef (hard, needs scope analysis)
  • no-unexpected-multiline (easy)
  • no-unreachable (hard, needs code path analysis)
  • no-unsafe-finally
  • no-unsafe-negation
  • no-unused-labels
  • no-unused-vars (hard, needs scope analysis)
  • no-useless-escape (easy) (#48 (comment))
  • no-with
  • require-yield
  • use-isnan
  • valid-typeof

fallthrough in switch/case statements

Not essential but I think it would be cool and catch bugs.

I propose that the deno fmt command would do the following to each case block in a switch statement:

  • Add a // fallthrough comment to the end of the case if there is no unconditional break statement.
  • Remove any // fallthrough comment otherwise.

Examples

switch foo {
  case "bar":
    console.log("bar");
    break;
    // fallthrough
  case "baz":
    console.log("baz");
  case "coconut":
    if (jellybean) {
      break;
    }
}

would be formatted to:

switch foo {
  case "bar":
    console.log("bar");
    break;
  case "baz":
    console.log("baz");
    // fallthrough
  case "coconut":
    if (jellybean) {
      break;
    }
    // fallthrough
}

Why?

  • Makes it clear if the case falls through or not - pretty self-explanatory
  • Catches many bugs where you forget to include the break keyword. This is because it will add a // fallthrough comment, and you should notice this. If falling through was not intended you can then ensure you add in that break statement.

Roadmap

Opening this issue for tracking purposes as well as to better communicate what can be expected of deno_lint in the near future.

Here are the issues that current development focuses on (in the order it must be implemented):

  • Scopes analysis #160 - requirement for any "more advanced" rule
  • ESLint recommended rule set #48
  • typescript-eslint recommended rule set #86

Next:

Future:

  • documentation website
  • use deno_lint from Node project #169
  • autofix

Refactor testing setup

Writing test cases for lint rules is a cumbersome at the moment.

We should refactor testing setup to be able to write tests easily, here are some things that would be useful:

  • create testing fn for lint rule:
    • assert_lint_ok<T>(lint_rule: T, source_code)
    • assert_lint_err<T>(lint_rule: T, source_code) -> Vec<Diagnostic>
  • compare diagnostics partially - ie. assert that starts on certain column but ignore filename/line

write tests for existing rules

  • remove test.ts
  • write tests for each existing rule

Example test:

#[cfg(test)]
mod tests {
use super::*;
use crate::test_util::test_lint;
use serde_json::json;
#[test]
fn no_var_test() {
test_lint(
"no_var",
r#"
var someVar = "someString";
const c = "c";
let a = "a";
"#,
vec![NoVar::new()],
json!([{
"code": "noVar",
"message": "`var` keyword is not allowed",
"location": {
"filename": "no_var",
"line": 2,
"col": 2,
}
}]),
)
}
}

no-octal rule broken

(no-octal) `Octal number` is not allowed
   --> ../deno/std/encoding/toml_test.ts:331:13
    |
331 |       flt3: -0.01,
    |              ^^^^
    |

CC @disizali

False negatives in no-explicit-any

(ban-unused-ignore) Ignore for code "no-explicit-any" was not used.
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    at ./std/node/module.ts:104:4
  static _extensions: {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    [key: string]: (module: Module, filename: string) => any;
  } = Object.create(null);
(ban-unused-ignore) Ignore for code "no-explicit-any" was not used.
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    at ./std/node/module.ts:1048:2
type RequireWrapper = (
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  exports: any,
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  require: any,
  module: Module,
  __filename: string,
  __dirname: string
) => void;

Interoperate with Existing ESLint Rules and Configs

@axetroy If you say there are too many options, why build out a deno lint at all? That's only adding more options. We already have a linting tool for linting JavaScript and TypeScript and it is called eslint. I don't see a value in duplicating all that work again.

Unless a developer only writes backend code, they will still need to touch eslint.

If a parallel lint tool must be built (are there really not better ways to improve deno?) then it MUST be able to consume existing eslint rules. Otherwise people will waste their time reinventing the wheel, writing duplicate rules for deno and frontend code rather than application code.

Instead deno lint would be more useful as something like golangci-lint where it aggregates and calls other already written lint rules and linters. It provides a "default lint config" (via url import) with sane default but still extendable.

Originally posted by @brandonkal in denoland/deno#1880 (comment)

Emphasis added.

Integration with Deno CLI

Tracking issue for all things that need to be worked out before linter can be integrated with Deno CLI

  • CLI integration (clap)
  • source file loading
  • error handling
  • dependency duplication (termcolor)
  • publish crate with lib.rs to crates.io (#83)

Support for React

I love the promise of this project (linting, but fast).

We are using eslint not just for js/ts but also React specific rules (e.g. hooks, etc.).

Is this project eventually planned to support this?

Disable all non `recommended` rules for now

I think while we have no config options we should disable all non recommended rules because they can give false positives very often (eg eqeqeq, https://discordapp.com/channels/684898665143206084/684911491035430919/720016434314608710)

โœ”๏ธ are all rules which are part of eslint:recommended or typescript-eslint:recommended
๐Ÿฆ• are all rules not present in eslint or typescript-eslint

  • ban-ts-comment โœ”๏ธ
  • ban-ts-ignore ๐Ÿฆ•
  • ban-untagged-ignore ๐Ÿฆ•
  • ban-untagged-todo ๐Ÿฆ•
  • constructor-super โœ”๏ธ
  • default-param-last
  • eqeqeq
  • explicit-function-return-type
  • for-direction โœ”๏ธ
  • getter-return โœ”๏ธ
  • no-array-constructor
  • no-async-promise-executor โœ”๏ธ
  • no-case-declarations โœ”๏ธ
  • no-class-assign โœ”๏ธ
  • no-compare-neg-zero โœ”๏ธ
  • no-cond-assign โœ”๏ธ
  • no-debugger โœ”๏ธ
  • no-delete-var โœ”๏ธ
  • no-dupe-args โœ”๏ธ
  • no-dupe-keys โœ”๏ธ
  • no-duplicate-case โœ”๏ธ
  • no-empty โœ”๏ธ
  • no-empty-character-class โœ”๏ธ
  • no-empty-function โœ”๏ธ
  • no-empty-interface โœ”๏ธ
  • no-empty-pattern โœ”๏ธ
  • no-eval
  • no-ex-assign โœ”๏ธ
  • no-explicit-any โœ”๏ธ
  • no-func-assign โœ”๏ธ
  • no-misused-new โœ”๏ธ
  • no-namespace โœ”๏ธ
  • no-new-symbol โœ”๏ธ
  • no-obj-call โœ”๏ธ
  • no-octal โœ”๏ธ
  • no-prototype-builtins โœ”๏ธ
  • no-setter-return โœ”๏ธ
  • no-sparse-array โœ”๏ธ
  • no-this-alias โœ”๏ธ
  • no-throw-literal
  • no-unsafe-finally โœ”๏ธ
  • no-unsafe-negation โœ”๏ธ
  • no-var
  • no-with โœ”๏ธ
  • prefer-namespace-keyword โœ”๏ธ
  • require-yield โœ”๏ธ
  • single-var-declarator ๐Ÿฆ•
  • triple-slash-reference โœ”๏ธ
  • use-isnan โœ”๏ธ
  • valid-typeof โœ”๏ธ

Feature Request: Webpack Loader for Deno Lint

I think it would be great if we have a webpack loader for deno lint, and would be configurable to break the build whenever it encounters a lint error

example webpack configuration:

{
                    test: /\.(ts|js)$/,
                    enforce: "pre",
                    loader: "deno-lint-loader",
                    options: {
                        emitErrors: true,
                        failOnHint: true
                    }
                }

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.