cli/lib/compiler/
base_compiler.rs1use 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}