Skip to content

Commit 599dd04

Browse files
author
Raashish Aggarwal
authored
fix: avoid unresolved embedded CSS tag ranges (#9631)
1 parent 13b3261 commit 599dd04

3 files changed

Lines changed: 81 additions & 0 deletions

File tree

.changeset/witty-files-hear.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+
Fixed [#9625](https://github.com/biomejs/biome/issues/9625): `experimentalEmbeddedSnippetsEnabled` no longer crashes when a file mixes formatable CSS-in-JS templates with tagged templates that the embedded formatter can't currently delegate, such as a styled-components interpolation returning `css```.

crates/biome_js_formatter/src/js/auxiliary/template_chunk_element.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,13 @@ const KNOWN_EMBED_OBJECTS: &[&str] = &["styled", "graphql"];
6767
///
6868
/// Returns `None` when the AST is malformed.
6969
fn is_plausible_embed_template(expr: &JsTemplateExpression) -> Option<bool> {
70+
// The service currently only extracts embedded snippets from single-chunk
71+
// templates. Keep the formatter in sync so it doesn't emit StartEmbedded /
72+
// EndEmbedded tags for ranges that won't be resolved later.
73+
if expr.elements().len() != 1 {
74+
return Some(false);
75+
}
76+
7077
if let Some(tag) = expr.tag() {
7178
return Some(match tag {
7279
// css``, gql``, graphql``

crates/biome_service/src/workspace/server.tests.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,75 @@ const Bar = styled(Component)`
630630
");
631631
}
632632

633+
#[test]
634+
fn issue_9625() {
635+
const FILE_PATH: &str = "/project/file.js";
636+
const FILE_CONTENT: &str = r#"const Portfolio = styled.div`
637+
display: flex;
638+
align-items: center;
639+
`;
640+
641+
const PortfolioIcon = styled.div`
642+
${({ theme }) => css``
643+
};
644+
`;"#;
645+
646+
let fs = MemoryFileSystem::default();
647+
fs.insert(Utf8PathBuf::from(FILE_PATH), FILE_CONTENT);
648+
649+
let (workspace, project_key) = setup_workspace_and_open_project(fs, "/");
650+
651+
workspace
652+
.update_settings(UpdateSettingsParams {
653+
project_key,
654+
workspace_directory: None,
655+
configuration: Configuration {
656+
formatter: Some(FormatterConfiguration {
657+
indent_style: Some(IndentStyle::Space),
658+
..Default::default()
659+
}),
660+
javascript: Some(JsConfiguration {
661+
experimental_embedded_snippets_enabled: Some(true.into()),
662+
..Default::default()
663+
}),
664+
..Default::default()
665+
},
666+
extended_configurations: vec![],
667+
module_graph_resolution_kind: ModuleGraphResolutionKind::None,
668+
})
669+
.unwrap();
670+
671+
workspace
672+
.open_file(OpenFileParams {
673+
project_key,
674+
path: BiomePath::new(FILE_PATH),
675+
content: FileContent::FromServer,
676+
document_file_source: None,
677+
persist_node_cache: false,
678+
inline_config: None,
679+
})
680+
.unwrap();
681+
682+
let result = workspace
683+
.format_file(FormatFileParams {
684+
project_key,
685+
path: Utf8PathBuf::from(FILE_PATH).into(),
686+
inline_config: None,
687+
})
688+
.unwrap();
689+
690+
insta::assert_snapshot!(result.as_code(), @r"
691+
const Portfolio = styled.div`
692+
display: flex;
693+
align-items: center;
694+
`;
695+
696+
const PortfolioIcon = styled.div`
697+
${({ theme }) => css``};
698+
`;
699+
");
700+
}
701+
633702
#[test]
634703
fn format_js_with_embedded_graphql() {
635704
const FILE_PATH: &str = "/project/file.js";

0 commit comments

Comments
 (0)