fallow_engine/
discover.rs1use std::ffi::OsStr;
4use std::path::{Path, PathBuf};
5
6use fallow_config::{PackageJson, ResolvedConfig, WorkspaceInfo};
7pub use fallow_types::discover::{DiscoveredFile, EntryPoint, EntryPointSource, FileId};
8
9pub const SOURCE_EXTENSIONS: &[&str] = fallow_core::discover::SOURCE_EXTENSIONS;
10pub const PRODUCTION_EXCLUDE_PATTERNS: &[&str] = fallow_core::discover::PRODUCTION_EXCLUDE_PATTERNS;
11
12#[derive(Debug, Clone, Default)]
14pub struct CategorizedEntryPoints {
15 pub all: Vec<EntryPoint>,
16 pub runtime: Vec<EntryPoint>,
17 pub test: Vec<EntryPoint>,
18}
19
20impl CategorizedEntryPoints {
21 #[must_use]
22 pub fn dedup(mut self) -> Self {
23 dedup_entry_paths(&mut self.all);
24 dedup_entry_paths(&mut self.runtime);
25 dedup_entry_paths(&mut self.test);
26 self
27 }
28}
29
30impl From<fallow_core::discover::CategorizedEntryPoints> for CategorizedEntryPoints {
31 fn from(value: fallow_core::discover::CategorizedEntryPoints) -> Self {
32 Self {
33 all: value.all,
34 runtime: value.runtime,
35 test: value.test,
36 }
37 }
38}
39
40fn dedup_entry_paths(entries: &mut Vec<EntryPoint>) {
41 entries.sort_by(|a, b| a.path.cmp(&b.path));
42 entries.dedup_by(|a, b| a.path == b.path);
43}
44
45#[derive(Debug, Clone, PartialEq, Eq)]
47pub struct HiddenDirScope {
48 root: PathBuf,
49 dirs: Vec<String>,
50}
51
52impl HiddenDirScope {
53 #[must_use]
54 pub const fn new(root: PathBuf, dirs: Vec<String>) -> Self {
55 Self { root, dirs }
56 }
57
58 #[must_use]
59 pub fn root(&self) -> &Path {
60 &self.root
61 }
62
63 #[must_use]
64 pub fn dirs(&self) -> &[String] {
65 &self.dirs
66 }
67}
68
69impl From<fallow_core::discover::HiddenDirScope> for HiddenDirScope {
70 fn from(value: fallow_core::discover::HiddenDirScope) -> Self {
71 Self {
72 root: value.root().to_path_buf(),
73 dirs: value.dirs().to_vec(),
74 }
75 }
76}
77
78impl From<HiddenDirScope> for fallow_core::discover::HiddenDirScope {
79 fn from(value: HiddenDirScope) -> Self {
80 Self::new(value.root, value.dirs)
81 }
82}
83
84#[derive(Debug, Clone)]
86pub struct AnalysisDiscovery {
87 inner: fallow_core::AnalysisDiscovery,
88}
89
90impl AnalysisDiscovery {
91 pub(crate) const fn from_core(inner: fallow_core::AnalysisDiscovery) -> Self {
92 Self { inner }
93 }
94
95 pub(crate) const fn as_core(&self) -> &fallow_core::AnalysisDiscovery {
96 &self.inner
97 }
98
99 #[must_use]
101 pub fn files(&self) -> &[DiscoveredFile] {
102 self.inner.files()
103 }
104
105 #[must_use]
107 pub fn into_files(self) -> Vec<DiscoveredFile> {
108 self.inner.into_files()
109 }
110}
111
112#[must_use]
114pub fn is_allowed_hidden_dir(name: &OsStr) -> bool {
115 fallow_core::discover::is_allowed_hidden_dir(name)
116}
117
118#[must_use]
120pub fn collect_plugin_hidden_dir_scopes(
121 config: &ResolvedConfig,
122 root_pkg: Option<&PackageJson>,
123 workspaces: &[WorkspaceInfo],
124) -> Vec<HiddenDirScope> {
125 fallow_core::discover::collect_plugin_hidden_dir_scopes(config, root_pkg, workspaces)
126 .into_iter()
127 .map(Into::into)
128 .collect()
129}
130
131#[must_use]
133pub fn discover_files(config: &ResolvedConfig) -> Vec<DiscoveredFile> {
134 fallow_core::discover::discover_files(config)
135}
136
137#[must_use]
139pub fn discover_files_with_additional_hidden_dirs(
140 config: &ResolvedConfig,
141 additional_hidden_dir_scopes: &[HiddenDirScope],
142) -> Vec<DiscoveredFile> {
143 let scopes = to_core_hidden_dir_scopes(additional_hidden_dir_scopes);
144 fallow_core::discover::discover_files_with_additional_hidden_dirs(config, &scopes)
145}
146
147#[must_use]
149pub fn discover_files_with_plugin_scopes(config: &ResolvedConfig) -> Vec<DiscoveredFile> {
150 fallow_core::discover::discover_files_with_plugin_scopes(config)
151}
152
153#[must_use]
155pub fn discover_entry_points(config: &ResolvedConfig, files: &[DiscoveredFile]) -> Vec<EntryPoint> {
156 fallow_core::discover::discover_entry_points(config, files)
157}
158
159#[must_use]
161pub fn discover_workspace_entry_points(
162 ws_root: &Path,
163 config: &ResolvedConfig,
164 all_files: &[DiscoveredFile],
165) -> Vec<EntryPoint> {
166 fallow_core::discover::discover_workspace_entry_points(ws_root, config, all_files)
167}
168
169#[must_use]
171pub fn discover_plugin_entry_points(
172 plugin_result: &crate::plugins::AggregatedPluginResult,
173 config: &ResolvedConfig,
174 files: &[DiscoveredFile],
175) -> Vec<EntryPoint> {
176 fallow_core::discover::discover_plugin_entry_points(plugin_result.as_core(), config, files)
177}
178
179fn to_core_hidden_dir_scopes(
180 scopes: &[HiddenDirScope],
181) -> Vec<fallow_core::discover::HiddenDirScope> {
182 scopes.iter().cloned().map(Into::into).collect()
183}
184
185#[cfg(test)]
186mod tests {
187 use std::path::PathBuf;
188
189 use super::{CategorizedEntryPoints, EntryPoint, EntryPointSource, HiddenDirScope};
190
191 #[test]
192 fn hidden_dir_scope_round_trips_through_core() {
193 let scope = HiddenDirScope::new(PathBuf::from("/repo/packages/app"), vec![".next".into()]);
194
195 let core: fallow_core::discover::HiddenDirScope = scope.clone().into();
196 let engine: HiddenDirScope = core.into();
197
198 assert_eq!(engine, scope);
199 assert_eq!(engine.root(), scope.root());
200 assert_eq!(engine.dirs(), scope.dirs());
201 }
202
203 #[test]
204 fn categorized_entry_points_converts_from_core() {
205 let entry = EntryPoint {
206 path: PathBuf::from("/repo/src/index.ts"),
207 source: EntryPointSource::DefaultIndex,
208 };
209 let mut core = fallow_core::discover::CategorizedEntryPoints::default();
210 core.push_runtime(entry.clone());
211
212 let engine: CategorizedEntryPoints = core.into();
213
214 assert_eq!(engine.all.len(), 1);
215 assert_eq!(engine.runtime.len(), 1);
216 assert_eq!(engine.test.len(), 0);
217 assert_eq!(engine.all[0].path, entry.path);
218 }
219}