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

Commit 4b140fc

Browse files
committed
feat: link internal dependencies
1 parent 66797d7 commit 4b140fc

File tree

4 files changed

+144
-5
lines changed

4 files changed

+144
-5
lines changed

Cargo.lock

Lines changed: 7 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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ edition = "2021"
88
[dependencies]
99
clap = "3.0.0-beta.5"
1010
globwalk = "0.8.1"
11+
pathdiff = "0.1.0"
1112
serde = { version = "1.0", features = ["derive"] }
1213
serde_json = { version = "1.0", features = ["preserve_order"] }

src/io.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,25 @@ pub fn write_package_manifest<P: AsRef<Path>>(
6464
Ok(())
6565
}
6666

67+
pub fn read_tsconfig<P: AsRef<Path>>(path: P) -> Result<serde_json::Value, Box<dyn Error>> {
68+
let file = File::open(path)?;
69+
let reader = BufReader::new(file);
70+
let u = serde_json::from_reader(reader)?;
71+
Ok(u)
72+
}
73+
74+
pub fn write_tsconfig<P: AsRef<Path>>(
75+
path: P,
76+
tsconfig: &serde_json::Value,
77+
) -> Result<(), Box<dyn Error>> {
78+
let file = File::create(path)?;
79+
let mut writer = BufWriter::new(file);
80+
serde_json::to_writer_pretty(&mut writer, tsconfig)?;
81+
writer.write_all(b"\n")?;
82+
writer.flush()?;
83+
Ok(())
84+
}
85+
6786
pub fn write_project_references<P: AsRef<Path>>(
6887
path: P,
6988
references: &TypeScriptProjectReferences,
@@ -81,7 +100,6 @@ pub fn get_internal_package_manifest_files<P: AsRef<Path>>(
81100
lerna_manifest: &LernaManifest,
82101
ignore_globs: &Vec<String>,
83102
) -> Result<Vec<PathBuf>, Box<dyn Error>> {
84-
// dawid's tip: use cargo's clippy with rust-analyzer for enlightenment
85103
// dawid's tip: consider rayon for parallel iterators
86104

87105
let mut package_manifests: Vec<String> = lerna_manifest

src/link.rs

Lines changed: 117 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@ use std::collections::HashMap;
22
use std::error::Error;
33
use std::path::{Path, PathBuf};
44

5+
use pathdiff::diff_paths;
6+
7+
use serde_json::{json, Value};
8+
59
use crate::io::{
6-
get_internal_package_manifest_files, read_lerna_manifest, write_project_references,
10+
get_internal_package_manifest_files, read_internal_package_manifests, read_lerna_manifest,
11+
read_tsconfig, write_project_references, write_tsconfig, PackageManifest,
712
TypeScriptProjectReference, TypeScriptProjectReferences,
813
};
914

@@ -81,9 +86,111 @@ fn link_children_packages(
8186
.collect::<Result<(), Box<dyn Error>>>()
8287
}
8388

84-
// fn link_package_dependencies(root: &Path) -> Result<(), Box<dyn Error>> {
85-
// Ok(())
86-
// }
89+
fn tsconfig_filename<P: AsRef<Path>>(manifest_file: P) -> Result<PathBuf, Box<dyn Error>> {
90+
let tsconfig = manifest_file
91+
.as_ref()
92+
.parent()
93+
.ok_or::<Box<dyn Error>>(
94+
String::from("Unexpected internal package in monorepo root").into(),
95+
)?
96+
.join("tsconfig.json");
97+
Ok(tsconfig)
98+
}
99+
100+
// Map scoped internal-package name to relative path from monorepo root.
101+
fn key_internal_package_directory_by_package_name<P: AsRef<Path>>(
102+
root: P,
103+
internal_package_manifests: &HashMap<PathBuf, PackageManifest>,
104+
) -> HashMap<String, PathBuf> {
105+
internal_package_manifests.iter().fold(
106+
HashMap::with_capacity(internal_package_manifests.len()),
107+
|mut acc, (manifest_file, manifest)| {
108+
acc.insert(
109+
manifest.name.clone(),
110+
internal_package_relative_path(&root, manifest_file)
111+
.expect("Unable to create relative path to package from monorepo root"),
112+
);
113+
acc
114+
},
115+
)
116+
}
117+
118+
fn link_package_dependencies(
119+
opts: &crate::opts::Link,
120+
internal_package_manifest_files: &Vec<PathBuf>,
121+
) -> Result<(), Box<dyn Error>> {
122+
let internal_manifests = read_internal_package_manifests(internal_package_manifest_files)?;
123+
let package_directory_by_name =
124+
key_internal_package_directory_by_package_name(&opts.root, &internal_manifests);
125+
126+
let get_dependency_group = |package_manifest: &PackageManifest,
127+
dependency_group: &str|
128+
-> serde_json::Map<String, serde_json::Value> {
129+
package_manifest
130+
.extra_fields
131+
.get(dependency_group)
132+
.and_then(|v| Value::as_object(v).cloned())
133+
.unwrap_or(serde_json::Map::new())
134+
};
135+
136+
internal_package_manifest_files
137+
.iter()
138+
.map(|manifest_file| -> Result<(), Box<dyn Error>> {
139+
let package_directory = manifest_file.parent().ok_or::<Box<dyn Error>>(
140+
String::from("Unexpected internal package in monorepo root").into(),
141+
)?;
142+
let tsconfig_file = tsconfig_filename(manifest_file)?;
143+
let mut tsconfig = read_tsconfig(&tsconfig_file)?;
144+
let manifest = internal_manifests
145+
.get(manifest_file)
146+
.ok_or::<Box<dyn Error>>(
147+
String::from("Failed to lookup package by manifest path").into(),
148+
)?;
149+
150+
let desired_project_references: serde_json::Value = {
151+
let mut deps = get_dependency_group(manifest, "dependencies")
152+
.iter()
153+
.chain(get_dependency_group(manifest, "devDependencies").iter())
154+
.chain(get_dependency_group(manifest, "optionalDependencies").iter())
155+
.chain(get_dependency_group(manifest, "peerDependencies").iter())
156+
.filter_map(|(name, _version)| package_directory_by_name.get(name).cloned())
157+
.map(|dependency_directory| {
158+
diff_paths(&opts.root.join(dependency_directory), package_directory)
159+
.ok_or::<Box<dyn Error>>(String::from("Unable to calculate relative path between consuming directory and internal dependency").into())
160+
})
161+
.collect::<Result<Vec<_>, _>>()?;
162+
deps.sort_unstable();
163+
164+
let deps_to_write = serde_json::Value::Array(deps
165+
.iter()
166+
.map(|dep| {
167+
json!({
168+
"path": dep.to_str().expect("Path not valid UTF-8 encoded").to_string()
169+
})
170+
})
171+
.collect::<Vec<_>>());
172+
173+
deps_to_write
174+
};
175+
176+
// Compare the current references against the desired references
177+
let needs_update = !desired_project_references.eq(
178+
tsconfig.get("references").unwrap_or(&serde_json::Value::Array(vec![]))
179+
);
180+
if !needs_update {
181+
return Ok(());
182+
}
183+
184+
let new_tsconfig = tsconfig
185+
.as_object_mut()
186+
.ok_or::<Box<dyn Error>>(String::from("Expected tsconfig.json to contain an Object").into())?;
187+
new_tsconfig.insert(String::from("references"), desired_project_references);
188+
189+
write_tsconfig(tsconfig_file, &tsconfig)
190+
})
191+
.collect::<Result<Vec<_>, _>>()?;
192+
Ok(())
193+
}
87194

88195
pub fn link_typescript_project_references(opts: crate::opts::Link) -> Result<(), Box<dyn Error>> {
89196
let lerna_manifest = read_lerna_manifest(&opts.root).expect("Unable to read lerna manifest");
@@ -93,6 +200,12 @@ pub fn link_typescript_project_references(opts: crate::opts::Link) -> Result<(),
93200

94201
link_children_packages(&opts, &internal_package_manifest_files)
95202
.expect("Unable to link children packages");
203+
link_package_dependencies(&opts, &internal_package_manifest_files)
204+
.expect("Unable to link internal package dependencies");
205+
206+
// TODO: implement --check
207+
208+
// STRETCH TODO: create `tsconfig.settings.json` files
96209

97210
Ok(())
98211
}

0 commit comments

Comments
 (0)