use crate::BinderState;
use crate::SymbolTable;
use rustc_hash::FxHashMap;
use std::path::PathBuf;
use std::sync::Arc;
use tsz_common::diagnostics::Diagnostic;
use tsz_parser::parser::node::NodeArena;
use tsz_parser::parser::state::ParserState;
pub struct LibLoader {
lib_dir: PathBuf,
cache: FxHashMap<String, String>,
}
impl LibLoader {
#[must_use]
pub fn new(lib_dir: PathBuf) -> Self {
Self {
lib_dir,
cache: FxHashMap::default(),
}
}
pub fn load_lib(&mut self, lib_name: &str) -> Option<&str> {
let normalized = lib_name.trim().to_lowercase();
if self.cache.contains_key(&normalized) {
return self.cache.get(&normalized).map(std::string::String::as_str);
}
let candidates = [
self.lib_dir.join(format!("lib.{normalized}.d.ts")),
self.lib_dir.join(format!("{normalized}.d.ts")),
];
for candidate in &candidates {
if candidate.exists()
&& let Ok(content) = std::fs::read_to_string(candidate)
{
self.cache.insert(normalized.clone(), content);
return self.cache.get(&normalized).map(std::string::String::as_str);
}
}
None
}
pub fn clear_cache(&mut self) {
self.cache.clear();
}
#[must_use]
pub fn cache_size(&self) -> usize {
self.cache.len()
}
}
pub const CANNOT_FIND_GLOBAL_TYPE: u32 = 2318;
pub const MISSING_ES2015_LIB_SUPPORT: u32 = 2583;
#[derive(Clone)]
pub struct LibFile {
pub file_name: String,
pub arena: Arc<NodeArena>,
pub binder: Arc<BinderState>,
}
impl LibFile {
#[must_use]
pub const fn new(file_name: String, arena: Arc<NodeArena>, binder: Arc<BinderState>) -> Self {
Self {
file_name,
arena,
binder,
}
}
#[must_use]
pub fn file_locals(&self) -> &SymbolTable {
&self.binder.file_locals
}
#[must_use]
pub fn from_source(file_name: String, content: String) -> Self {
let mut parser = ParserState::new(file_name.clone(), content);
let source_file_idx = parser.parse_source_file();
let mut binder = BinderState::new();
binder.bind_source_file(parser.get_arena(), source_file_idx);
let arena = Arc::new(parser.into_arena());
let binder = Arc::new(binder);
Self::new(file_name, arena, binder)
}
}
#[must_use]
pub fn merge_lib_files(lib_files: Vec<Arc<LibFile>>) -> Vec<Arc<LibFile>> {
use crate::state::LibContext as BinderLibContext;
if lib_files.is_empty() {
return lib_files;
}
let mut merged_binder = BinderState::new();
let lib_contexts: Vec<_> = lib_files
.iter()
.map(|lib| BinderLibContext {
arena: Arc::clone(&lib.arena),
binder: Arc::clone(&lib.binder),
})
.collect();
merged_binder.merge_lib_contexts_into_binder(&lib_contexts);
let merged_arena = match lib_files.first() {
Some(lib_file) => Arc::clone(&lib_file.arena),
None => return lib_files,
};
let merged_binder = Arc::new(merged_binder);
let merged_file = Arc::new(LibFile::new(
"merged-libs".to_string(),
merged_arena,
merged_binder,
));
vec![merged_file]
}
#[must_use]
pub fn emit_error_global_type_missing(
name: &str,
file_name: String,
start: u32,
length: u32,
) -> Diagnostic {
Diagnostic::error(
file_name,
start,
length,
format!("Cannot find global type '{name}'."),
CANNOT_FIND_GLOBAL_TYPE,
)
}
#[must_use]
pub fn emit_error_lib_target_mismatch(
name: &str,
file_name: String,
start: u32,
length: u32,
) -> Diagnostic {
Diagnostic::error(
file_name,
start,
length,
format!(
"Cannot find name '{name}'. Do you need to change your target library? Try changing the 'lib' compiler option to es2015 or later."
),
MISSING_ES2015_LIB_SUPPORT,
)
}
const ES2015_PLUS_TYPES: &[&str] = &[
"Promise",
"PromiseLike",
"PromiseConstructor",
"PromiseConstructorLike",
"PromiseSettledResult",
"PromiseFulfilledResult",
"PromiseRejectedResult",
"Map",
"MapConstructor",
"Set",
"SetConstructor",
"WeakMap",
"WeakMapConstructor",
"WeakSet",
"WeakSetConstructor",
"Proxy",
"ProxyHandler",
"ProxyConstructor",
"Reflect",
"Symbol",
"SymbolConstructor",
"Iterator",
"IterableIterator",
"IteratorResult",
"IteratorYieldResult",
"IteratorReturnResult",
"AsyncIterator",
"AsyncIterable",
"AsyncIterableIterator",
"Generator",
"GeneratorFunction",
"GeneratorFunctionConstructor",
"ArrayLike",
"ReadonlyMap",
"ReadonlySet",
"TemplateStringsArray",
"TypedPropertyDescriptor",
"CallableFunction",
"NewableFunction",
"PropertyKey",
"AsyncFunction",
"AsyncFunctionConstructor",
"SharedArrayBuffer",
"SharedArrayBufferConstructor",
"Atomics",
"AsyncGenerator",
"AsyncGeneratorFunction",
"AsyncGeneratorFunctionConstructor",
"ObjectEntries",
"ObjectValues",
"BigInt",
"BigIntConstructor",
"BigInt64Array",
"BigInt64ArrayConstructor",
"BigUint64Array",
"BigUint64ArrayConstructor",
"FinalizationRegistry",
"FinalizationRegistryConstructor",
"WeakRef",
"WeakRefConstructor",
"AggregateError",
"AggregateErrorConstructor",
"Awaited",
"ErrorOptions",
"Disposable",
"AsyncDisposable",
];
#[must_use]
pub fn is_es2015_plus_type(name: &str) -> bool {
if name == "PromiseLike" {
return false;
}
ES2015_PLUS_TYPES.contains(&name)
}
#[cfg(test)]
#[path = "../tests/lib_loader.rs"]
mod tests;