tsconfig_includes/
typescript_package.rs

1use std::path::{Path, PathBuf};
2
3use serde::Deserialize;
4
5use crate::io::{read_json_from_file, FromFileError};
6
7#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
8pub(crate) struct TypescriptPackage {
9    pub scoped_package_name: String,
10    pub tsconfig_file: TypescriptConfigFile,
11}
12
13#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
14pub(crate) struct PackageManifestFile(PathBuf);
15
16impl From<PathBuf> for PackageManifestFile {
17    fn from(path: PathBuf) -> Self {
18        Self(path)
19    }
20}
21
22#[derive(Debug)]
23pub(crate) struct PackageInMonorepoRootError(pub PathBuf);
24
25impl TryFrom<TypescriptConfigFile> for PackageManifestFile {
26    type Error = PackageInMonorepoRootError;
27
28    fn try_from(tsconfig_file: TypescriptConfigFile) -> Result<Self, Self::Error> {
29        let package_directory = tsconfig_file
30            .0
31            .parent()
32            .ok_or_else(|| PackageInMonorepoRootError(tsconfig_file.0.clone()))?;
33        Ok(Self(package_directory.join("package.json")))
34    }
35}
36
37impl TryFrom<&TypescriptConfigFile> for PackageManifestFile {
38    type Error = PackageInMonorepoRootError;
39
40    fn try_from(value: &TypescriptConfigFile) -> Result<Self, Self::Error> {
41        Self::try_from(value.to_owned())
42    }
43}
44
45// This gives us a way to look up the typescript-tools PackageManifest
46// from the path to a package's tsconfig.json file, but it does incur
47// a runtime penalty of reading this information from disk again.
48//
49// It's a definite hack, but it unblocks today.
50#[derive(Debug, Deserialize)]
51pub(crate) struct PackageManifest {
52    pub name: String,
53}
54
55impl TryFrom<PackageManifestFile> for PackageManifest {
56    type Error = FromFileError;
57
58    fn try_from(manifest_file: PackageManifestFile) -> Result<Self, Self::Error> {
59        read_json_from_file(manifest_file.0)
60    }
61}
62
63#[derive(Debug)]
64pub(crate) enum FromTypescriptConfigFileError {
65    PackageInMonorepoRoot(PathBuf),
66    FromFile(FromFileError),
67}
68
69impl From<PackageInMonorepoRootError> for FromTypescriptConfigFileError {
70    fn from(err: PackageInMonorepoRootError) -> Self {
71        Self::PackageInMonorepoRoot(err.0)
72    }
73}
74
75impl From<FromFileError> for FromTypescriptConfigFileError {
76    fn from(err: FromFileError) -> Self {
77        Self::FromFile(err)
78    }
79}
80
81impl TryFrom<TypescriptConfigFile> for PackageManifest {
82    type Error = FromTypescriptConfigFileError;
83
84    fn try_from(tsconfig_file: TypescriptConfigFile) -> Result<Self, Self::Error> {
85        let package_manifest_file: PackageManifestFile = tsconfig_file.try_into()?;
86        let package_manifest: Self = package_manifest_file.try_into()?;
87        Ok(package_manifest)
88    }
89}
90
91impl TryFrom<&TypescriptConfigFile> for PackageManifest {
92    type Error = FromTypescriptConfigFileError;
93
94    fn try_from(value: &TypescriptConfigFile) -> Result<Self, Self::Error> {
95        Self::try_from(value.to_owned())
96    }
97}
98
99#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
100pub(crate) struct TypescriptConfigFile(PathBuf);
101
102impl TypescriptConfigFile {
103    pub fn as_path(&self) -> &Path {
104        &self.0
105    }
106
107    pub fn package_directory<P: AsRef<Path>>(
108        &self,
109        monorepo_root: P,
110    ) -> Result<PathBuf, PackageInMonorepoRootError> {
111        let tsconfig_path = monorepo_root.as_ref().join(&self.0);
112        tsconfig_path
113            .parent()
114            .map(ToOwned::to_owned)
115            .ok_or_else(|| PackageInMonorepoRootError(tsconfig_path))
116    }
117}
118
119impl<P> From<P> for TypescriptConfigFile
120where
121    P: AsRef<Path>,
122{
123    fn from(value: P) -> Self {
124        let path = value.as_ref();
125        Self(path.to_owned())
126    }
127}
128
129impl TryFrom<PackageManifestFile> for TypescriptConfigFile {
130    type Error = PackageInMonorepoRootError;
131
132    fn try_from(package_manifest_file: PackageManifestFile) -> Result<Self, Self::Error> {
133        let package_directory = package_manifest_file
134            .0
135            .parent()
136            .ok_or_else(|| PackageInMonorepoRootError(package_manifest_file.0.clone()))?;
137        Ok(Self(package_directory.join("tsconfig.json")))
138    }
139}
140
141impl TryFrom<&PackageManifestFile> for TypescriptConfigFile {
142    type Error = PackageInMonorepoRootError;
143
144    fn try_from(value: &PackageManifestFile) -> Result<Self, Self::Error> {
145        Self::try_from(value.to_owned())
146    }
147}