Skip to content

Commit 93db5f0

Browse files
authored
Reuse package resolution for ppx (#7776)
* Reuse package resolution for ppx * Add changelog
1 parent c6624e4 commit 93db5f0

File tree

6 files changed

+120
-88
lines changed

6 files changed

+120
-88
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
- Preserve `@as(...)` decorator on record fields when creating interface. https://github.com/rescript-lang/rescript/pull/7779
2828
- Fix parse error with nested record types and attributes on the field name that has the nested record type. https://github.com/rescript-lang/rescript/pull/7781
29+
- Fix ppx resolution with package inside monorepo. https://github.com/rescript-lang/rescript/pull/7776
2930

3031
#### :memo: Documentation
3132

rewatch/src/build.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ pub fn get_compiler_args(rescript_file_path: &Path) -> Result<String> {
8080
&project_context.current_config,
8181
relative_filename,
8282
&contents,
83-
);
83+
)?;
8484
let is_interface = filename.to_string_lossy().ends_with('i');
8585
let has_interface = if is_interface {
8686
true

rewatch/src/build/packages.rs

Lines changed: 1 addition & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -255,48 +255,7 @@ pub fn read_dependency(
255255
package_config: &Config,
256256
project_context: &ProjectContext,
257257
) -> Result<PathBuf> {
258-
// package folder + node_modules + package_name
259-
// This can happen in the following scenario:
260-
// The ProjectContext has a MonoRepoContext::MonorepoRoot.
261-
// We are reading a dependency from the root package.
262-
// And that local dependency has a hoisted dependency.
263-
let path_from_current_package = package_config
264-
.path
265-
.parent()
266-
.ok_or_else(|| {
267-
anyhow!(
268-
"Expected {} to have a parent folder",
269-
package_config.path.to_string_lossy()
270-
)
271-
})
272-
.map(|parent_path| helpers::package_path(parent_path, package_name))?;
273-
274-
// current folder + node_modules + package_name
275-
let path_from_current_config = project_context
276-
.current_config
277-
.path
278-
.parent()
279-
.ok_or_else(|| {
280-
anyhow!(
281-
"Expected {} to have a parent folder",
282-
project_context.current_config.path.to_string_lossy()
283-
)
284-
})
285-
.map(|parent_path| helpers::package_path(parent_path, package_name))?;
286-
287-
// root folder + node_modules + package_name
288-
let path_from_root = helpers::package_path(project_context.get_root_path(), package_name);
289-
let path = (if path_from_current_package.exists() {
290-
Ok(path_from_current_package)
291-
} else if path_from_current_config.exists() {
292-
Ok(path_from_current_config)
293-
} else if path_from_root.exists() {
294-
Ok(path_from_root)
295-
} else {
296-
Err(anyhow!(
297-
"The package \"{package_name}\" is not found (are node_modules up-to-date?)..."
298-
))
299-
})?;
258+
let path = helpers::try_package_path(package_config, project_context, package_name)?;
300259

301260
let canonical_path = match path
302261
.canonicalize()

rewatch/src/build/parse.rs

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::config::{Config, OneOrMore};
77
use crate::helpers;
88
use crate::project_context::ProjectContext;
99
use ahash::AHashSet;
10+
use anyhow::anyhow;
1011
use log::debug;
1112
use rayon::prelude::*;
1213
use std::path::{Path, PathBuf};
@@ -51,11 +52,13 @@ pub fn generate_asts(
5152
package.to_owned(),
5253
&source_file.implementation.path.to_owned(),
5354
build_state,
54-
);
55+
)
56+
.map_err(|e| e.to_string());
5557

5658
let iast_result = match source_file.interface.as_ref().map(|i| i.path.to_owned()) {
5759
Some(interface_file_path) => {
5860
generate_ast(package.to_owned(), &interface_file_path.to_owned(), build_state)
61+
.map_err(|e| e.to_string())
5962
.map(Some)
6063
}
6164
_ => Ok(None),
@@ -237,16 +240,15 @@ pub fn parser_args(
237240
package_config: &Config,
238241
filename: &Path,
239242
contents: &str,
240-
) -> (PathBuf, Vec<String>) {
243+
) -> anyhow::Result<(PathBuf, Vec<String>)> {
241244
let root_config = project_context.get_root_config();
242245
let file = &filename;
243246
let ast_path = helpers::get_ast_path(file);
244-
let node_modules_path = project_context.get_root_path().join("node_modules");
245247
let ppx_flags = config::flatten_ppx_flags(
246-
node_modules_path.as_path(),
248+
project_context,
249+
package_config,
247250
&filter_ppx_flags(&package_config.ppx_flags, contents),
248-
&package_config.name,
249-
);
251+
)?;
250252
let jsx_args = root_config.get_jsx_args();
251253
let jsx_module_args = root_config.get_jsx_module_args();
252254
let jsx_mode_args = root_config.get_jsx_mode_args();
@@ -255,7 +257,7 @@ pub fn parser_args(
255257

256258
let file = PathBuf::from("..").join("..").join(file);
257259

258-
(
260+
Ok((
259261
ast_path.to_owned(),
260262
[
261263
ppx_flags,
@@ -273,20 +275,20 @@ pub fn parser_args(
273275
],
274276
]
275277
.concat(),
276-
)
278+
))
277279
}
278280

279281
fn generate_ast(
280282
package: Package,
281283
filename: &Path,
282284
build_state: &BuildState,
283-
) -> Result<(PathBuf, Option<helpers::StdErr>), String> {
285+
) -> anyhow::Result<(PathBuf, Option<helpers::StdErr>)> {
284286
let file_path = PathBuf::from(&package.path).join(filename);
285287
let contents = helpers::read_file(&file_path).expect("Error reading file");
286288

287289
let build_path_abs = package.get_build_path();
288290
let (ast_path, parser_args) =
289-
parser_args(&build_state.project_context, &package.config, filename, &contents);
291+
parser_args(&build_state.project_context, &package.config, filename, &contents)?;
290292

291293
// generate the dir of the ast_path (it mirrors the source file dir)
292294
let ast_parent_path = package.get_build_path().join(ast_path.parent().unwrap());
@@ -298,7 +300,13 @@ fn generate_ast(
298300
.current_dir(&build_path_abs)
299301
.args(parser_args)
300302
.output()
301-
.expect("Error converting .res to .ast"),
303+
.map_err(|e| {
304+
anyhow!(
305+
"Error running bsc for parsing {}: {}",
306+
filename.to_string_lossy(),
307+
e
308+
)
309+
})?,
302310
) {
303311
Some(res_to_ast) => {
304312
let stderr = String::from_utf8_lossy(&res_to_ast.stderr).to_string();
@@ -307,7 +315,7 @@ fn generate_ast(
307315
if res_to_ast.status.success() {
308316
Ok((ast_path, Some(stderr.to_string())))
309317
} else {
310-
Err(format!("Error in {}:\n{}", package.name, stderr))
318+
Err(anyhow!("Error in {}:\n{}", package.name, stderr))
311319
}
312320
} else {
313321
Ok((ast_path, None))
@@ -316,7 +324,7 @@ fn generate_ast(
316324
_ => {
317325
log::info!("Parsing file {}...", filename.display());
318326

319-
Err(format!(
327+
Err(anyhow!(
320328
"Could not find canonicalize_string_path for file {} in package {}",
321329
filename.display(),
322330
package.name

rewatch/src/config.rs

Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
use crate::build::packages;
2+
use crate::helpers;
23
use crate::helpers::deserialize::*;
4+
use crate::project_context::ProjectContext;
35
use anyhow::{Result, bail};
46
use convert_case::{Case, Casing};
57
use serde::Deserialize;
68
use std::fs;
7-
use std::path::{Path, PathBuf};
9+
use std::path::{MAIN_SEPARATOR, Path, PathBuf};
810

911
#[derive(Deserialize, Debug, Clone)]
1012
#[serde(untagged)]
@@ -296,56 +298,61 @@ pub fn flatten_flags(flags: &Option<Vec<OneOrMore<String>>>) -> Vec<String> {
296298
/// Since ppx-flags could be one or more, and could potentially be nested, this function takes the
297299
/// flags and flattens them.
298300
pub fn flatten_ppx_flags(
299-
node_modules_dir: &Path,
301+
project_context: &ProjectContext,
302+
package_config: &Config,
300303
flags: &Option<Vec<OneOrMore<String>>>,
301-
package_name: &String,
302-
) -> Vec<String> {
304+
) -> Result<Vec<String>> {
303305
match flags {
304-
None => vec![],
305-
Some(flags) => flags
306-
.iter()
307-
.flat_map(|x| match x {
306+
None => Ok(vec![]),
307+
Some(flags) => flags.iter().try_fold(Vec::new(), |mut acc, x| {
308+
match x {
308309
OneOrMore::Single(y) => {
309310
let first_character = y.chars().next();
310311
match first_character {
311312
Some('.') => {
312-
vec![
313-
"-ppx".to_string(),
314-
node_modules_dir
315-
.join(package_name)
316-
.join(y)
317-
.to_string_lossy()
318-
.to_string(),
319-
]
313+
let path = helpers::try_package_path(
314+
package_config,
315+
project_context,
316+
format!("{}{}{}", &package_config.name, MAIN_SEPARATOR, y).as_str(),
317+
)
318+
.map(|p| p.to_string_lossy().to_string())?;
319+
320+
acc.push(String::from("-ppx"));
321+
acc.push(path);
322+
}
323+
_ => {
324+
acc.push(String::from("-ppx"));
325+
let path = helpers::try_package_path(package_config, project_context, y)
326+
.map(|p| p.to_string_lossy().to_string())?;
327+
acc.push(path);
320328
}
321-
_ => vec![
322-
"-ppx".to_string(),
323-
node_modules_dir.join(y).to_string_lossy().to_string(),
324-
],
325329
}
326330
}
327-
OneOrMore::Multiple(ys) if ys.is_empty() => vec![],
331+
OneOrMore::Multiple(ys) if ys.is_empty() => (),
328332
OneOrMore::Multiple(ys) => {
329333
let first_character = ys[0].chars().next();
330334
let ppx = match first_character {
331-
Some('.') => node_modules_dir
332-
.join(package_name)
333-
.join(&ys[0])
334-
.to_string_lossy()
335-
.to_string(),
336-
_ => node_modules_dir.join(&ys[0]).to_string_lossy().to_string(),
335+
Some('.') => helpers::try_package_path(
336+
package_config,
337+
project_context,
338+
format!("{}{}{}", package_config.name, MAIN_SEPARATOR, &ys[0]).as_str(),
339+
)
340+
.map(|p| p.to_string_lossy().to_string())?,
341+
_ => helpers::try_package_path(package_config, project_context, &ys[0])
342+
.map(|p| p.to_string_lossy().to_string())?,
337343
};
338-
vec![
339-
"-ppx".to_string(),
344+
acc.push(String::from("-ppx"));
345+
acc.push(
340346
vec![ppx]
341347
.into_iter()
342348
.chain(ys[1..].to_owned())
343349
.collect::<Vec<String>>()
344350
.join(" "),
345-
]
351+
);
346352
}
347-
})
348-
.collect::<Vec<String>>(),
353+
};
354+
Ok(acc)
355+
}),
349356
}
350357
}
351358

rewatch/src/helpers.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
use crate::build::packages;
2+
use crate::config::Config;
3+
use crate::helpers;
24
use crate::project_context::ProjectContext;
5+
use anyhow::anyhow;
36
use std::ffi::OsString;
47
use std::fs;
58
use std::fs::File;
@@ -103,6 +106,60 @@ pub fn package_path(root: &Path, package_name: &str) -> PathBuf {
103106
root.join("node_modules").join(package_name)
104107
}
105108

109+
/// Tries to find a path for input package_name.
110+
/// The node_modules folder may be found at different levels in the case of a monorepo.
111+
/// This helper tries a variety of paths.
112+
pub fn try_package_path(
113+
package_config: &Config,
114+
project_context: &ProjectContext,
115+
package_name: &str,
116+
) -> anyhow::Result<PathBuf> {
117+
// package folder + node_modules + package_name
118+
// This can happen in the following scenario:
119+
// The ProjectContext has a MonoRepoContext::MonorepoRoot.
120+
// We are reading a dependency from the root package.
121+
// And that local dependency has a hoisted dependency.
122+
// Example, we need to find package_name `foo` in the following scenario:
123+
// root/packages/a/node_modules/foo
124+
let path_from_current_package = package_config
125+
.path
126+
.parent()
127+
.ok_or_else(|| {
128+
anyhow!(
129+
"Expected {} to have a parent folder",
130+
package_config.path.to_string_lossy()
131+
)
132+
})
133+
.map(|parent_path| helpers::package_path(parent_path, package_name))?;
134+
135+
// current folder + node_modules + package_name
136+
let path_from_current_config = project_context
137+
.current_config
138+
.path
139+
.parent()
140+
.ok_or_else(|| {
141+
anyhow!(
142+
"Expected {} to have a parent folder",
143+
project_context.current_config.path.to_string_lossy()
144+
)
145+
})
146+
.map(|parent_path| package_path(parent_path, package_name))?;
147+
148+
// root folder + node_modules + package_name
149+
let path_from_root = package_path(project_context.get_root_path(), package_name);
150+
if path_from_current_package.exists() {
151+
Ok(path_from_current_package)
152+
} else if path_from_current_config.exists() {
153+
Ok(path_from_current_config)
154+
} else if path_from_root.exists() {
155+
Ok(path_from_root)
156+
} else {
157+
Err(anyhow!(
158+
"The package \"{package_name}\" is not found (are node_modules up-to-date?)..."
159+
))
160+
}
161+
}
162+
106163
pub fn get_abs_path(path: &Path) -> PathBuf {
107164
let abs_path_buf = PathBuf::from(path);
108165

0 commit comments

Comments
 (0)