From cd600fade40dc93074ae56f5c6008abe8b821308 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=AD=E5=85=89=E5=BF=97?= <411020382@qq.com> Date: Fri, 26 Apr 2024 22:54:05 +0800 Subject: [PATCH] feat(linter): add more "ban-ts-comment" test cases. (#3107) https://github.com/oxc-project/oxc/issues/2934 | name | passed | failed | | -------------- | ------ | ------ | | ts-expect-error| 14 | 20 | | ts-ignore | 15 | 20 | | ts-nocheck | 11 | 9 | | ts-check | 11 | 8 | | total | 51 | 57 --- .../src/rules/typescript/ban_ts_comment.rs | 539 ++++++++++++++---- .../src/snapshots/ban_ts_comment.snap | 239 +++++--- 2 files changed, 588 insertions(+), 190 deletions(-) diff --git a/crates/oxc_linter/src/rules/typescript/ban_ts_comment.rs b/crates/oxc_linter/src/rules/typescript/ban_ts_comment.rs index 2aaf157d4..dab6862bd 100644 --- a/crates/oxc_linter/src/rules/typescript/ban_ts_comment.rs +++ b/crates/oxc_linter/src/rules/typescript/ban_ts_comment.rs @@ -1,3 +1,4 @@ +use oxc_ast::CommentKind; use oxc_diagnostics::{ miette::{self, Diagnostic}, thiserror::{self, Error}, @@ -6,7 +7,7 @@ use oxc_macros::declare_oxc_lint; use oxc_span::Span; use regex::Regex; -use crate::{context::LintContext, rule::Rule}; +use crate::{context::LintContext, fixer::Fix, rule::Rule}; #[derive(Debug, Error, Diagnostic)] pub enum BanTsCommentDiagnostic { @@ -16,6 +17,12 @@ pub enum BanTsCommentDiagnostic { #[diagnostic(severity(warning))] Comment(String, #[label] Span), + #[error( + "typescript-eslint(ban-ts-comment): Use \"@ts-expect-error\" instead of @ts-ignore, as \"@ts-ignore\" will do nothing if the following line is error-free." + )] + #[diagnostic(severity(warning), help("Replace \"@ts-ignore\" with \"@ts-expect-error\"."))] + IgnoreInsteadOfExpectError(String, #[label] Span), + #[error("typescript-eslint(ban-ts-comment): Include a description after the @ts-{0} directive to explain why the @ts-{0} is necessary. The description must be {1} characters or longer.")] #[diagnostic(severity(warning))] CommentRequiresDescription(String, u64, #[label] Span), @@ -141,22 +148,48 @@ impl Rule for BanTsComment { let comments = ctx.semantic().trivias().comments(); for (kind, span) in comments { let raw = span.source_text(ctx.semantic().source_text()); - if let Some(captures) = find_ts_comment_directive(raw, kind.is_single_line()) { // safe to unwrap, if capture success, it can always capture one of the four directives - let (directive, description) = (captures.0, captures.1.trim()); + let (directive, description) = (captures.0, captures.1); + if CommentKind::MultiLine == kind + && (directive == "check" || directive == "nocheck") + { + continue; + } + + if raw.trim_start().starts_with('/') + && (directive == "check" || directive == "nocheck") + { + continue; + } match self.option(directive) { DirectiveConfig::Boolean(on) => { if *on { - ctx.diagnostic(BanTsCommentDiagnostic::Comment( - directive.to_string(), - span, - )); + if directive == "ignore" { + ctx.diagnostic_with_fix( + BanTsCommentDiagnostic::IgnoreInsteadOfExpectError( + directive.to_string(), + span, + ), + || { + Fix::new( + raw.replace("@ts-ignore", "@ts-expect-error"), + span, + ) + }, + ); + } else { + ctx.diagnostic(BanTsCommentDiagnostic::Comment( + directive.to_string(), + span, + )); + } } } config => { - if (description.len() as u64) < self.minimum_description_length { + let description_len = description.trim().len(); + if (description_len as u64) < self.minimum_description_length { ctx.diagnostic(BanTsCommentDiagnostic::CommentRequiresDescription( directive.to_string(), self.minimum_description_length, @@ -245,30 +278,73 @@ pub fn find_ts_comment_directive(raw: &str, single_line: bool) -> Option<(&str, #[test] fn test() { use crate::tester::Tester; - + // A total of 51 test cases passed successfully. let pass = vec![ + // ts-expect-error ("// just a comment containing @ts-expect-error somewhere", None), (r" - /* + /* @ts-expect-error running with long description in a block - */ - ", None), + */ + ", None), + (r" + /* @ts-expect-error not on the last line + */ + ", None), + (r" + /** + * @ts-expect-error not on the last line + */ + ", None), + (r" + /* not on the last line + * @ts-expect-error + */ + ", None), + (r" + /* @ts-expect-error + * not on the last line */ + ", None), ("// @ts-expect-error", Some(serde_json::json!([{ "ts-expect-error": false }]))), ("// @ts-expect-error here is why the error is expected", Some(serde_json::json!([{"ts-expect-error": "allow-with-description"},]))), + (r" + /* + * @ts-expect-error here is why the error is expected */ + ", Some(serde_json::json!([{"ts-expect-error": "allow-with-description"},]))), ("// @ts-expect-error exactly 21 characters", Some(serde_json::json!([ { "ts-expect-error": "allow-with-description", "minimumDescriptionLength": 21, }, ]))), + (r" + /* + * @ts-expect-error exactly 21 characters*/ + ", Some(serde_json::json!([{ + "ts-expect-error": "allow-with-description", + "minimumDescriptionLength": 21, + }]))), ("// @ts-expect-error: TS1234 because xyz", Some(serde_json::json!([ - { - "ts-expect-error": { - "descriptionFormat": "^: TS\\d+ because .+$", - }, - "minimumDescriptionLength" : 10, - }, - ]))), + { + "ts-expect-error": { + "descriptionFormat": "^: TS\\d+ because .+$", + }, + "minimumDescriptionLength" : 10, + }, + ]))), + (r" + /* + * @ts-expect-error: TS1234 because xyz */ + ", Some(serde_json::json!([ + { + "ts-expect-error": { + "descriptionFormat": "^: TS\\d+ because .+$", + }, + "minimumDescriptionLength" : 10, + }, + ]))), + ("// @ts-expect-error ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ", Some(serde_json::json!([{ "ts-expect-error": "allow-with-description" }]))), + // ts-ignore ("// just a comment containing @ts-ignore somewhere", None), ("// @ts-ignore", Some(serde_json::json!([{ "ts-ignore": false}]))), ("// @ts-ignore I think that I am exempted from any need to follow the rules!", Some(serde_json::json!([{ "ts-ignore": "allow-with-description" }]))), @@ -276,72 +352,250 @@ fn test() { /* @ts-ignore running with long description in a block */ - ", Some(serde_json::json!([ - { - "ts-ignore": "allow-with-description", - "minimumDescriptionLength": 21, - }, - ]))), - ("// @ts-ignore: TS1234 because xyz", Some(serde_json::json!([ - { - "ts-ignore": { - "descriptionFormat": "^: TS\\d+ because .+$", - }, - "minimumDescriptionLength": 10, - }, - ]))), - ("// just a comment containing @ts-nocheck somewhere", None), - ("// @ts-nocheck", Some(serde_json::json!([{ "ts-nocheck": false}]))), - ("// @ts-nocheck no doubt, people will put nonsense here from time to time just to get the rule to stop reporting, perhaps even long messages with other nonsense in them like other // @ts-nocheck or // @ts-ignore things", Some(serde_json::json!([{ "ts-nocheck": "allow-with-description" }]))), - (r" - /* - @ts-nocheck running with long description in a block - */", + ", Some(serde_json::json!([ + { + "ts-ignore": "allow-with-description", + "minimumDescriptionLength": 21, + }, + ]))), + (r" + /* + @ts-ignore + */ + ", None), + (r" + /* @ts-ignore not on the last line + */ + ", None), + (r" + /** + * @ts-ignore not on the last line + */ + ", None), + (r" + /* not on the last line + * @ts-expect-error + */ + ", None), + (r" + /* @ts-ignore + * not on the last line */ + ", None), + ("// @ts-ignore: TS1234 because xyz", Some(serde_json::json!([ + { + "ts-ignore": { + "descriptionFormat": "^: TS\\d+ because .+$", + }, + "minimumDescriptionLength": 10, + }, + ]))), + ("// @ts-ignore ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ", Some(serde_json::json!([ + { + "ts-ignore": "allow-with-description" + }, + ]))), + (r" + /* + * @ts-ignore here is why the error is expected */ + ", Some(serde_json::json!([ + { + "ts-ignore": "allow-with-description" + }, + ]))), + ("// @ts-ignore exactly 21 characters", Some(serde_json::json!([ + { + "ts-ignore": "allow-with-description", + "minimumDescriptionLength": 21, + }, + ]))), + (r" + /* + * @ts-ignore exactly 21 characters*/ + ", Some(serde_json::json!([ + { + "ts-ignore": "allow-with-description", + "minimumDescriptionLength": 21, + }, + ]))), + (r" + /* + * @ts-ignore: TS1234 because xyz */ + ", Some(serde_json::json!([ + { + "ts-ignore": { + "descriptionFormat": "^: TS\\d+ because .+$", + }, + "minimumDescriptionLength": 10, + }, + ]))), + // ts-nocheck + ("// just a comment containing @ts-nocheck somewhere", None), + ("// @ts-nocheck", Some(serde_json::json!([{ "ts-nocheck": false}]))), + ("// @ts-nocheck no doubt, people will put nonsense here from time to time just to get the rule to stop reporting, perhaps even long messages with other nonsense in them like other // @ts-nocheck or // @ts-ignore things", Some(serde_json::json!([{ "ts-nocheck": "allow-with-description" }]))), + (r" + /* + @ts-nocheck running with long description in a block + */", Some(serde_json::json!([ - { - "ts-nocheck": "allow-with-description", - "minimumDescriptionLength": 21, - }, - ]))), - ("// @ts-nocheck: TS1234 because xyz", Some(serde_json::json!([ - { - "ts-nocheck": { - "descriptionFormat": "^: TS\\d+ because .+$", - }, - "minimumDescriptionLength": 10, - }, - ]))), - ("// just a comment containing @ts-check somewhere", None), - (r" - /* - @ts-check running with long description in a block - */ - ", None), - ("// @ts-check", Some(serde_json::json!([{ "ts-check": false}]))), - ("// @ts-check with a description and also with a no-op // @ts-ignore", Some(serde_json::json!([ - {"ts-check": "allow-with-description", "minimumDescriptionLength": 3 }, - ]))), - ("// @ts-check: TS1234 because xyz", Some(serde_json::json!([ - { - "ts-check": { - "descriptionFormat": "^: TS\\d+ because .+$", - }, - "minimumDescriptionLength": 10, - }, - ]))), + { + "ts-nocheck": "allow-with-description", + "minimumDescriptionLength": 21, + }, + ]))), + ("// @ts-nocheck: TS1234 because xyz", Some(serde_json::json!([ + { + "ts-nocheck": { + "descriptionFormat": "^: TS\\d+ because .+$", + }, + "minimumDescriptionLength": 10, + }, + ]))), + ("// @ts-nocheck ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ", Some(serde_json::json!([ + { + "ts-nocheck": "allow-with-description", + }, + ]))), + ("//// @ts-nocheck - pragma comments may contain 2 or 3 leading slashes", None), + (r" + /** + @ts-nocheck + */ + ", None), + (r" + /* + @ts-nocheck + */ + ", None), + ("/** @ts-nocheck */", None), + ("/* @ts-nocheck */", None), + // ts-check + ("// just a comment containing @ts-check somewhere", None), + (r" + /* + @ts-check running with long description in a block + */ + ", None), + ("// @ts-check", Some(serde_json::json!([{ "ts-check": false}]))), + ("// @ts-check with a description and also with a no-op // @ts-ignore", Some(serde_json::json!([ + {"ts-check": "allow-with-description", "minimumDescriptionLength": 3 }, + ]))), + ("// @ts-check: TS1234 because xyz", Some(serde_json::json!([ + { + "ts-check": { + "descriptionFormat": "^: TS\\d+ because .+$", + }, + "minimumDescriptionLength": 10, + }, + ]))), + ("// @ts-check ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ", Some(serde_json::json!([ + { + "ts-check": "allow-with-description", + }, + ]))), + ("//// @ts-check - pragma comments may contain 2 or 3 leading slashes", Some(serde_json::json!([ + { + "ts-check": true, + }, + ]))), + (r" + /** + @ts-check + */ + ", Some(serde_json::json!([ + { + "ts-check": true, + }, + ]))), + (r" + /* + @ts-check + */ + ", Some(serde_json::json!([ + { + "ts-check": true, + }, + ]))), + ("/** @ts-check */", Some(serde_json::json!([ + { + "ts-check": true, + }, + ]))), + ("/* @ts-check */", Some(serde_json::json!([ + { + "ts-check": true, + }, + ]))), ]; + // A total of 57 test cases failed. let fail = vec![ + // ts-expect-error ("// @ts-expect-error", Some(serde_json::json!([{ "ts-expect-error": true }]))), ("/* @ts-expect-error */", Some(serde_json::json!([{ "ts-expect-error": true}]))), ( r" /* - @ts-expect-error -*/ - ", + @ts-expect-error */ + ", Some(serde_json::json!([{ "ts-expect-error": true}])), ), + ( + r" +/** on the last line + @ts-expect-error */ + ", + Some(serde_json::json!([{ "ts-expect-error": true}])), + ), + ( + r" +/** on the last line + * @ts-expect-error */ + ", + Some(serde_json::json!([{ "ts-expect-error": true}])), + ), + ( + r" +/** + * @ts-expect-error: TODO */ + ", + Some( + serde_json::json!([{ "ts-expect-error": "allow-with-description", "minimumDescriptionLength": 10}]), + ), + ), + ( + r" +/** + * @ts-expect-error: TS1234 because xyz */ + ", + Some(serde_json::json!([{ + "ts-expect-error": { + "descriptionFormat": "^: TS\\d+ because .+$", + }, + "minimumDescriptionLength": 25 + }])), + ), + ( + r" +/** + * @ts-expect-error: TS1234 */ + ", + Some(serde_json::json!([{ + "ts-expect-error": { + "descriptionFormat": "^: TS\\d+ because .+$", + }, + }])), + ), + ( + r" +/** + * @ts-expect-error : TS1234 */ + ", + Some(serde_json::json!([{ + "ts-expect-error": { + "descriptionFormat": "^: TS\\d+ because .+$", + }, + }])), + ), ("/** @ts-expect-error */", Some(serde_json::json!([{ "ts-expect-error": true}]))), ( "// @ts-expect-error: Suppress next line", @@ -354,8 +608,8 @@ fn test() { ( r" if (false) { - // @ts-expect-error: Unreachable code error - console.log('hello'); + // @ts-expect-error: Unreachable code error + console.log('hello'); } ", Some(serde_json::json!([{ "ts-expect-error": true}])), @@ -398,30 +652,91 @@ if (false) { }, ])), ), - ("// @ts-ignore", Some(serde_json::json!([{ "ts-ignore": true}]))), + ( + "// @ts-expect-error : TS1234 because xyz", + Some(serde_json::json!([ + { + "ts-expect-error": { + "descriptionFormat": "^: TS\\d+ because .+$", + }, + }, + ])), + ), + // ts-ignore + ( + "// @ts-ignore", + Some(serde_json::json!([{ "ts-ignore": true, "ts-expect-error": true }])), + ), + ( + "// @ts-ignore", + Some( + serde_json::json!([{ "ts-ignore": true, "ts-expect-error": "allow-with-description" }]), + ), + ), ("// @ts-ignore", None), ("/* @ts-ignore */", Some(serde_json::json!([{ "ts-ignore": true}]))), ( r" /* - @ts-ignore -*/ + @ts-ignore */ ", Some(serde_json::json!([{ "ts-ignore": true}])), ), - ("/** @ts-ignore */", Some(serde_json::json!([{ "ts-ignore": true}]))), + ( + r" +/** on the last line + @ts-ignore */ + ", + Some(serde_json::json!([{ "ts-ignore": true}])), + ), + ( + r" +/** on the last line + * @ts-ignore */ + ", + Some(serde_json::json!([{ "ts-ignore": true}])), + ), + ( + "/** @ts-ignore */", + Some(serde_json::json!([{ "ts-ignore": true, "ts-expect-error": false }])), + ), + ( + r" +/** + * @ts-ignore: TODO */ + ", + Some( + serde_json::json!([{ "ts-expect-error": "allow-with-description", "minimumDescriptionLength": 10 }]), + ), + ), + ( + r" +/** + * @ts-ignore: TS1234 because xyz */ + ", + Some(serde_json::json!([{ + "ts-expect-error": { + "descriptionFormat": "^: TS\\d+ because .+$", + }, + "minimumDescriptionLength": 25 + }])), + ), ("// @ts-ignore: Suppress next line", None), ("/////@ts-ignore: Suppress next line", None), ( r" if (false) { - // @ts-ignore: Unreachable code error - console.log('hello'); + // @ts-ignore: Unreachable code error + console.log('hello'); } ", None, ), ("// @ts-ignore", Some(serde_json::json!([{ "ts-ignore": "allow-with-description" }]))), + ( + "// @ts-ignore ", + Some(serde_json::json!([{ "ts-ignore": "allow-with-description" }])), + ), ( "// @ts-ignore .", Some(serde_json::json!([{ "ts-ignore": "allow-with-description" }])), @@ -447,25 +762,25 @@ if (false) { }, ])), ), + ( + "// @ts-ignore : TS1234 because xyz", + Some(serde_json::json!([ + { + "ts-ignore": { + "descriptionFormat": "^: TS\\d+ because .+$", + }, + }, + ])), + ), + // ts-nocheck ("// @ts-nocheck", Some(serde_json::json!([{ "ts-nocheck": true}]))), ("// @ts-nocheck", None), - ("/* @ts-nocheck */", Some(serde_json::json!([{ "ts-nocheck": true}]))), - ( - r" -/* - @ts-nocheck -*/ - ", - Some(serde_json::json!([{ "ts-nocheck": true}])), - ), - ("/** @ts-nocheck */", Some(serde_json::json!([{ "ts-nocheck": true}]))), ("// @ts-nocheck: Suppress next line", None), - ("/////@ts-nocheck: Suppress next line", None), ( r" if (false) { - // @ts-nocheck: Unreachable code error - console.log('hello'); + // @ts-nocheck: Unreachable code error + console.log('hello'); } ", None, @@ -492,24 +807,24 @@ if (false) { }, ])), ), - ("// @ts-check", Some(serde_json::json!([{ "ts-check":true}]))), - ("/* @ts-check */", Some(serde_json::json!([{ "ts-check":true}]))), ( - r" -/* - @ts-check -*/ - ", - Some(serde_json::json!([{ "ts-check":true}])), + "// @ts-nocheck : TS1234 because xyz", + Some(serde_json::json!([ + { + "ts-nocheck": { + "descriptionFormat": "^: TS\\d+ because .+$", + }, + }, + ])), ), - ("/** @ts-check */", Some(serde_json::json!([{ "ts-check":true}]))), + // ts-check + ("// @ts-check", Some(serde_json::json!([{ "ts-check": true}]))), ("// @ts-check: Suppress next line", Some(serde_json::json!([{ "ts-check":true}]))), - ("/////@ts-check: Suppress next line", Some(serde_json::json!([{ "ts-check":true}]))), ( r" if (false) { - // @ts-check: Unreachable code error - console.log('hello'); + // @ts-check: Unreachable code error + console.log('hello'); } ", Some(serde_json::json!([{ "ts-check":true}])), @@ -536,6 +851,16 @@ if (false) { }, ])), ), + ( + "// @ts-check : TS1234 because xyz", + Some(serde_json::json!([ + { + "ts-check": { + "descriptionFormat": "^: TS\\d+ because .+$", + }, + }, + ])), + ), ]; Tester::new(BanTsComment::NAME, pass, fail).test_and_snapshot(); diff --git a/crates/oxc_linter/src/snapshots/ban_ts_comment.snap b/crates/oxc_linter/src/snapshots/ban_ts_comment.snap index 54ab968c9..6da43928c 100644 --- a/crates/oxc_linter/src/snapshots/ban_ts_comment.snap +++ b/crates/oxc_linter/src/snapshots/ban_ts_comment.snap @@ -18,9 +18,56 @@ expression: ban_ts_comment โ•ญโ”€[ban_ts_comment.tsx:2:3] 1 โ”‚ 2 โ”‚ โ•ญโ”€โ–ถ /* - 3 โ”‚ โ•ฐโ”€โ–ถ @ts-expect-error - 4 โ”‚ */ - 5 โ”‚ + 3 โ”‚ โ•ฐโ”€โ–ถ @ts-expect-error */ + 4 โ”‚ + โ•ฐโ”€โ”€โ”€โ”€ + + โš  typescript-eslint(ban-ts-comment): Do not use @ts-expect-error because it alters compilation errors. + โ•ญโ”€[ban_ts_comment.tsx:2:3] + 1 โ”‚ + 2 โ”‚ โ•ญโ”€โ–ถ /** on the last line + 3 โ”‚ โ•ฐโ”€โ–ถ @ts-expect-error */ + 4 โ”‚ + โ•ฐโ”€โ”€โ”€โ”€ + + โš  typescript-eslint(ban-ts-comment): Do not use @ts-expect-error because it alters compilation errors. + โ•ญโ”€[ban_ts_comment.tsx:2:3] + 1 โ”‚ + 2 โ”‚ โ•ญโ”€โ–ถ /** on the last line + 3 โ”‚ โ•ฐโ”€โ–ถ * @ts-expect-error */ + 4 โ”‚ + โ•ฐโ”€โ”€โ”€โ”€ + + โš  typescript-eslint(ban-ts-comment): Include a description after the @ts-expect-error directive to explain why the @ts-expect-error is necessary. The description must be 10 characters or longer. + โ•ญโ”€[ban_ts_comment.tsx:2:3] + 1 โ”‚ + 2 โ”‚ โ•ญโ”€โ–ถ /** + 3 โ”‚ โ•ฐโ”€โ–ถ * @ts-expect-error: TODO */ + 4 โ”‚ + โ•ฐโ”€โ”€โ”€โ”€ + + โš  typescript-eslint(ban-ts-comment): Include a description after the @ts-expect-error directive to explain why the @ts-expect-error is necessary. The description must be 25 characters or longer. + โ•ญโ”€[ban_ts_comment.tsx:2:3] + 1 โ”‚ + 2 โ”‚ โ•ญโ”€โ–ถ /** + 3 โ”‚ โ•ฐโ”€โ–ถ * @ts-expect-error: TS1234 because xyz */ + 4 โ”‚ + โ•ฐโ”€โ”€โ”€โ”€ + + โš  typescript-eslint(ban-ts-comment): The description for the @ts-expect-error directive must match the ^: TS\d+ because .+$ format. + โ•ญโ”€[ban_ts_comment.tsx:2:3] + 1 โ”‚ + 2 โ”‚ โ•ญโ”€โ–ถ /** + 3 โ”‚ โ•ฐโ”€โ–ถ * @ts-expect-error: TS1234 */ + 4 โ”‚ + โ•ฐโ”€โ”€โ”€โ”€ + + โš  typescript-eslint(ban-ts-comment): The description for the @ts-expect-error directive must match the ^: TS\d+ because .+$ format. + โ•ญโ”€[ban_ts_comment.tsx:2:3] + 1 โ”‚ + 2 โ”‚ โ•ญโ”€โ–ถ /** + 3 โ”‚ โ•ฐโ”€โ–ถ * @ts-expect-error : TS1234 */ + 4 โ”‚ โ•ฐโ”€โ”€โ”€โ”€ โš  typescript-eslint(ban-ts-comment): Do not use @ts-expect-error because it alters compilation errors. @@ -42,11 +89,11 @@ expression: ban_ts_comment โ•ฐโ”€โ”€โ”€โ”€ โš  typescript-eslint(ban-ts-comment): Do not use @ts-expect-error because it alters compilation errors. - โ•ญโ”€[ban_ts_comment.tsx:3:5] + โ•ญโ”€[ban_ts_comment.tsx:3:7] 2 โ”‚ if (false) { - 3 โ”‚ // @ts-expect-error: Unreachable code error - ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - 4 โ”‚ console.log('hello'); + 3 โ”‚ // @ts-expect-error: Unreachable code error + ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + 4 โ”‚ console.log('hello'); โ•ฐโ”€โ”€โ”€โ”€ โš  typescript-eslint(ban-ts-comment): Include a description after the @ts-expect-error directive to explain why the @ts-expect-error is necessary. The description must be 3 characters or longer. @@ -73,58 +120,114 @@ expression: ban_ts_comment ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ•ฐโ”€โ”€โ”€โ”€ - โš  typescript-eslint(ban-ts-comment): Do not use @ts-ignore because it alters compilation errors. + โš  typescript-eslint(ban-ts-comment): The description for the @ts-expect-error directive must match the ^: TS\d+ because .+$ format. + โ•ญโ”€[ban_ts_comment.tsx:1:3] + 1 โ”‚ // @ts-expect-error : TS1234 because xyz + ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + โ•ฐโ”€โ”€โ”€โ”€ + + โš  typescript-eslint(ban-ts-comment): Use "@ts-expect-error" instead of @ts-ignore, as "@ts-ignore" will do nothing if the following line is error-free. โ•ญโ”€[ban_ts_comment.tsx:1:3] 1 โ”‚ // @ts-ignore ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ•ฐโ”€โ”€โ”€โ”€ + help: Replace "@ts-ignore" with "@ts-expect-error". - โš  typescript-eslint(ban-ts-comment): Do not use @ts-ignore because it alters compilation errors. + โš  typescript-eslint(ban-ts-comment): Use "@ts-expect-error" instead of @ts-ignore, as "@ts-ignore" will do nothing if the following line is error-free. โ•ญโ”€[ban_ts_comment.tsx:1:3] 1 โ”‚ // @ts-ignore ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ•ฐโ”€โ”€โ”€โ”€ + help: Replace "@ts-ignore" with "@ts-expect-error". - โš  typescript-eslint(ban-ts-comment): Do not use @ts-ignore because it alters compilation errors. + โš  typescript-eslint(ban-ts-comment): Use "@ts-expect-error" instead of @ts-ignore, as "@ts-ignore" will do nothing if the following line is error-free. + โ•ญโ”€[ban_ts_comment.tsx:1:3] + 1 โ”‚ // @ts-ignore + ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + โ•ฐโ”€โ”€โ”€โ”€ + help: Replace "@ts-ignore" with "@ts-expect-error". + + โš  typescript-eslint(ban-ts-comment): Use "@ts-expect-error" instead of @ts-ignore, as "@ts-ignore" will do nothing if the following line is error-free. โ•ญโ”€[ban_ts_comment.tsx:1:3] 1 โ”‚ /* @ts-ignore */ ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ•ฐโ”€โ”€โ”€โ”€ + help: Replace "@ts-ignore" with "@ts-expect-error". - โš  typescript-eslint(ban-ts-comment): Do not use @ts-ignore because it alters compilation errors. + โš  typescript-eslint(ban-ts-comment): Use "@ts-expect-error" instead of @ts-ignore, as "@ts-ignore" will do nothing if the following line is error-free. โ•ญโ”€[ban_ts_comment.tsx:2:3] 1 โ”‚ 2 โ”‚ โ•ญโ”€โ–ถ /* - 3 โ”‚ โ•ฐโ”€โ–ถ @ts-ignore - 4 โ”‚ */ - 5 โ”‚ + 3 โ”‚ โ•ฐโ”€โ–ถ @ts-ignore */ + 4 โ”‚ โ•ฐโ”€โ”€โ”€โ”€ + help: Replace "@ts-ignore" with "@ts-expect-error". - โš  typescript-eslint(ban-ts-comment): Do not use @ts-ignore because it alters compilation errors. + โš  typescript-eslint(ban-ts-comment): Use "@ts-expect-error" instead of @ts-ignore, as "@ts-ignore" will do nothing if the following line is error-free. + โ•ญโ”€[ban_ts_comment.tsx:2:3] + 1 โ”‚ + 2 โ”‚ โ•ญโ”€โ–ถ /** on the last line + 3 โ”‚ โ•ฐโ”€โ–ถ @ts-ignore */ + 4 โ”‚ + โ•ฐโ”€โ”€โ”€โ”€ + help: Replace "@ts-ignore" with "@ts-expect-error". + + โš  typescript-eslint(ban-ts-comment): Use "@ts-expect-error" instead of @ts-ignore, as "@ts-ignore" will do nothing if the following line is error-free. + โ•ญโ”€[ban_ts_comment.tsx:2:3] + 1 โ”‚ + 2 โ”‚ โ•ญโ”€โ–ถ /** on the last line + 3 โ”‚ โ•ฐโ”€โ–ถ * @ts-ignore */ + 4 โ”‚ + โ•ฐโ”€โ”€โ”€โ”€ + help: Replace "@ts-ignore" with "@ts-expect-error". + + โš  typescript-eslint(ban-ts-comment): Use "@ts-expect-error" instead of @ts-ignore, as "@ts-ignore" will do nothing if the following line is error-free. โ•ญโ”€[ban_ts_comment.tsx:1:3] 1 โ”‚ /** @ts-ignore */ ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ•ฐโ”€โ”€โ”€โ”€ + help: Replace "@ts-ignore" with "@ts-expect-error". - โš  typescript-eslint(ban-ts-comment): Do not use @ts-ignore because it alters compilation errors. + โš  typescript-eslint(ban-ts-comment): Use "@ts-expect-error" instead of @ts-ignore, as "@ts-ignore" will do nothing if the following line is error-free. + โ•ญโ”€[ban_ts_comment.tsx:2:3] + 1 โ”‚ + 2 โ”‚ โ•ญโ”€โ–ถ /** + 3 โ”‚ โ•ฐโ”€โ–ถ * @ts-ignore: TODO */ + 4 โ”‚ + โ•ฐโ”€โ”€โ”€โ”€ + help: Replace "@ts-ignore" with "@ts-expect-error". + + โš  typescript-eslint(ban-ts-comment): Use "@ts-expect-error" instead of @ts-ignore, as "@ts-ignore" will do nothing if the following line is error-free. + โ•ญโ”€[ban_ts_comment.tsx:2:3] + 1 โ”‚ + 2 โ”‚ โ•ญโ”€โ–ถ /** + 3 โ”‚ โ•ฐโ”€โ–ถ * @ts-ignore: TS1234 because xyz */ + 4 โ”‚ + โ•ฐโ”€โ”€โ”€โ”€ + help: Replace "@ts-ignore" with "@ts-expect-error". + + โš  typescript-eslint(ban-ts-comment): Use "@ts-expect-error" instead of @ts-ignore, as "@ts-ignore" will do nothing if the following line is error-free. โ•ญโ”€[ban_ts_comment.tsx:1:3] 1 โ”‚ // @ts-ignore: Suppress next line ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ•ฐโ”€โ”€โ”€โ”€ + help: Replace "@ts-ignore" with "@ts-expect-error". - โš  typescript-eslint(ban-ts-comment): Do not use @ts-ignore because it alters compilation errors. + โš  typescript-eslint(ban-ts-comment): Use "@ts-expect-error" instead of @ts-ignore, as "@ts-ignore" will do nothing if the following line is error-free. โ•ญโ”€[ban_ts_comment.tsx:1:3] 1 โ”‚ /////@ts-ignore: Suppress next line ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ•ฐโ”€โ”€โ”€โ”€ + help: Replace "@ts-ignore" with "@ts-expect-error". - โš  typescript-eslint(ban-ts-comment): Do not use @ts-ignore because it alters compilation errors. - โ•ญโ”€[ban_ts_comment.tsx:3:5] + โš  typescript-eslint(ban-ts-comment): Use "@ts-expect-error" instead of @ts-ignore, as "@ts-ignore" will do nothing if the following line is error-free. + โ•ญโ”€[ban_ts_comment.tsx:3:7] 2 โ”‚ if (false) { - 3 โ”‚ // @ts-ignore: Unreachable code error - ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - 4 โ”‚ console.log('hello'); + 3 โ”‚ // @ts-ignore: Unreachable code error + ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + 4 โ”‚ console.log('hello'); โ•ฐโ”€โ”€โ”€โ”€ + help: Replace "@ts-ignore" with "@ts-expect-error". โš  typescript-eslint(ban-ts-comment): Include a description after the @ts-ignore directive to explain why the @ts-ignore is necessary. The description must be 3 characters or longer. โ•ญโ”€[ban_ts_comment.tsx:1:3] @@ -132,6 +235,12 @@ expression: ban_ts_comment ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ•ฐโ”€โ”€โ”€โ”€ + โš  typescript-eslint(ban-ts-comment): Include a description after the @ts-ignore directive to explain why the @ts-ignore is necessary. The description must be 3 characters or longer. + โ•ญโ”€[ban_ts_comment.tsx:1:3] + 1 โ”‚ // @ts-ignore + ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + โ•ฐโ”€โ”€โ”€โ”€ + โš  typescript-eslint(ban-ts-comment): Include a description after the @ts-ignore directive to explain why the @ts-ignore is necessary. The description must be 3 characters or longer. โ•ญโ”€[ban_ts_comment.tsx:1:3] 1 โ”‚ // @ts-ignore . @@ -150,10 +259,10 @@ expression: ban_ts_comment ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ•ฐโ”€โ”€โ”€โ”€ - โš  typescript-eslint(ban-ts-comment): Do not use @ts-nocheck because it alters compilation errors. + โš  typescript-eslint(ban-ts-comment): The description for the @ts-ignore directive must match the ^: TS\d+ because .+$ format. โ•ญโ”€[ban_ts_comment.tsx:1:3] - 1 โ”‚ // @ts-nocheck - ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + 1 โ”‚ // @ts-ignore : TS1234 because xyz + ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ•ฐโ”€โ”€โ”€โ”€ โš  typescript-eslint(ban-ts-comment): Do not use @ts-nocheck because it alters compilation errors. @@ -164,23 +273,8 @@ expression: ban_ts_comment โš  typescript-eslint(ban-ts-comment): Do not use @ts-nocheck because it alters compilation errors. โ•ญโ”€[ban_ts_comment.tsx:1:3] - 1 โ”‚ /* @ts-nocheck */ - ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - โ•ฐโ”€โ”€โ”€โ”€ - - โš  typescript-eslint(ban-ts-comment): Do not use @ts-nocheck because it alters compilation errors. - โ•ญโ”€[ban_ts_comment.tsx:2:3] - 1 โ”‚ - 2 โ”‚ โ•ญโ”€โ–ถ /* - 3 โ”‚ โ•ฐโ”€โ–ถ @ts-nocheck - 4 โ”‚ */ - 5 โ”‚ - โ•ฐโ”€โ”€โ”€โ”€ - - โš  typescript-eslint(ban-ts-comment): Do not use @ts-nocheck because it alters compilation errors. - โ•ญโ”€[ban_ts_comment.tsx:1:3] - 1 โ”‚ /** @ts-nocheck */ - ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + 1 โ”‚ // @ts-nocheck + ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ•ฐโ”€โ”€โ”€โ”€ โš  typescript-eslint(ban-ts-comment): Do not use @ts-nocheck because it alters compilation errors. @@ -190,17 +284,11 @@ expression: ban_ts_comment โ•ฐโ”€โ”€โ”€โ”€ โš  typescript-eslint(ban-ts-comment): Do not use @ts-nocheck because it alters compilation errors. - โ•ญโ”€[ban_ts_comment.tsx:1:3] - 1 โ”‚ /////@ts-nocheck: Suppress next line - ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - โ•ฐโ”€โ”€โ”€โ”€ - - โš  typescript-eslint(ban-ts-comment): Do not use @ts-nocheck because it alters compilation errors. - โ•ญโ”€[ban_ts_comment.tsx:3:5] + โ•ญโ”€[ban_ts_comment.tsx:3:7] 2 โ”‚ if (false) { - 3 โ”‚ // @ts-nocheck: Unreachable code error - ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - 4 โ”‚ console.log('hello'); + 3 โ”‚ // @ts-nocheck: Unreachable code error + ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + 4 โ”‚ console.log('hello'); โ•ฐโ”€โ”€โ”€โ”€ โš  typescript-eslint(ban-ts-comment): Include a description after the @ts-nocheck directive to explain why the @ts-nocheck is necessary. The description must be 3 characters or longer. @@ -221,33 +309,18 @@ expression: ban_ts_comment ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ•ฐโ”€โ”€โ”€โ”€ + โš  typescript-eslint(ban-ts-comment): The description for the @ts-nocheck directive must match the ^: TS\d+ because .+$ format. + โ•ญโ”€[ban_ts_comment.tsx:1:3] + 1 โ”‚ // @ts-nocheck : TS1234 because xyz + ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + โ•ฐโ”€โ”€โ”€โ”€ + โš  typescript-eslint(ban-ts-comment): Do not use @ts-check because it alters compilation errors. โ•ญโ”€[ban_ts_comment.tsx:1:3] 1 โ”‚ // @ts-check ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ•ฐโ”€โ”€โ”€โ”€ - โš  typescript-eslint(ban-ts-comment): Do not use @ts-check because it alters compilation errors. - โ•ญโ”€[ban_ts_comment.tsx:1:3] - 1 โ”‚ /* @ts-check */ - ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - โ•ฐโ”€โ”€โ”€โ”€ - - โš  typescript-eslint(ban-ts-comment): Do not use @ts-check because it alters compilation errors. - โ•ญโ”€[ban_ts_comment.tsx:2:3] - 1 โ”‚ - 2 โ”‚ โ•ญโ”€โ–ถ /* - 3 โ”‚ โ•ฐโ”€โ–ถ @ts-check - 4 โ”‚ */ - 5 โ”‚ - โ•ฐโ”€โ”€โ”€โ”€ - - โš  typescript-eslint(ban-ts-comment): Do not use @ts-check because it alters compilation errors. - โ•ญโ”€[ban_ts_comment.tsx:1:3] - 1 โ”‚ /** @ts-check */ - ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - โ•ฐโ”€โ”€โ”€โ”€ - โš  typescript-eslint(ban-ts-comment): Do not use @ts-check because it alters compilation errors. โ•ญโ”€[ban_ts_comment.tsx:1:3] 1 โ”‚ // @ts-check: Suppress next line @@ -255,17 +328,11 @@ expression: ban_ts_comment โ•ฐโ”€โ”€โ”€โ”€ โš  typescript-eslint(ban-ts-comment): Do not use @ts-check because it alters compilation errors. - โ•ญโ”€[ban_ts_comment.tsx:1:3] - 1 โ”‚ /////@ts-check: Suppress next line - ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - โ•ฐโ”€โ”€โ”€โ”€ - - โš  typescript-eslint(ban-ts-comment): Do not use @ts-check because it alters compilation errors. - โ•ญโ”€[ban_ts_comment.tsx:3:5] + โ•ญโ”€[ban_ts_comment.tsx:3:7] 2 โ”‚ if (false) { - 3 โ”‚ // @ts-check: Unreachable code error - ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ - 4 โ”‚ console.log('hello'); + 3 โ”‚ // @ts-check: Unreachable code error + ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + 4 โ”‚ console.log('hello'); โ•ฐโ”€โ”€โ”€โ”€ โš  typescript-eslint(ban-ts-comment): Include a description after the @ts-check directive to explain why the @ts-check is necessary. The description must be 3 characters or longer. @@ -285,3 +352,9 @@ expression: ban_ts_comment 1 โ”‚ // @ts-check: TS1234 ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ•ฐโ”€โ”€โ”€โ”€ + + โš  typescript-eslint(ban-ts-comment): The description for the @ts-check directive must match the ^: TS\d+ because .+$ format. + โ•ญโ”€[ban_ts_comment.tsx:1:3] + 1 โ”‚ // @ts-check : TS1234 because xyz + ยท โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + โ•ฐโ”€โ”€โ”€โ”€