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

Commit 6dec5df

Browse files
committed
fix: provide better error message for tsconfig parsing
This commit also refactors some of the tsconfig.json-parsing code that was pretty messy. It's an incremental step, there is more to refactor. Ticket: 0
1 parent c1bdc5a commit 6dec5df

File tree

3 files changed

+103
-65
lines changed

3 files changed

+103
-65
lines changed

src/io.rs

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,11 @@
11
use anyhow::Context;
22
use std::fs::File;
3-
use std::io::{BufWriter, Read, Write};
3+
use std::io::Read;
44
use std::path::Path;
55

66
use anyhow::Result;
77

8-
use serde::{Deserialize, Serialize};
9-
10-
// REFACTOR: this belongs in a different file
11-
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
12-
pub struct TypescriptProjectReference {
13-
pub path: String,
14-
}
15-
16-
// REFACTOR: this belongs in a different file
17-
#[derive(Serialize, PartialEq, Eq)]
18-
pub struct TypescriptParentProjectReference {
19-
pub files: Vec<String>,
20-
pub references: Vec<TypescriptProjectReference>,
21-
}
22-
23-
pub fn write_project_references<P: AsRef<Path>>(
24-
path: P,
25-
references: &TypescriptParentProjectReference,
26-
) -> Result<()> {
27-
let file = File::create(&path)?;
28-
let mut writer = BufWriter::new(file);
29-
serde_json::to_writer_pretty(&mut writer, references)?;
30-
writer.write_all(b"\n")?;
31-
writer.flush()?;
32-
Ok(())
33-
}
8+
use serde::Deserialize;
349

3510
pub(crate) fn read_json_from_file<T>(filename: &Path) -> Result<T>
3611
where

src/link.rs

Lines changed: 20 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
use std::collections::HashMap;
22
use std::path::PathBuf;
33

4-
use anyhow::{anyhow, bail, Result};
4+
use anyhow::{bail, Result};
55

66
use pathdiff::diff_paths;
77

88
use crate::configuration_file::ConfigurationFile;
9-
use crate::io::{
10-
write_project_references, TypescriptParentProjectReference, TypescriptProjectReference,
11-
};
129
use crate::monorepo_manifest::MonorepoManifest;
1310
use crate::opts;
1411
use crate::package_manifest::PackageManifest;
15-
use crate::typescript_config::TypescriptConfig;
12+
use crate::typescript_config::{
13+
TypescriptConfig, TypescriptParentProjectReference, TypescriptProjectReference,
14+
};
1615

