From 6acc7c40445ce0cfc506ac4d7f5c12489664e686 Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Tue, 15 Jul 2025 14:11:35 +0100 Subject: [PATCH 1/7] feat(wasm): expose new functions --- .changeset/six-corners-lay.md | 11 +++ Cargo.lock | 2 + crates/biome_module_graph/Cargo.toml | 8 +- .../biome_module_graph/src/js_module_info.rs | 39 +++++++- crates/biome_module_graph/src/lib.rs | 1 + crates/biome_service/Cargo.toml | 3 +- ..._workspace__tests__debug_module_graph.snap | 33 +++++++ crates/biome_service/src/workspace.rs | 98 +++++++++++++------ crates/biome_service/src/workspace.tests.rs | 93 +++++++++++++++++- crates/biome_service/src/workspace/client.rs | 18 +++- crates/biome_service/src/workspace/server.rs | 28 +++++- crates/biome_service/src/workspace_types.rs | 5 +- crates/biome_wasm/src/lib.rs | 37 ++++++- .../@biomejs/backend-jsonrpc/src/workspace.ts | 31 +++++- 14 files changed, 355 insertions(+), 52 deletions(-) create mode 100644 .changeset/six-corners-lay.md create mode 100644 crates/biome_service/src/snapshots/biome_service__workspace__tests__debug_module_graph.snap diff --git a/.changeset/six-corners-lay.md b/.changeset/six-corners-lay.md new file mode 100644 index 000000000000..433a415d815a --- /dev/null +++ b/.changeset/six-corners-lay.md @@ -0,0 +1,11 @@ +--- +"@biomejs/wasm-bundler": minor +"@biomejs/wasm-nodejs": minor +"@biomejs/wasm-web": minor +--- + +Added new functions: +- `fileExists`: returns whether the input file exists in the workspace. +- `isPathIgnored`: returns whether the input path is ignored. +- `updateModuleGraph`: updates the internal module graph of the input path. +- `getModuleGraph`: it returns a serialized version of the internal module graph. diff --git a/Cargo.lock b/Cargo.lock index 197399bacca4..9430462a710b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1274,6 +1274,8 @@ dependencies = [ "papaya", "rust-lapper", "rustc-hash 2.1.1", + "schemars", + "serde", "serde_json", "static_assertions", "tikv-jemallocator", diff --git a/crates/biome_module_graph/Cargo.toml b/crates/biome_module_graph/Cargo.toml index 09dca3d7189a..1751af7e2b45 100644 --- a/crates/biome_module_graph/Cargo.toml +++ b/crates/biome_module_graph/Cargo.toml @@ -27,10 +27,12 @@ biome_rowan = { workspace = true } camino = { workspace = true } cfg-if = { workspace = true } indexmap = { workspace = true } -once_cell = "1.21.3" # Use `std::sync::OnceLock::get_or_try_init` when it is stable. +once_cell = "1.21.3" # Use `std::sync::OnceLock::get_or_try_init` when it is stable. papaya = { workspace = true } rust-lapper = { workspace = true } rustc-hash = { workspace = true } +schemars = { workspace = true, optional = true } +serde = { workspace = true, optional = true } serde_json = { workspace = true } static_assertions = { workspace = true } @@ -51,6 +53,10 @@ mimalloc = { workspace = true } [target.'cfg(all(target_family="unix", not(all(target_arch = "aarch64", target_env = "musl"))))'.dev-dependencies] tikv-jemallocator = { workspace = true } +[features] +schema = ["dep:schemars"] +serde = ["dep:serde"] + [[bench]] harness = false name = "module_graph" diff --git a/crates/biome_module_graph/src/js_module_info.rs b/crates/biome_module_graph/src/js_module_info.rs index b95c714d3380..21b19e6ac937 100644 --- a/crates/biome_module_graph/src/js_module_info.rs +++ b/crates/biome_module_graph/src/js_module_info.rs @@ -13,7 +13,7 @@ use biome_resolver::ResolvedPath; use biome_rowan::{Text, TextRange}; use indexmap::IndexMap; use rust_lapper::Lapper; -use rustc_hash::FxHashMap; +use rustc_hash::{FxHashMap, FxHashSet}; use crate::ModuleGraph; @@ -82,6 +82,31 @@ impl JsModuleInfo { id: scope_id_for_range(&self.0.scope_by_range, range), } } + + /// Returns a serializable version of this module + pub fn dump(&self) -> SerializedJsModuleInfo { + SerializedJsModuleInfo { + static_imports: self + .static_imports + .iter() + .map(|(text, static_import)| { + (text.to_string(), static_import.specifier.to_string()) + }) + .collect::>(), + + exports: self + .exports + .iter() + .map(|(text, _)| text.to_string()) + .collect::>(), + + dynamic_imports: self + .dynamic_import_paths + .iter() + .map(|(text, _)| text.to_string()) + .collect::>(), + } + } } #[derive(Debug)] @@ -345,3 +370,15 @@ fn scope_id_for_range(scope_by_range: &Lapper, range: TextRange) - ScopeId::new(interval.val.index()) }) } + +#[derive(Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct SerializedJsModuleInfo { + /// Static imports + static_imports: FxHashMap, + /// Dynamic imports + dynamic_imports: FxHashSet, + /// Exported symbols + exports: FxHashSet, +} diff --git a/crates/biome_module_graph/src/lib.rs b/crates/biome_module_graph/src/lib.rs index b573bfcfbcb8..305d2b5a54b2 100644 --- a/crates/biome_module_graph/src/lib.rs +++ b/crates/biome_module_graph/src/lib.rs @@ -9,5 +9,6 @@ pub use biome_resolver::ResolvedPath; pub use js_module_info::{ JsExport, JsImport, JsModuleInfo, JsOwnExport, JsReexport, ModuleResolver, + SerializedJsModuleInfo, }; pub use module_graph::{ModuleGraph, SUPPORTED_EXTENSIONS}; diff --git a/crates/biome_service/Cargo.toml b/crates/biome_service/Cargo.toml index 5d0e72cf2577..21992e81ecc1 100644 --- a/crates/biome_service/Cargo.toml +++ b/crates/biome_service/Cargo.toml @@ -49,7 +49,7 @@ biome_json_analyze = { workspace = true } biome_json_formatter = { workspace = true, features = ["serde"] } biome_json_parser = { workspace = true } biome_json_syntax = { workspace = true } -biome_module_graph = { workspace = true } +biome_module_graph = { workspace = true, features = ["serde"] } biome_package = { workspace = true } biome_parser = { workspace = true } biome_plugin_loader = { workspace = true } @@ -95,6 +95,7 @@ schema = [ "biome_html_syntax/schema", "biome_html_formatter/schema", "biome_fs/schema", + "biome_module_graph/schema", ] [lints] diff --git a/crates/biome_service/src/snapshots/biome_service__workspace__tests__debug_module_graph.snap b/crates/biome_service/src/snapshots/biome_service__workspace__tests__debug_module_graph.snap new file mode 100644 index 000000000000..228ad44c4992 --- /dev/null +++ b/crates/biome_service/src/snapshots/biome_service__workspace__tests__debug_module_graph.snap @@ -0,0 +1,33 @@ +[] bb--- +source: crates/biome_service/src/workspace.tests.rs +expression: result +--- +GetModuleGraphResult { + data: { + "/project/utils.js": SerializedJsModuleInfo { + static_imports: {}, + dynamic_imports: {}, + exports: { + "filter", + "debounce", + }, + }, + "/project/dynamic.js": SerializedJsModuleInfo { + static_imports: {}, + dynamic_imports: {}, + exports: { + "squash", + }, + }, + "/project/file.js": SerializedJsModuleInfo { + static_imports: { + "filter": "./utils.js", + "debounce": "./utils.js", + }, + dynamic_imports: { + "./dynamic.js", + }, + exports: {}, + }, + }, +} diff --git a/crates/biome_service/src/workspace.rs b/crates/biome_service/src/workspace.rs index 8e22987aa638..43707dd1a52b 100644 --- a/crates/biome_service/src/workspace.rs +++ b/crates/biome_service/src/workspace.rs @@ -75,6 +75,7 @@ use biome_formatter::Printed; use biome_fs::BiomePath; use biome_grit_patterns::GritTargetLanguage; use biome_js_syntax::{TextRange, TextSize}; +use biome_module_graph::SerializedJsModuleInfo; use biome_resolver::FsWithResolverProxy; use biome_text_edit::TextEdit; use camino::Utf8Path; @@ -82,6 +83,7 @@ pub use client::{TransportRequest, WorkspaceClient, WorkspaceTransport}; use core::str; use crossbeam::channel::bounded; use enumflags2::{BitFlags, bitflags}; +use rustc_hash::FxHashMap; #[cfg(feature = "schema")] use schemars::{r#gen::SchemaGenerator, schema::Schema}; pub use server::WorkspaceServer; @@ -868,6 +870,22 @@ pub struct CloseFileParams { pub project_key: ProjectKey, pub path: BiomePath, } +#[derive(Debug, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[serde(rename_all = "camelCase")] +pub struct UpdateModuleGraphParams { + pub path: BiomePath, + /// The kind of update to apply to the module graph + pub update_kind: UpdateKind, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[serde(rename_all = "camelCase")] +pub enum UpdateKind { + AddOrUpdate, + Remove, +} #[derive(Debug, serde::Serialize, serde::Deserialize)] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] @@ -1277,6 +1295,13 @@ impl From for FileExitsParams { } } +#[derive(Debug, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[serde(rename_all = "camelCase")] +pub struct GetModuleGraphResult { + pub data: FxHashMap, +} + pub trait Workspace: Send + Sync + RefUnwindSafe { // #region PROJECT-LEVEL METHODS @@ -1383,36 +1408,6 @@ pub trait Workspace: Send + Sync + RefUnwindSafe { /// considered ignored. fn is_path_ignored(&self, params: IsPathIgnoredParams) -> Result; - /// Returns a textual, debug representation of the syntax tree for a given - /// document. - fn get_syntax_tree( - &self, - params: GetSyntaxTreeParams, - ) -> Result; - - /// Returns a textual, debug representation of the control flow graph at a - /// given position in the document. - fn get_control_flow_graph( - &self, - params: GetControlFlowGraphParams, - ) -> Result; - - /// Returns a textual, debug representation of the formatter IR for a given - /// document. - fn get_formatter_ir(&self, params: GetFormatterIRParams) -> Result; - - /// Returns an IR of the type information of the document - fn get_type_info(&self, params: GetTypeInfoParams) -> Result; - - /// Returns the registered types of the document - fn get_registered_types( - &self, - params: GetRegisteredTypesParams, - ) -> Result; - - /// Returns a textual, debug representation of the semantic model for the document. - fn get_semantic_model(&self, params: GetSemanticModelParams) -> Result; - /// Returns the content of a given file. fn get_file_content(&self, params: GetFileContentParams) -> Result; @@ -1460,6 +1455,14 @@ pub trait Workspace: Send + Sync + RefUnwindSafe { /// may still be required for multi-file analysis. fn close_file(&self, params: CloseFileParams) -> Result<(), WorkspaceError>; + /// Updates the internal module graph using the provided path. + /// + /// ## Errors + /// + /// An error is emitted if the path doesn't exist inside the workspace. Use + /// the method [Workspace::open_file] before updating the module graph. + fn update_module_graph(&self, params: UpdateModuleGraphParams) -> Result<(), WorkspaceError>; + /// Returns the filesystem implementation to open files with. /// /// This may be an in-memory file system. @@ -1487,7 +1490,40 @@ pub trait Workspace: Send + Sync + RefUnwindSafe { // #endregion - // #region MISC METHODS + // #region DEBUGGING METHODS + + /// Returns a textual, debug representation of the syntax tree for a given + /// document. + fn get_syntax_tree( + &self, + params: GetSyntaxTreeParams, + ) -> Result; + + /// Returns a textual, debug representation of the control flow graph at a + /// given position in the document. + fn get_control_flow_graph( + &self, + params: GetControlFlowGraphParams, + ) -> Result; + + /// Returns a textual, debug representation of the formatter IR for a given + /// document. + fn get_formatter_ir(&self, params: GetFormatterIRParams) -> Result; + + /// Returns an IR of the type information of the document + fn get_type_info(&self, params: GetTypeInfoParams) -> Result; + + /// Returns the registered types of the document + fn get_registered_types( + &self, + params: GetRegisteredTypesParams, + ) -> Result; + + /// Returns a textual, debug representation of the semantic model for the document. + fn get_semantic_model(&self, params: GetSemanticModelParams) -> Result; + + /// Returns a serializable version of the module graph + fn get_module_graph(&self) -> Result; /// Returns debug information about this workspace. fn rage(&self, params: RageParams) -> Result; diff --git a/crates/biome_service/src/workspace.tests.rs b/crates/biome_service/src/workspace.tests.rs index f0fdf6cd898d..4367f266a758 100644 --- a/crates/biome_service/src/workspace.tests.rs +++ b/crates/biome_service/src/workspace.tests.rs @@ -21,8 +21,8 @@ use crate::{Workspace, WorkspaceError}; use super::{ CloseFileParams, CloseProjectParams, FileContent, FileFeaturesResult, FileGuard, GetFileContentParams, GetSyntaxTreeParams, OpenFileParams, OpenProjectParams, - OpenProjectResult, PullDiagnosticsParams, ScanKind, ScanProjectFolderParams, - UpdateSettingsParams, server, + OpenProjectResult, PullDiagnosticsParams, ScanKind, ScanProjectFolderParams, UpdateKind, + UpdateModuleGraphParams, UpdateSettingsParams, server, }; fn create_server() -> (Box, ProjectKey) { @@ -884,3 +884,92 @@ class Person { assert!(result.is_ok()); assert_snapshot!(result.unwrap()); } + +#[test] +fn debug_module_graph() { + let fs = MemoryFileSystem::default(); + + let workspace = server(Arc::new(fs), None); + let OpenProjectResult { project_key, .. } = workspace + .open_project(OpenProjectParams { + path: Utf8PathBuf::from("/project").into(), + open_uninitialized: true, + only_rules: Some(Vec::new()), + skip_rules: None, + }) + .unwrap(); + + workspace + .open_file(OpenFileParams { + project_key, + path: BiomePath::new("/project/file.js"), + content: FileContent::from_client( + r#" +import { filter, debounce } from "./utils.js"; + +async function test() { + const {squash} = import("./dynamic.js"); +} +"#, + ), + document_file_source: None, + persist_node_cache: false, + }) + .unwrap(); + + workspace + .open_file(OpenFileParams { + project_key, + path: BiomePath::new("/project/utils.js"), + + content: FileContent::from_client( + r#" +export const filter = function filter() {}; + +export const debounce = function debounce() {}; +"#, + ), + document_file_source: None, + persist_node_cache: false, + }) + .unwrap(); + + workspace + .open_file(OpenFileParams { + project_key, + path: BiomePath::new("/project/dynamic.js"), + + content: FileContent::from_client( + r#" +export const squash = function squash() {}; +"#, + ), + document_file_source: None, + persist_node_cache: false, + }) + .unwrap(); + + workspace + .update_module_graph(UpdateModuleGraphParams { + path: BiomePath::new("/project/file.js"), + update_kind: UpdateKind::AddOrUpdate, + }) + .unwrap(); + workspace + .update_module_graph(UpdateModuleGraphParams { + path: BiomePath::new("/project/utils.js"), + update_kind: UpdateKind::AddOrUpdate, + }) + .unwrap(); + + workspace + .update_module_graph(UpdateModuleGraphParams { + path: BiomePath::new("/project/dynamic.js"), + update_kind: UpdateKind::AddOrUpdate, + }) + .unwrap(); + + let result = workspace.get_module_graph().unwrap(); + + assert_debug_snapshot!(result) +} diff --git a/crates/biome_service/src/workspace/client.rs b/crates/biome_service/src/workspace/client.rs index b58d75eaf582..fd356d19d299 100644 --- a/crates/biome_service/src/workspace/client.rs +++ b/crates/biome_service/src/workspace/client.rs @@ -1,11 +1,11 @@ use super::{ ChangeFileParams, CloseFileParams, FileExitsParams, FixFileParams, FixFileResult, FormatFileParams, FormatOnTypeParams, FormatRangeParams, GetControlFlowGraphParams, - GetFormatterIRParams, GetSemanticModelParams, GetSyntaxTreeParams, GetSyntaxTreeResult, - OpenFileParams, PullActionsParams, PullActionsResult, PullDiagnosticsParams, - PullDiagnosticsResult, RenameParams, RenameResult, ScanProjectFolderParams, - ScanProjectFolderResult, SearchPatternParams, SearchResults, SupportsFeatureParams, - UpdateSettingsParams, UpdateSettingsResult, + GetFormatterIRParams, GetModuleGraphResult, GetSemanticModelParams, GetSyntaxTreeParams, + GetSyntaxTreeResult, OpenFileParams, PullActionsParams, PullActionsResult, + PullDiagnosticsParams, PullDiagnosticsResult, RenameParams, RenameResult, + ScanProjectFolderParams, ScanProjectFolderResult, SearchPatternParams, SearchResults, + SupportsFeatureParams, UpdateModuleGraphParams, UpdateSettingsParams, UpdateSettingsResult, }; use crate::workspace::{ CheckFileSizeParams, CheckFileSizeResult, CloseProjectParams, FileFeaturesResult, @@ -231,6 +231,10 @@ where self.request("biome/close_file", params) } + fn update_module_graph(&self, params: UpdateModuleGraphParams) -> Result<(), WorkspaceError> { + self.request("biome/update_module_graph", params) + } + fn fs(&self) -> &dyn FsWithResolverProxy { self.fs.as_ref() } @@ -257,4 +261,8 @@ where fn server_info(&self) -> Option<&ServerInfo> { self.server_info.as_ref() } + + fn get_module_graph(&self) -> Result { + self.request("biome/get_module_graph", ()) + } } diff --git a/crates/biome_service/src/workspace/server.rs b/crates/biome_service/src/workspace/server.rs index 3732eea72e0b..6bb0feee660d 100644 --- a/crates/biome_service/src/workspace/server.rs +++ b/crates/biome_service/src/workspace/server.rs @@ -803,7 +803,7 @@ impl WorkspaceServer { /// Updates the [ModuleGraph] for the given `path` with an optional `root`. #[tracing::instrument(level = "debug", skip(self, root))] - fn update_module_graph( + fn update_module_graph_internal( &self, signal_kind: WatcherSignalKind, path: &BiomePath, @@ -842,7 +842,7 @@ impl WorkspaceServer { self.update_project_layout(signal_kind, &path)?; } - self.update_module_graph(signal_kind, &path, root); + self.update_module_graph_internal(signal_kind, &path, root); match signal_kind { WatcherSignalKind::AddedOrChanged(OpenFileReason::InitialScan) => { @@ -1687,6 +1687,19 @@ impl Workspace for WorkspaceServer { } } + fn update_module_graph(&self, params: UpdateModuleGraphParams) -> Result<(), WorkspaceError> { + let parsed = self.get_parse(params.path.as_path())?; + let signal = match params.update_kind { + UpdateKind::AddOrUpdate => { + WatcherSignalKind::AddedOrChanged(OpenFileReason::ClientRequest) + } + UpdateKind::Remove => WatcherSignalKind::Removed, + }; + + self.update_module_graph_internal(signal, ¶ms.path, Some(parsed.root())); + Ok(()) + } + fn fs(&self) -> &dyn FsWithResolverProxy { self.fs.as_ref() } @@ -1751,6 +1764,17 @@ impl Workspace for WorkspaceServer { fn server_info(&self) -> Option<&ServerInfo> { None } + + fn get_module_graph(&self) -> Result { + let module_graph = self.module_graph.data(); + let mut data = FxHashMap::default(); + + for (path, info) in module_graph.iter() { + data.insert(path.as_str().to_string(), info.dump()); + } + + Ok(GetModuleGraphResult { data }) + } } /// Sets up the global Rayon thread pool the first time it's called. diff --git a/crates/biome_service/src/workspace_types.rs b/crates/biome_service/src/workspace_types.rs index 6634a9e8b02a..18547011d668 100644 --- a/crates/biome_service/src/workspace_types.rs +++ b/crates/biome_service/src/workspace_types.rs @@ -548,7 +548,7 @@ macro_rules! workspace_method { } /// Returns a list of signature for all the methods in the [Workspace] trait -pub fn methods() -> [WorkspaceMethod; 25] { +pub fn methods() -> [WorkspaceMethod; 28] { [ workspace_method!(file_features), workspace_method!(update_settings), @@ -556,6 +556,9 @@ pub fn methods() -> [WorkspaceMethod; 25] { workspace_method!(open_file), workspace_method!(change_file), workspace_method!(close_file), + workspace_method!(file_exists), + workspace_method!(is_path_ignored), + workspace_method!(update_module_graph), workspace_method!(get_syntax_tree), workspace_method!(file_exists), workspace_method!(check_file_size), diff --git a/crates/biome_wasm/src/lib.rs b/crates/biome_wasm/src/lib.rs index c0c8fe2b8762..665fd4d9f70b 100644 --- a/crates/biome_wasm/src/lib.rs +++ b/crates/biome_wasm/src/lib.rs @@ -4,11 +4,11 @@ use js_sys::Error; use wasm_bindgen::prelude::*; use biome_service::workspace::{ - self, ChangeFileParams, CloseFileParams, FixFileParams, FormatFileParams, FormatOnTypeParams, - FormatRangeParams, GetControlFlowGraphParams, GetFileContentParams, GetFormatterIRParams, - GetRegisteredTypesParams, GetSemanticModelParams, GetSyntaxTreeParams, GetTypeInfoParams, - OpenProjectParams, PullActionsParams, PullDiagnosticsParams, RenameParams, - UpdateSettingsParams, + self, ChangeFileParams, CloseFileParams, FileExitsParams, FixFileParams, FormatFileParams, + FormatOnTypeParams, FormatRangeParams, GetControlFlowGraphParams, GetFileContentParams, + GetFormatterIRParams, GetModuleGraphResult, GetRegisteredTypesParams, GetSemanticModelParams, + GetSyntaxTreeParams, GetTypeInfoParams, OpenProjectParams, PullActionsParams, + PullDiagnosticsParams, RenameParams, UpdateModuleGraphParams, UpdateSettingsParams, }; use biome_service::workspace::{OpenFileParams, SupportsFeatureParams}; use camino::{Utf8Path, Utf8PathBuf}; @@ -193,6 +193,33 @@ impl Workspace { self.inner.close_file(params).map_err(into_error) } + #[wasm_bindgen(js_name = fileExists)] + pub fn file_exists(&self, params: IFileExitsParams) -> Result { + let params: FileExitsParams = + serde_wasm_bindgen::from_value(params.into()).map_err(into_error)?; + self.inner.file_exists(params).map_err(into_error) + } + + #[wasm_bindgen(js_name = isPathIgnored)] + pub fn is_path_ignored(&self, params: IIsPathIgnoredParams) -> Result { + let params: IsPathIgnoredParams = + serde_wasm_bindgen::from_value(params.into()).map_err(into_error)?; + self.inner.is_path_ignored(params).map_err(into_error) + } + + #[wasm_bindgen(js_name = updateModuleGraph)] + pub fn update_module_graph(&self, params: IUpdateModuleGraphParams) -> Result<(), Error> { + let params: UpdateModuleGraphParams = + serde_wasm_bindgen::from_value(params.into()).map_err(into_error)?; + + self.inner.update_module_graph(params).map_err(into_error) + } + + #[wasm_bindgen(js_name = getModuleGraph)] + pub fn get_module_graph(&self) -> Result { + self.inner.get_control_flow_graph().map_err(into_error) + } + #[wasm_bindgen(js_name = pullDiagnostics)] pub fn pull_diagnostics( &self, diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index 51e299ca6427..19d9796ac494 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -8866,6 +8866,22 @@ export interface CloseFileParams { path: BiomePath; projectKey: ProjectKey; } +export interface FileExitsParams { + filePath: BiomePath; +} +export interface IsPathIgnoredParams { + features: FeatureName; + path: BiomePath; + projectKey: ProjectKey; +} +export interface UpdateModuleGraphParams { + path: BiomePath; + /** + * The kind of update to apply to the module graph + */ + updateKind: UpdateKind; +} +export type UpdateKind = "addOrUpdate" | "remove"; export interface GetSyntaxTreeParams { path: BiomePath; projectKey: ProjectKey; @@ -8874,9 +8890,6 @@ export interface GetSyntaxTreeResult { ast: string; cst: string; } -export interface FileExitsParams { - filePath: BiomePath; -} export interface CheckFileSizeParams { path: BiomePath; projectKey: ProjectKey; @@ -9123,6 +9136,9 @@ export interface Workspace { openFile(params: OpenFileParams): Promise; changeFile(params: ChangeFileParams): Promise; closeFile(params: CloseFileParams): Promise; + fileExists(params: FileExitsParams): Promise; + isPathIgnored(params: IsPathIgnoredParams): Promise; + updateModuleGraph(params: UpdateModuleGraphParams): Promise; getSyntaxTree(params: GetSyntaxTreeParams): Promise; fileExists(params: FileExitsParams): Promise; checkFileSize(params: CheckFileSizeParams): Promise; @@ -9166,6 +9182,15 @@ export function createWorkspace(transport: Transport): Workspace { closeFile(params) { return transport.request("biome/close_file", params); }, + fileExists(params) { + return transport.request("biome/file_exists", params); + }, + isPathIgnored(params) { + return transport.request("biome/is_path_ignored", params); + }, + updateModuleGraph(params) { + return transport.request("biome/update_module_graph", params); + }, getSyntaxTree(params) { return transport.request("biome/get_syntax_tree", params); }, From e7ae55ca22b45032016732ad2a5344aea14d86a0 Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Tue, 15 Jul 2025 20:53:20 +0100 Subject: [PATCH 2/7] apply suggestions --- .changeset/six-corners-lay.md | 4 +--- crates/biome_module_graph/src/js_module_info.rs | 17 ++++++++--------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/.changeset/six-corners-lay.md b/.changeset/six-corners-lay.md index 433a415d815a..3af141bf26fe 100644 --- a/.changeset/six-corners-lay.md +++ b/.changeset/six-corners-lay.md @@ -1,7 +1,5 @@ --- -"@biomejs/wasm-bundler": minor -"@biomejs/wasm-nodejs": minor -"@biomejs/wasm-web": minor +"@biomejs/biome": minor --- Added new functions: diff --git a/crates/biome_module_graph/src/js_module_info.rs b/crates/biome_module_graph/src/js_module_info.rs index 21b19e6ac937..f02fa9b813f1 100644 --- a/crates/biome_module_graph/src/js_module_info.rs +++ b/crates/biome_module_graph/src/js_module_info.rs @@ -3,9 +3,6 @@ mod collector; mod module_resolver; mod scope; mod visitor; - -use std::{ops::Deref, sync::Arc}; - use biome_js_syntax::AnyJsImportLike; use biome_js_type_info::{BindingId, ImportSymbol, ResolvedTypeId, ScopeId, TypeData}; use biome_jsdoc_comment::JsdocComment; @@ -14,6 +11,8 @@ use biome_rowan::{Text, TextRange}; use indexmap::IndexMap; use rust_lapper::Lapper; use rustc_hash::{FxHashMap, FxHashSet}; +use std::collections::BTreeSet; +use std::{collections::BTreeMap, ops::Deref, sync::Arc}; use crate::ModuleGraph; @@ -92,19 +91,19 @@ impl JsModuleInfo { .map(|(text, static_import)| { (text.to_string(), static_import.specifier.to_string()) }) - .collect::>(), + .collect::>(), exports: self .exports .iter() .map(|(text, _)| text.to_string()) - .collect::>(), + .collect::>(), dynamic_imports: self .dynamic_import_paths .iter() .map(|(text, _)| text.to_string()) - .collect::>(), + .collect::>(), } } } @@ -376,9 +375,9 @@ fn scope_id_for_range(scope_by_range: &Lapper, range: TextRange) - #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub struct SerializedJsModuleInfo { /// Static imports - static_imports: FxHashMap, + static_imports: BTreeMap, /// Dynamic imports - dynamic_imports: FxHashSet, + dynamic_imports: BTreeSet, /// Exported symbols - exports: FxHashSet, + exports: BTreeSet, } From fd60ff239e210710b15a8196991b3e6e729c8efd Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Wed, 16 Jul 2025 10:17:59 -0400 Subject: [PATCH 3/7] chore: merge/rebase --- crates/biome_module_graph/src/js_module_info.rs | 2 +- crates/biome_service/src/workspace/server.rs | 15 ++++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/crates/biome_module_graph/src/js_module_info.rs b/crates/biome_module_graph/src/js_module_info.rs index f02fa9b813f1..e75279e787e3 100644 --- a/crates/biome_module_graph/src/js_module_info.rs +++ b/crates/biome_module_graph/src/js_module_info.rs @@ -10,7 +10,7 @@ use biome_resolver::ResolvedPath; use biome_rowan::{Text, TextRange}; use indexmap::IndexMap; use rust_lapper::Lapper; -use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_hash::FxHashMap; use std::collections::BTreeSet; use std::{collections::BTreeMap, ops::Deref, sync::Arc}; diff --git a/crates/biome_service/src/workspace/server.rs b/crates/biome_service/src/workspace/server.rs index 6bb0feee660d..1bbe891332a4 100644 --- a/crates/biome_service/src/workspace/server.rs +++ b/crates/biome_service/src/workspace/server.rs @@ -1,13 +1,14 @@ use super::document::Document; use super::{ ChangeFileParams, CheckFileSizeParams, CheckFileSizeResult, CloseFileParams, - CloseProjectParams, FileContent, FileExitsParams, FixFileParams, FixFileResult, - FormatFileParams, FormatOnTypeParams, FormatRangeParams, GetControlFlowGraphParams, - GetFormatterIRParams, GetSemanticModelParams, GetSyntaxTreeParams, GetSyntaxTreeResult, - IgnoreKind, OpenFileParams, OpenProjectParams, ParsePatternParams, ParsePatternResult, - PatternId, ProjectKey, PullActionsParams, PullActionsResult, PullDiagnosticsParams, - PullDiagnosticsResult, RenameResult, ScanProjectFolderParams, ScanProjectFolderResult, - SearchPatternParams, SearchResults, ServiceDataNotification, SupportsFeatureParams, + CloseProjectParams, FeaturesBuilder, FileContent, FileExitsParams, FixFileParams, + FixFileResult, FormatFileParams, FormatOnTypeParams, FormatRangeParams, + GetControlFlowGraphParams, GetFormatterIRParams, GetModuleGraphResult, GetSemanticModelParams, + GetSyntaxTreeParams, GetSyntaxTreeResult, IgnoreKind, OpenFileParams, OpenProjectParams, + ParsePatternParams, ParsePatternResult, PatternId, ProjectKey, PullActionsParams, + PullActionsResult, PullDiagnosticsParams, PullDiagnosticsResult, RenameResult, + ScanProjectFolderParams, ScanProjectFolderResult, SearchPatternParams, SearchResults, + ServiceDataNotification, SupportsFeatureParams, UpdateKind, UpdateModuleGraphParams, UpdateSettingsParams, UpdateSettingsResult, }; use crate::configuration::{LoadedConfiguration, ProjectScanComputer, read_config}; From b86b2e4686b1bedfa33be2ddff6943774fce48e7 Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Wed, 16 Jul 2025 10:19:09 -0400 Subject: [PATCH 4/7] chore: reword --- .changeset/six-corners-lay.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/six-corners-lay.md b/.changeset/six-corners-lay.md index 3af141bf26fe..1acdcdfd876b 100644 --- a/.changeset/six-corners-lay.md +++ b/.changeset/six-corners-lay.md @@ -2,7 +2,7 @@ "@biomejs/biome": minor --- -Added new functions: +Added new functions to the `@biomejs/wasm-*` packages: - `fileExists`: returns whether the input file exists in the workspace. - `isPathIgnored`: returns whether the input path is ignored. - `updateModuleGraph`: updates the internal module graph of the input path. From 28930d60ddad81f07c6c27e24ecd51ffb8a3818f Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Wed, 16 Jul 2025 10:38:51 -0400 Subject: [PATCH 5/7] chore: reword type so it doesn't conflict --- crates/biome_cli/src/execute/traverse.rs | 4 +- crates/biome_lsp/src/handlers/analysis.rs | 8 ++-- crates/biome_lsp/src/handlers/formatting.rs | 14 +++---- .../biome_lsp/src/handlers/text_document.rs | 6 +-- crates/biome_lsp/src/requests/syntax_tree.rs | 4 +- ..._workspace__tests__debug_module_graph.snap | 6 +-- crates/biome_service/src/workspace.rs | 14 +++++-- crates/biome_service/src/workspace/client.rs | 17 +++++---- crates/biome_service/src/workspace/scanner.rs | 6 +-- crates/biome_service/src/workspace/server.rs | 23 +++++++----- crates/biome_service/src/workspace_types.rs | 3 +- crates/biome_wasm/src/lib.rs | 24 ++++++++---- .../@biomejs/backend-jsonrpc/src/workspace.ts | 37 ++++++++++++++++++- 13 files changed, 112 insertions(+), 54 deletions(-) diff --git a/crates/biome_cli/src/execute/traverse.rs b/crates/biome_cli/src/execute/traverse.rs index ff451ba472a7..85405ea03e73 100644 --- a/crates/biome_cli/src/execute/traverse.rs +++ b/crates/biome_cli/src/execute/traverse.rs @@ -12,7 +12,7 @@ use biome_fs::{BiomePath, FileSystem, PathInterner}; use biome_fs::{TraversalContext, TraversalScope}; use biome_service::projects::ProjectKey; use biome_service::workspace::{ - DocumentFileSource, DropPatternParams, FileFeaturesResult, IgnoreKind, IsPathIgnoredParams, + DocumentFileSource, DropPatternParams, FileFeaturesResult, IgnoreKind, PathIsIgnoredParams, }; use biome_service::{Workspace, WorkspaceError, extension_error, workspace::SupportsFeatureParams}; use camino::{Utf8Path, Utf8PathBuf}; @@ -537,7 +537,7 @@ impl TraversalContext for TraversalOptions<'_, '_> { // Note that `symlink/subdir` is not an existing file. let can_handle = !self .workspace - .is_path_ignored(IsPathIgnoredParams { + .is_path_ignored(PathIsIgnoredParams { project_key: self.project_key, path: biome_path.clone(), features: self.execution.to_feature(), diff --git a/crates/biome_lsp/src/handlers/analysis.rs b/crates/biome_lsp/src/handlers/analysis.rs index 258771acdc66..7bfd08947c0d 100644 --- a/crates/biome_lsp/src/handlers/analysis.rs +++ b/crates/biome_lsp/src/handlers/analysis.rs @@ -17,7 +17,7 @@ use biome_service::WorkspaceError; use biome_service::file_handlers::{AstroFileHandler, SvelteFileHandler, VueFileHandler}; use biome_service::workspace::{ CheckFileSizeParams, FeaturesBuilder, FileFeaturesResult, FixFileMode, FixFileParams, - GetFileContentParams, IgnoreKind, IsPathIgnoredParams, PullActionsParams, + GetFileContentParams, IgnoreKind, PathIsIgnoredParams, PullActionsParams, SupportsFeatureParams, }; use std::borrow::Cow; @@ -62,7 +62,7 @@ pub(crate) fn code_actions( return Ok(None); } - if session.workspace.is_path_ignored(IsPathIgnoredParams { + if session.workspace.is_path_ignored(PathIsIgnoredParams { path: path.clone(), project_key: doc.project_key, features, @@ -309,7 +309,7 @@ fn fix_all( return Ok(None); } - if session.workspace.is_path_ignored(IsPathIgnoredParams { + if session.workspace.is_path_ignored(PathIsIgnoredParams { path: path.clone(), project_key: doc.project_key, features: analyzer_features, @@ -331,7 +331,7 @@ fn fix_all( })?; let should_format = file_features.supports_format(); - if session.workspace.is_path_ignored(IsPathIgnoredParams { + if session.workspace.is_path_ignored(PathIsIgnoredParams { path: path.clone(), project_key: doc.project_key, features: analyzer_features, diff --git a/crates/biome_lsp/src/handlers/formatting.rs b/crates/biome_lsp/src/handlers/formatting.rs index 8bacebab478f..29cb47a5ec9d 100644 --- a/crates/biome_lsp/src/handlers/formatting.rs +++ b/crates/biome_lsp/src/handlers/formatting.rs @@ -8,7 +8,7 @@ use biome_rowan::{TextLen, TextRange, TextSize}; use biome_service::file_handlers::{AstroFileHandler, SvelteFileHandler, VueFileHandler}; use biome_service::workspace::{ CheckFileSizeParams, FeaturesBuilder, FeaturesSupported, FileFeaturesResult, FormatFileParams, - FormatOnTypeParams, FormatRangeParams, GetFileContentParams, IgnoreKind, IsPathIgnoredParams, + FormatOnTypeParams, FormatRangeParams, GetFileContentParams, IgnoreKind, PathIsIgnoredParams, SupportsFeatureParams, }; use biome_service::{WorkspaceError, extension_error}; @@ -30,7 +30,7 @@ pub(crate) fn format( } let features = FeaturesBuilder::new().with_formatter().build(); - if session.workspace.is_path_ignored(IsPathIgnoredParams { + if session.workspace.is_path_ignored(PathIsIgnoredParams { path: path.clone(), project_key: doc.project_key, features, @@ -48,7 +48,7 @@ pub(crate) fn format( })?; if file_features.supports_format() - && !session.workspace.is_path_ignored(IsPathIgnoredParams { + && !session.workspace.is_path_ignored(PathIsIgnoredParams { path: path.clone(), project_key: doc.project_key, features, @@ -114,7 +114,7 @@ pub(crate) fn format_range( } let features = FeaturesBuilder::new().with_formatter().build(); - if session.workspace.is_path_ignored(IsPathIgnoredParams { + if session.workspace.is_path_ignored(PathIsIgnoredParams { path: path.clone(), project_key: doc.project_key, features, @@ -133,7 +133,7 @@ pub(crate) fn format_range( })?; if file_features.supports_format() - && !session.workspace.is_path_ignored(IsPathIgnoredParams { + && !session.workspace.is_path_ignored(PathIsIgnoredParams { path: path.clone(), project_key: doc.project_key, features, @@ -230,7 +230,7 @@ pub(crate) fn format_on_type( features, })?; - if session.workspace.is_path_ignored(IsPathIgnoredParams { + if session.workspace.is_path_ignored(PathIsIgnoredParams { path: path.clone(), project_key: doc.project_key, features, @@ -240,7 +240,7 @@ pub(crate) fn format_on_type( } if file_features.supports_format() - && !session.workspace.is_path_ignored(IsPathIgnoredParams { + && !session.workspace.is_path_ignored(PathIsIgnoredParams { path: path.clone(), project_key: doc.project_key, features, diff --git a/crates/biome_lsp/src/handlers/text_document.rs b/crates/biome_lsp/src/handlers/text_document.rs index 5e1a8abb736f..c60683a89da1 100644 --- a/crates/biome_lsp/src/handlers/text_document.rs +++ b/crates/biome_lsp/src/handlers/text_document.rs @@ -6,7 +6,7 @@ use crate::{documents::Document, session::Session}; use biome_fs::BiomePath; use biome_service::workspace::{ ChangeFileParams, CloseFileParams, DocumentFileSource, FeaturesBuilder, FileContent, - GetFileContentParams, IgnoreKind, IsPathIgnoredParams, OpenFileParams, OpenProjectParams, + GetFileContentParams, IgnoreKind, OpenFileParams, OpenProjectParams, PathIsIgnoredParams, ScanKind, }; use tower_lsp_server::lsp_types; @@ -60,7 +60,7 @@ pub(crate) async fn did_open( let is_ignored = session .workspace - .is_path_ignored(IsPathIgnoredParams { + .is_path_ignored(PathIsIgnoredParams { project_key, path: path.clone(), features: FeaturesBuilder::new().build(), @@ -108,7 +108,7 @@ pub(crate) async fn did_change( return Ok(()); } let features = FeaturesBuilder::new().build(); - if session.workspace.is_path_ignored(IsPathIgnoredParams { + if session.workspace.is_path_ignored(PathIsIgnoredParams { path: path.clone(), project_key: doc.project_key, features, diff --git a/crates/biome_lsp/src/requests/syntax_tree.rs b/crates/biome_lsp/src/requests/syntax_tree.rs index 9d784190b298..008fd6b221a7 100644 --- a/crates/biome_lsp/src/requests/syntax_tree.rs +++ b/crates/biome_lsp/src/requests/syntax_tree.rs @@ -1,6 +1,6 @@ use crate::{diagnostics::LspError, session::Session}; use biome_service::workspace::{ - FeaturesBuilder, GetSyntaxTreeParams, IgnoreKind, IsPathIgnoredParams, + FeaturesBuilder, GetSyntaxTreeParams, IgnoreKind, PathIsIgnoredParams, }; use serde::{Deserialize, Serialize}; use tower_lsp_server::lsp_types::{TextDocumentIdentifier, Uri}; @@ -22,7 +22,7 @@ pub(crate) fn syntax_tree(session: &Session, url: &Uri) -> Result }; let features = FeaturesBuilder::new().build(); - if session.workspace.is_path_ignored(IsPathIgnoredParams { + if session.workspace.is_path_ignored(PathIsIgnoredParams { path: path.clone(), project_key: doc.project_key, features, diff --git a/crates/biome_service/src/snapshots/biome_service__workspace__tests__debug_module_graph.snap b/crates/biome_service/src/snapshots/biome_service__workspace__tests__debug_module_graph.snap index 228ad44c4992..5585727c0fdd 100644 --- a/crates/biome_service/src/snapshots/biome_service__workspace__tests__debug_module_graph.snap +++ b/crates/biome_service/src/snapshots/biome_service__workspace__tests__debug_module_graph.snap @@ -1,4 +1,4 @@ -[] bb--- +--- source: crates/biome_service/src/workspace.tests.rs expression: result --- @@ -8,8 +8,8 @@ GetModuleGraphResult { static_imports: {}, dynamic_imports: {}, exports: { - "filter", "debounce", + "filter", }, }, "/project/dynamic.js": SerializedJsModuleInfo { @@ -21,8 +21,8 @@ GetModuleGraphResult { }, "/project/file.js": SerializedJsModuleInfo { static_imports: { - "filter": "./utils.js", "debounce": "./utils.js", + "filter": "./utils.js", }, dynamic_imports: { "./dynamic.js", diff --git a/crates/biome_service/src/workspace.rs b/crates/biome_service/src/workspace.rs index 43707dd1a52b..e6962e0f0b4b 100644 --- a/crates/biome_service/src/workspace.rs +++ b/crates/biome_service/src/workspace.rs @@ -815,6 +815,11 @@ pub struct GetSemanticModelParams { pub path: BiomePath, } +#[derive(Debug, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[serde(rename_all = "camelCase")] +pub struct GetModuleGraphParams {} + #[derive(Debug, serde::Serialize, serde::Deserialize)] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[serde(rename_all = "camelCase")] @@ -1191,7 +1196,7 @@ impl From<&str> for PatternId { #[derive(Debug, serde::Serialize, serde::Deserialize)] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[serde(rename_all = "camelCase")] -pub struct IsPathIgnoredParams { +pub struct PathIsIgnoredParams { pub project_key: ProjectKey, /// The path to inspect pub path: BiomePath, @@ -1406,7 +1411,7 @@ pub trait Workspace: Send + Sync + RefUnwindSafe { /// /// If the file path matches, `true` is returned, and it should be /// considered ignored. - fn is_path_ignored(&self, params: IsPathIgnoredParams) -> Result; + fn is_path_ignored(&self, params: PathIsIgnoredParams) -> Result; /// Returns the content of a given file. fn get_file_content(&self, params: GetFileContentParams) -> Result; @@ -1523,7 +1528,10 @@ pub trait Workspace: Send + Sync + RefUnwindSafe { fn get_semantic_model(&self, params: GetSemanticModelParams) -> Result; /// Returns a serializable version of the module graph - fn get_module_graph(&self) -> Result; + fn get_module_graph( + &self, + params: GetModuleGraphParams, + ) -> Result; /// Returns debug information about this workspace. fn rage(&self, params: RageParams) -> Result; diff --git a/crates/biome_service/src/workspace/client.rs b/crates/biome_service/src/workspace/client.rs index fd356d19d299..0bbddaf1bc37 100644 --- a/crates/biome_service/src/workspace/client.rs +++ b/crates/biome_service/src/workspace/client.rs @@ -1,16 +1,16 @@ use super::{ ChangeFileParams, CloseFileParams, FileExitsParams, FixFileParams, FixFileResult, FormatFileParams, FormatOnTypeParams, FormatRangeParams, GetControlFlowGraphParams, - GetFormatterIRParams, GetModuleGraphResult, GetSemanticModelParams, GetSyntaxTreeParams, - GetSyntaxTreeResult, OpenFileParams, PullActionsParams, PullActionsResult, + GetFormatterIRParams, GetModuleGraphParams, GetModuleGraphResult, GetSemanticModelParams, + GetSyntaxTreeParams, GetSyntaxTreeResult, OpenFileParams, PullActionsParams, PullActionsResult, PullDiagnosticsParams, PullDiagnosticsResult, RenameParams, RenameResult, ScanProjectFolderParams, ScanProjectFolderResult, SearchPatternParams, SearchResults, SupportsFeatureParams, UpdateModuleGraphParams, UpdateSettingsParams, UpdateSettingsResult, }; use crate::workspace::{ CheckFileSizeParams, CheckFileSizeResult, CloseProjectParams, FileFeaturesResult, - GetFileContentParams, GetRegisteredTypesParams, GetTypeInfoParams, IsPathIgnoredParams, - OpenProjectParams, OpenProjectResult, RageParams, RageResult, ServerInfo, + GetFileContentParams, GetRegisteredTypesParams, GetTypeInfoParams, OpenProjectParams, + OpenProjectResult, PathIsIgnoredParams, RageParams, RageResult, ServerInfo, }; use crate::{TransportError, Workspace, WorkspaceError}; use biome_formatter::Printed; @@ -144,7 +144,7 @@ where self.request("biome/file_features", params) } - fn is_path_ignored(&self, params: IsPathIgnoredParams) -> Result { + fn is_path_ignored(&self, params: PathIsIgnoredParams) -> Result { self.request("biome/is_path_ignored", params) } @@ -262,7 +262,10 @@ where self.server_info.as_ref() } - fn get_module_graph(&self) -> Result { - self.request("biome/get_module_graph", ()) + fn get_module_graph( + &self, + params: GetModuleGraphParams, + ) -> Result { + self.request("biome/get_module_graph", params) } } diff --git a/crates/biome_service/src/workspace/scanner.rs b/crates/biome_service/src/workspace/scanner.rs index b1d10c89de1e..72442d0d21ec 100644 --- a/crates/biome_service/src/workspace/scanner.rs +++ b/crates/biome_service/src/workspace/scanner.rs @@ -9,7 +9,7 @@ //! well as the watcher to allow continuous scanning. use super::server::WorkspaceServer; -use super::{FeaturesBuilder, IgnoreKind, IsPathIgnoredParams}; +use super::{FeaturesBuilder, IgnoreKind, PathIsIgnoredParams}; use crate::diagnostics::Panic; use crate::projects::ProjectKey; use crate::workspace::DocumentFileSource; @@ -450,7 +450,7 @@ fn open_file(ctx: &ScanContext, path: &BiomePath) { path.parent() .and_then(|dir_path| { ctx.workspace - .is_path_ignored(IsPathIgnoredParams { + .is_path_ignored(PathIsIgnoredParams { project_key: ctx.project_key, path: dir_path.into(), features: FeaturesBuilder::new().build(), @@ -461,7 +461,7 @@ fn open_file(ctx: &ScanContext, path: &BiomePath) { .unwrap_or_default() } else { ctx.workspace - .is_path_ignored(IsPathIgnoredParams { + .is_path_ignored(PathIsIgnoredParams { project_key: ctx.project_key, path: path.clone(), features: FeaturesBuilder::new().build(), diff --git a/crates/biome_service/src/workspace/server.rs b/crates/biome_service/src/workspace/server.rs index 1bbe891332a4..53d536e08ddb 100644 --- a/crates/biome_service/src/workspace/server.rs +++ b/crates/biome_service/src/workspace/server.rs @@ -3,13 +3,13 @@ use super::{ ChangeFileParams, CheckFileSizeParams, CheckFileSizeResult, CloseFileParams, CloseProjectParams, FeaturesBuilder, FileContent, FileExitsParams, FixFileParams, FixFileResult, FormatFileParams, FormatOnTypeParams, FormatRangeParams, - GetControlFlowGraphParams, GetFormatterIRParams, GetModuleGraphResult, GetSemanticModelParams, - GetSyntaxTreeParams, GetSyntaxTreeResult, IgnoreKind, OpenFileParams, OpenProjectParams, - ParsePatternParams, ParsePatternResult, PatternId, ProjectKey, PullActionsParams, - PullActionsResult, PullDiagnosticsParams, PullDiagnosticsResult, RenameResult, - ScanProjectFolderParams, ScanProjectFolderResult, SearchPatternParams, SearchResults, - ServiceDataNotification, SupportsFeatureParams, UpdateKind, UpdateModuleGraphParams, - UpdateSettingsParams, UpdateSettingsResult, + GetControlFlowGraphParams, GetFormatterIRParams, GetModuleGraphParams, GetModuleGraphResult, + GetSemanticModelParams, GetSyntaxTreeParams, GetSyntaxTreeResult, IgnoreKind, OpenFileParams, + OpenProjectParams, ParsePatternParams, ParsePatternResult, PatternId, ProjectKey, + PullActionsParams, PullActionsResult, PullDiagnosticsParams, PullDiagnosticsResult, + RenameResult, ScanProjectFolderParams, ScanProjectFolderResult, SearchPatternParams, + SearchResults, ServiceDataNotification, SupportsFeatureParams, UpdateKind, + UpdateModuleGraphParams, UpdateSettingsParams, UpdateSettingsResult, }; use crate::configuration::{LoadedConfiguration, ProjectScanComputer, read_config}; use crate::diagnostics::{FileTooLarge, NoIgnoreFileFound, VcsDiagnostic}; @@ -22,7 +22,7 @@ use crate::projects::Projects; use crate::workspace::scanner::ScanOptions; use crate::workspace::{ FileFeaturesResult, GetFileContentParams, GetRegisteredTypesParams, GetTypeInfoParams, - IsPathIgnoredParams, OpenProjectResult, RageEntry, RageParams, RageResult, ScanKind, + OpenProjectResult, PathIsIgnoredParams, RageEntry, RageParams, RageResult, ScanKind, ServerInfo, }; use crate::workspace_watcher::{OpenFileReason, WatcherSignalKind}; @@ -1102,7 +1102,7 @@ impl Workspace for WorkspaceServer { ) } - fn is_path_ignored(&self, params: IsPathIgnoredParams) -> Result { + fn is_path_ignored(&self, params: PathIsIgnoredParams) -> Result { // Never ignore Biome's top-level config file regardless of `includes`. if params.path.file_name().is_some_and(|file_name| { file_name == ConfigName::biome_json() || file_name == ConfigName::biome_jsonc() @@ -1766,7 +1766,10 @@ impl Workspace for WorkspaceServer { None } - fn get_module_graph(&self) -> Result { + fn get_module_graph( + &self, + _params: GetModuleGraphParams, + ) -> Result { let module_graph = self.module_graph.data(); let mut data = FxHashMap::default(); diff --git a/crates/biome_service/src/workspace_types.rs b/crates/biome_service/src/workspace_types.rs index 18547011d668..082ac3954880 100644 --- a/crates/biome_service/src/workspace_types.rs +++ b/crates/biome_service/src/workspace_types.rs @@ -548,7 +548,7 @@ macro_rules! workspace_method { } /// Returns a list of signature for all the methods in the [Workspace] trait -pub fn methods() -> [WorkspaceMethod; 28] { +pub fn methods() -> [WorkspaceMethod; 29] { [ workspace_method!(file_features), workspace_method!(update_settings), @@ -568,6 +568,7 @@ pub fn methods() -> [WorkspaceMethod; 28] { workspace_method!(get_type_info), workspace_method!(get_registered_types), workspace_method!(get_semantic_model), + workspace_method!(get_module_graph), workspace_method!(pull_diagnostics), workspace_method!(pull_actions), workspace_method!(format_file), diff --git a/crates/biome_wasm/src/lib.rs b/crates/biome_wasm/src/lib.rs index 665fd4d9f70b..d31b833d4f81 100644 --- a/crates/biome_wasm/src/lib.rs +++ b/crates/biome_wasm/src/lib.rs @@ -6,9 +6,10 @@ use wasm_bindgen::prelude::*; use biome_service::workspace::{ self, ChangeFileParams, CloseFileParams, FileExitsParams, FixFileParams, FormatFileParams, FormatOnTypeParams, FormatRangeParams, GetControlFlowGraphParams, GetFileContentParams, - GetFormatterIRParams, GetModuleGraphResult, GetRegisteredTypesParams, GetSemanticModelParams, - GetSyntaxTreeParams, GetTypeInfoParams, OpenProjectParams, PullActionsParams, - PullDiagnosticsParams, RenameParams, UpdateModuleGraphParams, UpdateSettingsParams, + GetFormatterIRParams, GetModuleGraphParams, GetRegisteredTypesParams, GetSemanticModelParams, + GetSyntaxTreeParams, GetTypeInfoParams, OpenProjectParams, PathIsIgnoredParams, + PullActionsParams, PullDiagnosticsParams, RenameParams, UpdateModuleGraphParams, + UpdateSettingsParams, }; use biome_service::workspace::{OpenFileParams, SupportsFeatureParams}; use camino::{Utf8Path, Utf8PathBuf}; @@ -201,8 +202,8 @@ impl Workspace { } #[wasm_bindgen(js_name = isPathIgnored)] - pub fn is_path_ignored(&self, params: IIsPathIgnoredParams) -> Result { - let params: IsPathIgnoredParams = + pub fn is_path_ignored(&self, params: IPathIsIgnoredParams) -> Result { + let params: PathIsIgnoredParams = serde_wasm_bindgen::from_value(params.into()).map_err(into_error)?; self.inner.is_path_ignored(params).map_err(into_error) } @@ -216,8 +217,17 @@ impl Workspace { } #[wasm_bindgen(js_name = getModuleGraph)] - pub fn get_module_graph(&self) -> Result { - self.inner.get_control_flow_graph().map_err(into_error) + pub fn get_module_graph( + &self, + params: IGetModuleGraphParams, + ) -> Result { + let params: GetModuleGraphParams = + serde_wasm_bindgen::from_value(params.into()).map_err(into_error)?; + + let result = self.inner.get_module_graph(params).map_err(into_error)?; + to_value(&result) + .map(IGetModuleGraphResult::from) + .map_err(into_error) } #[wasm_bindgen(js_name = pullDiagnostics)] diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index 19d9796ac494..68026f0359a9 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -8869,11 +8869,22 @@ export interface CloseFileParams { export interface FileExitsParams { filePath: BiomePath; } -export interface IsPathIgnoredParams { +export interface PathIsIgnoredParams { + /** + * Whether the path is ignored for specific features e.g. `formatter.includes`. When this field is empty, Biome checks only `files.includes`. + */ features: FeatureName; + /** + * Controls how to ignore check should be done + */ + ignoreKind?: IgnoreKind; + /** + * The path to inspect + */ path: BiomePath; projectKey: ProjectKey; } +export type IgnoreKind = "path" | "ancestors"; export interface UpdateModuleGraphParams { path: BiomePath; /** @@ -8923,6 +8934,24 @@ export interface GetSemanticModelParams { path: BiomePath; projectKey: ProjectKey; } +export interface GetModuleGraphParams {} +export interface GetModuleGraphResult { + data: Record; +} +export interface SerializedJsModuleInfo { + /** + * Dynamic imports + */ + dynamic_imports: string[]; + /** + * Exported symbols + */ + exports: string[]; + /** + * Static imports + */ + static_imports: Record; +} export interface PullDiagnosticsParams { categories: RuleCategories; /** @@ -9137,7 +9166,7 @@ export interface Workspace { changeFile(params: ChangeFileParams): Promise; closeFile(params: CloseFileParams): Promise; fileExists(params: FileExitsParams): Promise; - isPathIgnored(params: IsPathIgnoredParams): Promise; + isPathIgnored(params: PathIsIgnoredParams): Promise; updateModuleGraph(params: UpdateModuleGraphParams): Promise; getSyntaxTree(params: GetSyntaxTreeParams): Promise; fileExists(params: FileExitsParams): Promise; @@ -9148,6 +9177,7 @@ export interface Workspace { getTypeInfo(params: GetTypeInfoParams): Promise; getRegisteredTypes(params: GetRegisteredTypesParams): Promise; getSemanticModel(params: GetSemanticModelParams): Promise; + getModuleGraph(params: GetModuleGraphParams): Promise; pullDiagnostics( params: PullDiagnosticsParams, ): Promise; @@ -9218,6 +9248,9 @@ export function createWorkspace(transport: Transport): Workspace { getSemanticModel(params) { return transport.request("biome/get_semantic_model", params); }, + getModuleGraph(params) { + return transport.request("biome/get_module_graph", params); + }, pullDiagnostics(params) { return transport.request("biome/pull_diagnostics", params); }, From 1d5ea8a9dbd1dfb6e73dd28851abbb2f3087c105 Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Wed, 16 Jul 2025 11:08:40 -0400 Subject: [PATCH 6/7] fix regression --- crates/biome_service/src/workspace.tests.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/biome_service/src/workspace.tests.rs b/crates/biome_service/src/workspace.tests.rs index 4367f266a758..c3ba8477257c 100644 --- a/crates/biome_service/src/workspace.tests.rs +++ b/crates/biome_service/src/workspace.tests.rs @@ -20,9 +20,9 @@ use crate::{Workspace, WorkspaceError}; use super::{ CloseFileParams, CloseProjectParams, FileContent, FileFeaturesResult, FileGuard, - GetFileContentParams, GetSyntaxTreeParams, OpenFileParams, OpenProjectParams, - OpenProjectResult, PullDiagnosticsParams, ScanKind, ScanProjectFolderParams, UpdateKind, - UpdateModuleGraphParams, UpdateSettingsParams, server, + GetFileContentParams, GetModuleGraphParams, GetSyntaxTreeParams, OpenFileParams, + OpenProjectParams, OpenProjectResult, PullDiagnosticsParams, ScanKind, ScanProjectFolderParams, + UpdateKind, UpdateModuleGraphParams, UpdateSettingsParams, server, }; fn create_server() -> (Box, ProjectKey) { @@ -969,7 +969,7 @@ export const squash = function squash() {}; }) .unwrap(); - let result = workspace.get_module_graph().unwrap(); + let result = workspace.get_module_graph(GetModuleGraphParams {}).unwrap(); assert_debug_snapshot!(result) } From bb616cbb400edef4a3cf34534a8a18293bee91c8 Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Thu, 24 Jul 2025 12:11:29 +0100 Subject: [PATCH 7/7] address linting --- crates/biome_service/src/workspace/server.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/biome_service/src/workspace/server.rs b/crates/biome_service/src/workspace/server.rs index 53d536e08ddb..10613da869ed 100644 --- a/crates/biome_service/src/workspace/server.rs +++ b/crates/biome_service/src/workspace/server.rs @@ -1,15 +1,15 @@ use super::document::Document; use super::{ ChangeFileParams, CheckFileSizeParams, CheckFileSizeResult, CloseFileParams, - CloseProjectParams, FeaturesBuilder, FileContent, FileExitsParams, FixFileParams, - FixFileResult, FormatFileParams, FormatOnTypeParams, FormatRangeParams, - GetControlFlowGraphParams, GetFormatterIRParams, GetModuleGraphParams, GetModuleGraphResult, - GetSemanticModelParams, GetSyntaxTreeParams, GetSyntaxTreeResult, IgnoreKind, OpenFileParams, - OpenProjectParams, ParsePatternParams, ParsePatternResult, PatternId, ProjectKey, - PullActionsParams, PullActionsResult, PullDiagnosticsParams, PullDiagnosticsResult, - RenameResult, ScanProjectFolderParams, ScanProjectFolderResult, SearchPatternParams, - SearchResults, ServiceDataNotification, SupportsFeatureParams, UpdateKind, - UpdateModuleGraphParams, UpdateSettingsParams, UpdateSettingsResult, + CloseProjectParams, FileContent, FileExitsParams, FixFileParams, FixFileResult, + FormatFileParams, FormatOnTypeParams, FormatRangeParams, GetControlFlowGraphParams, + GetFormatterIRParams, GetModuleGraphParams, GetModuleGraphResult, GetSemanticModelParams, + GetSyntaxTreeParams, GetSyntaxTreeResult, IgnoreKind, OpenFileParams, OpenProjectParams, + ParsePatternParams, ParsePatternResult, PatternId, ProjectKey, PullActionsParams, + PullActionsResult, PullDiagnosticsParams, PullDiagnosticsResult, RenameResult, + ScanProjectFolderParams, ScanProjectFolderResult, SearchPatternParams, SearchResults, + ServiceDataNotification, SupportsFeatureParams, UpdateKind, UpdateModuleGraphParams, + UpdateSettingsParams, UpdateSettingsResult, }; use crate::configuration::{LoadedConfiguration, ProjectScanComputer, read_config}; use crate::diagnostics::{FileTooLarge, NoIgnoreFileFound, VcsDiagnostic};