Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .changeset/angry-carpets-switch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"@biomejs/biome": patch
---

Fixed [#8117](https://github.com/biomejs/biome/issues/8117): [`useValidLang`](https://biomejs.dev/linter/rules/use-valid-lang/) now accepts valid [BCP 47 language tags](https://developer.mozilla.org/en-US/docs/Glossary/BCP_47_language_tag) with script subtags.

**Valid:**

```html
<html lang="zh-Hans-CN"></html>
```
26 changes: 17 additions & 9 deletions crates/biome_aria_metadata/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,21 @@ const ISO_COUNTRIES: &[&str] = &[

const ISO_LANGUAGES: &[&str] = &[
"ab", "aa", "af", "sq", "am", "ar", "an", "hy", "as", "ay", "az", "ba", "eu", "bn", "dz", "bh",
"bi", "br", "bg", "my", "be", "km", "ca", "zh", "zh-Hans", "zh-Hant", "co", "hr", "cs", "da",
"nl", "en", "eo", "et", "fo", "fa", "fj", "fi", "fr", "fy", "gl", "gd", "gv", "ka", "de", "el",
"kl", "gn", "gu", "ht", "ha", "he", "iw", "hi", "hu", "is", "io", "id", "in", "ia", "ie", "iu",
"ik", "ga", "it", "ja", "jv", "kn", "ks", "kk", "rw", "ky", "rn", "ko", "ku", "lo", "la", "lv",
"li", "ln", "lt", "mk", "mg", "ms", "ml", "mt", "mi", "mr", "mo", "mn", "na", "ne", "no", "nb",
"nn", "oc", "or", "om", "ps", "pl", "pt", "pa", "qu", "rm", "ro", "ru", "sm", "sg", "sa", "sr",
"sh", "st", "tn", "sn", "ii", "sd", "si", "ss", "sk", "sl", "so", "es", "su", "sw", "sv", "tl",
"tg", "ta", "tt", "te", "th", "bo", "ti", "to", "ts", "tr", "tk", "tw", "ug", "uk", "ur", "uz",
"vi", "vo", "wa", "cy", "wo", "xh", "yi", "ji", "yo", "zu",
"bi", "br", "bg", "my", "be", "km", "ca", "zh", "co", "hr", "cs", "da", "nl", "en", "eo", "et",
"fo", "fa", "fj", "fi", "fr", "fy", "gl", "gd", "gv", "ka", "de", "el", "kl", "gn", "gu", "ht",
"ha", "he", "iw", "hi", "hu", "is", "io", "id", "in", "ia", "ie", "iu", "ik", "ga", "it", "ja",
"jv", "kn", "ks", "kk", "rw", "ky", "rn", "ko", "ku", "lo", "la", "lv", "li", "ln", "lt", "mk",
"mg", "ms", "ml", "mt", "mi", "mr", "mo", "mn", "na", "ne", "no", "nb", "nn", "oc", "or", "om",
"ps", "pl", "pt", "pa", "qu", "rm", "ro", "ru", "sm", "sg", "sa", "sr", "sh", "st", "tn", "sn",
"ii", "sd", "si", "ss", "sk", "sl", "so", "es", "su", "sw", "sv", "tl", "tg", "ta", "tt", "te",
"th", "bo", "ti", "to", "ts", "tr", "tk", "tw", "ug", "uk", "ur", "uz", "vi", "vo", "wa", "cy",
"wo", "xh", "yi", "ji", "yo", "zu",
];

const ISO_SCRIPTS: &[&str] = &[
"Arab", "Armn", "Beng", "Cyrl", "Deva", "Ethi", "Grek", "Gujr", "Guru", "Hang", "Hani", "Hans",
"Hant", "Hebr", "Hira", "Kana", "Khmr", "Laoo", "Latn", "Mlym", "Mymr", "Orya", "Sinh", "Taml",
"Telu", "Thai", "Tibt", "Zyyy",
];

#[derive(Debug, Default, biome_deserialize_macros::Merge, serde::Deserialize)]
Expand Down Expand Up @@ -266,12 +272,14 @@ fn main() -> io::Result<()> {

let iso_countries = generate_enums(ISO_COUNTRIES, "IsoCountries");
let iso_languages = generate_enums(ISO_LANGUAGES, "IsoLanguages");
let iso_scripts = generate_enums(ISO_SCRIPTS, "IsoScripts");

let tokens = quote! {
#aria_attributes
#aria_roles
#iso_countries
#iso_languages
#iso_scripts
};
let ast = tokens.to_string();

Expand Down
36 changes: 26 additions & 10 deletions crates/biome_aria_metadata/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,23 @@ pub const ISO_COUNTRIES: [&str; 233] = [
"UZ", "VU", "VE", "VN", "WF", "EH", "YE", "ZM", "ZW",
];

pub const ISO_LANGUAGES: [&str; 152] = [
pub const ISO_LANGUAGES: [&str; 150] = [
"ab", "aa", "af", "sq", "am", "ar", "an", "hy", "as", "ay", "az", "ba", "eu", "bn", "dz", "bh",
"bi", "br", "bg", "my", "be", "km", "ca", "zh", "zh-Hans", "zh-Hant", "co", "hr", "cs", "da",
"nl", "en", "eo", "et", "fo", "fa", "fj", "fi", "fr", "fy", "gl", "gd", "gv", "ka", "de", "el",
"kl", "gn", "gu", "ht", "ha", "he", "iw", "hi", "hu", "is", "io", "id", "in", "ia", "ie", "iu",
"ik", "ga", "it", "ja", "jv", "kn", "ks", "kk", "rw", "ky", "rn", "ko", "ku", "lo", "la", "lv",
"li", "ln", "lt", "mk", "mg", "ms", "ml", "mt", "mi", "mr", "mo", "mn", "na", "ne", "no", "nb",
"nn", "oc", "or", "om", "ps", "pl", "pt", "pa", "qu", "rm", "ro", "ru", "sm", "sg", "sa", "sr",
"sh", "st", "tn", "sn", "ii", "sd", "si", "ss", "sk", "sl", "so", "es", "su", "sw", "sv", "tl",
"tg", "ta", "tt", "te", "th", "bo", "ti", "to", "ts", "tr", "tk", "tw", "ug", "uk", "ur", "uz",
"vi", "vo", "wa", "cy", "wo", "xh", "yi", "ji", "yo", "zu",
"bi", "br", "bg", "my", "be", "km", "ca", "zh", "co", "hr", "cs", "da", "nl", "en", "eo", "et",
"fo", "fa", "fj", "fi", "fr", "fy", "gl", "gd", "gv", "ka", "de", "el", "kl", "gn", "gu", "ht",
"ha", "he", "iw", "hi", "hu", "is", "io", "id", "in", "ia", "ie", "iu", "ik", "ga", "it", "ja",
"jv", "kn", "ks", "kk", "rw", "ky", "rn", "ko", "ku", "lo", "la", "lv", "li", "ln", "lt", "mk",
"mg", "ms", "ml", "mt", "mi", "mr", "mo", "mn", "na", "ne", "no", "nb", "nn", "oc", "or", "om",
"ps", "pl", "pt", "pa", "qu", "rm", "ro", "ru", "sm", "sg", "sa", "sr", "sh", "st", "tn", "sn",
"ii", "sd", "si", "ss", "sk", "sl", "so", "es", "su", "sw", "sv", "tl", "tg", "ta", "tt", "te",
"th", "bo", "ti", "to", "ts", "tr", "tk", "tw", "ug", "uk", "ur", "uz", "vi", "vo", "wa", "cy",
"wo", "xh", "yi", "ji", "yo", "zu",
];

pub const ISO_SCRIPTS: [&str; 28] = [
"Arab", "Armn", "Beng", "Cyrl", "Deva", "Ethi", "Grek", "Gujr", "Guru", "Hang", "Hani", "Hans",
"Hant", "Hebr", "Hira", "Kana", "Khmr", "Laoo", "Latn", "Mlym", "Mymr", "Orya", "Sinh", "Taml",
"Telu", "Thai", "Tibt", "Zyyy",
];

/// Returns a list of valid ISO countries
Expand All @@ -43,6 +49,11 @@ pub fn is_valid_language(language: &str) -> bool {
IsoLanguages::from_str(language).is_ok()
}

/// Returns whether the given script code is a valid ISO script
pub fn is_valid_script(script: &str) -> bool {
IsoScripts::from_str(script).is_ok()
}

/// An array of all available countries
pub fn countries() -> &'static [&'static str] {
&ISO_COUNTRIES
Expand All @@ -53,6 +64,11 @@ pub fn languages() -> &'static [&'static str] {
&ISO_LANGUAGES
}

/// An array of all available scripts
pub fn scripts() -> &'static [&'static str] {
&ISO_SCRIPTS
}

#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum AriaAttributeKind {
Property,
Expand Down
62 changes: 55 additions & 7 deletions crates/biome_js_analyze/src/lint/a11y/use_valid_lang.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use biome_analyze::context::RuleContext;
use biome_analyze::{Ast, Rule, RuleDiagnostic, RuleSource, declare_lint_rule};
use biome_aria_metadata::{is_valid_country, is_valid_language};
use biome_aria_metadata::{is_valid_country, is_valid_language, is_valid_script};
use biome_console::markup;
use biome_diagnostics::Severity;
use biome_js_syntax::jsx_ext::AnyJsxElement;
Expand Down Expand Up @@ -43,6 +43,7 @@ declare_lint_rule! {
enum InvalidKind {
Language,
Country,
Script,
Value,
}

Expand All @@ -66,27 +67,64 @@ impl Rule for UseValidLang {
let attribute_static_value = attribute_value.as_static_value()?;
let attribute_text = attribute_static_value.text();
let mut split_value = attribute_text.split('-');
match (split_value.next(), split_value.next()) {
(Some(language), Some(country)) => {
if !is_valid_language(language) {
match (split_value.next(), split_value.next(), split_value.next()) {
(Some(language), Some(script), Some(country)) => {
if split_value.next().is_some() {
return Some(UseValidLangState {
attribute_range: attribute_value.range(),
invalid_kind: InvalidKind::Value,
});
} else if !is_valid_language(language) {
return Some(UseValidLangState {
attribute_range: attribute_value.range(),
invalid_kind: InvalidKind::Language,
});
} else if !is_valid_script(script) {
return Some(UseValidLangState {
attribute_range: attribute_value.range(),
invalid_kind: InvalidKind::Script,
});
} else if !is_valid_country(country) {
return Some(UseValidLangState {
attribute_range: attribute_value.range(),
invalid_kind: InvalidKind::Country,
});
} else if split_value.next().is_some() {
}
}

(Some(language), Some(script_or_country), None) => {
if !is_valid_language(language) {
return Some(UseValidLangState {
attribute_range: attribute_value.range(),
invalid_kind: InvalidKind::Value,
invalid_kind: InvalidKind::Language,
});
} else if !is_valid_script(script_or_country)
&& !is_valid_country(script_or_country)
{
match script_or_country.len() {
4 => {
return Some(UseValidLangState {
attribute_range: attribute_value.range(),
invalid_kind: InvalidKind::Script,
});
}
2 | 3 => {
return Some(UseValidLangState {
attribute_range: attribute_value.range(),
invalid_kind: InvalidKind::Country,
});
}
_ => {
return Some(UseValidLangState {
attribute_range: attribute_value.range(),
invalid_kind: InvalidKind::Value,
});
}
}
}
}

(Some(language), None) => {
(Some(language), None, None) => {
if !is_valid_language(language) {
return Some(UseValidLangState {
attribute_range: attribute_value.range(),
Expand Down Expand Up @@ -130,6 +168,16 @@ impl Rule for UseValidLang {

diagnostic.footer_list("Some of valid countries:", countries)
}
InvalidKind::Script => {
let scripts = biome_aria_metadata::scripts();
let scripts = if scripts.len() > 15 {
&scripts[..15]
} else {
scripts
};

diagnostic.footer_list("Some of valid scripts:", scripts)
}
InvalidKind::Value => diagnostic,
};
Some(diagnostic)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
let a = <html lang="lorem" />;
let a = <html lang="en-babab" />;
let a = <html lang="en-GB-something" />;
let a = <html lang="zh-Xxxx" />;
let a = <html lang="zh-Hans-ZZ" />;
let a = <html lang="en-US-GB-Extra" />;
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
---
source: crates/biome_js_analyze/tests/spec_tests.rs
expression: invalid.jsx
snapshot_kind: text
---
# Input
```jsx
let a = <html lang="lorem" />;
let a = <html lang="en-babab" />;
let a = <html lang="en-GB-something" />;
let a = <html lang="zh-Xxxx" />;
let a = <html lang="zh-Hans-ZZ" />;
let a = <html lang="en-US-GB-Extra" />;
```

Expand Down Expand Up @@ -52,7 +54,88 @@ invalid.jsx:2:20 lint/a11y/useValidLang ━━━━━━━━━━━━━
> 2 │ let a = <html lang="en-babab" />;
│ ^^^^^^^^^^
3 │ let a = <html lang="en-GB-something" />;
4 │
4 │ let a = <html lang="zh-Xxxx" />;
```

```
invalid.jsx:3:20 lint/a11y/useValidLang ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
× Provide a valid value for the lang attribute.
1 │ let a = <html lang="lorem" />;
2 │ let a = <html lang="en-babab" />;
> 3 │ let a = <html lang="en-GB-something" />;
│ ^^^^^^^^^^^^^^^^^
4 │ let a = <html lang="zh-Xxxx" />;
5 │ let a = <html lang="zh-Hans-ZZ" />;
i Some of valid scripts:
- Arab
- Armn
- Beng
- Cyrl
- Deva
- Ethi
- Grek
- Gujr
- Guru
- Hang
- Hani
- Hans
- Hant
- Hebr
- Hira
```

```
invalid.jsx:4:20 lint/a11y/useValidLang ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
× Provide a valid value for the lang attribute.
2 │ let a = <html lang="en-babab" />;
3 │ let a = <html lang="en-GB-something" />;
> 4 │ let a = <html lang="zh-Xxxx" />;
│ ^^^^^^^^^
5 │ let a = <html lang="zh-Hans-ZZ" />;
6 │ let a = <html lang="en-US-GB-Extra" />;
i Some of valid scripts:
- Arab
- Armn
- Beng
- Cyrl
- Deva
- Ethi
- Grek
- Gujr
- Guru
- Hang
- Hani
- Hans
- Hant
- Hebr
- Hira
```

```
invalid.jsx:5:20 lint/a11y/useValidLang ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
× Provide a valid value for the lang attribute.
3 │ let a = <html lang="en-GB-something" />;
4 │ let a = <html lang="zh-Xxxx" />;
> 5 │ let a = <html lang="zh-Hans-ZZ" />;
│ ^^^^^^^^^^^^
6 │ let a = <html lang="en-US-GB-Extra" />;
7 │
i Some of valid countries:
Expand All @@ -76,15 +159,15 @@ invalid.jsx:2:20 lint/a11y/useValidLang ━━━━━━━━━━━━━
```

```
invalid.jsx:3:20 lint/a11y/useValidLang ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
invalid.jsx:6:20 lint/a11y/useValidLang ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
× Provide a valid value for the lang attribute.
1 │ let a = <html lang="lorem" />;
2 │ let a = <html lang="en-babab" />;
> 3 │ let a = <html lang="en-GB-something" />;
│ ^^^^^^^^^^^^^^^^^
4
4 │ let a = <html lang="zh-Xxxx" />;
5 │ let a = <html lang="zh-Hans-ZZ" />;
> 6 │ let a = <html lang="en-US-GB-Extra" />;
│ ^^^^^^^^^^^^^^^^
7
```
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ let a = <html lang="en"></html>;
let a = <html lang={lang}></html>;
let a = <html lang="nb"></html>;
let a = <html lang="nn"></html>;
let a = <html lang="zh-Hant"></html>;
let a = <html lang="zh-Hans-CN"></html>;
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,7 @@ let a = <html lang="en"></html>;
let a = <html lang={lang}></html>;
let a = <html lang="nb"></html>;
let a = <html lang="nn"></html>;
let a = <html lang="zh-Hant"></html>;
let a = <html lang="zh-Hans-CN"></html>;
```