diff --git a/cli/args/flags.rs b/cli/args/flags.rs index cdeaa1b335682c..7f7c6615f4aff8 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -302,6 +302,7 @@ pub struct LintFlags { pub json: bool, pub compact: bool, pub watch: Option, + pub internal_print_all_rules: bool, } impl LintFlags { @@ -2901,6 +2902,12 @@ To ignore linting on an entire file, you can add an ignore comment at the top of .action(ArgAction::Append) .value_hint(ValueHint::AnyPath), ) + .arg( + Arg::new("internal-print-all-rules") + .long("internal-print-all-rules") + .action(ArgAction::SetTrue) + .hide(true) + ) .arg(watch_arg(false)) .arg(watch_exclude_arg()) .arg(no_clear_screen_arg()) @@ -5110,6 +5117,7 @@ fn lint_parse( let json = matches.get_flag("json"); let compact = matches.get_flag("compact"); + let internal_print_all_rules = matches.get_flag("internal-print-all-rules"); flags.subcommand = DenoSubcommand::Lint(LintFlags { files: FileFlags { @@ -5124,6 +5132,7 @@ fn lint_parse( json, compact, watch: watch_arg_parse(matches)?, + internal_print_all_rules, }); Ok(()) } @@ -7133,6 +7142,7 @@ mod tests { json: false, compact: false, watch: Default::default(), + internal_print_all_rules: false, }), ..Flags::default() } @@ -7161,6 +7171,7 @@ mod tests { json: false, compact: false, watch: Some(Default::default()), + internal_print_all_rules: false, }), ..Flags::default() } @@ -7194,6 +7205,7 @@ mod tests { no_clear_screen: true, exclude: vec![], }), + internal_print_all_rules: false, }), ..Flags::default() } @@ -7221,6 +7233,7 @@ mod tests { json: false, compact: false, watch: Default::default(), + internal_print_all_rules: false, }), ..Flags::default() } @@ -7243,6 +7256,7 @@ mod tests { json: false, compact: false, watch: Default::default(), + internal_print_all_rules: false, }), ..Flags::default() } @@ -7270,6 +7284,7 @@ mod tests { json: false, compact: false, watch: Default::default(), + internal_print_all_rules: false, }), ..Flags::default() } @@ -7298,6 +7313,7 @@ mod tests { json: false, compact: false, watch: Default::default(), + internal_print_all_rules: false, }), ..Flags::default() } @@ -7320,6 +7336,7 @@ mod tests { json: true, compact: false, watch: Default::default(), + internal_print_all_rules: false, }), ..Flags::default() } @@ -7349,6 +7366,7 @@ mod tests { json: true, compact: false, watch: Default::default(), + internal_print_all_rules: false, }), config_flag: ConfigFlag::Path("Deno.jsonc".to_string()), ..Flags::default() @@ -7379,11 +7397,33 @@ mod tests { json: false, compact: true, watch: Default::default(), + internal_print_all_rules: false, }), config_flag: ConfigFlag::Path("Deno.jsonc".to_string()), ..Flags::default() } ); + + let r = + flags_from_vec(svec!["deno", "lint", "--internal-print-all-rules",]); + assert_eq!( + r.unwrap(), + Flags { + subcommand: DenoSubcommand::Lint(LintFlags { + files: FileFlags::default(), + fix: false, + rules: false, + maybe_rules_tags: None, + maybe_rules_include: None, + maybe_rules_exclude: None, + json: false, + compact: false, + watch: Default::default(), + internal_print_all_rules: true, + }), + ..Flags::default() + } + ); } #[test] diff --git a/cli/main.rs b/cli/main.rs index d47f1e363c4ba4..aad67f6c668ae3 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -176,7 +176,10 @@ async fn run_subcommand(flags: Arc) -> Result { lsp::start().await }), DenoSubcommand::Lint(lint_flags) => spawn_subcommand(async { - if lint_flags.rules { + if lint_flags.internal_print_all_rules { + tools::lint::internal_print_all_rules(); + Ok(()) + } else if lint_flags.rules { tools::lint::print_rules_list( lint_flags.json, lint_flags.maybe_rules_tags, diff --git a/cli/schemas/config-file.v1.json b/cli/schemas/config-file.v1.json index a64cb2ff655d42..b7c2914b9d1437 100644 --- a/cli/schemas/config-file.v1.json +++ b/cli/schemas/config-file.v1.json @@ -291,7 +291,7 @@ "type": "array", "description": "List of tag names that will be run. Empty list disables all tags and will only use rules from `include`.", "items": { - "$ref": "https://raw.githubusercontent.com/denoland/deno_lint/main/schemas/tags.v1.json" + "$ref": "https://deno.land/x/deno/cli/schemas/lint-tags.v1.json" }, "minItems": 0, "uniqueItems": true @@ -300,7 +300,7 @@ "type": "array", "description": "List of rule names that will be excluded from configured tag sets. If the same rule is in `include` it will be run.", "items": { - "$ref": "https://raw.githubusercontent.com/denoland/deno_lint/main/schemas/rules.v1.json" + "$ref": "https://deno.land/x/deno/cli/schemas/lint-rules.v1.json" }, "minItems": 0, "uniqueItems": true @@ -309,7 +309,7 @@ "type": "array", "description": "List of rule names that will be run. Even if the same rule is in `exclude` it will be run.", "items": { - "$ref": "https://raw.githubusercontent.com/denoland/deno_lint/main/schemas/rules.v1.json" + "$ref": "https://deno.land/x/deno/cli/schemas/lint-rules.v1.json" }, "minItems": 0, "uniqueItems": true diff --git a/cli/schemas/lint-rules.v1.json b/cli/schemas/lint-rules.v1.json new file mode 100644 index 00000000000000..87d24ed6adebca --- /dev/null +++ b/cli/schemas/lint-rules.v1.json @@ -0,0 +1,113 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "enum": [ + "adjacent-overload-signatures", + "ban-ts-comment", + "ban-types", + "ban-unknown-rule-code", + "ban-untagged-ignore", + "ban-untagged-todo", + "ban-unused-ignore", + + "camelcase", + "constructor-super", + "default-param-last", + "eqeqeq", + "explicit-function-return-type", + "explicit-module-boundary-types", + "for-direction", + "fresh-handler-export", + "fresh-server-event-handlers", + "getter-return", + "guard-for-in", + "no-array-constructor", + "no-async-promise-executor", + "no-await-in-loop", + "no-await-in-sync-fn", + "no-boolean-literal-for-arguments", + "no-case-declarations", + "no-class-assign", + "no-compare-neg-zero", + "no-cond-assign", + "no-console", + "no-const-assign", + "no-constant-condition", + "no-control-regex", + "no-debugger", + "no-delete-var", + "no-deprecated-deno-api", + "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-enum", + "no-empty-interface", + "no-empty-pattern", + "no-eval", + "no-ex-assign", + "no-explicit-any", + "no-external-import", + "no-extra-boolean-cast", + "no-extra-non-null-assertion", + "no-fallthrough", + "no-func-assign", + "no-global-assign", + "no-implicit-declare-namespace-export", + "no-import-assertions", + "no-import-assign", + "no-inferrable-types", + "no-inner-declarations", + "no-invalid-regexp", + "no-invalid-triple-slash-reference", + "no-irregular-whitespace", + "no-misused-new", + "no-namespace", + "no-new-symbol", + "no-node-globals", + "no-non-null-asserted-optional-chain", + "no-non-null-assertion", + "no-obj-calls", + "no-octal", + "no-process-globals", + "no-prototype-builtins", + "no-redeclare", + "no-regex-spaces", + "no-self-assign", + "no-self-compare", + "no-setter-return", + "no-shadow-restricted-names", + "no-sloppy-imports", + "no-slow-types", + "no-sparse-arrays", + "no-sync-fn-in-async-fn", + "no-this-alias", + "no-this-before-super", + "no-throw-literal", + "no-top-level-await", + "no-undef", + "no-unreachable", + "no-unsafe-finally", + "no-unsafe-negation", + "no-unused-labels", + "no-unused-vars", + "no-var", + "no-window", + "no-window-prefix", + "no-with", + "prefer-as-const", + "prefer-ascii", + "prefer-const", + "prefer-namespace-keyword", + "prefer-primordials", + "require-await", + "require-yield", + "single-var-declarator", + "triple-slash-reference", + "use-isnan", + "valid-typeof", + "verbatim-module-syntax" + ] +} diff --git a/cli/schemas/lint-tags.v1.json b/cli/schemas/lint-tags.v1.json new file mode 100644 index 00000000000000..4b4f0e48db400a --- /dev/null +++ b/cli/schemas/lint-tags.v1.json @@ -0,0 +1,4 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "enum": ["fresh", "jsr", "jsx", "react", "recommended"] +} diff --git a/cli/tools/lint/docs/adjacent_overload_signatures.md b/cli/tools/lint/docs/adjacent_overload_signatures.md new file mode 100644 index 00000000000000..8435f53a468bd3 --- /dev/null +++ b/cli/tools/lint/docs/adjacent_overload_signatures.md @@ -0,0 +1,80 @@ +Requires overload signatures to be adjacent to each other. + +Overloaded signatures which are not next to each other can lead to code which is +hard to read and maintain. + +### Invalid: + +(`bar` is declared in-between `foo` overloads) + +```typescript +type FooType = { + foo(s: string): void; + foo(n: number): void; + bar(): void; + foo(sn: string | number): void; +}; +``` + +```typescript +interface FooInterface { + foo(s: string): void; + foo(n: number): void; + bar(): void; + foo(sn: string | number): void; +} +``` + +```typescript +class FooClass { + foo(s: string): void; + foo(n: number): void; + bar(): void {} + foo(sn: string | number): void {} +} +``` + +```typescript +export function foo(s: string): void; +export function foo(n: number): void; +export function bar(): void {} +export function foo(sn: string | number): void {} +``` + +### Valid: + +(`bar` is declared after `foo`) + +```typescript +type FooType = { + foo(s: string): void; + foo(n: number): void; + foo(sn: string | number): void; + bar(): void; +}; +``` + +```typescript +interface FooInterface { + foo(s: string): void; + foo(n: number): void; + foo(sn: string | number): void; + bar(): void; +} +``` + +```typescript +class FooClass { + foo(s: string): void; + foo(n: number): void; + foo(sn: string | number): void {} + bar(): void {} +} +``` + +```typescript +export function foo(s: string): void; +export function foo(n: number): void; +export function foo(sn: string | number): void {} +export function bar(): void {} +``` diff --git a/cli/tools/lint/docs/ban_ts_comment.md b/cli/tools/lint/docs/ban_ts_comment.md new file mode 100644 index 00000000000000..fbf59cc2ee516b --- /dev/null +++ b/cli/tools/lint/docs/ban_ts_comment.md @@ -0,0 +1,39 @@ +Disallows the use of Typescript directives without a comment. + +Typescript directives reduce the effectiveness of the compiler, something which +should only be done in exceptional circumstances. The reason why should be +documented in a comment alongside the directive. + +### Invalid: + +```typescript +// @ts-expect-error +let a: number = "I am a string"; +``` + +```typescript +// @ts-ignore +let a: number = "I am a string"; +``` + +```typescript +// @ts-nocheck +let a: number = "I am a string"; +``` + +### Valid: + +```typescript +// @ts-expect-error: Temporary workaround (see ticket #422) +let a: number = "I am a string"; +``` + +```typescript +// @ts-ignore: Temporary workaround (see ticket #422) +let a: number = "I am a string"; +``` + +```typescript +// @ts-nocheck: Temporary workaround (see ticket #422) +let a: number = "I am a string"; +``` diff --git a/cli/tools/lint/docs/ban_types.md b/cli/tools/lint/docs/ban_types.md new file mode 100644 index 00000000000000..e7dca4922ea7ba --- /dev/null +++ b/cli/tools/lint/docs/ban_types.md @@ -0,0 +1,42 @@ +Bans the use of primitive wrapper objects (e.g. `String` the object is a wrapper +of `string` the primitive) in addition to the non-explicit `Function` type and +the misunderstood `Object` type. + +There are very few situations where primitive wrapper objects are desired and +far more often a mistake was made with the case of the primitive type. You also +cannot assign a primitive wrapper object to a primitive leading to type issues +down the line. For reference, [the TypeScript handbook] also says we shouldn't +ever use these wrapper objects. + +[the TypeScript handbook]: https://www.typescriptlang.org/docs/handbook/declaration-files/do-s-and-don-ts.html#number-string-boolean-symbol-and-object + +With `Function`, it is better to explicitly define the entire function signature +rather than use the non-specific `Function` type which won't give you type +safety with the function. + +Finally, `Object` and `{}` means "any non-nullish value" rather than "any object +type". `object` is a good choice for a meaning of "any object type". + +### Invalid: + +```typescript +let a: Boolean; +let b: String; +let c: Number; +let d: Symbol; +let e: Function; +let f: Object; +let g: {}; +``` + +### Valid: + +```typescript +let a: boolean; +let b: string; +let c: number; +let d: symbol; +let e: () => number; +let f: object; +let g: Record; +``` diff --git a/cli/tools/lint/docs/ban_unknown_rule_code.md b/cli/tools/lint/docs/ban_unknown_rule_code.md new file mode 100644 index 00000000000000..6d33ad43250d2e --- /dev/null +++ b/cli/tools/lint/docs/ban_unknown_rule_code.md @@ -0,0 +1,35 @@ +Warns the usage of unknown rule codes in ignore directives + +We sometimes have to suppress and ignore lint errors for some reasons. We can do +so using [ignore directives](https://lint.deno.land/ignoring-rules) with rule +names that should be ignored like so: + +```typescript +// deno-lint-ignore no-explicit-any no-unused-vars +const foo: any = 42; +``` + +This rule checks for the validity of the specified rule names (i.e. whether +`deno_lint` provides the rule or not). + +### Invalid: + +```typescript +// typo +// deno-lint-ignore eq-eq-e +console.assert(x == 42); + +// unknown rule name +// deno-lint-ignore UNKNOWN_RULE_NAME +const b = "b"; +``` + +### Valid: + +```typescript +// deno-lint-ignore eq-eq-eq +console.assert(x == 42); + +// deno-lint-ignore no-unused-vars +const b = "b"; +``` diff --git a/cli/tools/lint/docs/ban_untagged_ignore.md b/cli/tools/lint/docs/ban_untagged_ignore.md new file mode 100644 index 00000000000000..48af34a5b06f11 --- /dev/null +++ b/cli/tools/lint/docs/ban_untagged_ignore.md @@ -0,0 +1,18 @@ +Requires `deno-lint-ignore` to be annotated with one or more rule names. + +Ignoring all rules can mask unexpected or future problems. Therefore you need to +explicitly specify which rule(s) are to be ignored. + +### Invalid: + +```typescript +// deno-lint-ignore +export function duplicateArgumentsFn(a, b, a) {} +``` + +### Valid: + +```typescript +// deno-lint-ignore no-dupe-args +export function duplicateArgumentsFn(a, b, a) {} +``` diff --git a/cli/tools/lint/docs/ban_untagged_todo.md b/cli/tools/lint/docs/ban_untagged_todo.md new file mode 100644 index 00000000000000..a48b6c1cd82ca9 --- /dev/null +++ b/cli/tools/lint/docs/ban_untagged_todo.md @@ -0,0 +1,44 @@ +Requires TODOs to be annotated with either a user tag (`@user`) or an issue +reference (`#issue`). + +TODOs without reference to a user or an issue become stale with no easy way to +get more information. + +### Invalid: + +```typescript +// TODO Improve calc engine +export function calcValue(): number {} +``` + +```typescript +// TODO Improve calc engine (@djones) +export function calcValue(): number {} +``` + +```typescript +// TODO Improve calc engine (#332) +export function calcValue(): number {} +``` + +### Valid: + +```typescript +// TODO(djones) Improve calc engine +export function calcValue(): number {} +``` + +```typescript +// TODO(@djones) Improve calc engine +export function calcValue(): number {} +``` + +```typescript +// TODO(#332) +export function calcValue(): number {} +``` + +```typescript +// TODO(#332) Improve calc engine +export function calcValue(): number {} +``` diff --git a/cli/tools/lint/docs/ban_unused_ignore.md b/cli/tools/lint/docs/ban_unused_ignore.md new file mode 100644 index 00000000000000..5978ee28bf8afa --- /dev/null +++ b/cli/tools/lint/docs/ban_unused_ignore.md @@ -0,0 +1,25 @@ +Warns unused ignore directives + +We sometimes have to suppress and ignore lint errors for some reasons and we can +do so using [ignore directives](https://lint.deno.land/ignoring-rules). + +In some cases, however, like after refactoring, we may end up having ignore +directives that are no longer necessary. Such superfluous ignore directives are +likely to confuse future code readers, and to make matters worse, might hide +future lint errors unintentionally. To prevent such situations, this rule +detects unused, superfluous ignore directives. + +### Invalid: + +```typescript +// Actually this line is valid since `export` means "used", +// so this directive is superfluous +// deno-lint-ignore no-unused-vars +export const foo = 42; +``` + +### Valid: + +```typescript +export const foo = 42; +``` diff --git a/cli/tools/lint/docs/button_has_type.md b/cli/tools/lint/docs/button_has_type.md new file mode 100644 index 00000000000000..5607e13f662a09 --- /dev/null +++ b/cli/tools/lint/docs/button_has_type.md @@ -0,0 +1,21 @@ +Checks that a `