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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

57 changes: 57 additions & 0 deletions crates/biome_cli/tests/cases/html.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ fn should_pull_diagnostics_from_embedded_languages_when_linting() {
fs.insert(
html_file.into(),
r#"<script>debugger</script>
<script>import z from "zod"</script>
<style>#id .class div { background-color: red; background-color: red; } </style>
"#
.as_bytes(),
Expand Down Expand Up @@ -274,3 +275,59 @@ fn should_pull_diagnostics_from_embedded_languages_when_linting() {
result,
));
}

#[test]
fn should_apply_fixes_to_embedded_languages() {
let fs = MemoryFileSystem::default();
let mut console = BufferConsole::default();

let html_file = Utf8Path::new("file.html");
fs.insert(
html_file.into(),
r#"<script type="module">import z from "zod"; import _ from "lodash";

debugger

let schema = z.object({}).optional().nullable();

</script>
<style>#id .class div { background-color: red; background-color: red; } </style>
"#
.as_bytes(),
);

fs.insert(
Utf8Path::new("biome.json").into(),
r#"{
"html": {
"formatter": {
"enabled": true,
"indentScriptAndStyle": true
},
"linter": {
"enabled": true
},
"assist": {
"enabled": true
}
}
}"#
.as_bytes(),
);

let (fs, result) = run_cli(
fs,
&mut console,
Args::from(["check", "--write", "--unsafe", html_file.as_str()].as_slice()),
);

assert!(result.is_err(), "run_cli returned {result:?}");

assert_cli_snapshot(SnapshotPayload::new(
module_path!(),
"should_apply_fixes_to_embedded_languages",
fs,
console,
result,
));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
---
source: crates/biome_cli/tests/snap_test.rs
expression: redactor(content)
---
## `biome.json`

```json
{
"html": {
"formatter": {
"enabled": true,
"indentScriptAndStyle": true
},
"linter": {
"enabled": true
},
"assist": {
"enabled": true
}
}
}
```

## `file.html`

```html
<script type="module">
import z from "zod";

const _schema = z.object({}).optional().nullable();
Comment on lines +28 to +30
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Notice how the unsafe fixes are applied:

  • removed unused import
  • renamed _schema because unused
  • removed debugger

</script>
<style>
#id .class div {
background-color: red;
background-color: red;
}
</style>

```

# Termination Message

```block
check ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× Some errors were emitted while running checks.



```

# Emitted Messages

```block
file.html:7:48 lint/suspicious/noDuplicateProperties ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× Duplicate properties can lead to unexpected behavior and may override previous declarations unintentionally.

6 │ </script>
> 7 │ <style>#id .class div { background-color: red; background-color: red; } </style>
│ ^^^^^^^^^^^^^^^^
8 │

i background-color is already defined here.

6 │ </script>
> 7 │ <style>#id .class div { background-color: red; background-color: red; } </style>
│ ^^^^^^^^^^^^^^^^
8 │

i Remove or rename the duplicate property to ensure consistent styling.


```

```block
Checked 1 file in <TIME>. Fixed 1 file.
Found 1 error.
```
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ expression: redactor(content)

```html
<script>debugger</script>
<script>import z from "zod"</script>
<style>#id .class div { background-color: red; background-color: red; } </style>

```
Expand All @@ -39,15 +40,35 @@ lint ━━━━━━━━━━━━━━━━━━━━━━━━━

# Emitted Messages

```block
file.html:2:16 lint/correctness/noUnusedImports FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

! This import is unused.

1 │ <script>debugger</script>
> 2 │ <script>import z from "zod"</script>
│ ^
3 │ <style>#id .class div { background-color: red; background-color: red; } </style>
4 │

i Unused imports might be the result of an incomplete refactoring.

i Unsafe fix: Remove the unused imports.

1 │ import·z·from·"zod"
│ -------------------

```

```block
file.html:1:9 lint/suspicious/noDebugger FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× This is an unexpected use of the debugger statement.

> 1 │ <script>debugger</script>
│ ^^^^^^^^
2 │ <style>#id .class div { background-color: red; background-color: red; } </style>
3 │
2 │ <script>import z from "zod"</script>
3 │ <style>#id .class div { background-color: red; background-color: red; } </style>

i Unsafe fix: Remove debugger statement

Expand All @@ -57,21 +78,39 @@ file.html:1:9 lint/suspicious/noDebugger FIXABLE ━━━━━━━━━
```

```block
file.html:2:48 lint/suspicious/noDuplicateProperties ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
file.html:2:9 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× Illegal use of an import declaration outside of a module

1 │ <script>debugger</script>
> 2 │ <script>import z from "zod"</script>
│ ^^^^^^^^^^^^^^^^^^^
3 │ <style>#id .class div { background-color: red; background-color: red; } </style>
4 │

i not allowed inside scripts


```

