Skip to content

Commit

Permalink
feat!: return ResolveError:Builtin("node:{specifier}") from package…
Browse files Browse the repository at this point in the history
… 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:`
  • Loading branch information
Dunqing authored May 27, 2024
1 parent 99cb3dd commit e1713c5
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 12 deletions.
12 changes: 12 additions & 0 deletions fixtures/enhanced_resolve/test/fixtures/builtins/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "builtins",
"private": true,
"imports": {
"#fs": {
"default": "fs"
},
"#http": {
"node": "node:http"
}
}
}
21 changes: 17 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,11 +308,19 @@ impl<Fs: FileSystem> ResolverGeneric<Fs> {
}
}

// PACKAGE_RESOLVE(packageSpecifier, parentURL)
// 3. If packageSpecifier is a Node.js builtin module name, then
// 1. Return the string "node:" concatenated with packageSpecifier.
fn require_core(&self, specifier: &str) -> Result<(), ResolveError> {
if self.options.builtin_modules
&& (specifier.starts_with("node:") || NODEJS_BUILTINS.binary_search(&specifier).is_ok())
{
return Err(ResolveError::Builtin(specifier.to_string()));
if self.options.builtin_modules {
let starts_with_node = specifier.starts_with("node:");
if starts_with_node || NODEJS_BUILTINS.binary_search(&specifier).is_ok() {
let mut specifier = specifier.to_string();
if !starts_with_node {
specifier = format!("node:{specifier}");
}
return Err(ResolveError::Builtin(specifier));
}
}
Ok(())
}
Expand Down Expand Up @@ -1118,6 +1126,11 @@ impl<Fs: FileSystem> ResolverGeneric<Fs> {
ctx: &mut Ctx,
) -> ResolveResult {
let (package_name, subpath) = Self::parse_package_specifier(specifier);

// 3. If packageSpecifier is a Node.js builtin module name, then
// 1. Return the string "node:" concatenated with packageSpecifier.
self.require_core(package_name)?;

// 11. While parentURL is not the file system root,
for module_name in &self.options.modules {
for cached_path in std::iter::successors(Some(cached_path), |p| p.parent()) {
Expand Down
39 changes: 31 additions & 8 deletions src/tests/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ fn builtins_off() {
fn builtins() {
let f = Path::new("/");

let resolver =
Resolver::new(ResolveOptions { builtin_modules: true, ..ResolveOptions::default() });
let resolver = Resolver::new(ResolveOptions::default().with_builtin_modules(true));

let pass = [
"_http_agent",
Expand Down Expand Up @@ -86,13 +85,37 @@ fn builtins() {
];

for request in pass {
let resolved_path = resolver.resolve(f, request).map(|r| r.full_path());
assert_eq!(resolved_path, Err(ResolveError::Builtin(request.to_string())), "{request}");
let prefixed_request = format!("node:{request}");
for request in [prefixed_request.clone(), request.to_string()] {
let resolved_path = resolver.resolve(f, &request).map(|r| r.full_path());
let err = ResolveError::Builtin(prefixed_request.clone());
assert_eq!(resolved_path, Err(err), "{request}");
}
}
}

for request in pass {
let request = format!("node:{request}");
let resolved_path = resolver.resolve(f, &request).map(|r| r.full_path());
assert_eq!(resolved_path, Err(ResolveError::Builtin(request.to_string())), "{request}");
#[test]
fn fail() {
let f = Path::new("/");
let resolver = Resolver::new(ResolveOptions::default().with_builtin_modules(true));
let request = "xxx";
let resolved_path = resolver.resolve(f, request);
let err = ResolveError::NotFound(request.to_string());
assert_eq!(resolved_path, Err(err), "{request}");
}

#[test]
fn imports() {
let f = super::fixture().join("builtins");
let resolver = Resolver::new(ResolveOptions {
builtin_modules: true,
condition_names: vec!["node".into()],
..ResolveOptions::default()
});

for request in ["#fs", "#http"] {
let resolved_path = resolver.resolve(f.clone(), request).map(|r| r.full_path());
let err = ResolveError::Builtin(format!("node:{}", request.trim_start_matches('#')));
assert_eq!(resolved_path, Err(err));
}
}

0 comments on commit e1713c5

Please sign in to comment.