Skip to content

Commit 33e5cdf

Browse files
authored
fix(noUndeclaredVariables): consult embedded bindings service before flagging false positives (#9269)
1 parent 65ae4c1 commit 33e5cdf

4 files changed

Lines changed: 88 additions & 0 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@biomejs/biome": patch
3+
---
4+
5+
Fixed a false positive where `noUndeclaredVariables` reported bindings from Vue `<script setup>` as undeclared when used in `<template>`.
6+
7+
This change ensures embedded bindings collected from script snippets (like imports and `defineModel` results) are respected by the rule.

crates/biome_cli/tests/cases/handle_vue_files.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1687,3 +1687,44 @@ let counter = 0;
16871687
result,
16881688
));
16891689
}
1690+
1691+
#[test]
1692+
fn no_undeclared_variables_not_triggered_for_script_setup_bindings() {
1693+
let fs = MemoryFileSystem::default();
1694+
let mut console = BufferConsole::default();
1695+
1696+
fs.insert(
1697+
"biome.json".into(),
1698+
r#"{ "html": { "linter": {"enabled": true}, "experimentalFullSupportEnabled": true } }"#
1699+
.as_bytes(),
1700+
);
1701+
1702+
let file = Utf8Path::new("file.vue");
1703+
fs.insert(
1704+
file.into(),
1705+
r#"<script setup lang="ts">
1706+
import { ALL_SKIP_CATEGORIES } from "ott-common";
1707+
const model = defineModel<string[]>();
1708+
</script>
1709+
<template>
1710+
<div :data-categories="ALL_SKIP_CATEGORIES">{{ model }}</div>
1711+
</template>"#
1712+
.as_bytes(),
1713+
);
1714+
1715+
let (fs, result) = run_cli(
1716+
fs,
1717+
&mut console,
1718+
Args::from(["lint", "--only=noUndeclaredVariables", file.as_str()].as_slice()),
1719+
);
1720+
1721+
assert!(result.is_ok(), "run_cli returned {result:?}");
1722+
1723+
assert_cli_snapshot(SnapshotPayload::new(
1724+
module_path!(),
1725+
"no_undeclared_variables_not_triggered_for_script_setup_bindings",
1726+
fs,
1727+
console,
1728+
result,
1729+
));
1730+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
source: crates/biome_cli/tests/snap_test.rs
3+
expression: redactor(content)
4+
---
5+
## `biome.json`
6+
7+
```json
8+
{
9+
"html": {
10+
"linter": { "enabled": true },
11+
"experimentalFullSupportEnabled": true
12+
}
13+
}
14+
```
15+
16+
## `file.vue`
17+
18+
```vue
19+
<script setup lang="ts">
20+
import { ALL_SKIP_CATEGORIES } from "ott-common";
21+
const model = defineModel<string[]>();
22+
</script>
23+
<template>
24+
<div :data-categories="ALL_SKIP_CATEGORIES">{{ model }}</div>
25+
</template>
26+
```
27+
28+
# Emitted Messages
29+
30+
```block
31+
Checked 1 file in <TIME>. No fixes applied.
32+
```

crates/biome_js_analyze/src/lint/correctness/no_undeclared_variables.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::globals::{is_js_global, is_ts_global};
2+
use crate::services::embedded_bindings::EmbeddedBindings;
23
use crate::services::semantic::SemanticServices;
34
use biome_analyze::context::RuleContext;
45
use biome_analyze::{Rule, RuleDiagnostic, RuleSource, declare_lint_rule};
@@ -86,6 +87,13 @@ impl Rule for NoUndeclaredVariables {
8687
return None;
8788
}
8889

90+
let embedded_bindings = ctx
91+
.get_service::<EmbeddedBindings>()
92+
.expect("embedded bindings service");
93+
if embedded_bindings.contains_binding(text) {
94+
return None;
95+
}
96+
8997
// Typescript Const Assertion
9098
if text == "const" && under_as_expression {
9199
return None;

0 commit comments

Comments
 (0)