1716
fn key_children_by_parent(
1817
mut accumulator: HashMap<PathBuf, Vec<String>>,
@@ -35,17 +34,14 @@ fn key_children_by_parent(
3534
accumulator
3635
}
3736

38-
fn create_project_references(mut children: Vec<String>) -> TypescriptParentProjectReference {
37+
fn create_project_references(mut children: Vec<String>) -> Vec<TypescriptProjectReference> {
3938
// Sort the TypeScript project references for deterministic file contents.
4039
// This minimizes diffs since the tsconfig.json files are stored in version control.
4140
children.sort_unstable();
42-
TypescriptParentProjectReference {
43-
files: Vec::new(),
44-
references: children
45-
.into_iter()
46-
.map(|path| TypescriptProjectReference { path })
47-
.collect(),
48-
}
41+
children
42+
.into_iter()
43+
.map(|path| TypescriptProjectReference { path })
44+
.collect()
4945
}
5046

5147
fn vecs_match<T: PartialEq>(a: &[T], b: &[T]) -> bool {
@@ -54,7 +50,7 @@ fn vecs_match<T: PartialEq>(a: &[T], b: &[T]) -> bool {
5450
}
5551

5652
// Create a tsconfig.json file in each parent directory to an internal package.
57-
// This permits us to build the monorepo from the top down.
53+
// This permits us to compile the monorepo from the top down.
5854
fn link_children_packages(opts: &opts::Link, lerna_manifest: &MonorepoManifest) -> Result<bool> {
5955
let mut is_exit_success = true;
6056

@@ -66,19 +62,10 @@ fn link_children_packages(opts: &opts::Link, lerna_manifest: &MonorepoManifest)
6662
.try_for_each(|(directory, children)| -> Result<()> {
6763
let desired_project_references = create_project_references(children);
6864
let tsconfig_filename = opts.root.join(&directory).join("tsconfig.json");
69-
let tsconfig = TypescriptConfig::from_directory(&opts.root, &directory)?;
70-
let current_project_references = tsconfig
71-
.contents
72-
.get("references")
73-
.map(|value| {
74-
serde_json::from_value::<Vec<TypescriptProjectReference>>(value.clone())
75-
.expect("Value starting as JSON should be serializable as JSON")
76-
})
77-
.unwrap_or_default();
78-
let needs_update = !vecs_match(
79-
&current_project_references,
80-
&desired_project_references.references,
81-
);
65+
let mut tsconfig =
66+
TypescriptParentProjectReference::from_directory(&opts.root, &directory)?;
67+
let current_project_references = tsconfig.contents.references;
68+
let needs_update = !current_project_references.eq(&desired_project_references);
8269
if !needs_update {
8370
return Ok(());
8471
}
@@ -92,7 +79,8 @@ fn link_children_packages(opts: &opts::Link, lerna_manifest: &MonorepoManifest)
9279
println!("{}", serialized);
9380
Ok(())
9481
} else {
95-
write_project_references(tsconfig_filename, &desired_project_references)
82+
tsconfig.contents.references = desired_project_references;
83+
tsconfig.write()
9684
}
9785
})?;
9886

@@ -150,14 +138,10 @@ fn link_package_dependencies(opts: &opts::Link, lerna_manifest: &MonorepoManifes
150138
}
151139

152140
// Update the current tsconfig with the desired references
153-
tsconfig
154-
.contents
155-
.as_object_mut()
156-
.ok_or_else(|| anyhow!("Expected tsconfig.json to contain an Object"))?
157-
.insert(
158-
String::from("references"),
159-
serde_json::to_value(desired_project_references)?,
160-
);
141+
tsconfig.contents.insert(
142+
String::from("references"),
143+
serde_json::to_value(desired_project_references)?,
144+
);
161145

162146
Ok(Some(tsconfig))
163147
},

src/typescript_config.rs

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,94 @@ use std::fs::File;
22
use std::io::{BufReader, BufWriter, Write};
33
use std::path::{Path, PathBuf};
44

5-
use anyhow::Result;
5+
use anyhow::{Context, Result};
6+
use indoc::formatdoc;
7+
use serde::{Deserialize, Serialize};
68

79
use crate::configuration_file::ConfigurationFile;
10+
use crate::io::read_json_from_file;
11+
12+
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
13+
pub struct TypescriptProjectReference {
14+
pub path: String,
15+
}
16+
17+
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
18+
pub struct TypescriptParentProjectReferenceFile {
19+
/// This list is expected to be empty, but must be present to satisfy the
20+
/// TypeScript compiler.
21+
#[serde(default)]
22+
pub files: Vec<String>,
23+
#[serde(default)]
24+
pub references: Vec<TypescriptProjectReference>,
25+
}
26+
27+
pub struct TypescriptParentProjectReference {
28+
monorepo_root: PathBuf,
29+
directory: PathBuf,
30+
pub contents: TypescriptParentProjectReferenceFile,
31+
}
32+
33+
impl ConfigurationFile<TypescriptParentProjectReference> for TypescriptParentProjectReference {
34+
const FILENAME: &'static str = "tsconfig.json";
35+
36+
fn from_directory(
37+
monorepo_root: &Path,
38+
directory: &Path,
39+
) -> Result<TypescriptParentProjectReference> {
40+
let filename = monorepo_root.join(directory).join(Self::FILENAME);
41+
let manifest_contents: TypescriptParentProjectReferenceFile =
42+
read_json_from_file(&filename).with_context(|| {
43+
formatdoc!(
44+
"
45+
Unexpected contents in {:?}
46+
47+
I'm trying to parse the following property and value out
48+
of this tsconfig.json file:
49+
50+
- references: {{ path: string }}[]
51+
52+
and the following value, if present:
53+
54+
- files: string[]
55+
",
56+
filename
57+
)
58+
})?;
59+
Ok(TypescriptParentProjectReference {
60+
monorepo_root: monorepo_root.to_owned(),
61+
directory: directory.to_owned(),
62+
contents: manifest_contents,
63+
})
64+
}
65+
66+
fn directory(&self) -> PathBuf {
67+
self.directory.to_owned()
68+
}
69+
70+
fn path(&self) -> PathBuf {
71+
self.directory.join(Self::FILENAME)
72+
}
73+
74+
fn write(&self) -> Result<()> {
75+
let file = File::create(
76+
self.monorepo_root
77+
.join(&self.directory)
78+
.join(Self::FILENAME),
79+
)?;
80+
let mut writer = BufWriter::new(file);
81+
serde_json::to_writer_pretty(&mut writer, &self.contents)?;
82+
writer.write_all(b"\n")?;
83+
writer.flush()?;
84+
Ok(())
85+
}
86+
}
887

988
pub struct TypescriptConfig {
1089
// FIXME: how many times do we need to duplicate this value?
1190
monorepo_root: PathBuf,
1291
directory: PathBuf,
13-
pub contents: serde_json::Value,
92+
pub contents: serde_json::Map<String, serde_json::Value>,
1493
}
1594

1695
impl ConfigurationFile<TypescriptConfig> for TypescriptConfig {

0 commit comments

Comments
 (0)