diff --git a/crates/oxc_linter/src/generated/rule_runner_impls.rs b/crates/oxc_linter/src/generated/rule_runner_impls.rs index 9b04ffa9757e4..4bf1d2858fb83 100644 --- a/crates/oxc_linter/src/generated/rule_runner_impls.rs +++ b/crates/oxc_linter/src/generated/rule_runner_impls.rs @@ -3597,6 +3597,14 @@ impl RuleRunner for crate::rules::unicorn::prefer_includes::PreferIncludes { const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run; } +impl RuleRunner for crate::rules::unicorn::prefer_keyboard_event_key::PreferKeyboardEventKey { + const NODE_TYPES: Option<&AstTypesBitset> = Some(&AstTypesBitset::from_types(&[ + AstType::BindingProperty, + AstType::StaticMemberExpression, + ])); + const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run; +} + impl RuleRunner for crate::rules::unicorn::prefer_logical_operator_over_ternary::PreferLogicalOperatorOverTernary { const NODE_TYPES: Option<&AstTypesBitset> = Some(&AstTypesBitset::from_types(&[AstType::ConditionalExpression])); const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run; diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index c0197fb58d191..ea9a4f358c9a9 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -480,6 +480,7 @@ pub(crate) mod unicorn { pub mod prefer_event_target; pub mod prefer_global_this; pub mod prefer_includes; + pub mod prefer_keyboard_event_key; pub mod prefer_logical_operator_over_ternary; pub mod prefer_math_min_max; pub mod prefer_math_trunc; @@ -1227,6 +1228,7 @@ oxc_macros::declare_all_lint_rules! { unicorn::prefer_top_level_await, unicorn::prefer_at, unicorn::prefer_global_this, + unicorn::prefer_keyboard_event_key, unicorn::prefer_object_from_entries, unicorn::prefer_array_find, unicorn::prefer_array_index_of, diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_keyboard_event_key.rs b/crates/oxc_linter/src/rules/unicorn/prefer_keyboard_event_key.rs new file mode 100644 index 0000000000000..bf431cdcc80ca --- /dev/null +++ b/crates/oxc_linter/src/rules/unicorn/prefer_keyboard_event_key.rs @@ -0,0 +1,1164 @@ +use phf::{Map, phf_map}; + +use oxc_allocator::Allocator; +use oxc_ast::{ + AstBuilder, AstKind, + ast::{ + Argument, BindingPatternKind, BindingProperty, CallExpression, Expression, Function, + MemberExpression, PropertyKey, + }, +}; +use oxc_codegen::CodegenOptions; +use oxc_diagnostics::OxcDiagnostic; +use oxc_macros::declare_oxc_lint; +use oxc_span::{SPAN, Span}; + +use crate::{AstNode, context::LintContext, rule::Rule}; + +fn prefer_keyboard_event_key_diagnostic(span: Span, deprecated_prop: &str) -> OxcDiagnostic { + OxcDiagnostic::warn(format!("Use `.key` instead of `.{deprecated_prop}`")) + .with_help(format!("The `{deprecated_prop}` property is deprecated.")) + .with_label(span) +} + +#[derive(Debug, Default, Clone)] +pub struct PreferKeyboardEventKey; + +declare_oxc_lint!( + /// ### What it does + /// + /// Enforces the use of [`KeyboardEvent#key`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) over [`KeyboardEvent#keyCode`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode) which is deprecated. + /// The `.key` property is also more semantic and readable. + /// + /// ### Why is this bad? + /// + /// The `keyCode`, `which`, and `charCode` properties are deprecated and should be avoided in favor of the `key` property. + /// + /// ### Examples + /// + /// Examples of **incorrect** code for this rule: + /// ```js + /// window.addEventListener('keydown', event => { + /// if (event.keyCode === 8) { + /// console.log('Backspace was pressed'); + /// } + /// }); + /// + /// window.addEventListener('keydown', event => { + /// console.log(event.keyCode); + /// }); + /// ``` + /// + /// Examples of **correct** code for this rule: + /// ```js + /// window.addEventListener('keydown', event => { + /// if (event.key === 'Backspace') { + /// console.log('Backspace was pressed'); + /// } + /// }); + /// + /// window.addEventListener('click', event => { + /// console.log(event.key); + /// }); + /// ``` + PreferKeyboardEventKey, + unicorn, + style, + fix +); + +impl Rule for PreferKeyboardEventKey { + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + match node.kind() { + AstKind::StaticMemberExpression(member_expr) => { + Self::check_static_member_expression(member_expr, node, ctx); + } + AstKind::BindingProperty(prop) => { + Self::check_binding_property(prop, node, ctx); + } + _ => {} + } + } +} + +impl PreferKeyboardEventKey { + /// Check direct property access like `event.keyCode` + fn check_static_member_expression<'a>( + member_expr: &oxc_ast::ast::StaticMemberExpression<'a>, + node: &AstNode<'a>, + ctx: &LintContext<'a>, + ) { + // Check if this is a deprecated property name + let property_name = member_expr.property.name.as_str(); + if !is_deprecated_property(property_name) { + return; + } + + // Get the object being accessed + let object = &member_expr.object; + + // Check if the object is an event parameter from addEventListener + let Some(event_param_symbol_id) = Self::get_event_param_symbol_id(node, ctx) else { + return; + }; + + // Check if the object references the event parameter + if !Self::is_reference_to_symbol(object, event_param_symbol_id, ctx) { + return; + } + + // Report the diagnostic + Self::report_diagnostic_with_fix(property_name, member_expr.property.span, node, ctx); + } + + /// Check destructuring patterns like `const { keyCode } = event` + fn check_binding_property<'a>( + prop: &BindingProperty<'a>, + node: &AstNode<'a>, + ctx: &LintContext<'a>, + ) { + // Get the property name from the key + let PropertyKey::StaticIdentifier(key_ident) = &prop.key else { + return; + }; + let key_name = key_ident.name.as_str(); + + // Get the value name (the identifier being introduced) + let BindingPatternKind::BindingIdentifier(value_ident) = &prop.value.kind else { + return; + }; + let value_name = value_ident.name.as_str(); + + // Determine which property name we're checking and if we should report + // There are several cases: + // 1. Shorthand: { keyCode } = event -> report keyCode + // 2. Non-shorthand: { keyCode: abc } = event -> OK (renaming to non-deprecated name) + // 3. Non-shorthand: { a: keyCode } = event -> report keyCode (introducing deprecated variable) + let (should_report, deprecated_name) = if prop.shorthand { + // Shorthand case: key and value are the same + (is_deprecated_property(key_name), key_name) + } else if is_deprecated_property(key_name) && !is_deprecated_property(value_name) { + // { keyCode: abc } = event -> OK, renaming away from deprecated + (false, key_name) + } else if is_deprecated_property(value_name) { + // { a: keyCode } = event -> bad, introducing deprecated variable name + (true, value_name) + } else { + (false, key_name) + }; + + if !should_report { + return; + } + + // Find the VariableDeclarator parent to check what we're destructuring + let Some((declarator_node, init)) = Self::find_variable_declarator_init(node, ctx) else { + // Also check if the property is directly in the function parameter destructuring + if Self::is_in_event_parameter_destructuring(node, ctx) { + // Get the span for the value (the identifier being introduced) + let value_span = match &prop.value.kind { + BindingPatternKind::BindingIdentifier(ident) => ident.span, + _ => return, + }; + ctx.diagnostic(prefer_keyboard_event_key_diagnostic(value_span, deprecated_name)); + } + return; + }; + + // Check if the init is an event parameter from addEventListener + let Some(event_param_symbol_id) = Self::get_event_param_symbol_id(declarator_node, ctx) + else { + return; + }; + + // Check if init references the event parameter + if !Self::is_reference_to_symbol(init, event_param_symbol_id, ctx) { + return; + } + + let BindingPatternKind::BindingIdentifier(ident) = &prop.value.kind else { + return; + }; + + ctx.diagnostic(prefer_keyboard_event_key_diagnostic(ident.span, deprecated_name)); + } + + /// Check if this property is directly in the event parameter destructuring + /// e.g., addEventListener('click', ({ keyCode }) => {}) + fn is_in_event_parameter_destructuring<'a>(node: &AstNode<'a>, ctx: &LintContext<'a>) -> bool { + // Walk up to find if we're in a function parameter + let mut current = node; + loop { + let parent = ctx.nodes().parent_node(current.id()); + match parent.kind() { + AstKind::FormalParameters(_) => { + // Check if the function containing these params is an addEventListener callback + let func_node = ctx.nodes().parent_node(parent.id()); + return Self::is_add_event_listener_callback(func_node, ctx); + } + AstKind::Function(_) + | AstKind::ArrowFunctionExpression(_) + | AstKind::FunctionBody(_) + | AstKind::Program(_) => { + return false; + } + _ => { + current = parent; + } + } + } + } + + /// Find the init expression of a VariableDeclarator + fn find_variable_declarator_init<'a, 'b>( + node: &'b AstNode<'a>, + ctx: &'b LintContext<'a>, + ) -> Option<(&'b AstNode<'a>, &'a Expression<'a>)> { + let mut current = node; + loop { + let parent = ctx.nodes().parent_node(current.id()); + match parent.kind() { + AstKind::VariableDeclarator(declarator) => { + return declarator.init.as_ref().map(|init| (parent, init)); + } + AstKind::Function(_) + | AstKind::ArrowFunctionExpression(_) + | AstKind::FunctionBody(_) + | AstKind::Program(_) => { + return None; + } + _ => { + current = parent; + } + } + } + } + + /// Get the symbol ID of the event parameter from addEventListener + fn get_event_param_symbol_id<'a>( + node: &AstNode<'a>, + ctx: &LintContext<'a>, + ) -> Option { + // Walk up to find the addEventListener call and its callback + let callback = Self::find_add_event_listener_callback(node, ctx)?; + + // Get the first parameter of the callback (the event) + let params = match callback { + CallbackFunction::Arrow(arrow) => &arrow.params, + CallbackFunction::Regular(func) => &func.params, + }; + + let first_param = params.items.first()?; + + match &first_param.pattern.kind { + BindingPatternKind::BindingIdentifier(ident) => Some(ident.symbol_id()), + BindingPatternKind::ObjectPattern(_) => { + // If the event itself is destructured, we handle this separately + None + } + _ => None, + } + } + + /// Check if an expression references a specific symbol + fn is_reference_to_symbol( + expr: &Expression, + symbol_id: oxc_semantic::SymbolId, + ctx: &LintContext, + ) -> bool { + match expr { + Expression::Identifier(ident) => { + ctx.scoping().get_reference(ident.reference_id()).symbol_id() == Some(symbol_id) + } + _ => false, + } + } + + /// Check if a node is an addEventListener callback + fn is_add_event_listener_callback<'a>(node: &AstNode<'a>, ctx: &LintContext<'a>) -> bool { + Self::find_add_event_listener_callback(node, ctx).is_some() + } + + /// Find the callback function of an addEventListener call that contains this node + fn find_add_event_listener_callback<'a>( + node: &AstNode<'a>, + ctx: &LintContext<'a>, + ) -> Option> { + let mut current_node = node; + let mut callback: Option> = None; + + // Check if the node itself is a function + match node.kind() { + AstKind::ArrowFunctionExpression(arrow) => { + callback = Some(CallbackFunction::Arrow(arrow)); + } + AstKind::Function(func) => { + callback = Some(CallbackFunction::Regular(func)); + } + _ => {} + } + + loop { + let parent = ctx.nodes().parent_node(current_node.id()); + + match parent.kind() { + AstKind::ArrowFunctionExpression(arrow) => { + callback = Some(CallbackFunction::Arrow(arrow)); + } + AstKind::Function(func) => { + callback = Some(CallbackFunction::Regular(func)); + } + AstKind::CallExpression(call) => { + // Check if this is addEventListener + if Self::is_add_event_listener_call(call) && callback.is_some() { + // Verify the callback is the second argument + if Self::is_callback_argument(call, callback.as_ref()) { + return callback; + } + } + // Not our addEventListener, keep looking up + } + AstKind::Program(_) => { + return None; + } + _ => {} + } + + current_node = parent; + } + } + + /// Check if a call expression is addEventListener + fn is_add_event_listener_call(call: &CallExpression) -> bool { + let Some(member_expr) = call.callee.get_member_expr() else { + return false; + }; + + let MemberExpression::StaticMemberExpression(static_member) = member_expr else { + return false; + }; + + static_member.property.name == "addEventListener" + } + + /// Check if the callback is the second argument of the call + fn is_callback_argument(call: &CallExpression, callback: Option<&CallbackFunction>) -> bool { + let Some(callback) = callback else { + return false; + }; + + let Some(second_arg) = call.arguments.get(1) else { + return false; + }; + + match second_arg { + Argument::ArrowFunctionExpression(arrow) => { + matches!(callback, CallbackFunction::Arrow(cb) if std::ptr::eq(*cb, arrow.as_ref())) + } + Argument::FunctionExpression(func) => { + matches!(callback, CallbackFunction::Regular(cb) if std::ptr::eq(*cb, func.as_ref())) + } + _ => false, + } + } + + /// Report diagnostic with auto-fix for direct property access + fn report_diagnostic_with_fix<'a>( + property_name: &str, + property_span: Span, + member_node: &AstNode<'a>, + ctx: &LintContext<'a>, + ) { + // Try to find if this is part of a binary comparison like `event.keyCode === 27` + let parent = ctx.nodes().parent_node(member_node.id()); + + let AstKind::BinaryExpression(binary) = parent.kind() else { + ctx.diagnostic(prefer_keyboard_event_key_diagnostic(property_span, property_name)); + return; + }; + + // Check if it's an equality comparison + if !matches!( + binary.operator, + oxc_ast::ast::BinaryOperator::Equality | oxc_ast::ast::BinaryOperator::StrictEquality + ) { + ctx.diagnostic(prefer_keyboard_event_key_diagnostic(property_span, property_name)); + return; + } + + // Get the numeric literal from the comparison + let number_value = match (&binary.left, &binary.right) { + (_, Expression::NumericLiteral(num)) | (Expression::NumericLiteral(num), _) => { + Some((num.value, num.span)) + } + _ => None, + }; + + let Some((code_value, number_span)) = number_value else { + ctx.diagnostic(prefer_keyboard_event_key_diagnostic(property_span, property_name)); + return; + }; + + // Get the key name for this code + let Some(key_name) = get_key_from_code(code_value) else { + ctx.diagnostic(prefer_keyboard_event_key_diagnostic(property_span, property_name)); + return; + }; + + // Apply fix + ctx.diagnostic_with_fix( + prefer_keyboard_event_key_diagnostic(property_span, property_name), + |fixer| { + let mut codegen = fixer + .codegen() + .with_options(CodegenOptions { single_quote: true, ..Default::default() }); + let alloc = Allocator::default(); + let ast = AstBuilder::new(&alloc); + codegen.print_expression(&ast.expression_string_literal( + SPAN, + ast.atom(&key_name), + None, + )); + let key_str = codegen.into_source_text(); + + let mut fix = fixer.new_fix_with_capacity(2); + fix.push(fixer.replace(property_span, "key")); + fix.push(fixer.replace(number_span, key_str)); + fix.with_message(format!("Replace `.{property_name}` with `.key`")) + }, + ); + } +} + +/// Enum to represent either an arrow function or regular function callback +enum CallbackFunction<'a> { + Arrow(&'a oxc_ast::ast::ArrowFunctionExpression<'a>), + Regular(&'a Function<'a>), +} + +/// Check if a property name is a deprecated keyboard event property +fn is_deprecated_property(name: &str) -> bool { + matches!(name, "keyCode" | "charCode" | "which") +} + +/// Map from key code to key name for auto-fix +/// Based on +const KEY_CODE_TO_KEY: Map = phf_map! { + 8u32 => "Backspace", + 9u32 => "Tab", + 12u32 => "Clear", + 13u32 => "Enter", + 16u32 => "Shift", + 17u32 => "Control", + 18u32 => "Alt", + 19u32 => "Pause", + 20u32 => "CapsLock", + 27u32 => "Escape", + 32u32 => " ", + 33u32 => "PageUp", + 34u32 => "PageDown", + 35u32 => "End", + 36u32 => "Home", + 37u32 => "ArrowLeft", + 38u32 => "ArrowUp", + 39u32 => "ArrowRight", + 40u32 => "ArrowDown", + 45u32 => "Insert", + 46u32 => "Delete", + 112u32 => "F1", + 113u32 => "F2", + 114u32 => "F3", + 115u32 => "F4", + 116u32 => "F5", + 117u32 => "F6", + 118u32 => "F7", + 119u32 => "F8", + 120u32 => "F9", + 121u32 => "F10", + 122u32 => "F11", + 123u32 => "F12", + 144u32 => "NumLock", + 145u32 => "ScrollLock", + 186u32 => ";", + 187u32 => "=", + 188u32 => ",", + 189u32 => "-", + 190u32 => ".", + 191u32 => "/", + 219u32 => "[", + 220u32 => "\\", + 221u32 => "]", + 222u32 => "'", + 224u32 => "Meta", +}; + +/// Get the key string from a key code, either from the mapping or from char code +fn get_key_from_code(code: f64) -> Option { + #[expect(clippy::cast_possible_truncation, clippy::cast_sign_loss)] + let code_u32 = code as u32; + + // First try the known key mapping + if let Some(key) = KEY_CODE_TO_KEY.get(&code_u32) { + return Some((*key).to_string()); + } + + // For printable characters (A-Z, 0-9, etc.), convert from char code + char::from_u32(code_u32).map(|c| c.to_string()) +} + +#[test] +fn test() { + use crate::tester::Tester; + + let pass = vec![ + "window.addEventListener('click', e => { + console.log(e.key); + })", + "window.addEventListener('click', () => { + console.log(keyCode, which, charCode); + console.log(window.keyCode); + })", + "foo.addEventListener('click', (e, r, fg) => { + function a() { + if (true) { + { + { + const e = {}; + const { charCode } = e; + console.log(e.keyCode, charCode); + } + } + } + } + });", + "const e = {} + foo.addEventListener('click', function (event) { + function a() { + if (true) { + { + { + console.log(e.keyCode); + } + } + } + } + });", + "const { keyCode } = e", + "const { charCode } = e", + "const {a, b, c} = event", + "const keyCode = () => 4", + "const which = keyCode => 5", + "function which(abc) { const {keyCode} = abc; return keyCode}", + "const { which } = e", + "const { keyCode: key } = e", + "const { keyCode: abc } = e", + "foo.addEventListener('keydown', e => { + (function (abc) { + if (e.key === 'ArrowLeft') return true; + const { charCode } = abc; + }()) + })", + "foo.addEventListener('keydown', e => { + if (e.key === 'ArrowLeft') return true; + })", + "a.addEventListener('keyup', function (event) { + const key = event.key; + })", + "a.addEventListener('keyup', function (event) { + const { key } = event; + })", + "foo.addEventListener('click', e => { + const good = {}; + good.keyCode = '34'; + });", + "foo.addEventListener('click', e => { + const good = {}; + good.charCode = '34'; + });", + "foo.addEventListener('click', e => { + const good = {}; + good.which = '34'; + });", + "foo.addEventListener('click', e => { + const {keyCode: a, charCode: b, charCode: c} = e; + });", + "add.addEventListener('keyup', event => { + f.addEventList('some', e => { + const {charCode} = e; + console.log(event.key) + }) + })", + "foo.addEventListener('click', e => { + { + const e = {}; + console.log(e.keyCode); + } + });", + ]; + + let fail = vec![ + "window.addEventListener('click', e => { + console.log(e.keyCode); + })", + "window.addEventListener('click', ({keyCode}) => { + console.log(keyCode); + })", + "window.addEventListener('click', ({which}) => { + if (which === 23) { + console.log('Wrong!') + } + })", + "window.addEventListener('click', ({which, another}) => { + if (which === 23) { + console.log('Wrong!') + } + })", + "foo123.addEventListener('click', event => { + if (event.keyCode === 27) { + } + });", + "foo.addEventListener('click', event => { + if (event.keyCode === 65) {} + });", + "foo.addEventListener('click', event => { + if (event.keyCode === 10) {} + });", + "foo.addEventListener('click', event => { + if (!event.keyCode) {} + });", + "foo.addEventListener('click', a => { + if (a.keyCode === 27) { + } + });", + "foo.addEventListener('click', (a, b, c) => { + if (a.keyCode === 27) { + } + });", + "foo.addEventListener('click', function(a, b, c) { + if (a.keyCode === 27) { + } + });", + "foo.addEventListener('click', function(b) { + if (b.keyCode === 27) { + } + });", + "foo.addEventListener('click', e => { + const {keyCode, a, b} = e; + });", + "foo.addEventListener('click', e => { + const {a: keyCode, a, b} = e; + });", + "add.addEventListener('keyup', event => { + f.addEventList('some', e => { + const {keyCode} = event; + console.log(event.key) + }) + })", + "window.addEventListener('click', e => { + console.log(e.charCode); + })", + "foo11111111.addEventListener('click', event => { + if (event.charCode === 27) { + } + });", + "foo.addEventListener('click', a => { + if (a.charCode === 27) { + } + });", + "foo.addEventListener('click', (a, b, c) => { + if (a.charCode === 27) { + } + });", + "foo.addEventListener('click', function(a, b, c) { + if (a.charCode === 27) { + } + });", + "foo.addEventListener('click', function(b) { + if (b.charCode === 27) { + } + });", + "foo.addEventListener('click', e => { + const {charCode, a, b} = e; + });", + "foo.addEventListener('click', e => { + const {a: charCode, a, b} = e; + });", + "window.addEventListener('click', e => { + console.log(e.which); + })", + "foo.addEventListener('click', event => { + if (event.which === 27) { + } + });", + "foo.addEventListener('click', a => { + if (a.which === 27) { + } + });", + "foo.addEventListener('click', (a, b, c) => { + if (a.which === 27) { + } + });", + "foo.addEventListener('click', function(a, b, c) { + if (a.which === 27) { + } + });", + "foo.addEventListener('click', function(b) { + if (b.which === 27) { + } + });", + "foo.addEventListener('click', e => { + const {which, a, b} = e; + });", + "foo.addEventListener('click', e => { + const {a: which, a, b} = e; + });", + "foo.addEventListener('click', function(b) { + if (b.which === 27) { + } + const {keyCode} = b; + if (keyCode === 32) return 4; + });", + "foo.addEventListener('click', function(b) { + if (b.which > 27) { + } + const {keyCode} = b; + if (keyCode === 32) return 4; + });", + "const e = {} + foo.addEventListener('click', (e, r, fg) => { + function a() { + if (true) { + { + { + const { charCode } = e; + console.log(e.keyCode, charCode); + } + } + } + } + });", + "foo123.addEventListener('click', event => { + if (event.keyCode === 13) { + } + });", + "foo123.addEventListener('click', event => { + if (event.keyCode === 38) { + } + });", + "foo123.addEventListener('click', event => { + if (event.keyCode === 40) { + } + });", + "foo123.addEventListener('click', event => { + if (event.keyCode === 37) { + } + });", + "foo123.addEventListener('click', event => { + if (event.keyCode === 39) { + } + });", + "foo123.addEventListener('click', event => { + if (event.keyCode === 221) { + } + });", + "foo123.addEventListener('click', event => { + if (event.keyCode === 186) { + } + });", + "foo123.addEventListener('click', event => { + if (event.keyCode === 187) { + } + });", + "foo123.addEventListener('click', event => { + if (event.keyCode === 188) { + } + });", + "foo123.addEventListener('click', event => { + if (event.keyCode === 189) { + } + });", + "foo123.addEventListener('click', event => { + if (event.keyCode === 190) { + } + });", + "foo123.addEventListener('click', event => { + if (event.keyCode === 191) { + } + });", + "foo123.addEventListener('click', event => { + if (event.keyCode === 219) { + } + });", + "foo123.addEventListener('click', event => { + if (event.keyCode === 222) { + } + });", + "window.addEventListener('click', ({which, another}) => { + if (which === 23) { + console.log('Wrong!') + } + })", + "foo123.addEventListener('click', event => { + if (event.keyCode === 27) { + } + });", + ]; + + let fix = vec![ + ( + "foo123.addEventListener('click', event => { + if (event.keyCode === 27) { + } + });", + "foo123.addEventListener('click', event => { + if (event.key === 'Escape') { + } + });", + None, + ), + ( + "foo.addEventListener('click', event => { + if (event.keyCode === 65) {} + });", + "foo.addEventListener('click', event => { + if (event.key === 'A') {} + });", + None, + ), + ( + "foo.addEventListener('click', event => { + if (event.keyCode === 10) {} + });", + r"foo.addEventListener('click', event => { + if (event.key === '\n') {} + });", + None, + ), + ( + "foo.addEventListener('click', a => { + if (a.keyCode === 27) { + } + });", + "foo.addEventListener('click', a => { + if (a.key === 'Escape') { + } + });", + None, + ), + ( + "foo.addEventListener('click', (a, b, c) => { + if (a.keyCode === 27) { + } + });", + "foo.addEventListener('click', (a, b, c) => { + if (a.key === 'Escape') { + } + });", + None, + ), + ( + "foo.addEventListener('click', function(a, b, c) { + if (a.keyCode === 27) { + } + });", + "foo.addEventListener('click', function(a, b, c) { + if (a.key === 'Escape') { + } + });", + None, + ), + ( + "foo.addEventListener('click', function(b) { + if (b.keyCode === 27) { + } + });", + "foo.addEventListener('click', function(b) { + if (b.key === 'Escape') { + } + });", + None, + ), + ( + "foo11111111.addEventListener('click', event => { + if (event.charCode === 27) { + } + });", + "foo11111111.addEventListener('click', event => { + if (event.key === 'Escape') { + } + });", + None, + ), + ( + "foo.addEventListener('click', a => { + if (a.charCode === 27) { + } + });", + "foo.addEventListener('click', a => { + if (a.key === 'Escape') { + } + });", + None, + ), + ( + "foo.addEventListener('click', (a, b, c) => { + if (a.charCode === 27) { + } + });", + "foo.addEventListener('click', (a, b, c) => { + if (a.key === 'Escape') { + } + });", + None, + ), + ( + "foo.addEventListener('click', function(a, b, c) { + if (a.charCode === 27) { + } + });", + "foo.addEventListener('click', function(a, b, c) { + if (a.key === 'Escape') { + } + });", + None, + ), + ( + "foo.addEventListener('click', function(b) { + if (b.charCode === 27) { + } + });", + "foo.addEventListener('click', function(b) { + if (b.key === 'Escape') { + } + });", + None, + ), + ( + "foo.addEventListener('click', event => { + if (event.which === 27) { + } + });", + "foo.addEventListener('click', event => { + if (event.key === 'Escape') { + } + });", + None, + ), + ( + "foo.addEventListener('click', a => { + if (a.which === 27) { + } + });", + "foo.addEventListener('click', a => { + if (a.key === 'Escape') { + } + });", + None, + ), + ( + "foo.addEventListener('click', (a, b, c) => { + if (a.which === 27) { + } + });", + "foo.addEventListener('click', (a, b, c) => { + if (a.key === 'Escape') { + } + });", + None, + ), + ( + "foo.addEventListener('click', function(a, b, c) { + if (a.which === 27) { + } + });", + "foo.addEventListener('click', function(a, b, c) { + if (a.key === 'Escape') { + } + });", + None, + ), + ( + "foo.addEventListener('click', function(b) { + if (b.which === 27) { + } + });", + "foo.addEventListener('click', function(b) { + if (b.key === 'Escape') { + } + });", + None, + ), + ( + "foo.addEventListener('click', function(b) { + if (b.which === 27) { + } + const {keyCode} = b; + if (keyCode === 32) return 4; + });", + "foo.addEventListener('click', function(b) { + if (b.key === 'Escape') { + } + const {keyCode} = b; + if (keyCode === 32) return 4; + });", + None, + ), + ( + "foo123.addEventListener('click', event => { + if (event.keyCode === 13) { + } + });", + "foo123.addEventListener('click', event => { + if (event.key === 'Enter') { + } + });", + None, + ), + ( + "foo123.addEventListener('click', event => { + if (event.keyCode === 38) { + } + });", + "foo123.addEventListener('click', event => { + if (event.key === 'ArrowUp') { + } + });", + None, + ), + ( + "foo123.addEventListener('click', event => { + if (event.keyCode === 40) { + } + });", + "foo123.addEventListener('click', event => { + if (event.key === 'ArrowDown') { + } + });", + None, + ), + ( + "foo123.addEventListener('click', event => { + if (event.keyCode === 37) { + } + });", + "foo123.addEventListener('click', event => { + if (event.key === 'ArrowLeft') { + } + });", + None, + ), + ( + "foo123.addEventListener('click', event => { + if (event.keyCode === 39) { + } + });", + "foo123.addEventListener('click', event => { + if (event.key === 'ArrowRight') { + } + });", + None, + ), + ( + "foo123.addEventListener('click', event => { + if (event.keyCode === 221) { + } + });", + "foo123.addEventListener('click', event => { + if (event.key === ']') { + } + });", + None, + ), + ( + "foo123.addEventListener('click', event => { + if (event.keyCode === 186) { + } + });", + "foo123.addEventListener('click', event => { + if (event.key === ';') { + } + });", + None, + ), + ( + "foo123.addEventListener('click', event => { + if (event.keyCode === 187) { + } + });", + "foo123.addEventListener('click', event => { + if (event.key === '=') { + } + });", + None, + ), + ( + "foo123.addEventListener('click', event => { + if (event.keyCode === 188) { + } + });", + "foo123.addEventListener('click', event => { + if (event.key === ',') { + } + });", + None, + ), + ( + "foo123.addEventListener('click', event => { + if (event.keyCode === 189) { + } + });", + "foo123.addEventListener('click', event => { + if (event.key === '-') { + } + });", + None, + ), + ( + "foo123.addEventListener('click', event => { + if (event.keyCode === 190) { + } + });", + "foo123.addEventListener('click', event => { + if (event.key === '.') { + } + });", + None, + ), + ( + "foo123.addEventListener('click', event => { + if (event.keyCode === 191) { + } + });", + "foo123.addEventListener('click', event => { + if (event.key === '/') { + } + });", + None, + ), + ( + "foo123.addEventListener('click', event => { + if (event.keyCode === 219) { + } + });", + "foo123.addEventListener('click', event => { + if (event.key === '[') { + } + });", + None, + ), + ( + "foo123.addEventListener('click', event => { + if (event.keyCode === 222) { + } + });", + "foo123.addEventListener('click', event => { + if (event.key === '\\'') { + } + });", + None, + ), + ]; + Tester::new(PreferKeyboardEventKey::NAME, PreferKeyboardEventKey::PLUGIN, pass, fail) + .expect_fix(fix) + .test_and_snapshot(); +} diff --git a/crates/oxc_linter/src/snapshots/unicorn_prefer_keyboard_event_key.snap b/crates/oxc_linter/src/snapshots/unicorn_prefer_keyboard_event_key.snap new file mode 100644 index 0000000000000..e73b608467883 --- /dev/null +++ b/crates/oxc_linter/src/snapshots/unicorn_prefer_keyboard_event_key.snap @@ -0,0 +1,475 @@ +--- +source: crates/oxc_linter/src/tester.rs +--- + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.keyCode` + ╭─[prefer_keyboard_event_key.tsx:2:19] + 1 │ window.addEventListener('click', e => { + 2 │ console.log(e.keyCode); + · ─────── + 3 │ }) + ╰──── + help: The `keyCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.keyCode` + ╭─[prefer_keyboard_event_key.tsx:1:36] + 1 │ window.addEventListener('click', ({keyCode}) => { + · ─────── + 2 │ console.log(keyCode); + ╰──── + help: The `keyCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.which` + ╭─[prefer_keyboard_event_key.tsx:1:36] + 1 │ window.addEventListener('click', ({which}) => { + · ───── + 2 │ if (which === 23) { + ╰──── + help: The `which` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.which` + ╭─[prefer_keyboard_event_key.tsx:1:36] + 1 │ window.addEventListener('click', ({which, another}) => { + · ───── + 2 │ if (which === 23) { + ╰──── + help: The `which` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.keyCode` + ╭─[prefer_keyboard_event_key.tsx:2:15] + 1 │ foo123.addEventListener('click', event => { + 2 │ if (event.keyCode === 27) { + · ─────── + 3 │ } + ╰──── + help: The `keyCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.keyCode` + ╭─[prefer_keyboard_event_key.tsx:2:15] + 1 │ foo.addEventListener('click', event => { + 2 │ if (event.keyCode === 65) {} + · ─────── + 3 │ }); + ╰──── + help: The `keyCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.keyCode` + ╭─[prefer_keyboard_event_key.tsx:2:15] + 1 │ foo.addEventListener('click', event => { + 2 │ if (event.keyCode === 10) {} + · ─────── + 3 │ }); + ╰──── + help: The `keyCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.keyCode` + ╭─[prefer_keyboard_event_key.tsx:2:16] + 1 │ foo.addEventListener('click', event => { + 2 │ if (!event.keyCode) {} + · ─────── + 3 │ }); + ╰──── + help: The `keyCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.keyCode` + ╭─[prefer_keyboard_event_key.tsx:2:11] + 1 │ foo.addEventListener('click', a => { + 2 │ if (a.keyCode === 27) { + · ─────── + 3 │ } + ╰──── + help: The `keyCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.keyCode` + ╭─[prefer_keyboard_event_key.tsx:2:11] + 1 │ foo.addEventListener('click', (a, b, c) => { + 2 │ if (a.keyCode === 27) { + · ─────── + 3 │ } + ╰──── + help: The `keyCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.keyCode` + ╭─[prefer_keyboard_event_key.tsx:2:11] + 1 │ foo.addEventListener('click', function(a, b, c) { + 2 │ if (a.keyCode === 27) { + · ─────── + 3 │ } + ╰──── + help: The `keyCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.keyCode` + ╭─[prefer_keyboard_event_key.tsx:2:11] + 1 │ foo.addEventListener('click', function(b) { + 2 │ if (b.keyCode === 27) { + · ─────── + 3 │ } + ╰──── + help: The `keyCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.keyCode` + ╭─[prefer_keyboard_event_key.tsx:2:12] + 1 │ foo.addEventListener('click', e => { + 2 │ const {keyCode, a, b} = e; + · ─────── + 3 │ }); + ╰──── + help: The `keyCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.keyCode` + ╭─[prefer_keyboard_event_key.tsx:2:15] + 1 │ foo.addEventListener('click', e => { + 2 │ const {a: keyCode, a, b} = e; + · ─────── + 3 │ }); + ╰──── + help: The `keyCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.keyCode` + ╭─[prefer_keyboard_event_key.tsx:3:13] + 2 │ f.addEventList('some', e => { + 3 │ const {keyCode} = event; + · ─────── + 4 │ console.log(event.key) + ╰──── + help: The `keyCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.charCode` + ╭─[prefer_keyboard_event_key.tsx:2:19] + 1 │ window.addEventListener('click', e => { + 2 │ console.log(e.charCode); + · ──────── + 3 │ }) + ╰──── + help: The `charCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.charCode` + ╭─[prefer_keyboard_event_key.tsx:2:15] + 1 │ foo11111111.addEventListener('click', event => { + 2 │ if (event.charCode === 27) { + · ──────── + 3 │ } + ╰──── + help: The `charCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.charCode` + ╭─[prefer_keyboard_event_key.tsx:2:11] + 1 │ foo.addEventListener('click', a => { + 2 │ if (a.charCode === 27) { + · ──────── + 3 │ } + ╰──── + help: The `charCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.charCode` + ╭─[prefer_keyboard_event_key.tsx:2:11] + 1 │ foo.addEventListener('click', (a, b, c) => { + 2 │ if (a.charCode === 27) { + · ──────── + 3 │ } + ╰──── + help: The `charCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.charCode` + ╭─[prefer_keyboard_event_key.tsx:2:11] + 1 │ foo.addEventListener('click', function(a, b, c) { + 2 │ if (a.charCode === 27) { + · ──────── + 3 │ } + ╰──── + help: The `charCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.charCode` + ╭─[prefer_keyboard_event_key.tsx:2:11] + 1 │ foo.addEventListener('click', function(b) { + 2 │ if (b.charCode === 27) { + · ──────── + 3 │ } + ╰──── + help: The `charCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.charCode` + ╭─[prefer_keyboard_event_key.tsx:2:12] + 1 │ foo.addEventListener('click', e => { + 2 │ const {charCode, a, b} = e; + · ──────── + 3 │ }); + ╰──── + help: The `charCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.charCode` + ╭─[prefer_keyboard_event_key.tsx:2:15] + 1 │ foo.addEventListener('click', e => { + 2 │ const {a: charCode, a, b} = e; + · ──────── + 3 │ }); + ╰──── + help: The `charCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.which` + ╭─[prefer_keyboard_event_key.tsx:2:19] + 1 │ window.addEventListener('click', e => { + 2 │ console.log(e.which); + · ───── + 3 │ }) + ╰──── + help: The `which` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.which` + ╭─[prefer_keyboard_event_key.tsx:2:15] + 1 │ foo.addEventListener('click', event => { + 2 │ if (event.which === 27) { + · ───── + 3 │ } + ╰──── + help: The `which` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.which` + ╭─[prefer_keyboard_event_key.tsx:2:11] + 1 │ foo.addEventListener('click', a => { + 2 │ if (a.which === 27) { + · ───── + 3 │ } + ╰──── + help: The `which` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.which` + ╭─[prefer_keyboard_event_key.tsx:2:11] + 1 │ foo.addEventListener('click', (a, b, c) => { + 2 │ if (a.which === 27) { + · ───── + 3 │ } + ╰──── + help: The `which` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.which` + ╭─[prefer_keyboard_event_key.tsx:2:11] + 1 │ foo.addEventListener('click', function(a, b, c) { + 2 │ if (a.which === 27) { + · ───── + 3 │ } + ╰──── + help: The `which` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.which` + ╭─[prefer_keyboard_event_key.tsx:2:11] + 1 │ foo.addEventListener('click', function(b) { + 2 │ if (b.which === 27) { + · ───── + 3 │ } + ╰──── + help: The `which` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.which` + ╭─[prefer_keyboard_event_key.tsx:2:12] + 1 │ foo.addEventListener('click', e => { + 2 │ const {which, a, b} = e; + · ───── + 3 │ }); + ╰──── + help: The `which` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.which` + ╭─[prefer_keyboard_event_key.tsx:2:15] + 1 │ foo.addEventListener('click', e => { + 2 │ const {a: which, a, b} = e; + · ───── + 3 │ }); + ╰──── + help: The `which` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.which` + ╭─[prefer_keyboard_event_key.tsx:2:11] + 1 │ foo.addEventListener('click', function(b) { + 2 │ if (b.which === 27) { + · ───── + 3 │ } + ╰──── + help: The `which` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.keyCode` + ╭─[prefer_keyboard_event_key.tsx:4:12] + 3 │ } + 4 │ const {keyCode} = b; + · ─────── + 5 │ if (keyCode === 32) return 4; + ╰──── + help: The `keyCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.which` + ╭─[prefer_keyboard_event_key.tsx:2:11] + 1 │ foo.addEventListener('click', function(b) { + 2 │ if (b.which > 27) { + · ───── + 3 │ } + ╰──── + help: The `which` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.keyCode` + ╭─[prefer_keyboard_event_key.tsx:4:12] + 3 │ } + 4 │ const {keyCode} = b; + · ─────── + 5 │ if (keyCode === 32) return 4; + ╰──── + help: The `keyCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.charCode` + ╭─[prefer_keyboard_event_key.tsx:7:17] + 6 │ { + 7 │ const { charCode } = e; + · ──────── + 8 │ console.log(e.keyCode, charCode); + ╰──── + help: The `charCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.keyCode` + ╭─[prefer_keyboard_event_key.tsx:8:23] + 7 │ const { charCode } = e; + 8 │ console.log(e.keyCode, charCode); + · ─────── + 9 │ } + ╰──── + help: The `keyCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.keyCode` + ╭─[prefer_keyboard_event_key.tsx:2:15] + 1 │ foo123.addEventListener('click', event => { + 2 │ if (event.keyCode === 13) { + · ─────── + 3 │ } + ╰──── + help: The `keyCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.keyCode` + ╭─[prefer_keyboard_event_key.tsx:2:15] + 1 │ foo123.addEventListener('click', event => { + 2 │ if (event.keyCode === 38) { + · ─────── + 3 │ } + ╰──── + help: The `keyCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.keyCode` + ╭─[prefer_keyboard_event_key.tsx:2:15] + 1 │ foo123.addEventListener('click', event => { + 2 │ if (event.keyCode === 40) { + · ─────── + 3 │ } + ╰──── + help: The `keyCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.keyCode` + ╭─[prefer_keyboard_event_key.tsx:2:15] + 1 │ foo123.addEventListener('click', event => { + 2 │ if (event.keyCode === 37) { + · ─────── + 3 │ } + ╰──── + help: The `keyCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.keyCode` + ╭─[prefer_keyboard_event_key.tsx:2:15] + 1 │ foo123.addEventListener('click', event => { + 2 │ if (event.keyCode === 39) { + · ─────── + 3 │ } + ╰──── + help: The `keyCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.keyCode` + ╭─[prefer_keyboard_event_key.tsx:2:15] + 1 │ foo123.addEventListener('click', event => { + 2 │ if (event.keyCode === 221) { + · ─────── + 3 │ } + ╰──── + help: The `keyCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.keyCode` + ╭─[prefer_keyboard_event_key.tsx:2:15] + 1 │ foo123.addEventListener('click', event => { + 2 │ if (event.keyCode === 186) { + · ─────── + 3 │ } + ╰──── + help: The `keyCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.keyCode` + ╭─[prefer_keyboard_event_key.tsx:2:15] + 1 │ foo123.addEventListener('click', event => { + 2 │ if (event.keyCode === 187) { + · ─────── + 3 │ } + ╰──── + help: The `keyCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.keyCode` + ╭─[prefer_keyboard_event_key.tsx:2:15] + 1 │ foo123.addEventListener('click', event => { + 2 │ if (event.keyCode === 188) { + · ─────── + 3 │ } + ╰──── + help: The `keyCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.keyCode` + ╭─[prefer_keyboard_event_key.tsx:2:15] + 1 │ foo123.addEventListener('click', event => { + 2 │ if (event.keyCode === 189) { + · ─────── + 3 │ } + ╰──── + help: The `keyCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.keyCode` + ╭─[prefer_keyboard_event_key.tsx:2:15] + 1 │ foo123.addEventListener('click', event => { + 2 │ if (event.keyCode === 190) { + · ─────── + 3 │ } + ╰──── + help: The `keyCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.keyCode` + ╭─[prefer_keyboard_event_key.tsx:2:15] + 1 │ foo123.addEventListener('click', event => { + 2 │ if (event.keyCode === 191) { + · ─────── + 3 │ } + ╰──── + help: The `keyCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.keyCode` + ╭─[prefer_keyboard_event_key.tsx:2:15] + 1 │ foo123.addEventListener('click', event => { + 2 │ if (event.keyCode === 219) { + · ─────── + 3 │ } + ╰──── + help: The `keyCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.keyCode` + ╭─[prefer_keyboard_event_key.tsx:2:15] + 1 │ foo123.addEventListener('click', event => { + 2 │ if (event.keyCode === 222) { + · ─────── + 3 │ } + ╰──── + help: The `keyCode` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.which` + ╭─[prefer_keyboard_event_key.tsx:1:36] + 1 │ window.addEventListener('click', ({which, another}) => { + · ───── + 2 │ if (which === 23) { + ╰──── + help: The `which` property is deprecated. + + ⚠ eslint-plugin-unicorn(prefer-keyboard-event-key): Use `.key` instead of `.keyCode` + ╭─[prefer_keyboard_event_key.tsx:2:15] + 1 │ foo123.addEventListener('click', event => { + 2 │ if (event.keyCode === 27) { + · ─────── + 3 │ } + ╰──── + help: The `keyCode` property is deprecated.