typescript_tools/
make_depend.rs

1use std::collections::HashMap;
2use std::fmt::Display;
3use std::fs;
4use std::path::{Path, PathBuf};
5
6use pathdiff::diff_paths;
7use rinja::Template;
8
9use crate::configuration_file::ConfigurationFile;
10use crate::io::FromFileError;
11use crate::monorepo_manifest::{EnumeratePackageManifestsError, MonorepoManifest};
12use crate::package_manifest::PackageManifest;
13use crate::types::{Directory, PackageName};
14
15#[derive(Template)]
16#[template(path = "makefile")]
17
18struct MakefileTemplate<'a> {
19    monorepo_kind: &'a str,
20    root: &'a str,
21    output_file: &'a str,
22    package_directory: &'a str,
23    scoped_package_name: &'a PackageName,
24    unscoped_package_name: &'a str,
25    internal_dependency_package_json_filenames_inclusive: &'a Vec<String>,
26    create_pack_target: &'a bool,
27    npm_pack_archive_dependencies: &'a HashMap<String, String>,
28    internal_npm_dependencies_exclusive: &'a Vec<&'a str>,
29}
30
31#[derive(Debug)]
32#[non_exhaustive]
33pub struct MakeDependencyMakefileError {
34    pub kind: MakeDependencyMakefileErrorKind,
35}
36
37impl Display for MakeDependencyMakefileError {
38    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39        write!(f, "error creating package makefile")
40    }
41}
42
43impl std::error::Error for MakeDependencyMakefileError {
44    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
45        match &self.kind {
46            MakeDependencyMakefileErrorKind::FromFile(err) => Some(err),
47            MakeDependencyMakefileErrorKind::EnumeratePackageManifests(err) => Some(err),
48        }
49    }
50}
51
52impl From<FromFileError> for MakeDependencyMakefileError {
53    fn from(err: FromFileError) -> Self {
54        Self {
55            kind: MakeDependencyMakefileErrorKind::FromFile(err),
56        }
57    }
58}
59
60impl From<EnumeratePackageManifestsError> for MakeDependencyMakefileError {
61    fn from(err: EnumeratePackageManifestsError) -> Self {
62        Self {
63            kind: MakeDependencyMakefileErrorKind::EnumeratePackageManifests(err),
64        }
65    }
66}
67
68#[derive(Debug)]
69pub enum MakeDependencyMakefileErrorKind {
70    #[non_exhaustive]
71    FromFile(FromFileError),
72    #[non_exhaustive]
73    EnumeratePackageManifests(EnumeratePackageManifestsError),
74}
75
76pub fn make_dependency_makefile(
77    root: &Path,
78    package_directory: &Path,
79    output_file: &Path,
80    create_pack_target: bool,
81) -> Result<(), MakeDependencyMakefileError> {
82    let monorepo_manifest = MonorepoManifest::from_directory(root)?;
83    let package_manifest = PackageManifest::from_directory(
84        &monorepo_manifest.root,
85        // FIXME: this should be checked but it was the behavior today
86        Directory::unchecked_from_path(package_directory),
87    )?;
88
89    // determine the complete set of internal dependencies (and self!)
90    let package_manifest_by_package_name = monorepo_manifest.package_manifests_by_package_name()?;
91
92    let internal_dependencies_exclusive: Vec<_> = package_manifest
93        .transitive_internal_dependency_package_names_exclusive(&package_manifest_by_package_name)
94        .collect();
95
96    let internal_dependency_package_json_filenames_inclusive: Vec<PathBuf> = {
97        let mut dependency_dirs = internal_dependencies_exclusive
98            .iter()
99            .map(|internal_dependency| (*internal_dependency).path())
100            .collect::<Vec<_>>();
101        dependency_dirs.push(package_manifest.path());
102        dependency_dirs
103    };
104
105    let npm_pack_archive_dependencies = &internal_dependencies_exclusive
106        .iter()
107        .map(|dependency| {
108            let target_directory = package_manifest
109                .directory()
110                .join(".internal-npm-dependencies");
111            let target = target_directory
112                .join(dependency.npm_pack_file_basename())
113                .to_str()
114                .expect("npm pack filename is not UTF-8 encodable")
115                .to_owned();
116            let source_package_directory = dependency.directory();
117            let source = diff_paths(source_package_directory, target_directory)
118                .expect("No relative path to source package")
119                .to_str()
120                .expect("Source package path is not UTF-8 encodable")
121                .to_owned();
122            (target, source)
123        })
124        .collect();
125
126    // create a string of the makefile contents
127    let makefile_contents = MakefileTemplate {
128        monorepo_kind: monorepo_manifest.kind.into(),
129        root: root.to_str().expect("Monorepo root is not UTF_8 encodable"),
130        output_file: output_file
131            .to_str()
132            .expect("Output file is not UTF-8 encodable"),
133        package_directory: package_manifest
134            .directory()
135            .to_str()
136            .expect("Package directory is not UTF-8 encodable"),
137        scoped_package_name: &package_manifest.contents.name,
138        unscoped_package_name: package_manifest.unscoped_package_name(),
139        internal_dependency_package_json_filenames_inclusive:
140            &internal_dependency_package_json_filenames_inclusive
141                .iter()
142                .map(|internal_dependency| {
143                    internal_dependency
144                        .to_str()
145                        .expect("Internal package directory is not UTF-8 encodable")
146                        .to_owned()
147                })
148                .collect(),
149        create_pack_target: &create_pack_target,
150        npm_pack_archive_dependencies,
151        internal_npm_dependencies_exclusive: &npm_pack_archive_dependencies
152            .keys()
153            .map(String::as_str)
154            .collect::<Vec<_>>(),
155    }
156    .render()
157    .expect("Unable to render makefile template");
158
159    fs::write(package_directory.join(output_file), makefile_contents)
160        .expect("Unable to write makefile");
161
162    Ok(())
163}