mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
feat(resolver): check for node.js core modules (#794)
This commit is contained in:
parent
1bc1418ee6
commit
3d8ee2567f
7 changed files with 191 additions and 9 deletions
|
|
@ -31,7 +31,7 @@ fn data() -> Vec<(PathBuf, &'static str)> {
|
||||||
(cwd.join("test/fixtures"), "m1/a.js?query#fragment"),
|
(cwd.join("test/fixtures"), "m1/a.js?query#fragment"),
|
||||||
// extensions
|
// extensions
|
||||||
(cwd.join("test/fixtures/extensions"), "./foo"),
|
(cwd.join("test/fixtures/extensions"), "./foo"),
|
||||||
(cwd.join("test/fixtures/extensions/module"), "module"),
|
(cwd.join("test/fixtures/extensions/module"), "module/"),
|
||||||
// browserField
|
// browserField
|
||||||
(cwd.join("test/fixtures/browser-module"), "./lib/replaced"),
|
(cwd.join("test/fixtures/browser-module"), "./lib/replaced"),
|
||||||
(cwd.join("test/fixtures/browser-module/lib"), "./replaced"),
|
(cwd.join("test/fixtures/browser-module/lib"), "./replaced"),
|
||||||
|
|
|
||||||
69
crates/oxc_resolver/src/builtins.rs
Normal file
69
crates/oxc_resolver/src/builtins.rs
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
// <https://nodejs.org/api/modules.html#core-modules>
|
||||||
|
// node -p "[...require('module').builtinModules].map(b => JSON.stringify(b)).join(',\n')"
|
||||||
|
pub const BUILTINS: &[&str] = &[
|
||||||
|
"_http_agent",
|
||||||
|
"_http_client",
|
||||||
|
"_http_common",
|
||||||
|
"_http_incoming",
|
||||||
|
"_http_outgoing",
|
||||||
|
"_http_server",
|
||||||
|
"_stream_duplex",
|
||||||
|
"_stream_passthrough",
|
||||||
|
"_stream_readable",
|
||||||
|
"_stream_transform",
|
||||||
|
"_stream_wrap",
|
||||||
|
"_stream_writable",
|
||||||
|
"_tls_common",
|
||||||
|
"_tls_wrap",
|
||||||
|
"assert",
|
||||||
|
"assert/strict",
|
||||||
|
"async_hooks",
|
||||||
|
"buffer",
|
||||||
|
"child_process",
|
||||||
|
"cluster",
|
||||||
|
"console",
|
||||||
|
"constants",
|
||||||
|
"crypto",
|
||||||
|
"dgram",
|
||||||
|
"diagnostics_channel",
|
||||||
|
"dns",
|
||||||
|
"dns/promises",
|
||||||
|
"domain",
|
||||||
|
"events",
|
||||||
|
"fs",
|
||||||
|
"fs/promises",
|
||||||
|
"http",
|
||||||
|
"http2",
|
||||||
|
"https",
|
||||||
|
"inspector",
|
||||||
|
"module",
|
||||||
|
"net",
|
||||||
|
"os",
|
||||||
|
"path",
|
||||||
|
"path/posix",
|
||||||
|
"path/win32",
|
||||||
|
"perf_hooks",
|
||||||
|
"process",
|
||||||
|
"punycode",
|
||||||
|
"querystring",
|
||||||
|
"readline",
|
||||||
|
"repl",
|
||||||
|
"stream",
|
||||||
|
"stream/consumers",
|
||||||
|
"stream/promises",
|
||||||
|
"stream/web",
|
||||||
|
"string_decoder",
|
||||||
|
"sys",
|
||||||
|
"timers",
|
||||||
|
"timers/promises",
|
||||||
|
"tls",
|
||||||
|
"trace_events",
|
||||||
|
"tty",
|
||||||
|
"url",
|
||||||
|
"util",
|
||||||
|
"util/types",
|
||||||
|
"v8",
|
||||||
|
"vm",
|
||||||
|
"worker_threads",
|
||||||
|
"zlib",
|
||||||
|
];
|
||||||
|
|
@ -19,6 +19,12 @@ pub enum ResolveError {
|
||||||
/// Path not found
|
/// Path not found
|
||||||
NotFound(PathBuf),
|
NotFound(PathBuf),
|
||||||
|
|
||||||
|
/// Node.js builtin modules
|
||||||
|
///
|
||||||
|
/// This is an error due to not being a Node.js runtime.
|
||||||
|
/// The `alias` option can be used to resolve a builtin module to a polyfill.
|
||||||
|
Builtin(String),
|
||||||
|
|
||||||
/// All of the aliased extension are not found
|
/// All of the aliased extension are not found
|
||||||
ExtensionAlias,
|
ExtensionAlias,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
//! [ECMAScript Module Resolution Algorithm]: https://nodejs.org/api/esm.html#resolution-algorithm-specification
|
//! [ECMAScript Module Resolution Algorithm]: https://nodejs.org/api/esm.html#resolution-algorithm-specification
|
||||||
//! [parcel-resolver]: https://github.com/parcel-bundler/parcel/blob/v2/packages/utils/node-resolver-rs
|
//! [parcel-resolver]: https://github.com/parcel-bundler/parcel/blob/v2/packages/utils/node-resolver-rs
|
||||||
|
|
||||||
|
mod builtins;
|
||||||
mod cache;
|
mod cache;
|
||||||
mod error;
|
mod error;
|
||||||
mod file_system;
|
mod file_system;
|
||||||
|
|
@ -41,6 +42,7 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
builtins::BUILTINS,
|
||||||
cache::{Cache, CachedPath},
|
cache::{Cache, CachedPath},
|
||||||
file_system::FileSystemOs,
|
file_system::FileSystemOs,
|
||||||
package_json::{ExportsField, ExportsKey, MatchObject},
|
package_json::{ExportsField, ExportsKey, MatchObject},
|
||||||
|
|
@ -190,8 +192,11 @@ impl<Fs: FileSystem> ResolverGeneric<Fs> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// require(X) from module at path Y
|
/// require(X) from module at path Y
|
||||||
|
///
|
||||||
/// X: specifier
|
/// X: specifier
|
||||||
/// Y: path
|
/// Y: path
|
||||||
|
///
|
||||||
|
/// <https://nodejs.org/api/modules.html#all-together>
|
||||||
fn require(
|
fn require(
|
||||||
&self,
|
&self,
|
||||||
cached_path: &CachedPath,
|
cached_path: &CachedPath,
|
||||||
|
|
@ -218,9 +223,6 @@ impl<Fs: FileSystem> ResolverGeneric<Fs> {
|
||||||
}
|
}
|
||||||
|
|
||||||
match specifier.as_bytes()[0] {
|
match specifier.as_bytes()[0] {
|
||||||
// 1. If X is a core module,
|
|
||||||
// a. return the core module
|
|
||||||
// b. STOP
|
|
||||||
// 2. If X begins with '/'
|
// 2. If X begins with '/'
|
||||||
// a. set Y to be the file system root
|
// a. set Y to be the file system root
|
||||||
b'/' => self.require_absolute(cached_path, specifier, ctx),
|
b'/' => self.require_absolute(cached_path, specifier, ctx),
|
||||||
|
|
@ -228,12 +230,27 @@ impl<Fs: FileSystem> ResolverGeneric<Fs> {
|
||||||
b'.' => self.require_relative(cached_path, specifier, ctx),
|
b'.' => self.require_relative(cached_path, specifier, ctx),
|
||||||
// 4. If X begins with '#'
|
// 4. If X begins with '#'
|
||||||
b'#' => self.require_hash(cached_path, specifier, ctx),
|
b'#' => self.require_hash(cached_path, specifier, ctx),
|
||||||
|
_ => {
|
||||||
|
// 1. If X is a core module,
|
||||||
|
// a. return the core module
|
||||||
|
// b. STOP
|
||||||
|
self.require_core(specifier)?;
|
||||||
|
|
||||||
// (ESM) 5. Otherwise,
|
// (ESM) 5. Otherwise,
|
||||||
// Note: specifier is now a bare specifier.
|
// Note: specifier is now a bare specifier.
|
||||||
// Set resolved the result of PACKAGE_RESOLVE(specifier, parentURL).
|
// Set resolved the result of PACKAGE_RESOLVE(specifier, parentURL).
|
||||||
_ => self.require_bare(cached_path, specifier, ctx),
|
self.require_bare(cached_path, specifier, ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::unused_self)]
|
||||||
|
fn require_core(&self, specifier: &str) -> Result<(), ResolveError> {
|
||||||
|
if specifier.starts_with("node:") || BUILTINS.binary_search(&specifier).is_ok() {
|
||||||
|
return Err(ResolveError::Builtin(specifier.to_string()));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn require_absolute(
|
fn require_absolute(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
||||||
88
crates/oxc_resolver/src/tests/builtins.rs
Normal file
88
crates/oxc_resolver/src/tests/builtins.rs
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
use crate::{ResolveError, Resolver};
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn builtins() {
|
||||||
|
let resolver = Resolver::default();
|
||||||
|
let f = Path::new("/");
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let pass = [
|
||||||
|
"_http_agent",
|
||||||
|
"_http_client",
|
||||||
|
"_http_common",
|
||||||
|
"_http_incoming",
|
||||||
|
"_http_outgoing",
|
||||||
|
"_http_server",
|
||||||
|
"_stream_duplex",
|
||||||
|
"_stream_passthrough",
|
||||||
|
"_stream_readable",
|
||||||
|
"_stream_transform",
|
||||||
|
"_stream_wrap",
|
||||||
|
"_stream_writable",
|
||||||
|
"_tls_common",
|
||||||
|
"_tls_wrap",
|
||||||
|
"assert",
|
||||||
|
"assert/strict",
|
||||||
|
"async_hooks",
|
||||||
|
"buffer",
|
||||||
|
"child_process",
|
||||||
|
"cluster",
|
||||||
|
"console",
|
||||||
|
"constants",
|
||||||
|
"crypto",
|
||||||
|
"dgram",
|
||||||
|
"diagnostics_channel",
|
||||||
|
"dns",
|
||||||
|
"dns/promises",
|
||||||
|
"domain",
|
||||||
|
"events",
|
||||||
|
"fs",
|
||||||
|
"fs/promises",
|
||||||
|
"http",
|
||||||
|
"http2",
|
||||||
|
"https",
|
||||||
|
"inspector",
|
||||||
|
"module",
|
||||||
|
"net",
|
||||||
|
"os",
|
||||||
|
"path",
|
||||||
|
"path/posix",
|
||||||
|
"path/win32",
|
||||||
|
"perf_hooks",
|
||||||
|
"process",
|
||||||
|
"punycode",
|
||||||
|
"querystring",
|
||||||
|
"readline",
|
||||||
|
"repl",
|
||||||
|
"stream",
|
||||||
|
"stream/consumers",
|
||||||
|
"stream/promises",
|
||||||
|
"stream/web",
|
||||||
|
"string_decoder",
|
||||||
|
"sys",
|
||||||
|
"timers",
|
||||||
|
"timers/promises",
|
||||||
|
"tls",
|
||||||
|
"trace_events",
|
||||||
|
"tty",
|
||||||
|
"url",
|
||||||
|
"util",
|
||||||
|
"util/types",
|
||||||
|
"v8",
|
||||||
|
"vm",
|
||||||
|
"worker_threads",
|
||||||
|
"zlib",
|
||||||
|
];
|
||||||
|
|
||||||
|
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}");
|
||||||
|
}
|
||||||
|
|
||||||
|
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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,7 +16,8 @@ fn extensions() {
|
||||||
("should resolve according to order of provided extensions", "./foo", "foo.ts"),
|
("should resolve according to order of provided extensions", "./foo", "foo.ts"),
|
||||||
("should resolve according to order of provided extensions (dir index)", "./dir", "dir/index.ts"),
|
("should resolve according to order of provided extensions (dir index)", "./dir", "dir/index.ts"),
|
||||||
("should resolve according to main field in module root", ".", "index.js"),
|
("should resolve according to main field in module root", ".", "index.js"),
|
||||||
("should resolve single file module before directory", "module", "node_modules/module.js"),
|
// This is a core module
|
||||||
|
// ("should resolve single file module before directory", "module", "node_modules/module.js"),
|
||||||
("should resolve trailing slash directory before single file", "module/", "node_modules/module/index.ts"),
|
("should resolve trailing slash directory before single file", "module/", "node_modules/module/index.ts"),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
mod alias;
|
mod alias;
|
||||||
mod browser_field;
|
mod browser_field;
|
||||||
|
mod builtins;
|
||||||
mod exports_field;
|
mod exports_field;
|
||||||
mod extension_alias;
|
mod extension_alias;
|
||||||
mod extensions;
|
mod extensions;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue