Skip to main content

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}