#![warn(missing_docs)]
mod asset_url;
pub mod astro;
pub mod cache;
pub(crate) mod complexity;
pub mod css;
pub mod flags;
pub mod html;
pub mod mdx;
mod parse;
pub mod sfc;
mod sfc_template;
pub mod suppress;
mod template_usage;
pub mod visitor;
use std::path::Path;
use rayon::prelude::*;
use cache::CacheStore;
use fallow_types::discover::{DiscoveredFile, FileId};
pub use fallow_types::extract::{
DynamicImportInfo, DynamicImportPattern, ExportInfo, ExportName, ImportInfo, ImportedName,
MemberAccess, MemberInfo, MemberKind, ModuleInfo, ParseResult, ReExportInfo, RequireCallInfo,
VisibilityTag, compute_line_offsets,
};
pub use astro::extract_astro_frontmatter;
pub use css::extract_css_module_exports;
pub use mdx::extract_mdx_statements;
pub use sfc::{extract_sfc_scripts, is_sfc_file};
pub use sfc_template::angular::ANGULAR_TPL_SENTINEL;
use parse::parse_source_to_module;
pub fn parse_all_files(
files: &[DiscoveredFile],
cache: Option<&CacheStore>,
need_complexity: bool,
) -> ParseResult {
use std::sync::atomic::{AtomicUsize, Ordering};
let cache_hits = AtomicUsize::new(0);
let cache_misses = AtomicUsize::new(0);
let modules: Vec<ModuleInfo> = files
.par_iter()
.filter_map(|file| {
parse_single_file_cached(file, cache, &cache_hits, &cache_misses, need_complexity)
})
.collect();
let hits = cache_hits.load(Ordering::Relaxed);
let misses = cache_misses.load(Ordering::Relaxed);
if hits > 0 || misses > 0 {
tracing::info!(
cache_hits = hits,
cache_misses = misses,
"incremental cache stats"
);
}
ParseResult {
modules,
cache_hits: hits,
cache_misses: misses,
}
}
fn mtime_secs(metadata: &std::fs::Metadata) -> u64 {
metadata
.modified()
.ok()
.and_then(|t| t.duration_since(std::time::SystemTime::UNIX_EPOCH).ok())
.map_or(0, |d| d.as_secs())
}
fn parse_single_file_cached(
file: &DiscoveredFile,
cache: Option<&CacheStore>,
cache_hits: &std::sync::atomic::AtomicUsize,
cache_misses: &std::sync::atomic::AtomicUsize,
need_complexity: bool,
) -> Option<ModuleInfo> {
use std::sync::atomic::Ordering;
if let Some(store) = cache
&& let Ok(metadata) = std::fs::metadata(&file.path)
{
let mt = mtime_secs(&metadata);
let sz = metadata.len();
if let Some(cached) = store.get_by_metadata(&file.path, mt, sz) {
if !need_complexity || !cached.complexity.is_empty() {
cache_hits.fetch_add(1, Ordering::Relaxed);
return Some(cache::cached_to_module(cached, file.id));
}
}
}
let source = std::fs::read_to_string(&file.path).ok()?;
let content_hash = xxhash_rust::xxh3::xxh3_64(source.as_bytes());
if let Some(store) = cache
&& let Some(cached) = store.get(&file.path, content_hash)
&& (!need_complexity || !cached.complexity.is_empty())
{
cache_hits.fetch_add(1, Ordering::Relaxed);
return Some(cache::cached_to_module(cached, file.id));
}
cache_misses.fetch_add(1, Ordering::Relaxed);
Some(parse_source_to_module(
file.id,
&file.path,
&source,
content_hash,
need_complexity,
))
}
#[must_use]
pub fn parse_single_file(file: &DiscoveredFile) -> Option<ModuleInfo> {
let source = std::fs::read_to_string(&file.path).ok()?;
let content_hash = xxhash_rust::xxh3::xxh3_64(source.as_bytes());
Some(parse_source_to_module(
file.id,
&file.path,
&source,
content_hash,
false,
))
}
#[must_use]
pub fn parse_from_content(file_id: FileId, path: &Path, content: &str) -> ModuleInfo {
let content_hash = xxhash_rust::xxh3::xxh3_64(content.as_bytes());
parse_source_to_module(file_id, path, content, content_hash, true)
}
#[cfg(all(test, not(miri)))]
mod tests;