Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: enable JSX parsing inside .vue files #3468

Merged
merged 3 commits into from
Aug 28, 2024
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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,7 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b
}
```
Contributed by @ah-yu
- Fix [#3464](https://github.com/biomejs/biome/issues/3464) by enabling JSX in `.vue` files that use the `lang='jsx'` or `lang='tsx'` attribute. Contributed by @ematipico

## v1.8.3 (2024-06-27)

Expand Down
15 changes: 15 additions & 0 deletions crates/biome_js_syntax/src/file_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -384,3 +384,18 @@ impl TryFrom<&Path> for JsFileSource {
Self::try_from_extension(extension)
}
}

impl From<Language> for JsFileSource {
fn from(value: Language) -> Self {
match value {
Language::JavaScript => JsFileSource::js_module(),
Language::TypeScript { definition_file } => {
if definition_file {
JsFileSource::d_ts()
} else {
JsFileSource::ts()
}
}
}
}
}
94 changes: 81 additions & 13 deletions crates/biome_service/src/file_handlers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ use biome_fs::BiomePath;
use biome_graphql_syntax::{GraphqlFileSource, GraphqlLanguage};
use biome_grit_patterns::{GritQuery, GritQueryResult, GritTargetFile};
use biome_js_parser::{parse, JsParserOptions};
use biome_js_syntax::{EmbeddingKind, JsFileSource, JsLanguage, Language, TextRange, TextSize};
use biome_js_syntax::{
EmbeddingKind, JsFileSource, JsLanguage, Language, LanguageVariant, TextRange, TextSize,
};
use biome_json_syntax::{JsonFileSource, JsonLanguage};
use biome_parser::AnyParse;
use biome_project::PackageJson;
Expand Down Expand Up @@ -563,7 +565,9 @@ pub(crate) fn is_diagnostic_error(
/// matched by regular expressions.
///
// TODO: We should change the parser when HTMLish languages are supported.
pub(crate) fn parse_lang_from_script_opening_tag(script_opening_tag: &str) -> Language {
pub(crate) fn parse_lang_from_script_opening_tag(
script_opening_tag: &str,
) -> (Language, LanguageVariant) {
parse(
script_opening_tag,
JsFileSource::jsx(),
Expand All @@ -584,14 +588,27 @@ pub(crate) fn parse_lang_from_script_opening_tag(script_opening_tag: &str) -> La
let attribute_inner_string =
attribute_value.as_jsx_string()?.inner_string_text().ok()?;
match attribute_inner_string.text() {
"ts" | "tsx" => Some(Language::TypeScript {
definition_file: false,
}),
"ts" => Some((
Language::TypeScript {
definition_file: false,
},
LanguageVariant::Standard,
)),
"tsx" => Some((
Language::TypeScript {
definition_file: false,
},
LanguageVariant::Jsx,
)),
"jsx" => Some((Language::JavaScript, LanguageVariant::Jsx)),
"js" => Some((Language::JavaScript, LanguageVariant::Standard)),
_ => None,
}
})
})
.map_or(Language::JavaScript, |lang| lang)
.map_or((Language::JavaScript, LanguageVariant::Standard), |lang| {
lang
})
}

pub(crate) fn search(
Expand Down Expand Up @@ -630,14 +647,24 @@ fn test_svelte_script_lang() {
const SVELTE_CONTEXT_MODULE_TS_SCRIPT_OPENING_TAG: &str =
r#"<script context="module" lang="ts">"#;

assert!(parse_lang_from_script_opening_tag(SVELTE_JS_SCRIPT_OPENING_TAG).is_javascript());
assert!(parse_lang_from_script_opening_tag(SVELTE_TS_SCRIPT_OPENING_TAG).is_typescript());
assert!(
parse_lang_from_script_opening_tag(SVELTE_JS_SCRIPT_OPENING_TAG)
.0
.is_javascript()
);
assert!(
parse_lang_from_script_opening_tag(SVELTE_TS_SCRIPT_OPENING_TAG)
.0
.is_typescript()
);
assert!(
parse_lang_from_script_opening_tag(SVELTE_CONTEXT_MODULE_JS_SCRIPT_OPENING_TAG)
.0
.is_javascript()
);
assert!(
parse_lang_from_script_opening_tag(SVELTE_CONTEXT_MODULE_TS_SCRIPT_OPENING_TAG)
.0
.is_typescript()
);
}
Expand Down Expand Up @@ -1137,12 +1164,53 @@ fn test_vue_script_lang() {
const VUE_JS_SCRIPT_OPENING_TAG: &str = r#"<script>"#;
const VUE_TS_SCRIPT_OPENING_TAG: &str = r#"<script lang="ts">"#;
const VUE_TSX_SCRIPT_OPENING_TAG: &str = r#"<script lang="tsx">"#;
const VUE_JSX_SCRIPT_OPENING_TAG: &str = r#"<script lang="jsx">"#;
const VUE_SETUP_JS_SCRIPT_OPENING_TAG: &str = r#"<script setup>"#;
const VUE_SETUP_TS_SCRIPT_OPENING_TAG: &str = r#"<script setup lang="ts">"#;

assert!(parse_lang_from_script_opening_tag(VUE_JS_SCRIPT_OPENING_TAG).is_javascript());
assert!(parse_lang_from_script_opening_tag(VUE_TS_SCRIPT_OPENING_TAG).is_typescript());
assert!(parse_lang_from_script_opening_tag(VUE_TSX_SCRIPT_OPENING_TAG).is_typescript());
assert!(parse_lang_from_script_opening_tag(VUE_SETUP_JS_SCRIPT_OPENING_TAG).is_javascript());
assert!(parse_lang_from_script_opening_tag(VUE_SETUP_TS_SCRIPT_OPENING_TAG).is_typescript());
assert!(
parse_lang_from_script_opening_tag(VUE_JS_SCRIPT_OPENING_TAG)
.0
.is_javascript()
);
assert!(
parse_lang_from_script_opening_tag(VUE_JS_SCRIPT_OPENING_TAG)
.1
.is_standard()
);
assert!(
parse_lang_from_script_opening_tag(VUE_TS_SCRIPT_OPENING_TAG)
.0
.is_typescript()
);
assert!(
parse_lang_from_script_opening_tag(VUE_TS_SCRIPT_OPENING_TAG)
.1
.is_standard()
);
assert!(
parse_lang_from_script_opening_tag(VUE_JSX_SCRIPT_OPENING_TAG)
.0
.is_javascript()
);
assert!(
parse_lang_from_script_opening_tag(VUE_JSX_SCRIPT_OPENING_TAG)
.1
.is_jsx()
);
assert!(
parse_lang_from_script_opening_tag(VUE_TSX_SCRIPT_OPENING_TAG)
.0
.is_typescript()
);
assert!(
parse_lang_from_script_opening_tag(VUE_SETUP_JS_SCRIPT_OPENING_TAG)
.0
.is_javascript()
);
assert!(
parse_lang_from_script_opening_tag(VUE_SETUP_TS_SCRIPT_OPENING_TAG)
.0
.is_typescript()
);
}
17 changes: 8 additions & 9 deletions crates/biome_service/src/file_handlers/svelte.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::WorkspaceError;
use biome_formatter::Printed;
use biome_fs::BiomePath;
use biome_js_parser::{parse_js_with_cache, JsParserOptions};
use biome_js_syntax::{EmbeddingKind, JsFileSource, Language, TextRange, TextSize};
use biome_js_syntax::{EmbeddingKind, JsFileSource, TextRange, TextSize};
use biome_parser::AnyParse;
use biome_rowan::NodeCache;
use regex::{Match, Regex};
Expand Down Expand Up @@ -69,14 +69,13 @@ impl SvelteFileHandler {
SVELTE_FENCE
.captures(text)
.and_then(|captures| {
match parse_lang_from_script_opening_tag(captures.name("opening")?.as_str()) {
Language::JavaScript => {
Some(JsFileSource::js_module().with_embedding_kind(EmbeddingKind::Svelte))
}
Language::TypeScript { .. } => {
Some(JsFileSource::ts().with_embedding_kind(EmbeddingKind::Svelte))
}
}
let (language, variant) =
parse_lang_from_script_opening_tag(captures.name("opening")?.as_str());
Some(
JsFileSource::from(language)
.with_variant(variant)
.with_embedding_kind(EmbeddingKind::Svelte),
)
})
.map_or(JsFileSource::js_module(), |fs| fs)
}
Expand Down
17 changes: 8 additions & 9 deletions crates/biome_service/src/file_handlers/vue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::WorkspaceError;
use biome_formatter::Printed;
use biome_fs::BiomePath;
use biome_js_parser::{parse_js_with_cache, JsParserOptions};
use biome_js_syntax::{EmbeddingKind, JsFileSource, Language, TextRange, TextSize};
use biome_js_syntax::{EmbeddingKind, JsFileSource, TextRange, TextSize};
use biome_parser::AnyParse;
use biome_rowan::NodeCache;
use regex::{Match, Regex};
Expand Down Expand Up @@ -69,14 +69,13 @@ impl VueFileHandler {
VUE_FENCE
.captures(text)
.and_then(|captures| {
match parse_lang_from_script_opening_tag(captures.name("opening")?.as_str()) {
Language::JavaScript => {
Some(JsFileSource::js_module().with_embedding_kind(EmbeddingKind::Vue))
}
Language::TypeScript { .. } => {
Some(JsFileSource::ts().with_embedding_kind(EmbeddingKind::Vue))
}
}
let (language, variant) =
parse_lang_from_script_opening_tag(captures.name("opening")?.as_str());
Some(
JsFileSource::from(language)
.with_variant(variant)
.with_embedding_kind(EmbeddingKind::Vue),
)
})
.map_or(JsFileSource::js_module(), |fs| fs)
}
Expand Down
Loading