```block
file.html:3:48 lint/suspicious/noDuplicateProperties ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× Duplicate properties can lead to unexpected behavior and may override previous declarations unintentionally.

1 │ <script>debugger</script>
> 2 │ <style>#id .class div { background-color: red; background-color: red; } </style>
2 │ <script>import z from "zod"</script>
> 3 │ <style>#id .class div { background-color: red; background-color: red; } </style>
│ ^^^^^^^^^^^^^^^^
3
4

i background-color is already defined here.

1 │ <script>debugger</script>
> 2 │ <style>#id .class div { background-color: red; background-color: red; } </style>
2 │ <script>import z from "zod"</script>
> 3 │ <style>#id .class div { background-color: red; background-color: red; } </style>
│ ^^^^^^^^^^^^^^^^
3
4

i Remove or rename the duplicate property to ensure consistent styling.

Expand All @@ -80,5 +119,6 @@ file.html:2:48 lint/suspicious/noDuplicateProperties ━━━━━━━━━

```block
Checked 1 file in <TIME>. No fixes applied.
Found 2 errors.
Found 3 errors.
Found 1 warning.
```
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ The configuration that is contained inside the file `biome.json`
self-closed. Defaults to never.
--html-linter-enabled=<true|false> Control the linter for HTML (and its super languages)
files.
--html-assist-enabled=<true|false> Control the assist for JSON (and its super languages)
--html-assist-enabled=<true|false> Control the assist for HTML (and its super languages)
files.
--assist-enabled=<true|false> Whether Biome should enable assist via LSP and CLI.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ The configuration that is contained inside the file `biome.json`
self-closed. Defaults to never.
--html-linter-enabled=<true|false> Control the linter for HTML (and its super languages)
files.
--html-assist-enabled=<true|false> Control the assist for JSON (and its super languages)
--html-assist-enabled=<true|false> Control the assist for HTML (and its super languages)
files.
--assist-enabled=<true|false> Whether Biome should enable assist via LSP and CLI.

Expand Down
2 changes: 2 additions & 0 deletions crates/biome_configuration/src/generated/domain_selector.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions crates/biome_html_syntax/src/element_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ impl HtmlElement {
name_token.text_trimmed().eq_ignore_ascii_case("script")
}

/// Returns `true` if the element is a `<script type="module">`
pub fn is_javascript_module(&self) -> SyntaxResult<bool> {
let is_script = self.is_script_tag();
let type_attribute = self.find_attribute_by_name("type");
Expand All @@ -165,6 +166,24 @@ impl HtmlElement {

Ok(is_script && is_type_module)
}

/// Returns `true` if the element is a `<script lang="ts">`
pub fn is_typescript_lang(&self) -> SyntaxResult<bool> {
let is_script = self.is_script_tag();
let lang_attribute = self.find_attribute_by_name("lang");
let is_lang_typescript = lang_attribute.is_some_and(|attribute| {
attribute
.initializer()
.and_then(|initializer| initializer.value().ok())
.and_then(|value| value.as_html_string().cloned())
.and_then(|value| value.value_token().ok())
.is_some_and(|token| {
let text = inner_string_text(&token);
text.eq_ignore_ascii_case("ts")
})
});
Ok(is_script && is_lang_typescript)
}
}

#[cfg(test)]
Expand Down
55 changes: 32 additions & 23 deletions crates/biome_html_syntax/src/file_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,6 @@ pub struct HtmlFileSource {
variant: HtmlVariant,
}

impl HtmlFileSource {
pub const fn is_astro(&self) -> bool {
matches!(self.variant, HtmlVariant::Astro)
}

pub fn variant(&self) -> &HtmlVariant {
&self.variant
}

pub fn text_expressions(&self) -> Option<&HtmlTextExpressions> {
if let HtmlVariant::Standard(text_expressions) = &self.variant {
Some(text_expressions)
} else {
None
}
}
}

#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(
Debug, Clone, Default, Copy, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize,
Expand Down Expand Up @@ -66,8 +48,32 @@ impl HtmlFileSource {

/// Returns `true` if the current file is `.html` and doesn't support
/// any text expression capability
pub fn is_html(&self) -> bool {
self.variant == HtmlVariant::default()
pub const fn is_html(&self) -> bool {
matches!(self.variant, HtmlVariant::Standard(_))
}

pub const fn is_vue(&self) -> bool {
matches!(self.variant, HtmlVariant::Vue)
}

pub const fn is_svelte(&self) -> bool {
matches!(self.variant, HtmlVariant::Svelte)
}

pub const fn is_astro(&self) -> bool {
matches!(self.variant, HtmlVariant::Astro)
}

pub fn variant(&self) -> &HtmlVariant {
&self.variant
}

pub fn text_expressions(&self) -> Option<&HtmlTextExpressions> {
if let HtmlVariant::Standard(text_expressions) = &self.variant {
Some(text_expressions)
} else {
None
}
}

pub fn html_with_text_expressions() -> Self {
Expand All @@ -94,9 +100,12 @@ impl HtmlFileSource {
}

/// Try to return the HTML file source corresponding to this file name from well-known files
pub fn try_from_well_known(_: &Utf8Path) -> Result<Self, FileSourceError> {
// TODO: to be implemented
Err(FileSourceError::UnknownFileName)
pub fn try_from_well_known(path: &Utf8Path) -> Result<Self, FileSourceError> {
let Some(extension) = path.extension() else {
return Err(FileSourceError::MissingFileExtension);
};

Self::try_from_extension(extension)
}

/// Try to return the HTML file source corresponding to this file extension
Expand Down
Loading
Loading