Skip to content

Commit 3698af5

Browse files
authored
feat: workspace support (#408)
1 parent f6b752c commit 3698af5

File tree

34 files changed

+1688
-292
lines changed

34 files changed

+1688
-292
lines changed

Cargo.lock

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ enumflags2 = "0.7.11"
2828
ignore = "0.4.23"
2929
indexmap = { version = "2.6.0", features = ["serde"] }
3030
insta = "1.31.0"
31+
oxc_resolver = "1.12.0"
3132
pg_query = "6.1.0"
3233
proc-macro2 = "1.0.66"
3334
quote = "1.0.33"
@@ -38,6 +39,7 @@ schemars = { version = "0.8.22", features = ["indexmap2", "small
3839
serde = "1.0.195"
3940
serde_json = "1.0.114"
4041
similar = "2.6.0"
42+
slotmap = "1.0.7"
4143
smallvec = { version = "1.13.2", features = ["union", "const_new", "serde"] }
4244
strum = { version = "0.27.1", features = ["derive"] }
4345
# this will use tokio if available, otherwise async-std

crates/pgt_cli/src/commands/mod.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ use bpaf::Bpaf;
99
use pgt_configuration::{PartialConfiguration, partial_configuration};
1010
use pgt_console::Console;
1111
use pgt_fs::FileSystem;
12+
use pgt_workspace::PartialConfigurationExt;
1213
use pgt_workspace::configuration::{LoadedConfiguration, load_configuration};
13-
use pgt_workspace::settings::PartialConfigurationExt;
14-
use pgt_workspace::workspace::UpdateSettingsParams;
14+
use pgt_workspace::workspace::{RegisterProjectFolderParams, UpdateSettingsParams};
1515
use pgt_workspace::{DynRef, Workspace, WorkspaceError};
1616
use std::ffi::OsString;
1717
use std::path::PathBuf;
@@ -301,6 +301,10 @@ pub(crate) trait CommandRunner: Sized {
301301
let (vcs_base_path, gitignore_matches) =
302302
configuration.retrieve_gitignore_matches(fs, vcs_base_path.as_deref())?;
303303
let paths = self.get_files_to_process(fs, &configuration)?;
304+
workspace.register_project_folder(RegisterProjectFolderParams {
305+
path: fs.working_directory(),
306+
set_as_current_workspace: true,
307+
})?;
304308

305309
workspace.update_settings(UpdateSettingsParams {
306310
workspace_directory: fs.working_directory(),

crates/pgt_cli/src/diagnostics.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,7 @@ mod test {
455455
fn termination_diagnostic_size() {
456456
assert_eq!(
457457
std::mem::size_of::<CliDiagnostic>(),
458-
80,
458+
96,
459459
"you successfully decreased the size of the diagnostic!"
460460
)
461461
}

crates/pgt_configuration/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ biome_deserialize = { workspace = true, features = ["schema"] }
1616
biome_deserialize_macros = { workspace = true }
1717
bpaf = { workspace = true }
1818
indexmap = { workspace = true }
19+
oxc_resolver = { workspace = true }
1920
pgt_analyse = { workspace = true }
2021
pgt_analyser = { workspace = true }
2122
pgt_console = { workspace = true }

crates/pgt_configuration/src/diagnostics.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use pgt_console::fmt::Display;
22
use pgt_console::{MarkupBuf, markup};
3+
use pgt_diagnostics::adapters::ResolveError;
4+
35
use pgt_diagnostics::{Advices, Diagnostic, Error, LogCategory, MessageAndDescription, Visit};
46
use serde::{Deserialize, Serialize};
57
use std::fmt::{Debug, Formatter};
@@ -21,6 +23,12 @@ pub enum ConfigurationDiagnostic {
2123

2224
/// Thrown when the pattern inside the `ignore` field errors
2325
InvalidIgnorePattern(InvalidIgnorePattern),
26+
27+
/// Thrown when there's something wrong with the files specified inside `"extends"`
28+
CantLoadExtendFile(CantLoadExtendFile),
29+
30+
/// Thrown when a configuration file can't be resolved from `node_modules`
31+
CantResolve(CantResolve),
2432
}
2533

2634
impl ConfigurationDiagnostic {
@@ -72,6 +80,18 @@ impl ConfigurationDiagnostic {
7280
message: MessageAndDescription::from(markup! {{message}}.to_owned()),
7381
})
7482
}
83+
84+
pub fn cant_resolve(path: impl Display, source: oxc_resolver::ResolveError) -> Self {
85+
Self::CantResolve(CantResolve {
86+
message: MessageAndDescription::from(
87+
markup! {
88+
"Failed to resolve the configuration from "{{path}}
89+
}
90+
.to_owned(),
91+
),
92+
source: Some(Error::from(ResolveError::from(source))),
93+
})
94+
}
7595
}
7696

7797
impl Debug for ConfigurationDiagnostic {
@@ -168,3 +188,36 @@ pub struct CantResolve {
168188
#[source]
169189
source: Option<Error>,
170190
}
191+
192+
#[derive(Debug, Serialize, Deserialize, Diagnostic)]
193+
#[diagnostic(
194+
category = "configuration",
195+
severity = Error,
196+
)]
197+
pub struct CantLoadExtendFile {
198+
#[location(resource)]
199+
file_path: String,
200+
#[message]
201+
#[description]
202+
message: MessageAndDescription,
203+
204+
#[verbose_advice]
205+
verbose_advice: ConfigurationAdvices,
206+
}
207+
208+
impl CantLoadExtendFile {
209+
pub fn new(file_path: impl Into<String>, message: impl Display) -> Self {
210+
Self {
211+
file_path: file_path.into(),
212+
message: MessageAndDescription::from(markup! {{message}}.to_owned()),
213+
verbose_advice: ConfigurationAdvices::default(),
214+
}
215+
}
216+
217+
pub fn with_verbose_advice(mut self, messsage: impl Display) -> Self {
218+
self.verbose_advice
219+
.messages
220+
.push(markup! {{messsage}}.to_owned());
221+
self
222+
}
223+
}

crates/pgt_configuration/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ pub use analyser::{
2222
RulePlainConfiguration, RuleSelector, RuleWithFixOptions, RuleWithOptions, Rules,
2323
partial_linter_configuration,
2424
};
25+
use biome_deserialize::StringSet;
2526
use biome_deserialize_macros::{Merge, Partial};
2627
use bpaf::Bpaf;
2728
use database::{
@@ -50,6 +51,10 @@ pub struct Configuration {
5051
#[partial(bpaf(hide))]
5152
pub schema: String,
5253

54+
/// A list of paths to other JSON files, used to extends the current configuration.
55+
#[partial(bpaf(hide))]
56+
pub extends: StringSet,
57+
5358
/// The configuration of the VCS integration
5459
#[partial(type, bpaf(external(partial_vcs_configuration), optional, hide_usage))]
5560
pub vcs: VcsConfiguration,
@@ -85,6 +90,7 @@ impl PartialConfiguration {
8590
pub fn init() -> Self {
8691
Self {
8792
schema: Some(format!("https://pgtools.dev/schemas/{VERSION}/schema.json")),
93+
extends: Some(StringSet::default()),
8894
files: Some(PartialFilesConfiguration {
8995
ignore: Some(Default::default()),
9096
..Default::default()

crates/pgt_diagnostics/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ version = "0.0.0"
1515
backtrace = "0.3.74"
1616
bpaf = { workspace = true }
1717
enumflags2 = { workspace = true }
18+
oxc_resolver = { workspace = true }
1819
pgt_console = { workspace = true, features = ["serde"] }
1920
pgt_diagnostics_categories = { workspace = true, features = ["serde"] }
2021
pgt_diagnostics_macros = { workspace = true }

crates/pgt_diagnostics/src/adapters.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,28 @@ impl Diagnostic for SerdeJsonError {
134134
fmt.write_markup(markup!({ AsConsoleDisplay(&self.error) }))
135135
}
136136
}
137+
138+
#[derive(Debug)]
139+
pub struct ResolveError {
140+
error: oxc_resolver::ResolveError,
141+
}
142+
143+
impl From<oxc_resolver::ResolveError> for ResolveError {
144+
fn from(error: oxc_resolver::ResolveError) -> Self {
145+
Self { error }
146+
}
147+
}
148+
149+
impl Diagnostic for ResolveError {
150+
fn category(&self) -> Option<&'static Category> {
151+
Some(category!("internalError/io"))
152+
}
153+
154+
fn description(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
155+
write!(fmt, "{}", self.error)
156+
}
157+
158+
fn message(&self, fmt: &mut fmt::Formatter<'_>) -> io::Result<()> {
159+
fmt.write_markup(markup!({ AsConsoleDisplay(&self.error) }))
160+
}
161+
}

crates/pgt_fs/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ version = "0.0.0"
1515
crossbeam = { workspace = true }
1616
directories = "5.0.1"
1717
enumflags2 = { workspace = true }
18+
oxc_resolver = { workspace = true }
1819
parking_lot = { version = "0.12.3", features = ["arc_lock"] }
1920
pgt_diagnostics = { workspace = true }
2021
rayon = { workspace = true }

crates/pgt_fs/src/fs.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::{PathInterner, PgTPath};
22
pub use memory::{ErrorEntry, MemoryFileSystem};
33
pub use os::OsFileSystem;
4+
use oxc_resolver::{Resolution, ResolveError};
45
use pgt_diagnostics::{Advices, Diagnostic, LogCategory, Visit, console};
56
use pgt_diagnostics::{Error, Severity};
67
use serde::{Deserialize, Serialize};
@@ -164,6 +165,12 @@ pub trait FileSystem: Send + Sync + RefUnwindSafe {
164165
fn get_changed_files(&self, base: &str) -> io::Result<Vec<String>>;
165166

166167
fn get_staged_files(&self) -> io::Result<Vec<String>>;
168+
169+
fn resolve_configuration(
170+
&self,
171+
specifier: &str,
172+
path: &Path,
173+
) -> Result<Resolution, ResolveError>;
167174
}
168175

169176
/// Result of the auto search
@@ -355,6 +362,14 @@ where
355362
fn get_staged_files(&self) -> io::Result<Vec<String>> {
356363
T::get_staged_files(self)
357364
}
365+
366+
fn resolve_configuration(
367+
&self,
368+
specifier: &str,
369+
path: &Path,
370+
) -> Result<Resolution, ResolveError> {
371+
T::resolve_configuration(self, specifier, path)
372+
}
358373
}
359374

360375
#[derive(Debug, Diagnostic, Deserialize, Serialize)]

crates/pgt_fs/src/fs/memory.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use oxc_resolver::{Resolution, ResolveError};
12
use rustc_hash::FxHashMap;
23
use std::collections::hash_map::{Entry, IntoIter};
34
use std::io;
@@ -227,6 +228,15 @@ impl FileSystem for MemoryFileSystem {
227228

228229
Ok(cb())
229230
}
231+
232+
fn resolve_configuration(
233+
&self,
234+
_specifier: &str,
235+
_path: &Path,
236+
) -> Result<Resolution, ResolveError> {
237+
// not needed for the memory file system
238+
todo!()
239+
}
230240
}
231241

232242
struct MemoryFile {

crates/pgt_fs/src/fs/os.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ use crate::{
55
FileSystem, PgTPath,
66
fs::{TraversalContext, TraversalScope},
77
};
8+
use oxc_resolver::{Resolution, ResolveError, ResolveOptions, Resolver};
89
use pgt_diagnostics::{DiagnosticExt, Error, Severity, adapters::IoError};
910
use rayon::{Scope, scope};
1011
use std::fs::{DirEntry, FileType};
12+
use std::panic::AssertUnwindSafe;
1113
use std::process::Command;
1214
use std::{
1315
env, fs,
@@ -21,12 +23,18 @@ const MAX_SYMLINK_DEPTH: u8 = 3;
2123
/// Implementation of [FileSystem] that directly calls through to the underlying OS
2224
pub struct OsFileSystem {
2325
pub working_directory: Option<PathBuf>,
26+
pub configuration_resolver: AssertUnwindSafe<Resolver>,
2427
}
2528

2629
impl OsFileSystem {
2730
pub fn new(working_directory: PathBuf) -> Self {
2831
Self {
2932
working_directory: Some(working_directory),
33+
configuration_resolver: AssertUnwindSafe(Resolver::new(ResolveOptions {
34+
condition_names: vec!["node".to_string(), "import".to_string()],
35+
extensions: vec![".json".to_string(), ".jsonc".to_string()],
36+
..ResolveOptions::default()
37+
})),
3038
}
3139
}
3240
}
@@ -35,6 +43,11 @@ impl Default for OsFileSystem {
3543
fn default() -> Self {
3644
Self {
3745
working_directory: env::current_dir().ok(),
46+
configuration_resolver: AssertUnwindSafe(Resolver::new(ResolveOptions {
47+
condition_names: vec!["node".to_string(), "import".to_string()],
48+
extensions: vec![".json".to_string(), ".jsonc".to_string()],
49+
..ResolveOptions::default()
50+
})),
3851
}
3952
}
4053
}
@@ -116,6 +129,14 @@ impl FileSystem for OsFileSystem {
116129
.map(|l| l.to_string())
117130
.collect())
118131
}
132+
133+
fn resolve_configuration(
134+
&self,
135+
specifier: &str,
136+
path: &Path,
137+
) -> Result<Resolution, ResolveError> {
138+
self.configuration_resolver.resolve(path, specifier)
139+
}
119140
}
120141

121142
struct OsFile {
@@ -387,8 +408,6 @@ fn follow_symlink(
387408
path: &Path,
388409
ctx: &dyn TraversalContext,
389410
) -> Result<(PathBuf, FileType), SymlinkExpansionError> {
390-
tracing::info!("Translating symlink: {path:?}");
391-
392411
let target_path = fs::read_link(path).map_err(|err| {
393412
ctx.push_diagnostic(IoError::from(err).with_file_path(path.to_string_lossy().to_string()));
394413
SymlinkExpansionError

0 commit comments

Comments
 (0)