1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use std::fs::File;
use std::io::{BufReader, BufWriter, Write};
use std::path::{Path, PathBuf};

use anyhow::{Context, Result};
use indoc::formatdoc;
use serde::{Deserialize, Serialize};

use crate::configuration_file::ConfigurationFile;
use crate::io::read_json_from_file;

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
pub struct TypescriptProjectReference {
    pub path: String,
}

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct TypescriptParentProjectReferenceFile {
    /// This list is expected to be empty, but must be present to satisfy the
    /// TypeScript compiler.
    #[serde(default)]
    pub files: Vec<String>,
    #[serde(default)]
    pub references: Vec<TypescriptProjectReference>,
}

pub struct TypescriptParentProjectReference {
    monorepo_root: PathBuf,
    directory: PathBuf,
    pub contents: TypescriptParentProjectReferenceFile,
}

impl ConfigurationFile<TypescriptParentProjectReference> for TypescriptParentProjectReference {
    const FILENAME: &'static str = "tsconfig.json";

    fn from_directory(
        monorepo_root: &Path,
        directory: &Path,
    ) -> Result<TypescriptParentProjectReference> {
        let filename = monorepo_root.join(directory).join(Self::FILENAME);
        let manifest_contents: TypescriptParentProjectReferenceFile =
            read_json_from_file(&filename).with_context(|| {
                formatdoc!(
                    "
                    Unexpected contents in {:?}

                    I'm trying to parse the following property and value out
                    of this tsconfig.json file:

                    - references: {{ path: string }}[]

                    and the following value, if present:

                    - files: string[]
                    ",
                    filename
                )
            })?;
        Ok(TypescriptParentProjectReference {
            monorepo_root: monorepo_root.to_owned(),
            directory: directory.to_owned(),
            contents: manifest_contents,
        })
    }

    fn directory(&self) -> PathBuf {
        self.directory.to_owned()
    }

    fn path(&self) -> PathBuf {
        self.directory.join(Self::FILENAME)
    }

    fn write(&self) -> Result<()> {
        let file = File::create(
            self.monorepo_root
                .join(&self.directory)
                .join(Self::FILENAME),
        )?;
        let mut writer = BufWriter::new(file);
        serde_json::to_writer_pretty(&mut writer, &self.contents)?;
        writer.write_all(b"\n")?;
        writer.flush()?;
        Ok(())
    }
}

pub struct TypescriptConfig {
    // FIXME: how many times do we need to duplicate this value?
    monorepo_root: PathBuf,
    directory: PathBuf,
    pub contents: serde_json::Map<String, serde_json::Value>,
}

impl ConfigurationFile<TypescriptConfig> for TypescriptConfig {
    const FILENAME: &'static str = "tsconfig.json";

    // TODO: parse with a helpful error message here
    fn from_directory(monorepo_root: &Path, directory: &Path) -> Result<TypescriptConfig> {
        let filename = monorepo_root.join(directory).join(Self::FILENAME);
        let reader = BufReader::new(File::open(filename)?);
        let tsconfig_contents = serde_json::from_reader(reader)?;
        Ok(TypescriptConfig {
            monorepo_root: monorepo_root.to_owned(),
            directory: directory.to_owned(),
            contents: tsconfig_contents,
        })
    }

    fn directory(&self) -> PathBuf {
        self.directory.to_owned()
    }

    fn path(&self) -> PathBuf {
        self.directory.join(Self::FILENAME)
    }

    fn write(&self) -> Result<()> {
        let file = File::create(
            self.monorepo_root
                .join(&self.directory)
                .join(Self::FILENAME),
        )?;
        let mut writer = BufWriter::new(file);
        serde_json::to_writer_pretty(&mut writer, &self.contents)?;
        writer.write_all(b"\n")?;
        writer.flush()?;
        Ok(())
    }
}