ts_gen/parse/resolve.rs
1//! Module specifier resolution using `oxc_resolver`.
2//!
3//! Resolves TypeScript module specifiers (e.g. `"node:buffer"`, `"es-module-lexer"`)
4//! to `.d.ts` file paths on disk using the `types` export condition.
5
6use std::path::{Path, PathBuf};
7
8use oxc_resolver::{ResolveOptions, Resolver};
9
10/// Create a resolver configured for TypeScript declaration resolution.
11///
12/// Uses the `types`, `import`, and `node` conditions to match how TypeScript
13/// resolves module specifiers in declaration files.
14fn create_resolver() -> Resolver {
15 Resolver::new(ResolveOptions {
16 condition_names: vec!["types".into(), "import".into(), "node".into()],
17 extensions: vec![".d.ts".into(), ".ts".into()],
18 main_fields: vec!["types".into(), "typings".into()],
19 ..ResolveOptions::default()
20 })
21}
22
23/// Attempt to resolve a module specifier to a `.d.ts` file path.
24///
25/// `base_dir` is the directory containing the file that has the import statement.
26/// `oxc_resolver` finds `node_modules` automatically by walking up from `base_dir`.
27///
28/// Returns `None` if the file can't be found.
29pub fn resolve_module(specifier: &str, base_dir: &Path) -> Option<PathBuf> {
30 let resolver = create_resolver();
31
32 // For node: builtins, resolve via @types/node
33 let effective_specifier = if let Some(module_name) = specifier.strip_prefix("node:") {
34 format!("@types/node/{module_name}")
35 } else {
36 specifier.to_string()
37 };
38
39 match resolver.resolve(base_dir, &effective_specifier) {
40 Ok(resolution) => Some(resolution.into_path_buf()),
41 Err(_) => None,
42 }
43}
44
45/// Resolve a module specifier from a directory path (convenience for build scripts).
46pub fn resolve(specifier: &str, from_dir: &Path) -> Option<PathBuf> {
47 resolve_module(specifier, from_dir)
48}