Skip to content

Commit b149f2d

Browse files
committed
refactor(linter): add FrameworkOptions to ContextSubHost
1 parent 0447c31 commit b149f2d

File tree

4 files changed

+62
-6
lines changed

4 files changed

+62
-6
lines changed

crates/oxc_linter/src/context/host.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::{
99
config::LintConfig,
1010
disable_directives::{DisableDirectives, DisableDirectivesBuilder, RuleCommentType},
1111
fixer::{Fix, FixKind, Message, PossibleFixes},
12-
frameworks,
12+
frameworks::{self, FrameworkOptions},
1313
module_record::ModuleRecord,
1414
options::LintOptions,
1515
rules::RuleEnum,
@@ -27,12 +27,23 @@ pub struct ContextSubHost<'a> {
2727
/// Information about specific rules that should be disabled or enabled, via comment directives like
2828
/// `eslint-disable` or `eslint-disable-next-line`.
2929
pub(super) disable_directives: Rc<DisableDirectives<'a>>,
30+
// Specific framework options, for example, whether the context is inside `<script setup>` in Vue files.
31+
#[expect(dead_code)]
32+
pub(super) framework_options: FrameworkOptions,
3033
}
3134

3235
impl<'a> ContextSubHost<'a> {
36+
pub fn new(semantic: Rc<Semantic<'a>>, module_record: Arc<ModuleRecord>) -> Self {
37+
Self::new_with_framework_options(semantic, module_record, FrameworkOptions::Default)
38+
}
39+
3340
/// # Panics
3441
/// If `semantic.cfg()` is `None`.
35-
pub fn new(semantic: Rc<Semantic<'a>>, module_record: Arc<ModuleRecord>) -> Self {
42+
pub fn new_with_framework_options(
43+
semantic: Rc<Semantic<'a>>,
44+
module_record: Arc<ModuleRecord>,
45+
frameworks_options: FrameworkOptions,
46+
) -> Self {
3647
// We should always check for `semantic.cfg()` being `Some` since we depend on it and it is
3748
// unwrapped without any runtime checks after construction.
3849
assert!(
@@ -43,7 +54,12 @@ impl<'a> ContextSubHost<'a> {
4354
let disable_directives =
4455
DisableDirectivesBuilder::new().build(semantic.source_text(), semantic.comments());
4556

46-
Self { semantic, module_record, disable_directives: Rc::new(disable_directives) }
57+
Self {
58+
semantic,
59+
module_record,
60+
disable_directives: Rc::new(disable_directives),
61+
framework_options: frameworks_options,
62+
}
4763
}
4864
}
4965
/// Stores shared information about a file being linted.

crates/oxc_linter/src/frameworks.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,10 @@ pub fn has_vitest_imports(module_record: &ModuleRecord) -> bool {
9595
pub fn has_jest_imports(module_record: &ModuleRecord) -> bool {
9696
module_record.import_entries.iter().any(|entry| entry.module_request.name() == "@jest/globals")
9797
}
98+
99+
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
100+
101+
pub enum FrameworkOptions {
102+
Default, // default
103+
VueSetup, // context is inside `<script setup>`
104+
}

crates/oxc_linter/src/loader/partial_loader/vue.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ use memchr::memmem::Finder;
22

33
use oxc_span::SourceType;
44

5+
use crate::frameworks::FrameworkOptions;
6+
57
use super::{JavaScriptSource, SCRIPT_END, SCRIPT_START, find_script_closing_angle};
68

79
pub struct VuePartialLoader<'a> {
@@ -52,6 +54,7 @@ impl<'a> VuePartialLoader<'a> {
5254

5355
// parse `lang`
5456
let lang = Self::extract_lang_attribute(content);
57+
let is_setup = content.contains("setup"); // check if "setup" is present, does not check if its inside an attribute
5558

5659
let Ok(mut source_type) = SourceType::from_extension(lang) else { return None };
5760
if !lang.contains('x') {
@@ -70,7 +73,12 @@ impl<'a> VuePartialLoader<'a> {
7073
let source_text = &self.source_text[js_start..js_end];
7174
// NOTE: loader checked that source_text.len() is less than u32::MAX
7275
#[expect(clippy::cast_possible_truncation)]
73-
Some(JavaScriptSource::partial(source_text, source_type, js_start as u32))
76+
Some(JavaScriptSource::partial_with_framework_options(
77+
source_text,
78+
source_type,
79+
if is_setup { FrameworkOptions::VueSetup } else { FrameworkOptions::Default },
80+
js_start as u32,
81+
))
7482
}
7583

7684
fn extract_lang_attribute(content: &str) -> &str {

crates/oxc_linter/src/loader/source.rs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use oxc_span::SourceType;
22

3+
use crate::frameworks::FrameworkOptions;
4+
35
#[derive(Debug, Clone, Copy)]
46
#[non_exhaustive]
57
pub struct JavaScriptSource<'a> {
@@ -10,15 +12,38 @@ pub struct JavaScriptSource<'a> {
1012
pub start: u32,
1113
#[expect(dead_code)]
1214
is_partial: bool,
15+
16+
// some partial sources can have special options defined, like Vue's `<script setup>`.
17+
pub framework_options: FrameworkOptions,
1318
}
1419

1520
impl<'a> JavaScriptSource<'a> {
1621
pub fn new(source_text: &'a str, source_type: SourceType) -> Self {
17-
Self { source_text, source_type, start: 0, is_partial: false }
22+
Self {
23+
source_text,
24+
source_type,
25+
start: 0,
26+
is_partial: false,
27+
framework_options: FrameworkOptions::Default,
28+
}
1829
}
1930

2031
pub fn partial(source_text: &'a str, source_type: SourceType, start: u32) -> Self {
21-
Self { source_text, source_type, start, is_partial: true }
32+
Self::partial_with_framework_options(
33+
source_text,
34+
source_type,
35+
FrameworkOptions::Default,
36+
start,
37+
)
38+
}
39+
40+
pub fn partial_with_framework_options(
41+
source_text: &'a str,
42+
source_type: SourceType,
43+
framework_options: FrameworkOptions,
44+
start: u32,
45+
) -> Self {
46+
Self { source_text, source_type, start, is_partial: true, framework_options }
2247
}
2348

2449
pub fn as_str(&self) -> &'a str {

0 commit comments

Comments
 (0)