diff --git a/CHANGELOG.md b/CHANGELOG.md
index 59a0d0852835..ac03385e8a59 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -92,6 +92,16 @@ Read our [guidelines for writing a good changelog entry](https://github.com/biom
```
+- Add partial for `.vue` files. Biome is able to format the script block of Vue files. Contributed by @nhedger
+
+ ```diff
+
+
+
+ ```
#### Bug fixes
diff --git a/crates/biome_cli/src/execute/process_file/format.rs b/crates/biome_cli/src/execute/process_file/format.rs
index 733843c3a01f..563eafddfaa9 100644
--- a/crates/biome_cli/src/execute/process_file/format.rs
+++ b/crates/biome_cli/src/execute/process_file/format.rs
@@ -5,7 +5,7 @@ use crate::execute::process_file::{
};
use crate::execute::TraversalMode;
use biome_diagnostics::{category, DiagnosticExt};
-use biome_service::file_handlers::ASTRO_FENCE;
+use biome_service::file_handlers::{ASTRO_FENCE, VUE_FENCE};
use biome_service::workspace::RuleCategories;
use std::path::Path;
use std::sync::atomic::Ordering;
@@ -84,6 +84,23 @@ pub(crate) fn format_with_guard<'ctx>(
}
}
+ if workspace_file.as_extension() == Some("vue") {
+ if output.is_empty() {
+ return Ok(FileStatus::Ignored);
+ }
+ if let Some(script) = VUE_FENCE
+ .captures(&input)
+ .and_then(|captures| captures.name("script"))
+ {
+ output = format!(
+ "{}{}{}",
+ &input[..script.start()],
+ output.as_str(),
+ &input[script.end()..]
+ );
+ }
+ }
+
if output != input {
if should_write {
workspace_file.update_file(output)?;
diff --git a/crates/biome_cli/tests/commands/format.rs b/crates/biome_cli/tests/commands/format.rs
index b8296f51a68d..4f83618b2a6f 100644
--- a/crates/biome_cli/tests/commands/format.rs
+++ b/crates/biome_cli/tests/commands/format.rs
@@ -58,6 +58,42 @@ statement();
---
"#;
+const VUE_IMPLICIT_JS_FILE_UNFORMATTED: &str = r#"
+"#;
+
+const VUE_IMPLICIT_JS_FILE_FORMATTED: &str = r#"
+"#;
+
+const VUE_EXPLICIT_JS_FILE_UNFORMATTED: &str = r#"
+"#;
+
+const VUE_EXPLICIT_JS_FILE_FORMATTED: &str = r#"
+"#;
+
+const VUE_TS_FILE_UNFORMATTED: &str = r#"
+"#;
+
+const VUE_TS_FILE_FORMATTED: &str = r#"
+"#;
+
const APPLY_TRAILING_COMMA_BEFORE: &str = r#"
const a = [
longlonglonglongItem1longlonglonglongItem1,
@@ -3028,3 +3064,266 @@ fn format_empty_astro_files_write() {
result,
));
}
+
+#[test]
+fn format_vue_implicit_js_files() {
+ let mut fs = MemoryFileSystem::default();
+ let mut console = BufferConsole::default();
+
+ let vue_file_path = Path::new("file.vue");
+ fs.insert(
+ vue_file_path.into(),
+ VUE_IMPLICIT_JS_FILE_UNFORMATTED.as_bytes(),
+ );
+
+ let result = run_cli(
+ DynRef::Borrowed(&mut fs),
+ &mut console,
+ Args::from([("format"), vue_file_path.as_os_str().to_str().unwrap()].as_slice()),
+ );
+
+ assert!(result.is_err(), "run_cli returned {result:?}");
+
+ assert_file_contents(&fs, vue_file_path, VUE_IMPLICIT_JS_FILE_UNFORMATTED);
+
+ assert_cli_snapshot(SnapshotPayload::new(
+ module_path!(),
+ "format_vue_implicit_js_files",
+ fs,
+ console,
+ result,
+ ));
+}
+
+#[test]
+fn format_vue_implicit_js_files_write() {
+ let mut fs = MemoryFileSystem::default();
+ let mut console = BufferConsole::default();
+
+ let vue_file_path = Path::new("file.vue");
+ fs.insert(
+ vue_file_path.into(),
+ VUE_IMPLICIT_JS_FILE_UNFORMATTED.as_bytes(),
+ );
+
+ let result = run_cli(
+ DynRef::Borrowed(&mut fs),
+ &mut console,
+ Args::from(
+ [
+ "format",
+ "--write",
+ vue_file_path.as_os_str().to_str().unwrap(),
+ ]
+ .as_slice(),
+ ),
+ );
+
+ assert!(result.is_ok(), "run_cli returned {result:?}");
+
+ assert_file_contents(&fs, vue_file_path, VUE_IMPLICIT_JS_FILE_FORMATTED);
+
+ assert_cli_snapshot(SnapshotPayload::new(
+ module_path!(),
+ "format_vue_implicit_js_files_write",
+ fs,
+ console,
+ result,
+ ));
+}
+
+#[test]
+fn format_vue_explicit_js_files() {
+ let mut fs = MemoryFileSystem::default();
+ let mut console = BufferConsole::default();
+
+ let vue_file_path = Path::new("file.vue");
+ fs.insert(
+ vue_file_path.into(),
+ VUE_EXPLICIT_JS_FILE_UNFORMATTED.as_bytes(),
+ );
+
+ let result = run_cli(
+ DynRef::Borrowed(&mut fs),
+ &mut console,
+ Args::from([("format"), vue_file_path.as_os_str().to_str().unwrap()].as_slice()),
+ );
+
+ assert!(result.is_err(), "run_cli returned {result:?}");
+
+ assert_file_contents(&fs, vue_file_path, VUE_EXPLICIT_JS_FILE_UNFORMATTED);
+
+ assert_cli_snapshot(SnapshotPayload::new(
+ module_path!(),
+ "format_vue_explicit_js_files",
+ fs,
+ console,
+ result,
+ ));
+}
+
+#[test]
+fn format_vue_explicit_js_files_write() {
+ let mut fs = MemoryFileSystem::default();
+ let mut console = BufferConsole::default();
+
+ let vue_file_path = Path::new("file.vue");
+ fs.insert(
+ vue_file_path.into(),
+ VUE_EXPLICIT_JS_FILE_UNFORMATTED.as_bytes(),
+ );
+
+ let result = run_cli(
+ DynRef::Borrowed(&mut fs),
+ &mut console,
+ Args::from(
+ [
+ "format",
+ "--write",
+ vue_file_path.as_os_str().to_str().unwrap(),
+ ]
+ .as_slice(),
+ ),
+ );
+
+ assert!(result.is_ok(), "run_cli returned {result:?}");
+
+ assert_file_contents(&fs, vue_file_path, VUE_EXPLICIT_JS_FILE_FORMATTED);
+
+ assert_cli_snapshot(SnapshotPayload::new(
+ module_path!(),
+ "format_vue_explicit_js_files_write",
+ fs,
+ console,
+ result,
+ ));
+}
+
+#[test]
+fn format_empty_vue_js_files_write() {
+ let mut fs = MemoryFileSystem::default();
+ let mut console = BufferConsole::default();
+
+ let vue_file_path = Path::new("file.vue");
+ fs.insert(vue_file_path.into(), "".as_bytes());
+
+ let result = run_cli(
+ DynRef::Borrowed(&mut fs),
+ &mut console,
+ Args::from(
+ [
+ "format",
+ "--write",
+ vue_file_path.as_os_str().to_str().unwrap(),
+ ]
+ .as_slice(),
+ ),
+ );
+
+ assert!(result.is_ok(), "run_cli returned {result:?}");
+
+ assert_file_contents(&fs, vue_file_path, "");
+
+ assert_cli_snapshot(SnapshotPayload::new(
+ module_path!(),
+ "format_empty_vue_js_files_write",
+ fs,
+ console,
+ result,
+ ));
+}
+
+#[test]
+fn format_vue_ts_files() {
+ let mut fs = MemoryFileSystem::default();
+ let mut console = BufferConsole::default();
+
+ let vue_file_path = Path::new("file.vue");
+ fs.insert(vue_file_path.into(), VUE_TS_FILE_UNFORMATTED.as_bytes());
+
+ let result = run_cli(
+ DynRef::Borrowed(&mut fs),
+ &mut console,
+ Args::from([("format"), vue_file_path.as_os_str().to_str().unwrap()].as_slice()),
+ );
+
+ assert!(result.is_err(), "run_cli returned {result:?}");
+
+ assert_file_contents(&fs, vue_file_path, VUE_TS_FILE_UNFORMATTED);
+
+ assert_cli_snapshot(SnapshotPayload::new(
+ module_path!(),
+ "format_vue_ts_files",
+ fs,
+ console,
+ result,
+ ));
+}
+
+#[test]
+fn format_vue_ts_files_write() {
+ let mut fs = MemoryFileSystem::default();
+ let mut console = BufferConsole::default();
+
+ let vue_file_path = Path::new("file.vue");
+ fs.insert(vue_file_path.into(), VUE_TS_FILE_UNFORMATTED.as_bytes());
+
+ let result = run_cli(
+ DynRef::Borrowed(&mut fs),
+ &mut console,
+ Args::from(
+ [
+ "format",
+ "--write",
+ vue_file_path.as_os_str().to_str().unwrap(),
+ ]
+ .as_slice(),
+ ),
+ );
+
+ assert!(result.is_ok(), "run_cli returned {result:?}");
+
+ assert_file_contents(&fs, vue_file_path, VUE_TS_FILE_FORMATTED);
+
+ assert_cli_snapshot(SnapshotPayload::new(
+ module_path!(),
+ "format_vue_ts_files_write",
+ fs,
+ console,
+ result,
+ ));
+}
+
+#[test]
+fn format_empty_vue_ts_files_write() {
+ let mut fs = MemoryFileSystem::default();
+ let mut console = BufferConsole::default();
+
+ let vue_file_path = Path::new("file.vue");
+ fs.insert(vue_file_path.into(), "".as_bytes());
+
+ let result = run_cli(
+ DynRef::Borrowed(&mut fs),
+ &mut console,
+ Args::from(
+ [
+ "format",
+ "--write",
+ vue_file_path.as_os_str().to_str().unwrap(),
+ ]
+ .as_slice(),
+ ),
+ );
+
+ assert!(result.is_ok(), "run_cli returned {result:?}");
+
+ assert_file_contents(&fs, vue_file_path, "");
+
+ assert_cli_snapshot(SnapshotPayload::new(
+ module_path!(),
+ "format_empty_vue_ts_files_write",
+ fs,
+ console,
+ result,
+ ));
+}
diff --git a/crates/biome_cli/tests/snapshots/main_commands_format/format_empty_vue_js_files_write.snap b/crates/biome_cli/tests/snapshots/main_commands_format/format_empty_vue_js_files_write.snap
new file mode 100644
index 000000000000..3803cc6a03db
--- /dev/null
+++ b/crates/biome_cli/tests/snapshots/main_commands_format/format_empty_vue_js_files_write.snap
@@ -0,0 +1,15 @@
+---
+source: crates/biome_cli/tests/snap_test.rs
+expression: content
+---
+## `file.vue`
+
+```vue
+
+```
+
+# Emitted Messages
+
+```block
+Formatted 1 file(s) in