Skip to content

Commit e1713c5

Browse files
authored
feat!: return ResolveError:Builtin("node:{specifier}") from package imports and exports (#165)
closes #164 According to the ESM specification https://nodejs.org/api/esm.html#resolution-and-loading-algorithm ``` // PACKAGE_RESOLVE(packageSpecifier, parentURL) // 3. If packageSpecifier is a Node.js builtin module name, then // 1. Return the string "node:" concatenated with packageSpecifier. ``` The returned value should be prefixed by `node:`
1 parent 99cb3dd commit e1713c5

File tree

3 files changed

+60
-12
lines changed

3 files changed

+60
-12
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "builtins",
3+
"private": true,
4+
"imports": {
5+
"#fs": {
6+
"default": "fs"
7+
},
8+
"#http": {
9+
"node": "node:http"
10+
}
11+
}
12+
}

src/lib.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -308,11 +308,19 @@ impl<Fs: FileSystem> ResolverGeneric<Fs> {
308308
}
309309
}
310310

311+
// PACKAGE_RESOLVE(packageSpecifier, parentURL)
312+
// 3. If packageSpecifier is a Node.js builtin module name, then
313+
// 1. Return the string "node:" concatenated with packageSpecifier.
311314
fn require_core(&self, specifier: &str) -> Result<(), ResolveError> {
312-
if self.options.builtin_modules
313-
&& (specifier.starts_with("node:") || NODEJS_BUILTINS.binary_search(&specifier).is_ok())
314-
{
315-
return Err(ResolveError::Builtin(specifier.to_string()));
315+
if self.options.builtin_modules {
316+
let starts_with_node = specifier.starts_with("node:");
317+
if starts_with_node || NODEJS_BUILTINS.binary_search(&specifier).is_ok() {
318+
let mut specifier = specifier.to_string();
319+
if !starts_with_node {
320+
specifier = format!("node:{specifier}");
321+
}
322+
return Err(ResolveError::Builtin(specifier));
323+
}
316324
}
317325
Ok(())
318326
}
@@ -1118,6 +1126,11 @@ impl<Fs: FileSystem> ResolverGeneric<Fs> {
11181126
ctx: &mut Ctx,
11191127
) -> ResolveResult {
11201128
let (package_name, subpath) = Self::parse_package_specifier(specifier);
1129+
1130+
// 3. If packageSpecifier is a Node.js builtin module name, then
1131+
// 1. Return the string "node:" concatenated with packageSpecifier.
1132+
self.require_core(package_name)?;
1133+
11211134
// 11. While parentURL is not the file system root,
11221135
for module_name in &self.options.modules {
11231136
for cached_path in std::iter::successors(Some(cached_path), |p| p.parent()) {

src/tests/builtins.rs

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ fn builtins_off() {
1414
fn builtins() {
1515
let f = Path::new("/");
1616

17-
let resolver =
18-
Resolver::new(ResolveOptions { builtin_modules: true, ..ResolveOptions::default() });
17+
let resolver = Resolver::new(ResolveOptions::default().with_builtin_modules(true));
1918

2019
let pass = [
2120
"_http_agent",
@@ -86,13 +85,37 @@ fn builtins() {
8685
];
8786

8887
for request in pass {
89-
let resolved_path = resolver.resolve(f, request).map(|r| r.full_path());
90-
assert_eq!(resolved_path, Err(ResolveError::Builtin(request.to_string())), "{request}");
88+
let prefixed_request = format!("node:{request}");
89+
for request in [prefixed_request.clone(), request.to_string()] {
90+
let resolved_path = resolver.resolve(f, &request).map(|r| r.full_path());
91+
let err = ResolveError::Builtin(prefixed_request.clone());
92+
assert_eq!(resolved_path, Err(err), "{request}");
93+
}
9194
}
95+
}
9296

93-
for request in pass {
94-
let request = format!("node:{request}");
95-
let resolved_path = resolver.resolve(f, &request).map(|r| r.full_path());
96-
assert_eq!(resolved_path, Err(ResolveError::Builtin(request.to_string())), "{request}");
97+
#[test]
98+
fn fail() {
99+
let f = Path::new("/");
100+
let resolver = Resolver::new(ResolveOptions::default().with_builtin_modules(true));
101+
let request = "xxx";
102+
let resolved_path = resolver.resolve(f, request);
103+
let err = ResolveError::NotFound(request.to_string());
104+
assert_eq!(resolved_path, Err(err), "{request}");
105+
}
106+
107+
#[test]
108+
fn imports() {
109+
let f = super::fixture().join("builtins");
110+
let resolver = Resolver::new(ResolveOptions {
111+
builtin_modules: true,
112+
condition_names: vec!["node".into()],
113+
..ResolveOptions::default()
114+
});
115+
116+
for request in ["#fs", "#http"] {
117+
let resolved_path = resolver.resolve(f.clone(), request).map(|r| r.full_path());
118+
let err = ResolveError::Builtin(format!("node:{}", request.trim_start_matches('#')));
119+
assert_eq!(resolved_path, Err(err));
97120
}
98121
}

0 commit comments

Comments
 (0)