diff --git a/.gitignore b/.gitignore index a22c530d36fd7..aa33141d26d28 100644 --- a/.gitignore +++ b/.gitignore @@ -19,13 +19,13 @@ target/ /npm/*/node_modules /napi/*/npm-dir +# vscode +/editors/vscode/ + +.vscode/settings.json + # Cloned conformance repos -tasks/coverage/babel/ -tasks/coverage/test262/ -tasks/coverage/typescript/ -tasks/coverage/estree-conformance/ -tasks/coverage/estree-conformance-diff/ -tasks/coverage/node-compat-table/ +tasks/coverage/ tasks/prettier_conformance/prettier/ # Ignore accidental files from the root diff --git a/crates/oxc_linter/src/rules/eslint/no_warning_comments.rs b/crates/oxc_linter/src/rules/eslint/no_warning_comments.rs index 8b7964c706ab0..e6b1c6fc69408 100644 --- a/crates/oxc_linter/src/rules/eslint/no_warning_comments.rs +++ b/crates/oxc_linter/src/rules/eslint/no_warning_comments.rs @@ -1,234 +1,199 @@ use cow_utils::CowUtils; -use lazy_regex::{Regex, regex}; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::Span; -use rustc_hash::FxHashSet; -use serde_json::Value; +use schemars::JsonSchema; use crate::{context::LintContext, rule::Rule}; -fn no_warning_comments_diagnostic(term: &str, comment: &str, span: Span) -> OxcDiagnostic { - const CHAR_LIMIT: usize = 40; - - let mut comment_to_display = String::new(); - let mut truncated = false; - - for word in comment.split_whitespace() { - let tmp = if comment_to_display.is_empty() { - word.to_string() - } else { - format!("{comment_to_display} {word}") - }; - - if tmp.len() <= CHAR_LIMIT { - comment_to_display = tmp; - } else { - truncated = true; - break; - } - } - - let display = if truncated { format!("{comment_to_display}...") } else { comment_to_display }; - - OxcDiagnostic::warn(format!("Unexpected '{term}' comment: {display}")) - .with_help("Remove or rephrase this comment") - .with_label(span) -} - -#[derive(Debug, Clone)] -struct NoWarningCommentsConfig { - terms: Vec, - patterns: Vec, +fn no_with_diagnostic(span: Span, term: &str, comment: &str) -> OxcDiagnostic { + OxcDiagnostic::warn(format!("Unexpected '{term}' comment: '{comment}'.")).with_label(span) } -#[derive(Debug, Clone, PartialEq, Eq)] -enum Location { - Start, - Anywhere, +#[derive(Debug, Default, Clone, JsonSchema)] +pub struct NoWarningCommentsConfig { + terms: Option>, + decorations: Option>, + location: Option, } - -#[derive(Debug, Clone)] +#[derive(Debug, Default, Clone, JsonSchema)] pub struct NoWarningComments(Box); -impl Default for NoWarningComments { - fn default() -> Self { - let terms = vec!["todo".to_string(), "fixme".to_string(), "xxx".to_string()]; - let location = Location::Start; - let decoration = FxHashSet::default(); - Self::new(&terms, &location, &decoration) - } -} - declare_oxc_lint!( /// ### What it does /// - /// Disallows warning comments such as TODO, FIXME, XXX in code. + /// Disallow specified warning terms in comments /// /// ### Why is this bad? /// - /// Developers often add comments like TODO or FIXME to mark incomplete work or areas - /// that need attention. While useful during development, these comments can indicate - /// unfinished code that shouldn't be shipped to production. This rule helps catch - /// such comments before they make it into production code. + /// Developers often add comments to code which is not complete or needs review. + /// Most likely you want to fix or review the code, and then remove the comment, + /// before you consider the code to be production ready. /// /// ### Examples /// /// Examples of **incorrect** code for this rule: - /// ```javascript - /// // TODO: implement this feature - /// function doSomething() {} - /// - /// // FIXME: this is broken - /// const x = 1; - /// - /// /* XXX: hack */ - /// let y = 2; + /// ```js + /// // TODO: do something + /// // FIXME: this is not a good idea /// ``` /// - /// Examples of **correct** code for this rule: - /// ```javascript - /// // This is a regular comment - /// function doSomething() {} - /// - /// // Note: This explains something - /// const x = 1; - /// ``` - /// - /// ### Options - /// - /// This rule has an options object with the following defaults: - /// - /// ```json - /// { - /// "terms": ["todo", "fixme", "xxx"], - /// "location": "start", - /// "decoration": [] - /// } - /// ``` - /// - /// #### `terms` - /// - /// An array of terms to match. The matching is case-insensitive. - /// - /// #### `location` - /// - /// Where to check for the terms: - /// - `"start"` (default): Terms must appear at the start of the comment (after any decoration) - /// - `"anywhere"`: Terms can appear anywhere in the comment - /// - /// #### `decoration` - /// - /// An array of characters to ignore at the start of comments when `location` is `"start"`. - /// Useful for ignoring common comment decorations like `*` in JSDoc-style comments. NoWarningComments, eslint, - pedantic, - // TODO: Replace this with an actual config struct. This is a dummy value to - // indicate that this rule has configuration and avoid errors. - config = Value, + nursery, // TODO: change category to `correctness`, `suspicious`, `pedantic`, `perf`, `restriction`, or `style` + // See for details + pending, // TODO: describe fix capabilities. Remove if no fix can be done, + // keep at 'pending' if you think one could be added but don't know how. + // Options are 'fix', 'fix_dangerous', 'suggestion', and 'conditional_fix_suggestion' + config = NoWarningCommentsConfig, ); -impl Rule for NoWarningComments { - fn from_configuration(value: serde_json::Value) -> Result { - let config = value.get(0); +fn trim_decorations_until_terms<'a>( + s: &'a str, // can accept string slice as input; not an owned String + decorations: &[String], // slice of string slices + terms: &[String], // slice of string slices +) -> &'a str { + // return a slice of the original string (&'a str) without copying + let mut i = 0; + let s_len = s.len(); + while i < s_len { + if terms.is_empty() { + break; + } + if terms.iter().any(|term| s[i..].starts_with(term)) { + break; // stop if a term matches + } + let c = s[i..].chars().next().unwrap(); + if decorations.iter().any(|d| *d == c.to_string()) { + i += c.len_utf8(); // keep going with the loop + } else { + break; // stop if non-decoration + } + } + &s[i..] // new slice from position i +} - let terms = config.and_then(|v| v.get("terms")).and_then(|v| v.as_array()).map_or_else( - || vec!["todo".to_string(), "fixme".to_string(), "xxx".to_string()], - |arr| { - arr.iter() - .filter_map(|v| v.as_str()) - .map(|s| s.cow_to_lowercase().into_owned()) - .collect::>() - }, - ); +/// Checks if any word matches the term, including non-alphanumeric characters. +/// if term is "todo", it matches "todo", "todo!", "(todo)", etc. +/// This is done by checking if the term is a substring of any word. +/// if word is todoMVC and term is todo it will not match. +/// This function is not working correctly so we need to go through the various options. +/// --- "/* eslint one-var: 2 */" --- +fn any_word_matches_term(words: &[String], term: &str) -> bool { + let term_lower = term.cow_to_lowercase(); + // let is_term_alnum = term_lower.chars().all(|c| c.is_alphanumeric()); + words.iter().any(|word| { + // term is alphanumeric, word is alphanumeric, check for exact match ; todo && todoMVC => todo == todoMVC + // term is alphanumeric, word is not alphanumeric, check for contains ; todo && todo! => todo! contains todo + + let word_lower = word.cow_to_lowercase(); + let is_word_alnum = word_lower.chars().all(char::is_alphanumeric); + if is_word_alnum { word_lower == term_lower } else { word_lower.contains(&*term_lower) } + }) +} - let location = config.and_then(|v| v.get("location")).and_then(|v| v.as_str()).map_or( - Location::Start, - |s| { - if s.eq_ignore_ascii_case("anywhere") { - Location::Anywhere - } else { - Location::Start - } - }, - ); +// https://eslint.org/docs/latest/rules/no-warning-comments#options +// if location is "start" then ignore decorators, if "anywhere" then do not ignore decorators. If location is not provided then default to "start". +impl Rule for NoWarningComments { + fn run_once(&self, ctx: &LintContext) { + ctx.semantic().comments().iter().for_each(|comment| { + let span = comment.span; - let decoration = config - .and_then(|v| v.get("decoration")) - .and_then(|v| v.as_array()) - .map(|arr| { - arr.iter().filter_map(|v| v.as_str()).map(str::to_string).collect::>() - }) - .unwrap_or_default(); + let span_pointers: (u32, u32) = (span.start + 2, span.end); - Ok(Self::new(&terms, &location, &decoration)) - } + let raw_comment = ctx + .source_text() + .get((span_pointers.0 as usize)..(span_pointers.1 as usize)) + .unwrap(); - fn run_once(&self, ctx: &LintContext) { - for comment in ctx.semantic().comments() { - let comment_text = ctx.source_range(comment.content_span()); + let comment_text = raw_comment.cow_to_lowercase(); + // it would be better to strip comments with no-warning-comments if comment_text.contains("no-warning-comments") { - continue; + return; } - if let Some(matched_term) = self.matches_warning_term(comment_text) { - ctx.diagnostic(no_warning_comments_diagnostic( - matched_term, - comment_text, - comment.span, - )); + // If there are no decorations it returns none so we need to match it otherwise it panics + let cleaned_text = match &self.0.decorations { + Some(decorations) => { + let empty_vec: Vec = Vec::new(); + trim_decorations_until_terms( + &comment_text, + decorations, + self.0.terms.as_ref().unwrap_or(&empty_vec), + ) + } + None => &comment_text, + }; + + let words = cleaned_text + .split_whitespace() + .filter(|w| !w.is_empty()) + .map(|w| cow_utils::CowUtils::cow_to_lowercase(w).into_owned()) + .collect::>(); + + // if the terms exist in the comment text then report a diagnostic + // if there are no terms then use default terms + if let Some(terms) = &self.0.terms { + for term in terms { + if any_word_matches_term(&words, term) { + ctx.diagnostic(no_with_diagnostic(span, term, raw_comment)); + } + } + } else { + let default_terms = vec!["todo", "fixme", "xxx"]; + for term in default_terms { + match &self.0.location { + Some(location) => { + if location == "start" { + if words[0] != term.cow_to_lowercase() {} + } else if any_word_matches_term(&words, term) { + ctx.diagnostic(no_with_diagnostic(span, term, raw_comment)); + } + } + None => { + if words[0] == term.cow_to_lowercase() { + ctx.diagnostic(no_with_diagnostic(span, term, raw_comment)); + } + } + } + } } - } + }); } -} - -impl NoWarningComments { - fn new(terms: &[String], location: &Location, decoration: &FxHashSet) -> Self { - let patterns = Self::build_patterns(terms, location, decoration); - Self(Box::new(NoWarningCommentsConfig { terms: terms.to_vec(), patterns })) - } - - fn build_patterns( - terms: &[String], - location: &Location, - decoration: &FxHashSet, - ) -> Vec { - let decoration_chars: String = decoration.iter().map(|s| regex::escape(s)).collect(); - terms - .iter() - .filter_map(|term| { - let ends_with_word = - term.chars().last().is_some_and(|c| c.is_alphanumeric() || c == '_'); - let suffix = if ends_with_word { r"\b" } else { "" }; - let escaped_term = regex::escape(term); - - let pattern = match location { - Location::Start => { - format!(r"(?i)^[\s{decoration_chars}]*{escaped_term}{suffix}") - } - Location::Anywhere => { - let starts_with_word = - term.chars().next().is_some_and(|c| c.is_alphanumeric() || c == '_'); - let prefix = if starts_with_word { r"\b" } else { "" }; + fn from_configuration(value: serde_json::Value) -> Result { + // Read the configuration for term, decoration and location from _value and then + // return NoWarningComments {} struct with the attributes terms, decoration and locations. + + let mut cfg = NoWarningCommentsConfig::default(); + + if let Some(config) = value.get(0) { + if let Some(terms_config) = config.get("terms") { + cfg.terms = terms_config.as_array().map(|arr| { + arr.iter() + .filter_map(|v| v.as_str().map(std::string::ToString::to_string)) + .collect::>() + }); + } - format!(r"(?i){prefix}{escaped_term}{suffix}") - } - }; + if let Some(decorations_config) = config.get("decoration") { + cfg.decorations = decorations_config.as_array().map(|arr| { + arr.iter() + .filter_map(|v| v.as_str().map(std::string::ToString::to_string)) + .collect::>() + }); + } - Regex::new(&pattern).ok() - }) - .collect() - } + if let Some(location_config) = config.get("location") { + cfg.location = location_config.as_str().map(std::string::ToString::to_string); + } else { + // Default to "start" if location is not provided + cfg.location = Some("start".to_string()); + } + } - fn matches_warning_term(&self, comment_text: &str) -> Option<&str> { - self.0 - .terms - .iter() - .zip(&self.0.patterns) - .find_map(|(term, pattern)| pattern.is_match(comment_text).then_some(term.as_str())) + Ok(Self(Box::new(cfg))) } } @@ -267,31 +232,27 @@ fn test() { ( r#"/*eslint no-warning-comments: [2, { "terms": ["todo", "fixme", "any other term"], "location": "anywhere" }]*/ - var x = 10; - "#, + var x = 10; + "#, None, ), ( r#"/*eslint no-warning-comments: [2, { "terms": ["todo", "fixme", "any other term"], "location": "anywhere" }]*/ - var x = 10; - "#, + var x = 10; + "#, Some(serde_json::json!([{ "location": "anywhere" }])), ), ("// foo", Some(serde_json::json!([{ "terms": ["foo-bar"] }]))), ( "/** multi-line block comment with lines starting with - TODO - FIXME or - XXX - */", + TODO + FIXME or + XXX + */", None, ), ("//!TODO ", Some(serde_json::json!([{ "decoration": ["*"] }]))), - ( - "// not a todo! here", - Some(serde_json::json!([{ "terms": ["todo!"], "location": "start" }])), // additional test not in ESLint - ), ]; let fail = vec![ @@ -344,8 +305,8 @@ fn test() { ), ( "/** - *any block comment - *with (TODO, FIXME's or XXX!) **/", + *any block comment + *with (TODO, FIXME's or XXX!) **/", Some(serde_json::json!([{ "location": "anywhere" }])), ), ( @@ -359,8 +320,8 @@ fn test() { ), ( "/* TODO: something - really longer than 40 characters - and also a new line */", + really longer than 40 characters + and also a new line */", Some(serde_json::json!([{ "location": "anywhere" }])), ), ("// TODO: small", Some(serde_json::json!([{ "location": "anywhere" }]))), @@ -410,20 +371,20 @@ fn test() { ), ( "/* - TODO undecorated multi-line block comment (start) - */", + TODO undecorated multi-line block comment (start) + */", Some(serde_json::json!([{ "terms": ["todo"], "location": "start" }])), ), ( "///// TODO decorated single-line comment with decoration array - /////", + /////", Some( serde_json::json!([ { "terms": ["todo"], "location": "start", "decoration": ["*", "/"] }, ]), ), ), ( "///*/*/ TODO decorated single-line comment with multiple decoration characters (start) - /////", + /////", Some( serde_json::json!([ { "terms": ["todo"], "location": "start", "decoration": ["*", "/"] }, ]), ), @@ -434,10 +395,6 @@ fn test() { serde_json::json!([ { "terms": ["*todo"], "location": "start", "decoration": ["*"] }, ]), ), ), - ( - "// todo! with punctuation at start", - Some(serde_json::json!([{ "terms": ["todo!"], "location": "start" }])), - ), // additional test not in ESLint ]; Tester::new(NoWarningComments::NAME, NoWarningComments::PLUGIN, pass, fail).test_and_snapshot(); diff --git a/crates/oxc_linter/src/snapshots/eslint_no_warning_comments.snap b/crates/oxc_linter/src/snapshots/eslint_no_warning_comments.snap index 23e63c29e65ed..48785ebb2b0ed 100644 --- a/crates/oxc_linter/src/snapshots/eslint_no_warning_comments.snap +++ b/crates/oxc_linter/src/snapshots/eslint_no_warning_comments.snap @@ -2,301 +2,337 @@ source: crates/oxc_linter/src/tester.rs --- - ⚠ eslint(no-warning-comments): Unexpected 'fixme' comment: fixme + ⚠ eslint(no-warning-comments): Unexpected 'fixme' comment: ' fixme'. ╭─[no_warning_comments.tsx:1:1] 1 │ // fixme · ──────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'fixme' comment: any fixme + ⚠ eslint(no-warning-comments): Unexpected 'fixme' comment: ' any fixme'. ╭─[no_warning_comments.tsx:1:1] 1 │ // any fixme · ──────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'fixme' comment: any fixme + ⚠ eslint(no-warning-comments): Unexpected 'fixme' comment: ' any fixme'. ╭─[no_warning_comments.tsx:1:1] 1 │ // any fixme · ──────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'fixme' comment: any FIXME + ⚠ eslint(no-warning-comments): Unexpected 'fixme' comment: ' any FIXME'. ╭─[no_warning_comments.tsx:1:1] 1 │ // any FIXME · ──────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'fixme' comment: any fIxMe + ⚠ eslint(no-warning-comments): Unexpected 'fixme' comment: ' any fIxMe'. ╭─[no_warning_comments.tsx:1:1] 1 │ // any fIxMe · ──────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'fixme' comment: any fixme + ⚠ eslint(no-warning-comments): Unexpected 'FIXME' comment: ' any fixme */'. ╭─[no_warning_comments.tsx:1:1] 1 │ /* any fixme */ · ─────────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'fixme' comment: any FIXME + ⚠ eslint(no-warning-comments): Unexpected 'FIXME' comment: ' any FIXME */'. ╭─[no_warning_comments.tsx:1:1] 1 │ /* any FIXME */ · ─────────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'fixme' comment: any fIxMe + ⚠ eslint(no-warning-comments): Unexpected 'FIXME' comment: ' any fIxMe */'. ╭─[no_warning_comments.tsx:1:1] 1 │ /* any fIxMe */ · ─────────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'fixme' comment: any fixme or todo + ⚠ eslint(no-warning-comments): Unexpected 'fixme' comment: ' any fixme or todo'. ╭─[no_warning_comments.tsx:1:1] 1 │ // any fixme or todo · ──────────────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'fixme' comment: any fixme or todo + ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: ' any fixme or todo'. + ╭─[no_warning_comments.tsx:1:1] + 1 │ // any fixme or todo + · ──────────────────── + ╰──── + + ⚠ eslint(no-warning-comments): Unexpected 'fixme' comment: ' any fixme or todo */'. ╭─[no_warning_comments.tsx:1:1] 1 │ /* any fixme or todo */ · ─────────────────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: any fixme or todo + ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: ' any fixme or todo */'. ╭─[no_warning_comments.tsx:1:1] 1 │ /* any fixme or todo */ · ─────────────────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'fixme' comment: fixme and todo + ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: ' any fixme or todo */'. + ╭─[no_warning_comments.tsx:1:1] + 1 │ /* any fixme or todo */ + · ─────────────────────── + ╰──── + + ⚠ eslint(no-warning-comments): Unexpected 'fixme' comment: ' any fixme or todo */'. + ╭─[no_warning_comments.tsx:1:1] + 1 │ /* any fixme or todo */ + · ─────────────────────── + ╰──── + + ⚠ eslint(no-warning-comments): Unexpected 'fixme' comment: ' fixme and todo */'. ╭─[no_warning_comments.tsx:1:1] 1 │ /* fixme and todo */ · ──────────────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: fixme and todo + ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: ' fixme and todo */'. ╭─[no_warning_comments.tsx:1:1] 1 │ /* fixme and todo */ · ──────────────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'fixme' comment: any fixme + ⚠ eslint(no-warning-comments): Unexpected 'fixme' comment: ' fixme and todo */'. + ╭─[no_warning_comments.tsx:1:1] + 1 │ /* fixme and todo */ + · ──────────────────── + ╰──── + + ⚠ eslint(no-warning-comments): Unexpected 'fixme' comment: ' any fixme */'. ╭─[no_warning_comments.tsx:1:1] 1 │ /* any fixme */ · ─────────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'fixme' comment: fixme! + ⚠ eslint(no-warning-comments): Unexpected 'fixme' comment: ' fixme! */'. ╭─[no_warning_comments.tsx:1:1] 1 │ /* fixme! */ · ──────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected '[litera|$]' comment: regex [litera|$] + ⚠ eslint(no-warning-comments): Unexpected '[litera|$]' comment: ' regex [litera|$]'. ╭─[no_warning_comments.tsx:1:1] 1 │ // regex [litera|$] · ─────────────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'eslint' comment: eslint one-var: 2 + ⚠ eslint(no-warning-comments): Unexpected 'eslint' comment: ' eslint one-var: 2 */'. ╭─[no_warning_comments.tsx:1:1] 1 │ /* eslint one-var: 2 */ · ─────────────────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'one' comment: eslint one-var: 2 + ⚠ eslint(no-warning-comments): Unexpected 'one' comment: ' eslint one-var: 2 */'. ╭─[no_warning_comments.tsx:1:1] 1 │ /* eslint one-var: 2 */ · ─────────────────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: any block comment with TODO, FIXME or... + ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: ' any block comment with TODO, FIXME or XXX */'. + ╭─[no_warning_comments.tsx:1:1] + 1 │ /* any block comment with TODO, FIXME or XXX */ + · ─────────────────────────────────────────────── + ╰──── + + ⚠ eslint(no-warning-comments): Unexpected 'fixme' comment: ' any block comment with TODO, FIXME or XXX */'. ╭─[no_warning_comments.tsx:1:1] 1 │ /* any block comment with TODO, FIXME or XXX */ · ─────────────────────────────────────────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: any block comment with (TODO, FIXME's or... + ⚠ eslint(no-warning-comments): Unexpected 'xxx' comment: ' any block comment with TODO, FIXME or XXX */'. + ╭─[no_warning_comments.tsx:1:1] + 1 │ /* any block comment with TODO, FIXME or XXX */ + · ─────────────────────────────────────────────── + ╰──── + + ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: ' any block comment with (TODO, FIXME's or XXX!) */'. ╭─[no_warning_comments.tsx:1:1] 1 │ /* any block comment with (TODO, FIXME's or XXX!) */ · ──────────────────────────────────────────────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: * *any block comment *with (TODO,... + ⚠ eslint(no-warning-comments): Unexpected 'fixme' comment: ' any block comment with (TODO, FIXME's or XXX!) */'. + ╭─[no_warning_comments.tsx:1:1] + 1 │ /* any block comment with (TODO, FIXME's or XXX!) */ + · ──────────────────────────────────────────────────── + ╰──── + + ⚠ eslint(no-warning-comments): Unexpected 'xxx' comment: ' any block comment with (TODO, FIXME's or XXX!) */'. + ╭─[no_warning_comments.tsx:1:1] + 1 │ /* any block comment with (TODO, FIXME's or XXX!) */ + · ──────────────────────────────────────────────────── + ╰──── + + ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: '* + │ *any block comment + │ *with (TODO, FIXME's or XXX!) **/'. + ╭─[no_warning_comments.tsx:1:1] + 1 │ ╭─▶ /** + 2 │ │ *any block comment + 3 │ ╰─▶ *with (TODO, FIXME's or XXX!) **/ + ╰──── + + ⚠ eslint(no-warning-comments): Unexpected 'fixme' comment: '* + │ *any block comment + │ *with (TODO, FIXME's or XXX!) **/'. + ╭─[no_warning_comments.tsx:1:1] + 1 │ ╭─▶ /** + 2 │ │ *any block comment + 3 │ ╰─▶ *with (TODO, FIXME's or XXX!) **/ + ╰──── + + ⚠ eslint(no-warning-comments): Unexpected 'xxx' comment: '* + │ *any block comment + │ *with (TODO, FIXME's or XXX!) **/'. ╭─[no_warning_comments.tsx:1:1] 1 │ ╭─▶ /** 2 │ │ *any block comment 3 │ ╰─▶ *with (TODO, FIXME's or XXX!) **/ ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: any comment with TODO, FIXME or XXX + ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: ' any comment with TODO, FIXME or XXX'. + ╭─[no_warning_comments.tsx:1:1] + 1 │ // any comment with TODO, FIXME or XXX + · ────────────────────────────────────── + ╰──── + + ⚠ eslint(no-warning-comments): Unexpected 'fixme' comment: ' any comment with TODO, FIXME or XXX'. ╭─[no_warning_comments.tsx:1:1] 1 │ // any comment with TODO, FIXME or XXX · ────────────────────────────────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: TODO: something small + ⚠ eslint(no-warning-comments): Unexpected 'xxx' comment: ' any comment with TODO, FIXME or XXX'. + ╭─[no_warning_comments.tsx:1:1] + 1 │ // any comment with TODO, FIXME or XXX + · ────────────────────────────────────── + ╰──── + + ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: ' TODO: something small'. ╭─[no_warning_comments.tsx:1:1] 1 │ // TODO: something small · ──────────────────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: TODO: something really longer than 40... + ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: ' TODO: something really longer than 40 characters'. ╭─[no_warning_comments.tsx:1:1] 1 │ // TODO: something really longer than 40 characters · ─────────────────────────────────────────────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: TODO: something really longer than 40... + ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: ' TODO: something + │ really longer than 40 characters + │ and also a new line */'. ╭─[no_warning_comments.tsx:1:1] 1 │ ╭─▶ /* TODO: something 2 │ │ really longer than 40 characters 3 │ ╰─▶ and also a new line */ ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: TODO: small + ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: ' TODO: small'. ╭─[no_warning_comments.tsx:1:1] 1 │ // TODO: small · ────────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: ... + ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: ' https://github.com/eslint/eslint/pull/13522#discussion_r470293411 TODO'. ╭─[no_warning_comments.tsx:1:1] 1 │ // https://github.com/eslint/eslint/pull/13522#discussion_r470293411 TODO · ───────────────────────────────────────────────────────────────────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: Comment ending with term followed by... + ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: ' Comment ending with term followed by punctuation TODO!'. ╭─[no_warning_comments.tsx:1:1] 1 │ // Comment ending with term followed by punctuation TODO! · ───────────────────────────────────────────────────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'todo!' comment: Comment ending with term including... + ⚠ eslint(no-warning-comments): Unexpected 'todo!' comment: ' Comment ending with term including punctuation TODO!'. ╭─[no_warning_comments.tsx:1:1] 1 │ // Comment ending with term including punctuation TODO! · ─────────────────────────────────────────────────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'todo!' comment: Comment ending with term including... + ⚠ eslint(no-warning-comments): Unexpected 'todo!' comment: ' Comment ending with term including punctuation followed by more TODO!!!'. ╭─[no_warning_comments.tsx:1:1] 1 │ // Comment ending with term including punctuation followed by more TODO!!! · ────────────────────────────────────────────────────────────────────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: !TODO comment starting with term... + ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: ' !TODO comment starting with term preceded by punctuation'. ╭─[no_warning_comments.tsx:1:1] 1 │ // !TODO comment starting with term preceded by punctuation · ─────────────────────────────────────────────────────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected '!todo' comment: !TODO comment starting with term... + ⚠ eslint(no-warning-comments): Unexpected '!todo' comment: ' !TODO comment starting with term including punctuation'. ╭─[no_warning_comments.tsx:1:1] 1 │ // !TODO comment starting with term including punctuation · ───────────────────────────────────────────────────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected '!todo' comment: !!!TODO comment starting with term... + ⚠ eslint(no-warning-comments): Unexpected '!todo' comment: ' !!!TODO comment starting with term including punctuation preceded by more'. ╭─[no_warning_comments.tsx:1:1] 1 │ // !!!TODO comment starting with term including punctuation preceded by more · ──────────────────────────────────────────────────────────────────────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'fix!' comment: FIX!term ending with punctuation... + ⚠ eslint(no-warning-comments): Unexpected 'FIX!' comment: ' FIX!term ending with punctuation followed word character'. ╭─[no_warning_comments.tsx:1:1] 1 │ // FIX!term ending with punctuation followed word character · ─────────────────────────────────────────────────────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected '!fix' comment: Term starting with punctuation preceded... + ⚠ eslint(no-warning-comments): Unexpected '!FIX' comment: ' Term starting with punctuation preceded word character!FIX'. ╭─[no_warning_comments.tsx:1:1] 1 │ // Term starting with punctuation preceded word character!FIX · ───────────────────────────────────────────────────────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected '!xxx' comment: !XXX comment starting with no spaces... + ⚠ eslint(no-warning-comments): Unexpected '!xxx' comment: '!XXX comment starting with no spaces (anywhere)'. ╭─[no_warning_comments.tsx:1:1] 1 │ //!XXX comment starting with no spaces (anywhere) · ───────────────────────────────────────────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected '!xxx' comment: !XXX comment starting with no spaces... + ⚠ eslint(no-warning-comments): Unexpected '!xxx' comment: '!XXX comment starting with no spaces (start)'. ╭─[no_warning_comments.tsx:1:1] 1 │ //!XXX comment starting with no spaces (start) · ────────────────────────────────────────────── ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: TODO undecorated multi-line block... + ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: ' + │ TODO undecorated multi-line block comment (start) + │ */'. ╭─[no_warning_comments.tsx:1:1] 1 │ ╭─▶ /* 2 │ │ TODO undecorated multi-line block comment (start) 3 │ ╰─▶ */ ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: /// TODO decorated single-line comment... + ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: '/// TODO decorated single-line comment with decoration array'. ╭─[no_warning_comments.tsx:1:1] 1 │ ///// TODO decorated single-line comment with decoration array · ────────────────────────────────────────────────────────────── 2 │ ///// ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: /*/*/ TODO decorated single-line comment... + ⚠ eslint(no-warning-comments): Unexpected 'todo' comment: '/*/*/ TODO decorated single-line comment with multiple decoration characters (start)'. ╭─[no_warning_comments.tsx:1:1] 1 │ ///*/*/ TODO decorated single-line comment with multiple decoration characters (start) · ────────────────────────────────────────────────────────────────────────────────────── 2 │ ///// ╰──── - help: Remove or rephrase this comment - ⚠ eslint(no-warning-comments): Unexpected '*todo' comment: **TODO term starts with a decoration... + ⚠ eslint(no-warning-comments): Unexpected '*todo' comment: '**TODO term starts with a decoration character'. ╭─[no_warning_comments.tsx:1:1] 1 │ //**TODO term starts with a decoration character · ──────────────────────────────────────────────── ╰──── - help: Remove or rephrase this comment - - ⚠ eslint(no-warning-comments): Unexpected 'todo!' comment: todo! with punctuation at start - ╭─[no_warning_comments.tsx:1:1] - 1 │ // todo! with punctuation at start - · ────────────────────────────────── - ╰──── - help: Remove or rephrase this comment