From 3d8ee2567f428db3013ff580df79bfec37c2de7b Mon Sep 17 00:00:00 2001 From: Boshen Date: Fri, 25 Aug 2023 22:33:10 +0800 Subject: [PATCH] feat(resolver): check for node.js core modules (#794) --- crates/oxc_resolver/benches/resolver.rs | 2 +- crates/oxc_resolver/src/builtins.rs | 69 ++++++++++++++++ crates/oxc_resolver/src/error.rs | 6 ++ crates/oxc_resolver/src/lib.rs | 31 ++++++-- crates/oxc_resolver/src/tests/builtins.rs | 88 +++++++++++++++++++++ crates/oxc_resolver/src/tests/extensions.rs | 3 +- crates/oxc_resolver/src/tests/mod.rs | 1 + 7 files changed, 191 insertions(+), 9 deletions(-) create mode 100644 crates/oxc_resolver/src/builtins.rs create mode 100644 crates/oxc_resolver/src/tests/builtins.rs diff --git a/crates/oxc_resolver/benches/resolver.rs b/crates/oxc_resolver/benches/resolver.rs index 205d54f42..783af777b 100644 --- a/crates/oxc_resolver/benches/resolver.rs +++ b/crates/oxc_resolver/benches/resolver.rs @@ -31,7 +31,7 @@ fn data() -> Vec<(PathBuf, &'static str)> { (cwd.join("test/fixtures"), "m1/a.js?query#fragment"), // extensions (cwd.join("test/fixtures/extensions"), "./foo"), - (cwd.join("test/fixtures/extensions/module"), "module"), + (cwd.join("test/fixtures/extensions/module"), "module/"), // browserField (cwd.join("test/fixtures/browser-module"), "./lib/replaced"), (cwd.join("test/fixtures/browser-module/lib"), "./replaced"), diff --git a/crates/oxc_resolver/src/builtins.rs b/crates/oxc_resolver/src/builtins.rs new file mode 100644 index 000000000..556739502 --- /dev/null +++ b/crates/oxc_resolver/src/builtins.rs @@ -0,0 +1,69 @@ +// +// 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", +]; diff --git a/crates/oxc_resolver/src/error.rs b/crates/oxc_resolver/src/error.rs index c65d3bc10..4bf642eef 100644 --- a/crates/oxc_resolver/src/error.rs +++ b/crates/oxc_resolver/src/error.rs @@ -19,6 +19,12 @@ pub enum ResolveError { /// Path not found 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 ExtensionAlias, diff --git a/crates/oxc_resolver/src/lib.rs b/crates/oxc_resolver/src/lib.rs index 5d3e0758c..0bb9867ba 100644 --- a/crates/oxc_resolver/src/lib.rs +++ b/crates/oxc_resolver/src/lib.rs @@ -15,6 +15,7 @@ //! [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 +mod builtins; mod cache; mod error; mod file_system; @@ -41,6 +42,7 @@ use std::{ }; use crate::{ + builtins::BUILTINS, cache::{Cache, CachedPath}, file_system::FileSystemOs, package_json::{ExportsField, ExportsKey, MatchObject}, @@ -190,8 +192,11 @@ impl ResolverGeneric { } /// require(X) from module at path Y + /// /// X: specifier /// Y: path + /// + /// fn require( &self, cached_path: &CachedPath, @@ -218,9 +223,6 @@ impl ResolverGeneric { } match specifier.as_bytes()[0] { - // 1. If X is a core module, - // a. return the core module - // b. STOP // 2. If X begins with '/' // a. set Y to be the file system root b'/' => self.require_absolute(cached_path, specifier, ctx), @@ -228,13 +230,28 @@ impl ResolverGeneric { b'.' => self.require_relative(cached_path, specifier, ctx), // 4. If X begins with '#' b'#' => self.require_hash(cached_path, specifier, ctx), - // (ESM) 5. Otherwise, - // Note: specifier is now a bare specifier. - // Set resolved the result of PACKAGE_RESOLVE(specifier, parentURL). - _ => self.require_bare(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, + // Note: specifier is now a bare specifier. + // Set resolved the result of PACKAGE_RESOLVE(specifier, parentURL). + 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( &self, cached_path: &CachedPath, diff --git a/crates/oxc_resolver/src/tests/builtins.rs b/crates/oxc_resolver/src/tests/builtins.rs new file mode 100644 index 000000000..55d748c6e --- /dev/null +++ b/crates/oxc_resolver/src/tests/builtins.rs @@ -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}"); + } +} diff --git a/crates/oxc_resolver/src/tests/extensions.rs b/crates/oxc_resolver/src/tests/extensions.rs index 9b3ea81ad..f30f5b4fa 100644 --- a/crates/oxc_resolver/src/tests/extensions.rs +++ b/crates/oxc_resolver/src/tests/extensions.rs @@ -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 (dir index)", "./dir", "dir/index.ts"), ("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"), ]; diff --git a/crates/oxc_resolver/src/tests/mod.rs b/crates/oxc_resolver/src/tests/mod.rs index aad1f9932..e08983285 100644 --- a/crates/oxc_resolver/src/tests/mod.rs +++ b/crates/oxc_resolver/src/tests/mod.rs @@ -1,5 +1,6 @@ mod alias; mod browser_field; +mod builtins; mod exports_field; mod extension_alias; mod extensions;