diff --git a/crates/oxc_linter/src/generated/rule_runner_impls.rs b/crates/oxc_linter/src/generated/rule_runner_impls.rs index f1b7ae230d6d5..84b24191c9450 100644 --- a/crates/oxc_linter/src/generated/rule_runner_impls.rs +++ b/crates/oxc_linter/src/generated/rule_runner_impls.rs @@ -4413,7 +4413,10 @@ impl RuleRunner for crate::rules::vue::no_multiple_slot_args::NoMultipleSlotArgs } impl RuleRunner for crate::rules::vue::no_required_prop_with_default::NoRequiredPropWithDefault { - const NODE_TYPES: Option<&AstTypesBitset> = None; + const NODE_TYPES: Option<&AstTypesBitset> = Some(&AstTypesBitset::from_types(&[ + AstType::CallExpression, + AstType::ExportDefaultDeclaration, + ])); const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Run; } diff --git a/crates/oxc_linter/src/rules/vue/no_required_prop_with_default.rs b/crates/oxc_linter/src/rules/vue/no_required_prop_with_default.rs index dd1b8a2036d72..c82151ebb36d2 100644 --- a/crates/oxc_linter/src/rules/vue/no_required_prop_with_default.rs +++ b/crates/oxc_linter/src/rules/vue/no_required_prop_with_default.rs @@ -1,15 +1,17 @@ +use rustc_hash::FxHashSet; + use oxc_ast::{ AstKind, ast::{ - BindingPattern, ExportDefaultDeclarationKind, Expression, ObjectExpression, - ObjectPropertyKind, PropertyKey, TSMethodSignatureKind, TSSignature, TSType, TSTypeName, - VariableDeclarator, + BindingPattern, CallExpression, ExportDefaultDeclaration, ExportDefaultDeclarationKind, + Expression, ObjectExpression, ObjectPropertyKind, PropertyKey, TSMethodSignatureKind, + TSSignature, TSType, TSTypeName, VariableDeclarator, }, }; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; +use oxc_semantic::NodeId; use oxc_span::{GetSpan, Span}; -use rustc_hash::FxHashSet; use crate::{ AstNode, @@ -79,31 +81,37 @@ declare_oxc_lint!( impl Rule for NoRequiredPropWithDefault { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let is_vue = ctx.file_extension().is_some_and(|ext| ext == "vue"); - if is_vue { - self.run_on_vue(node, ctx); - } else { - self.check_define_component(node, ctx); + match node.kind() { + AstKind::CallExpression(call_expr) => { + if ctx.file_extension().is_some_and(|ext| ext == "vue") { + Self::run_on_vue(node.id(), call_expr, ctx); + } else { + Self::check_define_component(call_expr, ctx); + } + } + AstKind::ExportDefaultDeclaration(export_default_decl) + if ctx.file_extension().is_some_and(|ext| ext == "vue") + && ctx.frameworks_options() != FrameworkOptions::VueSetup => + { + Self::run_on_composition(export_default_decl, ctx); + } + _ => {} } } } impl NoRequiredPropWithDefault { - fn run_on_vue<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + fn run_on_vue<'a>(node_id: NodeId, call_expr: &CallExpression<'a>, ctx: &LintContext<'a>) { if ctx.frameworks_options() == FrameworkOptions::VueSetup { - self.run_on_setup(node, ctx); + Self::run_on_setup(node_id, call_expr, ctx); } else { - self.run_on_composition(node, ctx); + Self::check_define_component(call_expr, ctx); } } - #[expect(clippy::unused_self)] - fn check_define_component(&self, node: &AstNode<'_>, ctx: &LintContext<'_>) { + fn check_define_component(call_expr: &CallExpression<'_>, ctx: &LintContext<'_>) { // only check `defineComponent` method // e.g. `let component = defineComponent({ props: { name: { required: true, default: 'a' } } })` - let AstKind::CallExpression(call_expr) = node.kind() else { - return; - }; let Some(ident) = call_expr.callee.get_identifier_reference() else { return; }; @@ -116,10 +124,7 @@ impl NoRequiredPropWithDefault { } } - fn run_on_setup<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let AstKind::CallExpression(call_expr) = node.kind() else { - return; - }; + fn run_on_setup<'a>(node_id: NodeId, call_expr: &CallExpression<'a>, ctx: &LintContext<'a>) { let Some(ident) = call_expr.callee.get_identifier_reference() else { return; }; @@ -134,7 +139,7 @@ impl NoRequiredPropWithDefault { // 1. const props = defineProps({ name: { required: true, default: 'a' } }) // 2. const { name = 'a' } = defineProps({ name: { required: true } }) let key_hash = - collect_hash_from_variable_declarator(ctx, node).unwrap_or_default(); + collect_hash_from_variable_declarator(ctx, node_id).unwrap_or_default(); handle_prop_object(ctx, obj, Some(&key_hash)); } if call_expr.arguments.is_empty() { @@ -146,7 +151,7 @@ impl NoRequiredPropWithDefault { let Some(first_type_argument) = type_args.params.first() else { return; }; - if let Some(key_hash) = collect_hash_from_variable_declarator(ctx, node) { + if let Some(key_hash) = collect_hash_from_variable_declarator(ctx, node_id) { handle_type_argument(ctx, first_type_argument, &key_hash); } } @@ -170,25 +175,21 @@ impl NoRequiredPropWithDefault { } } _ => { - self.check_define_component(node, ctx); + Self::check_define_component(call_expr, ctx); } } } - fn run_on_composition<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - match node.kind() { - AstKind::ExportDefaultDeclaration(export_default_decl) => { - let ExportDefaultDeclarationKind::ObjectExpression(obj_expr) = - &export_default_decl.declaration - else { - return; - }; - handle_object_expression(ctx, obj_expr); - } - _ => { - self.check_define_component(node, ctx); - } - } + fn run_on_composition<'a>( + export_default_decl: &ExportDefaultDeclaration<'a>, + ctx: &LintContext<'a>, + ) { + let ExportDefaultDeclarationKind::ObjectExpression(obj_expr) = + &export_default_decl.declaration + else { + return; + }; + handle_object_expression(ctx, obj_expr); } } @@ -214,9 +215,9 @@ fn collect_hash_from_object_expr(obj: &ObjectExpression) -> Option, - node: &AstNode, + node_id: NodeId, ) -> Option> { - let var_decl = get_first_variable_decl_ancestor(ctx, node)?; + let var_decl = get_first_variable_decl_ancestor(ctx, node_id)?; let BindingPattern::ObjectPattern(obj_pattern) = &var_decl.id else { return None; }; @@ -237,9 +238,9 @@ fn collect_hash_from_variable_declarator( fn get_first_variable_decl_ancestor<'a>( ctx: &LintContext<'a>, - node: &AstNode, + node_id: NodeId, ) -> Option<&'a VariableDeclarator<'a>> { - ctx.nodes().ancestors(node.id()).find_map(|ancestor| { + ctx.nodes().ancestors(node_id).find_map(|ancestor| { if let AstKind::VariableDeclarator(var_decl) = ancestor.kind() { Some(var_decl) } else {