use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use fallow_config::{PackageJson, ResolvedConfig, WorkspaceInfo};
pub use fallow_types::discover::{DiscoveredFile, EntryPoint, EntryPointSource, FileId};
pub const SOURCE_EXTENSIONS: &[&str] = fallow_core::discover::SOURCE_EXTENSIONS;
pub const PRODUCTION_EXCLUDE_PATTERNS: &[&str] = fallow_core::discover::PRODUCTION_EXCLUDE_PATTERNS;
#[derive(Debug, Clone, Default)]
pub struct CategorizedEntryPoints {
pub all: Vec<EntryPoint>,
pub runtime: Vec<EntryPoint>,
pub test: Vec<EntryPoint>,
}
impl CategorizedEntryPoints {
#[must_use]
pub fn dedup(mut self) -> Self {
dedup_entry_paths(&mut self.all);
dedup_entry_paths(&mut self.runtime);
dedup_entry_paths(&mut self.test);
self
}
}
impl From<fallow_core::discover::CategorizedEntryPoints> for CategorizedEntryPoints {
fn from(value: fallow_core::discover::CategorizedEntryPoints) -> Self {
Self {
all: value.all,
runtime: value.runtime,
test: value.test,
}
}
}
fn dedup_entry_paths(entries: &mut Vec<EntryPoint>) {
entries.sort_by(|a, b| a.path.cmp(&b.path));
entries.dedup_by(|a, b| a.path == b.path);
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HiddenDirScope {
root: PathBuf,
dirs: Vec<String>,
}
impl HiddenDirScope {
#[must_use]
pub const fn new(root: PathBuf, dirs: Vec<String>) -> Self {
Self { root, dirs }
}
#[must_use]
pub fn root(&self) -> &Path {
&self.root
}
#[must_use]
pub fn dirs(&self) -> &[String] {
&self.dirs
}
}
impl From<fallow_core::discover::HiddenDirScope> for HiddenDirScope {
fn from(value: fallow_core::discover::HiddenDirScope) -> Self {
Self {
root: value.root().to_path_buf(),
dirs: value.dirs().to_vec(),
}
}
}
impl From<HiddenDirScope> for fallow_core::discover::HiddenDirScope {
fn from(value: HiddenDirScope) -> Self {
Self::new(value.root, value.dirs)
}
}
#[derive(Debug, Clone)]
pub struct AnalysisDiscovery {
inner: fallow_core::AnalysisDiscovery,
}
impl AnalysisDiscovery {
pub(crate) const fn from_core(inner: fallow_core::AnalysisDiscovery) -> Self {
Self { inner }
}
pub(crate) const fn as_core(&self) -> &fallow_core::AnalysisDiscovery {
&self.inner
}
#[must_use]
pub fn files(&self) -> &[DiscoveredFile] {
self.inner.files()
}
#[must_use]
pub fn into_files(self) -> Vec<DiscoveredFile> {
self.inner.into_files()
}
}
#[must_use]
pub fn is_allowed_hidden_dir(name: &OsStr) -> bool {
fallow_core::discover::is_allowed_hidden_dir(name)
}
#[must_use]
pub fn collect_plugin_hidden_dir_scopes(
config: &ResolvedConfig,
root_pkg: Option<&PackageJson>,
workspaces: &[WorkspaceInfo],
) -> Vec<HiddenDirScope> {
fallow_core::discover::collect_plugin_hidden_dir_scopes(config, root_pkg, workspaces)
.into_iter()
.map(Into::into)
.collect()
}
#[must_use]
pub fn discover_files(config: &ResolvedConfig) -> Vec<DiscoveredFile> {
fallow_core::discover::discover_files(config)
}
#[must_use]
pub fn discover_files_with_additional_hidden_dirs(
config: &ResolvedConfig,
additional_hidden_dir_scopes: &[HiddenDirScope],
) -> Vec<DiscoveredFile> {
let scopes = to_core_hidden_dir_scopes(additional_hidden_dir_scopes);
fallow_core::discover::discover_files_with_additional_hidden_dirs(config, &scopes)
}
#[must_use]
pub fn discover_files_with_plugin_scopes(config: &ResolvedConfig) -> Vec<DiscoveredFile> {
fallow_core::discover::discover_files_with_plugin_scopes(config)
}
#[must_use]
pub fn discover_entry_points(config: &ResolvedConfig, files: &[DiscoveredFile]) -> Vec<EntryPoint> {
fallow_core::discover::discover_entry_points(config, files)
}
#[must_use]
pub fn discover_workspace_entry_points(
ws_root: &Path,
config: &ResolvedConfig,
all_files: &[DiscoveredFile],
) -> Vec<EntryPoint> {
fallow_core::discover::discover_workspace_entry_points(ws_root, config, all_files)
}
#[must_use]
pub fn discover_plugin_entry_points(
plugin_result: &crate::plugins::AggregatedPluginResult,
config: &ResolvedConfig,
files: &[DiscoveredFile],
) -> Vec<EntryPoint> {
fallow_core::discover::discover_plugin_entry_points(plugin_result.as_core(), config, files)
}
fn to_core_hidden_dir_scopes(
scopes: &[HiddenDirScope],
) -> Vec<fallow_core::discover::HiddenDirScope> {
scopes.iter().cloned().map(Into::into).collect()
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
use super::{CategorizedEntryPoints, EntryPoint, EntryPointSource, HiddenDirScope};
#[test]
fn hidden_dir_scope_round_trips_through_core() {
let scope = HiddenDirScope::new(PathBuf::from("/repo/packages/app"), vec![".next".into()]);
let core: fallow_core::discover::HiddenDirScope = scope.clone().into();
let engine: HiddenDirScope = core.into();
assert_eq!(engine, scope);
assert_eq!(engine.root(), scope.root());
assert_eq!(engine.dirs(), scope.dirs());
}
#[test]
fn categorized_entry_points_converts_from_core() {
let entry = EntryPoint {
path: PathBuf::from("/repo/src/index.ts"),
source: EntryPointSource::DefaultIndex,
};
let mut core = fallow_core::discover::CategorizedEntryPoints::default();
core.push_runtime(entry.clone());
let engine: CategorizedEntryPoints = core.into();
assert_eq!(engine.all.len(), 1);
assert_eq!(engine.runtime.len(), 1);
assert_eq!(engine.test.len(), 0);
assert_eq!(engine.all[0].path, entry.path);
}
}