-
Notifications
You must be signed in to change notification settings - Fork 471
Reuse package resolution for ppx #7776
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,10 +1,12 @@ | ||||||
use crate::build::packages; | ||||||
use crate::helpers; | ||||||
use crate::helpers::deserialize::*; | ||||||
use crate::project_context::ProjectContext; | ||||||
use anyhow::{Result, bail}; | ||||||
use convert_case::{Case, Casing}; | ||||||
use serde::Deserialize; | ||||||
use std::fs; | ||||||
use std::path::{Path, PathBuf}; | ||||||
use std::path::{MAIN_SEPARATOR, Path, PathBuf}; | ||||||
|
||||||
#[derive(Deserialize, Debug, Clone)] | ||||||
#[serde(untagged)] | ||||||
|
@@ -296,56 +298,61 @@ pub fn flatten_flags(flags: &Option<Vec<OneOrMore<String>>>) -> Vec<String> { | |||||
/// Since ppx-flags could be one or more, and could potentially be nested, this function takes the | ||||||
/// flags and flattens them. | ||||||
pub fn flatten_ppx_flags( | ||||||
node_modules_dir: &Path, | ||||||
project_context: &ProjectContext, | ||||||
package_config: &Config, | ||||||
flags: &Option<Vec<OneOrMore<String>>>, | ||||||
package_name: &String, | ||||||
) -> Vec<String> { | ||||||
) -> Result<Vec<String>> { | ||||||
match flags { | ||||||
None => vec![], | ||||||
Some(flags) => flags | ||||||
.iter() | ||||||
.flat_map(|x| match x { | ||||||
None => Ok(vec![]), | ||||||
Some(flags) => flags.iter().try_fold(Vec::new(), |mut acc, x| { | ||||||
match x { | ||||||
OneOrMore::Single(y) => { | ||||||
let first_character = y.chars().next(); | ||||||
match first_character { | ||||||
Some('.') => { | ||||||
vec![ | ||||||
"-ppx".to_string(), | ||||||
node_modules_dir | ||||||
.join(package_name) | ||||||
.join(y) | ||||||
.to_string_lossy() | ||||||
.to_string(), | ||||||
] | ||||||
let path = helpers::try_package_path( | ||||||
package_config, | ||||||
project_context, | ||||||
format!("{}{}{}", &package_config.name, MAIN_SEPARATOR, y).as_str(), | ||||||
) | ||||||
.map(|p| p.to_string_lossy().to_string())?; | ||||||
|
||||||
acc.push(String::from("-ppx")); | ||||||
acc.push(path); | ||||||
} | ||||||
_ => { | ||||||
acc.push(String::from("-ppx")); | ||||||
let path = helpers::try_package_path(package_config, project_context, y) | ||||||
.map(|p| p.to_string_lossy().to_string())?; | ||||||
acc.push(path); | ||||||
} | ||||||
_ => vec![ | ||||||
"-ppx".to_string(), | ||||||
node_modules_dir.join(y).to_string_lossy().to_string(), | ||||||
], | ||||||
} | ||||||
} | ||||||
OneOrMore::Multiple(ys) if ys.is_empty() => vec![], | ||||||
OneOrMore::Multiple(ys) if ys.is_empty() => (), | ||||||
OneOrMore::Multiple(ys) => { | ||||||
let first_character = ys[0].chars().next(); | ||||||
let ppx = match first_character { | ||||||
Some('.') => node_modules_dir | ||||||
.join(package_name) | ||||||
.join(&ys[0]) | ||||||
.to_string_lossy() | ||||||
.to_string(), | ||||||
_ => node_modules_dir.join(&ys[0]).to_string_lossy().to_string(), | ||||||
Some('.') => helpers::try_package_path( | ||||||
package_config, | ||||||
project_context, | ||||||
format!("{}{}{}", package_config.name, MAIN_SEPARATOR, &ys[0]).as_str(), | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar to the previous occurrence, using Path::join() would be more idiomatic and handle cross-platform path separators automatically.
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||
) | ||||||
.map(|p| p.to_string_lossy().to_string())?, | ||||||
_ => helpers::try_package_path(package_config, project_context, &ys[0]) | ||||||
.map(|p| p.to_string_lossy().to_string())?, | ||||||
}; | ||||||
vec![ | ||||||
"-ppx".to_string(), | ||||||
acc.push(String::from("-ppx")); | ||||||
acc.push( | ||||||
vec![ppx] | ||||||
.into_iter() | ||||||
.chain(ys[1..].to_owned()) | ||||||
.collect::<Vec<String>>() | ||||||
.join(" "), | ||||||
] | ||||||
); | ||||||
} | ||||||
}) | ||||||
.collect::<Vec<String>>(), | ||||||
}; | ||||||
Ok(acc) | ||||||
}), | ||||||
} | ||||||
} | ||||||
|
||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,8 @@ | ||
use crate::build::packages; | ||
use crate::config::Config; | ||
use crate::helpers; | ||
use crate::project_context::ProjectContext; | ||
use anyhow::anyhow; | ||
use std::ffi::OsString; | ||
use std::fs; | ||
use std::fs::File; | ||
|
@@ -103,6 +106,60 @@ pub fn package_path(root: &Path, package_name: &str) -> PathBuf { | |
root.join("node_modules").join(package_name) | ||
} | ||
|
||
/// Tries to find a path for input package_name. | ||
/// The node_modules folder may be found at different levels in the case of a monorepo. | ||
/// This helper tries a variety of paths. | ||
pub fn try_package_path( | ||
package_config: &Config, | ||
project_context: &ProjectContext, | ||
package_name: &str, | ||
) -> anyhow::Result<PathBuf> { | ||
// package folder + node_modules + package_name | ||
// This can happen in the following scenario: | ||
// The ProjectContext has a MonoRepoContext::MonorepoRoot. | ||
// We are reading a dependency from the root package. | ||
// And that local dependency has a hoisted dependency. | ||
// Example, we need to find package_name `foo` in the following scenario: | ||
// root/packages/a/node_modules/foo | ||
let path_from_current_package = package_config | ||
.path | ||
.parent() | ||
.ok_or_else(|| { | ||
anyhow!( | ||
"Expected {} to have a parent folder", | ||
package_config.path.to_string_lossy() | ||
) | ||
}) | ||
.map(|parent_path| helpers::package_path(parent_path, package_name))?; | ||
|
||
// current folder + node_modules + package_name | ||
let path_from_current_config = project_context | ||
.current_config | ||
.path | ||
.parent() | ||
.ok_or_else(|| { | ||
anyhow!( | ||
"Expected {} to have a parent folder", | ||
project_context.current_config.path.to_string_lossy() | ||
) | ||
}) | ||
.map(|parent_path| package_path(parent_path, package_name))?; | ||
|
||
// root folder + node_modules + package_name | ||
let path_from_root = package_path(project_context.get_root_path(), package_name); | ||
if path_from_current_package.exists() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the exists are quite expensive for every ppx run (== every file), can we not have one resolution per situation? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This of course specifically for the ppx resolution, and I know this is also used for the package scanning. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess I am saying that for ppx resolution we can probably create a new function that can look up the paths for the package based on the scanning that was done earlier instead of scanning again (and special casing the relative path). |
||
Ok(path_from_current_package) | ||
} else if path_from_current_config.exists() { | ||
Ok(path_from_current_config) | ||
} else if path_from_root.exists() { | ||
Ok(path_from_root) | ||
} else { | ||
Err(anyhow!( | ||
"The package \"{package_name}\" is not found (are node_modules up-to-date?)..." | ||
)) | ||
} | ||
} | ||
|
||
pub fn get_abs_path(path: &Path) -> PathBuf { | ||
let abs_path_buf = PathBuf::from(path); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The string formatting with MAIN_SEPARATOR could be simplified using Path::join() which handles path separators correctly across platforms.
Copilot uses AI. Check for mistakes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I disagree with this really, making a Path and calling unwrap later seems worse.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use path concat, I think this is the way to constructs paths, and it works on every platform. Generally in the codebase I use
to_string_lossy
and then you don't need unwrap.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
btw the function try_package_path should recieve a Path
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so it receives a package name or a path relative to the current package I think? Perhaps we can pass in a variant of
Name(String)
| Path({package_name, path}) or something like that?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jfrolich you mean pass
package_name
as Path?