Skip to content

Commit

Permalink
fix(core): parse project configs only in js
Browse files Browse the repository at this point in the history
  • Loading branch information
FrozenPandaz committed Jul 6, 2023
1 parent b40f0be commit eb8e908
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 154 deletions.
43 changes: 0 additions & 43 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 0 additions & 3 deletions packages/nx/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,9 @@ hashbrown = { version = "0.14.0", features = ["rayon"] }
ignore = '0.4'
ignore-files = "1.3.0"
itertools = "0.10.5"
jsonc-parser = { version = "0.21.1", features = ["serde"] }
napi = { version = '2.12.6', default-features = false, features = ['anyhow', 'napi4', 'tokio_rt'] }
napi-derive = '2.9.3'
rayon = "1.7.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
thiserror = "1.0.40"
tokio = { version = "1.28.2", features = ["fs"] }
tracing = "0.1.37"
Expand Down
4 changes: 4 additions & 0 deletions packages/nx/src/native/utils/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ fn normalize_path<P>(path: P) -> String
where
P: AsRef<Path>,
{
if path.as_ref() == Path::new("") {
return ".".into();
}

// convert back-slashes in Windows paths, since the js expects only forward-slash path separators
if cfg!(windows) {
path.as_ref().display().to_string().replace('\\', "/")
Expand Down
47 changes: 18 additions & 29 deletions packages/nx/src/native/workspace/get_config_files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::native::utils::glob::build_glob_set;
use crate::native::utils::path::Normalize;
use crate::native::walker::nx_walker;
use globset::GlobSet;

use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
Expand All @@ -11,20 +12,20 @@ use std::path::{Path, PathBuf};
pub fn get_config_files(workspace_root: String, globs: Vec<String>) -> anyhow::Result<Vec<String>> {
let globs = build_glob_set(globs)?;
Ok(nx_walker(workspace_root, move |rec| {
let mut config_paths: HashMap<PathBuf, (PathBuf, Vec<u8>)> = HashMap::new();
for (path, content) in rec {
insert_config_file_into_map((path, content), &mut config_paths, &globs);
let mut config_paths: HashMap<PathBuf, PathBuf> = HashMap::new();
for (path, _) in rec {
insert_config_file_into_map(path, &mut config_paths, &globs);
}
config_paths
.into_iter()
.map(|(_, (val, _))| val.to_normalized_string())
.into_values()
.map(|path| path.to_normalized_string())
.collect()
}))
}

pub fn insert_config_file_into_map(
(path, content): (PathBuf, Vec<u8>),
config_paths: &mut HashMap<PathBuf, (PathBuf, Vec<u8>)>,
path: PathBuf,
config_paths: &mut HashMap<PathBuf, PathBuf>,
globs: &GlobSet,
) {
if globs.is_match(&path) {
Expand All @@ -34,25 +35,24 @@ pub fn insert_config_file_into_map(
.file_name()
.expect("Config paths always have file names");
if file_name == "project.json" {
config_paths.insert(parent, (path, content));
config_paths.insert(parent, path);
} else if file_name == "package.json" {
match config_paths.entry(parent) {
Entry::Occupied(mut o) => {
if o.get()
.0
.file_name()
.expect("Config paths always have file names")
!= "project.json"
{
o.insert((path, content));
o.insert(path);
}
}
Entry::Vacant(v) => {
v.insert((path, content));
v.insert(path);
}
}
} else {
config_paths.entry(parent).or_insert((path, content));
config_paths.entry(parent).or_insert(path);
}
}
}
Expand All @@ -65,34 +65,23 @@ mod test {

#[test]
fn should_insert_config_files_properly() {
let mut config_paths: HashMap<PathBuf, (PathBuf, Vec<u8>)> = HashMap::new();
let mut config_paths: HashMap<PathBuf, PathBuf> = HashMap::new();
let globs = build_glob_set(vec!["**/*".into()]).unwrap();

insert_config_file_into_map(PathBuf::from("project.json"), &mut config_paths, &globs);
insert_config_file_into_map(PathBuf::from("package.json"), &mut config_paths, &globs);
insert_config_file_into_map(
(PathBuf::from("project.json"), vec![]),
&mut config_paths,
&globs,
);
insert_config_file_into_map(
(PathBuf::from("package.json"), vec![]),
&mut config_paths,
&globs,
);
insert_config_file_into_map(
(PathBuf::from("lib1/project.json"), vec![]),
PathBuf::from("lib1/project.json"),
&mut config_paths,
&globs,
);
insert_config_file_into_map(
(PathBuf::from("lib2/package.json"), vec![]),
PathBuf::from("lib2/package.json"),
&mut config_paths,
&globs,
);

let config_files: Vec<PathBuf> = config_paths
.into_iter()
.map(|(_, (path, _))| path)
.collect();
let config_files: Vec<PathBuf> = config_paths.into_values().collect();

assert!(config_files.contains(&PathBuf::from("project.json")));
assert!(config_files.contains(&PathBuf::from("lib1/project.json")));
Expand Down
90 changes: 24 additions & 66 deletions packages/nx/src/native/workspace/get_nx_workspace_files.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use jsonc_parser::ParseOptions;
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use std::path::{Path, PathBuf};

use rayon::prelude::*;
Expand All @@ -13,7 +12,7 @@ use crate::native::utils::path::Normalize;
use crate::native::walker::nx_walker;
use crate::native::workspace::errors::{InternalWorkspaceErrors, WorkspaceErrors};
use crate::native::workspace::get_config_files::insert_config_file_into_map;
use crate::native::workspace::types::{FileLocation, ProjectConfiguration};
use crate::native::workspace::types::FileLocation;

#[napi(object)]
pub struct NxWorkspaceFiles {
Expand All @@ -35,9 +34,10 @@ pub fn get_workspace_files_native(
let (projects, mut file_data) = get_file_data(&workspace_root, globs)
.map_err(|err| napi::Error::new(WorkspaceErrors::Generic, err.to_string()))?;

let root_map = create_root_map(&projects)?;
dbg!(&projects);
let root_set = create_root_set(&projects)?;

trace!(?root_map);
dbg!(&root_set);

// Files need to be sorted each time because when we do hashArray in the TaskHasher.js, the order of the files should be deterministic
file_data.par_sort();
Expand All @@ -48,12 +48,15 @@ pub fn get_workspace_files_native(
let file_path = Path::new(&file_data.file);
let mut parent = file_path.parent().unwrap_or_else(|| Path::new(""));

while root_map.get(parent).is_none() && parent != Path::new("") {
while root_set.get(parent).is_none() && parent != Path::new("") {
parent = parent.parent().unwrap_or_else(|| Path::new(""));
}

match root_map.get(parent) {
Some(project_name) => (FileLocation::Project(project_name.clone()), file_data),
match root_set.get(parent) {
Some(project_root) => (
FileLocation::Project(project_root.to_normalized_string()),
file_data,
),
None => (FileLocation::Global, file_data),
}
})
Expand All @@ -74,9 +77,9 @@ pub fn get_workspace_files_native(
for (file_location, file_data) in file_locations {
match file_location {
FileLocation::Global => global_files.push(file_data),
FileLocation::Project(project_name) => match project_file_map.get_mut(&project_name) {
FileLocation::Project(project_root) => match project_file_map.get_mut(&project_root) {
None => {
project_file_map.insert(project_name, vec![file_data]);
project_file_map.insert(project_root, vec![file_data]);
}
Some(project_files) => project_files.push(file_data),
},
Expand All @@ -87,58 +90,31 @@ pub fn get_workspace_files_native(
project_file_map,
global_files,
config_files: projects
.keys()
.iter()
.map(|path| path.to_normalized_string())
.collect(),
})
}

fn create_root_map(
projects: &HashMap<PathBuf, Vec<u8>>,
) -> Result<hashbrown::HashMap<&Path, String>, InternalWorkspaceErrors> {
fn create_root_set(
projects: &HashSet<PathBuf>,
) -> Result<hashbrown::HashSet<&Path>, InternalWorkspaceErrors> {
projects
.par_iter()
.map(|(path, content)| {
.map(|path| {
let file_name = path
.file_name()
.expect("path should always have a filename");
return if file_name == "project.json" || file_name == "package.json" {
// use serde_json to do the initial parse, if that fails fall back to jsonc_parser.
// If all those fail, expose the error from jsonc_parser
let project_configuration: ProjectConfiguration =
read_project_configuration(content, path)?;

let Some(parent_path) = path.parent() else {
return Err(InternalWorkspaceErrors::Generic {
return Err(InternalWorkspaceErrors::Generic {
msg: format!("{path:?} has no parent"),
})
};

let name: String = if let Some(name) = project_configuration.name {
Ok(name)
} else {
parent_path
.file_name()
.unwrap_or_default()
.to_os_string()
.into_string()
.map_err(|os_string| InternalWorkspaceErrors::Generic {
msg: format!("Cannot turn {os_string:?} into String"),
})
}?;
Ok((parent_path, name))
Ok(parent_path)
} else if let Some(parent_path) = path.parent() {
Ok((
parent_path,
parent_path
.file_name()
.unwrap_or_default()
.to_os_string()
.into_string()
.map_err(|os_string| InternalWorkspaceErrors::Generic {
msg: format!("Cannot turn {os_string:?} into String"),
})?,
))
Ok(parent_path)
} else {
Err(InternalWorkspaceErrors::Generic {
msg: format!("{path:?} has no parent"),
Expand All @@ -148,36 +124,18 @@ fn create_root_map(
.collect()
}

fn read_project_configuration(
content: &[u8],
path: &Path,
) -> Result<ProjectConfiguration, InternalWorkspaceErrors> {
serde_json::from_slice(content).or_else(|_| {
let content_str = std::str::from_utf8(content).expect("content should be valid utf8");
let parser_value =
jsonc_parser::parse_to_serde_value(content_str, &ParseOptions::default()).map_err(
|_| InternalWorkspaceErrors::ParseError {
file: PathBuf::from(path),
},
)?;
serde_json::from_value(parser_value.into()).map_err(|_| InternalWorkspaceErrors::Generic {
msg: format!("Failed to parse {path:?}"),
})
})
}

type WorkspaceData = (HashMap<PathBuf, Vec<u8>>, Vec<FileData>);
type WorkspaceData = (HashSet<PathBuf>, Vec<FileData>);
fn get_file_data(workspace_root: &str, globs: Vec<String>) -> anyhow::Result<WorkspaceData> {
let globs = build_glob_set(globs)?;
let (projects, file_data) = nx_walker(workspace_root, move |rec| {
let mut projects: HashMap<PathBuf, (PathBuf, Vec<u8>)> = HashMap::new();
let mut projects: HashMap<PathBuf, PathBuf> = HashMap::new();
let mut file_hashes: Vec<FileData> = vec![];
for (path, content) in rec {
file_hashes.push(FileData {
file: path.to_normalized_string(),
hash: xxh3::xxh3_64(&content).to_string(),
});
insert_config_file_into_map((path, content), &mut projects, &globs)
insert_config_file_into_map(path, &mut projects, &globs)
}
(projects, file_hashes)
});
Expand Down
7 changes: 0 additions & 7 deletions packages/nx/src/native/workspace/types.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
use serde::Deserialize;

#[derive(Debug, Deserialize)]
pub(crate) struct ProjectConfiguration {
pub name: Option<String>,
}

#[derive(Debug, Eq, PartialEq)]
pub enum FileLocation {
Global,
Expand Down
Loading

0 comments on commit eb8e908

Please sign in to comment.