Skip to content

Commit 2d5e5a5

Browse files
committed
fix(lsp): reload workspace on pnpm-workspace.yaml changes
1 parent e81614c commit 2d5e5a5

File tree

2 files changed

+114
-1
lines changed

2 files changed

+114
-1
lines changed

crates/biome_lsp/src/server.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,13 @@ impl LSPServer {
153153
}),
154154
kind: Some(WatchKind::all()),
155155
},
156+
FileSystemWatcher {
157+
glob_pattern: GlobPattern::Relative(RelativePattern {
158+
pattern: "pnpm-workspace.yaml".to_string(),
159+
base_uri: OneOf::Left(folder.clone()),
160+
}),
161+
kind: Some(WatchKind::all()),
162+
},
156163
FileSystemWatcher {
157164
glob_pattern: GlobPattern::Relative(RelativePattern {
158165
pattern: "**/.gitignore".to_string(),
@@ -190,6 +197,13 @@ impl LSPServer {
190197
)),
191198
kind: Some(WatchKind::all()),
192199
},
200+
FileSystemWatcher {
201+
glob_pattern: GlobPattern::String(format!(
202+
"{}/pnpm-workspace.yaml",
203+
base_path.as_path().as_str()
204+
)),
205+
kind: Some(WatchKind::all()),
206+
},
193207
FileSystemWatcher {
194208
glob_pattern: GlobPattern::String("**/.gitignore".to_string()),
195209
kind: Some(WatchKind::all()),
@@ -378,6 +392,7 @@ impl LanguageServer for LSPServer {
378392
.iter()
379393
.any(|file_name| watched_file.ends_with(file_name))
380394
|| (watched_file.ends_with(".editorconfig"))
395+
|| watched_file.ends_with("pnpm-workspace.yaml")
381396
|| watched_file.ends_with(".gitignore")
382397
|| watched_file.ends_with(".ignore"))
383398
{

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

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ use crate::settings::ModuleGraphResolutionKind;
33
use crate::test_utils::setup_workspace_and_open_project;
44
use biome_configuration::{
55
FormatterConfiguration, JsConfiguration,
6-
javascript::{JsFormatterConfiguration, JsParserConfiguration},
6+
javascript::{JsFormatterConfiguration, JsParserConfiguration, JsResolverConfiguration},
77
};
88
use biome_formatter::{IndentStyle, LineWidth};
99
use biome_fs::MemoryFileSystem;
1010
use biome_rowan::TextSize;
11+
use camino::Utf8Path;
1112

1213
#[test]
1314
fn commonjs_file_rejects_import_statement() {
@@ -66,6 +67,103 @@ fn commonjs_file_rejects_import_statement() {
6667
}
6768
}
6869

70+
#[test]
71+
fn pnpm_workspace_update_reapplies_catalogs() {
72+
const PACKAGE_JSON: &[u8] = br#"{
73+
"name": "app",
74+
"dependencies": {
75+
"react": "catalog:react19"
76+
}
77+
}"#;
78+
const WORKSPACE_V1: &[u8] = br#"catalogs:
79+
react19:
80+
react: 19.0.0
81+
"#;
82+
const WORKSPACE_V2: &[u8] = br#"catalogs:
83+
react19:
84+
react: 18.3.1
85+
"#;
86+
87+
let fs = MemoryFileSystem::default();
88+
fs.insert(Utf8PathBuf::from("/project/package.json"), PACKAGE_JSON);
89+
fs.insert(
90+
Utf8PathBuf::from("/project/pnpm-workspace.yaml"),
91+
WORKSPACE_V1,
92+
);
93+
94+
let fs_for_updates = MemoryFileSystem::from_files(fs.files.0.clone());
95+
let (workspace, project_key) = setup_workspace_and_open_project(fs, "/");
96+
97+
workspace
98+
.update_settings(UpdateSettingsParams {
99+
project_key,
100+
workspace_directory: Some(BiomePath::new("/project")),
101+
configuration: Configuration {
102+
javascript: Some(JsConfiguration {
103+
resolver: Some(JsResolverConfiguration {
104+
experimental_pnpm_catalogs: Some(Bool(true)),
105+
}),
106+
..Default::default()
107+
}),
108+
..Default::default()
109+
},
110+
extended_configurations: vec![],
111+
module_graph_resolution_kind: ModuleGraphResolutionKind::None,
112+
})
113+
.unwrap();
114+
115+
workspace
116+
.scan_project(ScanProjectParams {
117+
project_key,
118+
watch: false,
119+
force: false,
120+
scan_kind: ScanKind::Project,
121+
verbose: false,
122+
})
123+
.unwrap();
124+
125+
let package_manifest = workspace
126+
.project_layout
127+
.get_node_manifest_for_package(Utf8Path::new("/project"))
128+
.expect("package manifest should be indexed");
129+
let initial_react = package_manifest
130+
.catalog
131+
.as_ref()
132+
.and_then(|catalogs| catalogs.named.get("react19"))
133+
.and_then(|dependencies| dependencies.get("react"));
134+
assert_eq!(initial_react, Some("19.0.0"));
135+
136+
fs_for_updates.insert(
137+
Utf8PathBuf::from("/project/pnpm-workspace.yaml"),
138+
WORKSPACE_V2,
139+
);
140+
141+
workspace
142+
.open_file_internal(
143+
OpenFileReason::Index(IndexTrigger::Update),
144+
OpenFileParams {
145+
project_key,
146+
path: BiomePath::new("/project/pnpm-workspace.yaml"),
147+
content: FileContent::FromServer,
148+
document_file_source: None,
149+
persist_node_cache: false,
150+
inline_config: None,
151+
},
152+
)
153+
.unwrap();
154+
155+
let package_manifest = workspace
156+
.project_layout
157+
.get_node_manifest_for_package(Utf8Path::new("/project"))
158+
.expect("package manifest should be indexed");
159+
let updated_react = package_manifest
160+
.catalog
161+
.as_ref()
162+
.and_then(|catalogs| catalogs.named.get("react19"))
163+
.and_then(|dependencies| dependencies.get("react"));
164+
assert_eq!(updated_react, Some("18.3.1"));
165+
}
166+
69167
#[test]
70168
fn store_embedded_nodes_with_current_ranges() {
71169
const FILE_CONTENT: &str = r#"<html>

0 commit comments

Comments
 (0)