Skip to content

Commit 243c247

Browse files
committed
refactor(linter): able to use custom file system in runtime (#10828)
The goal of this Stack is to remove the Runtime properties behind a feature/test flag
1 parent ae70cc1 commit 243c247

File tree

1 file changed

+43
-2
lines changed

1 file changed

+43
-2
lines changed

crates/oxc_linter/src/service/runtime.rs

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ pub struct Runtime {
4545
pub(super) linter: Linter,
4646
resolver: Option<Resolver>,
4747

48+
pub(super) file_system: Box<dyn RuntimeFileSystem + Sync + Send>,
49+
4850
// The language server uses more up to date source_text provided by `workspace/didChange` request.
4951
// This is required to support `run: "onType"` configuration
5052
#[cfg(feature = "language_server")]
@@ -140,6 +142,35 @@ impl ModuleToLint {
140142
}
141143
}
142144

145+
/// A simple trait for the `Runtime` to load and save file from a filesystem
146+
/// The `Runtime` uses OsFileSystem as a default
147+
/// The Tester and `oxc_language_server` would like to provide the content from memory
148+
pub trait RuntimeFileSystem {
149+
/// reads the content of a file path
150+
///
151+
/// # Errors
152+
/// When no valid path is provided or the content is not valid UTF-8 Stream
153+
fn read_to_string(&self, path: &Path) -> Result<String, std::io::Error>;
154+
155+
/// write a file to the file system
156+
///
157+
/// # Errors
158+
/// When the program does not have write permission for the file system
159+
fn write_file(&self, path: &Path, content: String) -> Result<(), std::io::Error>;
160+
}
161+
162+
struct OsFileSystem;
163+
164+
impl RuntimeFileSystem for OsFileSystem {
165+
fn read_to_string(&self, path: &Path) -> Result<String, std::io::Error> {
166+
read_to_string(path)
167+
}
168+
169+
fn write_file(&self, path: &Path, content: String) -> Result<(), std::io::Error> {
170+
fs::write(path, content)
171+
}
172+
}
173+
143174
impl Runtime {
144175
pub(super) fn new(linter: Linter, options: LintServiceOptions) -> Self {
145176
let resolver = options.cross_module.then(|| {
@@ -150,13 +181,23 @@ impl Runtime {
150181
paths: options.paths.iter().cloned().collect(),
151182
linter,
152183
resolver,
184+
file_system: Box::new(OsFileSystem),
153185
#[cfg(feature = "language_server")]
154186
source_text_cache: FxHashMap::default(),
155187
#[cfg(test)]
156188
test_source: std::sync::RwLock::new(None),
157189
}
158190
}
159191

192+
#[expect(dead_code)]
193+
pub fn with_file_system(
194+
mut self,
195+
file_system: Box<dyn RuntimeFileSystem + Sync + Send>,
196+
) -> Self {
197+
self.file_system = file_system;
198+
self
199+
}
200+
160201
fn get_resolver(tsconfig_path: Option<PathBuf>) -> Resolver {
161202
use oxc_resolver::{ResolveOptions, TsconfigOptions, TsconfigReferences};
162203
let tsconfig = tsconfig_path.and_then(|path| {
@@ -209,7 +250,7 @@ impl Runtime {
209250
return Some(Ok((source_type, source_text.clone())));
210251
}
211252

212-
let file_result = read_to_string(path).map_err(|e| {
253+
let file_result = self.file_system.read_to_string(path).map_err(|e| {
213254
Error::new(OxcDiagnostic::error(format!(
214255
"Failed to open file {path:?} with error \"{e}\""
215256
)))
@@ -518,7 +559,7 @@ impl Runtime {
518559
// If the new source text is owned, that means it was modified,
519560
// so we write the new source text to the file.
520561
if let Cow::Owned(new_source_text) = new_source_text {
521-
fs::write(path, new_source_text).unwrap();
562+
me.file_system.write_file(path, new_source_text).unwrap();
522563
}
523564
});
524565
});

0 commit comments

Comments
 (0)