use std::path::{Component, Path, PathBuf};
use crate::configuration::{CompilerOptions, Configuration, DEFAULT_SOURCE_ROOT, Plugin};
use super::plugins::plugins_loader::{MultiNestCompilerPlugins, PluginsLoader};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BaseCompilerContext {
pub cwd: PathBuf,
pub configuration: Configuration,
}
impl BaseCompilerContext {
pub fn new(cwd: impl Into<PathBuf>, configuration: Configuration) -> Self {
Self {
cwd: cwd.into(),
configuration,
}
}
pub fn compiler_options_for(&self, app_name: Option<&str>) -> CompilerOptions {
app_name
.and_then(|name| self.configuration.projects.get(name))
.and_then(|project| project.compiler_options.clone())
.unwrap_or_else(|| self.configuration.compiler_options.clone())
}
pub fn source_root_for(&self, app_name: Option<&str>) -> String {
app_name
.and_then(|name| self.configuration.projects.get(name))
.and_then(|project| project.source_root.clone())
.unwrap_or_else(|| {
if self.configuration.source_root.is_empty() {
DEFAULT_SOURCE_ROOT.to_string()
} else {
self.configuration.source_root.clone()
}
})
}
pub fn plugins_for(&self, app_name: Option<&str>) -> Vec<Plugin> {
self.compiler_options_for(app_name).plugins
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BaseCompiler {
plugins_loader: PluginsLoader,
}
impl Default for BaseCompiler {
fn default() -> Self {
Self::new(PluginsLoader::default())
}
}
impl BaseCompiler {
pub fn new(plugins_loader: PluginsLoader) -> Self {
Self { plugins_loader }
}
pub fn load_plugins(
&self,
context: &BaseCompilerContext,
ts_config_path: impl AsRef<Path>,
app_name: Option<&str>,
) -> Result<MultiNestCompilerPlugins, String> {
let path_to_source = self.get_path_to_source(context, ts_config_path, app_name);
self.plugins_loader
.load(&context.plugins_for(app_name), Some(path_to_source))
}
pub fn get_path_to_source(
&self,
context: &BaseCompilerContext,
ts_config_path: impl AsRef<Path>,
app_name: Option<&str>,
) -> PathBuf {
let source_root = normalize_path(Path::new(&context.source_root_for(app_name)));
let ts_config_path = ts_config_path.as_ref();
let ts_config_dir = ts_config_path.parent().unwrap_or_else(|| Path::new(""));
let relative_root = normalize_path(ts_config_dir);
if starts_with_components(&source_root, &relative_root) {
context.cwd.join(source_root)
} else {
context.cwd.join(relative_root).join(source_root)
}
}
}
fn normalize_path(path: &Path) -> PathBuf {
let mut normalized = PathBuf::new();
for component in path.components() {
match component {
Component::CurDir => {}
Component::ParentDir => {
normalized.pop();
}
_ => normalized.push(component.as_os_str()),
}
}
normalized
}
fn starts_with_components(path: &Path, prefix: &Path) -> bool {
if prefix.as_os_str().is_empty() {
return true;
}
path.components()
.zip(prefix.components())
.all(|(a, b)| a == b)
&& path.components().count() >= prefix.components().count()
}