Skip to main content

cli/lib/compiler/
base_compiler.rs

1//! Upstream source: `../nest-cli/lib/compiler/base-compiler.ts`.
2
3use std::path::{Component, Path, PathBuf};
4
5use crate::configuration::{CompilerOptions, Configuration, DEFAULT_SOURCE_ROOT, Plugin};
6
7use super::plugins::plugins_loader::{MultiNestCompilerPlugins, PluginsLoader};
8
9#[derive(Clone, Debug, PartialEq, Eq)]
10pub struct BaseCompilerContext {
11    pub cwd: PathBuf,
12    pub configuration: Configuration,
13}
14
15impl BaseCompilerContext {
16    pub fn new(cwd: impl Into<PathBuf>, configuration: Configuration) -> Self {
17        Self {
18            cwd: cwd.into(),
19            configuration,
20        }
21    }
22
23    pub fn compiler_options_for(&self, app_name: Option<&str>) -> CompilerOptions {
24        app_name
25            .and_then(|name| self.configuration.projects.get(name))
26            .and_then(|project| project.compiler_options.clone())
27            .unwrap_or_else(|| self.configuration.compiler_options.clone())
28    }
29
30    pub fn source_root_for(&self, app_name: Option<&str>) -> String {
31        app_name
32            .and_then(|name| self.configuration.projects.get(name))
33            .and_then(|project| project.source_root.clone())
34            .unwrap_or_else(|| {
35                if self.configuration.source_root.is_empty() {
36                    DEFAULT_SOURCE_ROOT.to_string()
37                } else {
38                    self.configuration.source_root.clone()
39                }
40            })
41    }
42
43    pub fn plugins_for(&self, app_name: Option<&str>) -> Vec<Plugin> {
44        self.compiler_options_for(app_name).plugins
45    }
46}
47
48#[derive(Clone, Debug, PartialEq, Eq)]
49pub struct BaseCompiler {
50    plugins_loader: PluginsLoader,
51}
52
53impl Default for BaseCompiler {
54    fn default() -> Self {
55        Self::new(PluginsLoader::default())
56    }
57}
58
59impl BaseCompiler {
60    pub fn new(plugins_loader: PluginsLoader) -> Self {
61        Self { plugins_loader }
62    }
63
64    pub fn load_plugins(
65        &self,
66        context: &BaseCompilerContext,
67        ts_config_path: impl AsRef<Path>,
68        app_name: Option<&str>,
69    ) -> Result<MultiNestCompilerPlugins, String> {
70        let path_to_source = self.get_path_to_source(context, ts_config_path, app_name);
71        self.plugins_loader
72            .load(&context.plugins_for(app_name), Some(path_to_source))
73    }
74
75    pub fn get_path_to_source(
76        &self,
77        context: &BaseCompilerContext,
78        ts_config_path: impl AsRef<Path>,
79        app_name: Option<&str>,
80    ) -> PathBuf {
81        let source_root = normalize_path(Path::new(&context.source_root_for(app_name)));
82        let ts_config_path = ts_config_path.as_ref();
83        let ts_config_dir = ts_config_path.parent().unwrap_or_else(|| Path::new(""));
84        let relative_root = normalize_path(ts_config_dir);
85
86        if starts_with_components(&source_root, &relative_root) {
87            context.cwd.join(source_root)
88        } else {
89            context.cwd.join(relative_root).join(source_root)
90        }
91    }
92}
93
94fn normalize_path(path: &Path) -> PathBuf {
95    let mut normalized = PathBuf::new();
96    for component in path.components() {
97        match component {
98            Component::CurDir => {}
99            Component::ParentDir => {
100                normalized.pop();
101            }
102            _ => normalized.push(component.as_os_str()),
103        }
104    }
105    normalized
106}
107
108fn starts_with_components(path: &Path, prefix: &Path) -> bool {
109    if prefix.as_os_str().is_empty() {
110        return true;
111    }
112    path.components()
113        .zip(prefix.components())
114        .all(|(a, b)| a == b)
115        && path.components().count() >= prefix.components().count()
116}