Skip to content
This repository was archived by the owner on Oct 25, 2023. It is now read-only.

Commit eafb8ad

Browse files
committed
feat: add function to group includes by package name
1 parent 9bad927 commit eafb8ad

2 files changed

Lines changed: 132 additions & 4 deletions

File tree

src/lib.rs

Lines changed: 83 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
#![forbid(unsafe_code)]
2-
#![feature(absolute_path)]
3-
#![deny(warnings, missing_docs)]
4-
51
//! Enumerate source code files used by the TypeScript compiler during
62
//! compilation. The return value is a list of relative paths from the monorepo
73
//! root, sorted in alphabetical order.
@@ -44,7 +40,12 @@
4440
//! [listfilesonly]: https://www.typescriptlang.org/docs/handbook/compiler-options.html#compiler-options
4541
//! [tsconfig exclude]: https://www.typescriptlang.org/tsconfig#exclude
4642
43+
#![forbid(unsafe_code)]
44+
#![feature(absolute_path)]
45+
#![deny(warnings, missing_docs)]
46+
4747
use std::{
48+
collections::HashMap,
4849
fs::File,
4950
io::Read,
5051
path::{self, Path, PathBuf},
@@ -246,3 +247,81 @@ pub fn tsconfig_includes(
246247
debug!("tsconfig_includes: {:?}", included_files);
247248
Ok(included_files)
248249
}
250+
251+
/// Enumerate source code files used by the TypeScript compiler during
252+
/// compilation. The return value is a list of relative paths from the monorepo
253+
/// root, grouped by scoped package name.
254+
pub fn tsconfig_includes_by_package_name(
255+
tsconfig: &Path,
256+
calculation_type: Calculation,
257+
) -> Result<HashMap<String, Vec<PathBuf>>, Error> {
258+
let tsconfig = path::absolute(tsconfig).expect(&format!(
259+
"Should be able to convert parameter `tsconfig` ({:?}) into an absolute path",
260+
tsconfig,
261+
));
262+
debug!("tsconfig absolute path is {:?}", tsconfig);
263+
264+
let monorepo_root = find_up::find_file(&tsconfig, "lerna.json").ok_or_else(|| {
265+
Error::TypescriptProjectNotInMonorepo {
266+
filename: tsconfig.to_string_lossy().into_owned(),
267+
}
268+
})?;
269+
debug!("monorepo_root: {:?}", monorepo_root);
270+
271+
// This relies on an assumption that the package's package.json and tsconfig.json
272+
// live in the same directory (the package root).
273+
let target_package_manifest = tsconfig.parent().unwrap().join("package.json");
274+
debug!("target package manifest: {:?}", target_package_manifest);
275+
276+
let lerna_manifest = monorepo_manifest::MonorepoManifest::from_directory(&monorepo_root)?;
277+
let package_manifests_by_package_name = lerna_manifest.package_manifests_by_package_name()?;
278+
trace!("{:?}", lerna_manifest);
279+
280+
let package_manifest = lerna_manifest
281+
.internal_package_manifests()?
282+
.into_iter()
283+
.filter(|manifest| &target_package_manifest == &monorepo_root.join(manifest.path()))
284+
.take(1)
285+
.next()
286+
.expect("Expected project to reside in monorepo");
287+
288+
debug!("package_manifest: {:?}", package_manifest);
289+
290+
// Enumerate internal dependencies (exclusive)
291+
let transitive_internal_dependencies_inclusive = {
292+
let mut packages = package_manifest.transitive_internal_dependency_package_names_exclusive(
293+
&package_manifests_by_package_name,
294+
);
295+
// Make this list inclusive of the target package
296+
packages.push(&package_manifest);
297+
packages
298+
};
299+
300+
debug!(
301+
"transitive_internal_dependencies_inclusive: {:?}",
302+
transitive_internal_dependencies_inclusive
303+
.iter()
304+
.map(|manifest| manifest.contents.name.clone())
305+
.collect::<Vec<_>>()
306+
);
307+
308+
let included_files: HashMap<String, Vec<PathBuf>> = transitive_internal_dependencies_inclusive
309+
.into_par_iter()
310+
.map(|manifest| -> Result<(_, _), Error> {
311+
// This relies on the assumption that tsconfig.json is always the name of the tsconfig file
312+
let tsconfig = &monorepo_root
313+
.join(manifest.path())
314+
.parent()
315+
.unwrap()
316+
.join("tsconfig.json");
317+
let included = match calculation_type {
318+
Calculation::Estimate => tsconfig_includes_estimate(&monorepo_root, tsconfig),
319+
Calculation::Exact => tsconfig_includes_exact(&monorepo_root, tsconfig),
320+
}?;
321+
Ok((manifest.contents.name.clone(), included))
322+
})
323+
.collect::<Result<HashMap<_, _>, _>>()?;
324+
325+
debug!("tsconfig_includes: {:?}", included_files);
326+
Ok(included_files)
327+
}

tests/estimate-by-package-name.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
use std::collections::HashMap;
2+
use std::path::PathBuf;
3+
4+
use tsconfig_includes::{tsconfig_includes_by_package_name, Calculation};
5+
6+
fn check(tsconfig: &str, expected: &[(&str, &str)]) {
7+
match tsconfig_includes_by_package_name(&PathBuf::from(tsconfig), Calculation::Estimate) {
8+
Ok(actual) => {
9+
let expected = expected.iter().fold(
10+
HashMap::new(),
11+
|mut acc, (package_name, included_file)| -> HashMap<String, Vec<PathBuf>> {
12+
let included_files = acc.entry(package_name.to_owned().to_owned()).or_default();
13+
included_files.push(PathBuf::from(included_file));
14+
acc
15+
},
16+
);
17+
18+
assert_eq!(actual, expected);
19+
}
20+
// Don't care what went wrong for now
21+
Err(err) => {
22+
panic!("Unexpected error: {:?}", err);
23+
}
24+
};
25+
}
26+
27+
#[test]
28+
fn list_happy_path_dependencies_bar() {
29+
check(
30+
"test-data/happy-path/packages/bar/tsconfig.json",
31+
&[
32+
("bar", "packages/bar/src/bin.ts"),
33+
("bar", "packages/bar/src/index.ts"),
34+
("foo", "packages/foo/src/lib.ts"),
35+
("foo", "packages/foo/src/index.ts"),
36+
],
37+
);
38+
}
39+
40+
#[test]
41+
fn list_happy_path_dependencies_foo() {
42+
check(
43+
"test-data/happy-path/packages/foo/tsconfig.json",
44+
&[
45+
("foo", "packages/foo/src/lib.ts"),
46+
("foo", "packages/foo/src/index.ts"),
47+
],
48+
);
49+
}

0 commit comments

Comments
 (0)