Skip to content

Commit 2af85c1

Browse files
authored
fix(linter): improve Vue defineProps handling in noVueReservedKeys (#8448)
1 parent 67546bc commit 2af85c1

22 files changed

Lines changed: 518 additions & 382 deletions

File tree

.changeset/mighty-shoes-deny.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@biomejs/biome": patch
3+
---
4+
5+
Improved handling of `defineProps()` macro in Vue components. The [`noVueReservedKeys`](https://biomejs.dev/linter/rules/no-vue-reserved-keys/) rule now avoids false positives in non-setup scripts.

crates/biome_html_syntax/src/element_ext.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,11 @@ impl HtmlElement {
195195
self.is_script_tag() && self.has_attribute("lang", "ts")
196196
}
197197

198+
/// Returns `true` if the element is a `<script setup>` tag.
199+
pub fn is_script_with_setup_attribute(&self) -> bool {
200+
self.is_script_tag() && self.find_attribute_by_name("setup").is_some()
201+
}
202+
198203
/// Returns `true` if the element is a `<script lang="jsx">`
199204
pub fn is_jsx_lang(&self) -> bool {
200205
self.is_script_tag() && self.has_attribute("lang", "jsx")

crates/biome_js_analyze/src/frameworks/vue/vue_component.rs

Lines changed: 316 additions & 309 deletions
Large diffs are not rendered by default.

crates/biome_js_analyze/src/lint/nursery/no_vue_duplicate_keys.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,10 @@ impl Rule for NoVueDuplicateKeys {
125125
type Options = NoVueDuplicateKeysOptions;
126126

127127
fn run(ctx: &RuleContext<Self>) -> Self::Signals {
128+
let model = ctx.model();
128129
let Some(component) = VueComponent::from_potential_component(
129130
ctx.query(),
130-
ctx.model(),
131+
model,
131132
ctx.source_type(),
132133
ctx.file_path(),
133134
) else {
@@ -139,6 +140,12 @@ impl Rule for NoVueDuplicateKeys {
139140
// Collect all declarations across all Vue component sections
140141
for declaration in component.declarations(VueDeclarationCollectionFilter::all()) {
141142
if let Some(name) = declaration.declaration_name() {
143+
// Handle cases like `const { foo } = defineProps(...);`.
144+
if let VueDeclaration::Setup(ref setup_decl) = declaration
145+
&& setup_decl.is_assigned_to_props(model)
146+
{
147+
continue;
148+
}
142149
let key = name.text().to_string();
143150
key_declarations.entry(key).or_default().push(declaration);
144151
}

crates/biome_js_analyze/src/lint/nursery/no_vue_reserved_keys.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,11 @@ impl Rule for NoVueReservedKeys {
125125
return Box::new([]);
126126
};
127127
component
128-
.declarations(VueDeclarationCollectionFilter::all())
128+
.declarations(
129+
VueDeclarationCollectionFilter::all()
130+
& !(VueDeclarationCollectionFilter::Setup
131+
| VueDeclarationCollectionFilter::SetupImport),
132+
)
129133
.into_iter()
130134
.filter_map(|declaration| {
131135
if let Some(name) = declaration.declaration_name() {

crates/biome_js_analyze/src/lint/nursery/no_vue_setup_props_reactivity_loss.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ impl Rule for NoVueSetupPropsReactivityLoss {
8484
AnyPotentialVueComponent::JsCallExpression(call_expr) => {
8585
check_call_expression_setup(call_expr)
8686
}
87+
_ => Self::Signals::default(),
8788
}
8889
}
8990

crates/biome_js_analyze/tests/spec_tests.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use biome_diagnostics::advice::CodeSuggestionAdvice;
66
use biome_fs::OsFileSystem;
77
use biome_js_analyze::JsAnalyzerServices;
88
use biome_js_parser::{JsParserOptions, parse};
9-
use biome_js_syntax::{AnyJsRoot, EmbeddingKind, JsFileSource, JsLanguage, ModuleKind};
9+
use biome_js_syntax::{AnyJsRoot, JsFileSource, JsLanguage, ModuleKind};
1010
use biome_package::PackageType;
1111
use biome_plugin_loader::AnalyzerGritPlugin;
1212
use biome_rowan::{AstNode, FileSourceError};
@@ -121,7 +121,8 @@ fn run_test(input: &'static str, _: &str, _: &str, _: &str) {
121121
// This is needed to set the language to TypeScript for Vue files
122122
// because we can't do it in <script> definition in the current implementation.
123123
let source_type = if source_type.as_embedding_kind().is_vue() {
124-
JsFileSource::ts().with_embedding_kind(EmbeddingKind::Vue)
124+
JsFileSource::ts()
125+
.with_embedding_kind(*VueFileHandler::file_source(&input_code).as_embedding_kind())
125126
} else {
126127
source_type
127128
};

crates/biome_js_analyze/tests/specs/nursery/noVueReservedKeys/invalid-setup-interface.vue

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
<script setup lang="ts">
2-
; // this is a hack because vue files are still parsed as js/ts files.
32
interface Props {
43
$el: string
54
}

crates/biome_js_analyze/tests/specs/nursery/noVueReservedKeys/invalid-setup-interface.vue.snap

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
---
22
source: crates/biome_js_analyze/tests/spec_tests.rs
3+
assertion_line: 151
34
expression: invalid-setup-interface.vue
45
---
56
# Input
67
```ts
7-
; // this is a hack because vue files are still parsed as js/ts files.
88
interface Props {
99
$el: string
1010
}
@@ -14,16 +14,15 @@ defineProps<Props>();
1414
1515
# Diagnostics
1616
```
17-
invalid-setup-interface.vue:3:5 lint/nursery/noVueReservedKeys ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
17+
invalid-setup-interface.vue:2:5 lint/nursery/noVueReservedKeys ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1818

1919
× Key $el is reserved in Vue.
2020

21-
1 │ ; // this is a hack because vue files are still parsed as js/ts files.
22-
2 │ interface Props {
23-
> 3 │ $el: string
21+
1 │ interface Props {
22+
> 2 │ $el: string
2423
^^^
25-
4 │ }
26-
5defineProps<Props>();
24+
3 │ }
25+
4defineProps<Props>();
2726

2827
i Rename the key to avoid conflicts with Vue reserved keys.
2928

crates/biome_js_analyze/tests/specs/nursery/noVueReservedKeys/invalid-setup-type-alias.vue

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
<script setup lang="ts">
2-
; // this is a hack because vue files are still parsed as js/ts files.
32
type A = {
43
$el: string
54
};

0 commit comments

Comments
 (0)