summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/node/mod.rs90
-rw-r--r--cli/tools/repl/session.rs4
-rw-r--r--cli/worker.rs11
-rw-r--r--ext/node/lib.rs879
-rw-r--r--ext/node/ops.rs573
-rw-r--r--ext/node/polyfill.rs215
6 files changed, 900 insertions, 872 deletions
diff --git a/cli/node/mod.rs b/cli/node/mod.rs
index dffcb7437..c5d3de968 100644
--- a/cli/node/mod.rs
+++ b/cli/node/mod.rs
@@ -13,11 +13,11 @@ use deno_core::anyhow::bail;
use deno_core::anyhow::Context;
use deno_core::error::generic_error;
use deno_core::error::AnyError;
-use deno_core::located_script_name;
use deno_core::serde_json::Value;
use deno_core::url::Url;
-use deno_core::JsRuntime;
+use deno_runtime::deno_node;
use deno_runtime::deno_node::errors;
+use deno_runtime::deno_node::find_builtin_node_module;
use deno_runtime::deno_node::get_closest_package_json;
use deno_runtime::deno_node::legacy_main_resolve;
use deno_runtime::deno_node::package_exports_resolve;
@@ -25,7 +25,6 @@ use deno_runtime::deno_node::package_imports_resolve;
use deno_runtime::deno_node::package_resolve;
use deno_runtime::deno_node::path_to_declaration_path;
use deno_runtime::deno_node::NodeModuleKind;
-use deno_runtime::deno_node::NodeModulePolyfill;
use deno_runtime::deno_node::NodeModulePolyfillSpecifier;
use deno_runtime::deno_node::NodePermissions;
use deno_runtime::deno_node::NodeResolutionMode;
@@ -33,8 +32,6 @@ use deno_runtime::deno_node::PackageJson;
use deno_runtime::deno_node::PathClean;
use deno_runtime::deno_node::RequireNpmResolver;
use deno_runtime::deno_node::DEFAULT_CONDITIONS;
-use deno_runtime::deno_node::NODE_GLOBAL_THIS_NAME;
-use deno_runtime::deno_node::SUPPORTED_BUILTIN_NODE_MODULES;
use deno_runtime::permissions::PermissionsContainer;
use once_cell::sync::Lazy;
use regex::Regex;
@@ -123,16 +120,6 @@ static NODE_COMPAT_URL: Lazy<Url> = Lazy::new(|| {
pub static MODULE_ALL_URL: Lazy<Url> =
Lazy::new(|| NODE_COMPAT_URL.join("node/module_all.ts").unwrap());
-fn find_builtin_node_module(specifier: &str) -> Option<&NodeModulePolyfill> {
- SUPPORTED_BUILTIN_NODE_MODULES
- .iter()
- .find(|m| m.name == specifier)
-}
-
-fn is_builtin_node_module(specifier: &str) -> bool {
- find_builtin_node_module(specifier).is_some()
-}
-
pub fn resolve_builtin_node_module(specifier: &str) -> Result<Url, AnyError> {
if let Some(module) = find_builtin_node_module(specifier) {
match module.specifier {
@@ -203,49 +190,6 @@ static RESERVED_WORDS: Lazy<HashSet<&str>> = Lazy::new(|| {
])
});
-pub async fn initialize_runtime(
- js_runtime: &mut JsRuntime,
- uses_local_node_modules_dir: bool,
-) -> Result<(), AnyError> {
- let source_code = &format!(
- r#"(async function loadBuiltinNodeModules(moduleAllUrl, nodeGlobalThisName, usesLocalNodeModulesDir) {{
- const moduleAll = await import(moduleAllUrl);
- Deno[Deno.internal].node.initialize(moduleAll.default, nodeGlobalThisName);
- if (usesLocalNodeModulesDir) {{
- Deno[Deno.internal].require.setUsesLocalNodeModulesDir();
- }}
- }})('{}', '{}', {});"#,
- MODULE_ALL_URL.as_str(),
- NODE_GLOBAL_THIS_NAME.as_str(),
- uses_local_node_modules_dir,
- );
-
- let value =
- js_runtime.execute_script(&located_script_name!(), source_code)?;
- js_runtime.resolve_value(value).await?;
- Ok(())
-}
-
-pub async fn initialize_binary_command(
- js_runtime: &mut JsRuntime,
- binary_name: &str,
-) -> Result<(), AnyError> {
- // overwrite what's done in deno_std in order to set the binary arg name
- let source_code = &format!(
- r#"(async function initializeBinaryCommand(binaryName) {{
- const process = Deno[Deno.internal].node.globalThis.process;
- Object.defineProperty(process.argv, "0", {{
- get: () => binaryName,
- }});
- }})('{binary_name}');"#,
- );
-
- let value =
- js_runtime.execute_script(&located_script_name!(), source_code)?;
- js_runtime.resolve_value(value).await?;
- Ok(())
-}
-
/// This function is an implementation of `defaultResolve` in
/// `lib/internal/modules/esm/resolve.js` from Node.
pub fn node_resolve(
@@ -258,7 +202,7 @@ pub fn node_resolve(
// Note: if we are here, then the referrer is an esm module
// TODO(bartlomieju): skipped "policy" part as we don't plan to support it
- if is_builtin_node_module(specifier) {
+ if deno_node::is_builtin_node_module(specifier) {
return Ok(Some(NodeResolution::BuiltIn(specifier.to_string())));
}
@@ -273,7 +217,7 @@ pub fn node_resolve(
let split_specifier = url.as_str().split(':');
let specifier = split_specifier.skip(1).collect::<String>();
- if is_builtin_node_module(&specifier) {
+ if deno_node::is_builtin_node_module(&specifier) {
return Ok(Some(NodeResolution::BuiltIn(specifier)));
}
}
@@ -451,32 +395,6 @@ fn resolve_bin_entry_value<'a>(
}
}
-pub fn load_cjs_module_from_ext_node(
- js_runtime: &mut JsRuntime,
- module: &str,
- main: bool,
- inspect_brk: bool,
-) -> Result<(), AnyError> {
- fn escape_for_single_quote_string(text: &str) -> String {
- text.replace('\\', r"\\").replace('\'', r"\'")
- }
-
- let source_code = &format!(
- r#"(function loadCjsModule(module, inspectBrk) {{
- if (inspectBrk) {{
- Deno[Deno.internal].require.setInspectBrk();
- }}
- Deno[Deno.internal].require.Module._load(module, null, {main});
- }})('{module}', {inspect_brk});"#,
- main = main,
- module = escape_for_single_quote_string(module),
- inspect_brk = inspect_brk,
- );
-
- js_runtime.execute_script(&located_script_name!(), source_code)?;
- Ok(())
-}
-
fn package_config_resolve(
package_subpath: &str,
package_dir: &Path,
diff --git a/cli/tools/repl/session.rs b/cli/tools/repl/session.rs
index 0e40a1e32..18b40a1b9 100644
--- a/cli/tools/repl/session.rs
+++ b/cli/tools/repl/session.rs
@@ -19,6 +19,7 @@ use deno_core::serde_json;
use deno_core::serde_json::Value;
use deno_core::LocalInspectorSession;
use deno_graph::source::Resolver;
+use deno_runtime::deno_node;
use deno_runtime::worker::MainWorker;
use super::cdp;
@@ -460,8 +461,9 @@ impl ReplSession {
if !npm_imports.is_empty() || has_node_specifier {
if !self.has_initialized_node_runtime {
self.proc_state.prepare_node_std_graph().await?;
- crate::node::initialize_runtime(
+ deno_node::initialize_runtime(
&mut self.worker.js_runtime,
+ crate::node::MODULE_ALL_URL.as_str(),
self.proc_state.options.node_modules_dir(),
)
.await?;
diff --git a/cli/worker.rs b/cli/worker.rs
index 60663ebc0..3748c007b 100644
--- a/cli/worker.rs
+++ b/cli/worker.rs
@@ -15,6 +15,7 @@ use deno_core::v8;
use deno_core::Extension;
use deno_core::ModuleId;
use deno_runtime::colors;
+use deno_runtime::deno_node;
use deno_runtime::fmt_errors::format_js_error;
use deno_runtime::ops::worker_host::CreateWebWorkerCb;
use deno_runtime::ops::worker_host::WorkerEventCb;
@@ -68,7 +69,7 @@ impl CliMainWorker {
if self.is_main_cjs {
self.ps.prepare_node_std_graph().await?;
self.initialize_main_module_for_node().await?;
- node::load_cjs_module_from_ext_node(
+ deno_node::load_cjs_module(
&mut self.worker.js_runtime,
&self.main_module.to_file_path().unwrap().to_string_lossy(),
true,
@@ -304,8 +305,9 @@ impl CliMainWorker {
async fn initialize_main_module_for_node(&mut self) -> Result<(), AnyError> {
self.ps.prepare_node_std_graph().await?;
- node::initialize_runtime(
+ deno_node::initialize_runtime(
&mut self.worker.js_runtime,
+ node::MODULE_ALL_URL.as_str(),
self.ps.options.node_modules_dir(),
)
.await?;
@@ -317,7 +319,7 @@ impl CliMainWorker {
.sub_path
.as_deref()
.unwrap_or(pkg_ref.req.name.as_str());
- node::initialize_binary_command(
+ deno_node::initialize_binary_command(
&mut self.worker.js_runtime,
binary_name,
)
@@ -626,8 +628,9 @@ fn create_web_worker_pre_execute_module_callback(
let fut = async move {
// this will be up to date after pre-load
if ps.npm_resolver.has_packages() {
- node::initialize_runtime(
+ deno_node::initialize_runtime(
&mut worker.js_runtime,
+ node::MODULE_ALL_URL.as_str(),
ps.options.node_modules_dir(),
)
.await?;
diff --git a/ext/node/lib.rs b/ext/node/lib.rs
index fc506b3b3..aabbbfafd 100644
--- a/ext/node/lib.rs
+++ b/ext/node/lib.rs
@@ -1,14 +1,10 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
-use deno_core::error::generic_error;
use deno_core::error::AnyError;
use deno_core::include_js_files;
-use deno_core::normalize_path;
-use deno_core::op;
-use deno_core::url::Url;
+use deno_core::located_script_name;
use deno_core::Extension;
-use deno_core::JsRuntimeInspector;
-use deno_core::OpState;
+use deno_core::JsRuntime;
use once_cell::sync::Lazy;
use std::collections::HashSet;
use std::path::Path;
@@ -16,12 +12,19 @@ use std::path::PathBuf;
use std::rc::Rc;
pub mod errors;
+mod ops;
mod package_json;
mod path;
+mod polyfill;
mod resolution;
pub use package_json::PackageJson;
pub use path::PathClean;
+pub use polyfill::find_builtin_node_module;
+pub use polyfill::is_builtin_node_module;
+pub use polyfill::NodeModulePolyfill;
+pub use polyfill::NodeModulePolyfillSpecifier;
+pub use polyfill::SUPPORTED_BUILTIN_NODE_MODULES;
pub use resolution::get_closest_package_json;
pub use resolution::get_package_scope_config;
pub use resolution::legacy_main_resolve;
@@ -32,7 +35,6 @@ pub use resolution::path_to_declaration_path;
pub use resolution::NodeModuleKind;
pub use resolution::NodeResolutionMode;
pub use resolution::DEFAULT_CONDITIONS;
-use std::cell::RefCell;
pub trait NodePermissions {
fn check_read(&mut self, path: &Path) -> Result<(), AnyError>;
@@ -91,28 +93,28 @@ pub fn init<P: NodePermissions + 'static>(
"module_es_shim.js",
))
.ops(vec![
- op_require_init_paths::decl(),
- op_require_node_module_paths::decl::<P>(),
- op_require_proxy_path::decl(),
- op_require_is_deno_dir_package::decl(),
- op_require_resolve_deno_dir::decl(),
- op_require_is_request_relative::decl(),
- op_require_resolve_lookup_paths::decl(),
- op_require_try_self_parent_path::decl::<P>(),
- op_require_try_self::decl::<P>(),
- op_require_real_path::decl::<P>(),
- op_require_path_is_absolute::decl(),
- op_require_path_dirname::decl(),
- op_require_stat::decl::<P>(),
- op_require_path_resolve::decl(),
- op_require_path_basename::decl(),
- op_require_read_file::decl::<P>(),
- op_require_as_file_path::decl(),
- op_require_resolve_exports::decl::<P>(),
- op_require_read_closest_package_json::decl::<P>(),
- op_require_read_package_scope::decl::<P>(),
- op_require_package_imports_resolve::decl::<P>(),
- op_require_break_on_next_statement::decl(),
+ ops::op_require_init_paths::decl(),
+ ops::op_require_node_module_paths::decl::<P>(),
+ ops::op_require_proxy_path::decl(),
+ ops::op_require_is_deno_dir_package::decl(),
+ ops::op_require_resolve_deno_dir::decl(),
+ ops::op_require_is_request_relative::decl(),
+ ops::op_require_resolve_lookup_paths::decl(),
+ ops::op_require_try_self_parent_path::decl::<P>(),
+ ops::op_require_try_self::decl::<P>(),
+ ops::op_require_real_path::decl::<P>(),
+ ops::op_require_path_is_absolute::decl(),
+ ops::op_require_path_dirname::decl(),
+ ops::op_require_stat::decl::<P>(),
+ ops::op_require_path_resolve::decl(),
+ ops::op_require_path_basename::decl(),
+ ops::op_require_read_file::decl::<P>(),
+ ops::op_require_as_file_path::decl(),
+ ops::op_require_resolve_exports::decl::<P>(),
+ ops::op_require_read_closest_package_json::decl::<P>(),
+ ops::op_require_read_package_scope::decl::<P>(),
+ ops::op_require_package_imports_resolve::decl::<P>(),
+ ops::op_require_break_on_next_statement::decl(),
])
.state(move |state| {
if let Some(npm_resolver) = maybe_npm_resolver.clone() {
@@ -123,757 +125,72 @@ pub fn init<P: NodePermissions + 'static>(
.build()
}
-fn ensure_read_permission<P>(
- state: &mut OpState,
- file_path: &Path,
-) -> Result<(), AnyError>
-where
- P: NodePermissions + 'static,
-{
- let resolver = {
- let resolver = state.borrow::<Rc<dyn RequireNpmResolver>>();
- resolver.clone()
- };
- let permissions = state.borrow_mut::<P>();
- resolver.ensure_read_permission(permissions, file_path)
-}
-
-#[op]
-pub fn op_require_init_paths() -> Vec<String> {
- // todo(dsherret): this code is node compat mode specific and
- // we probably don't want it for small mammal, so ignore it for now
-
- // let (home_dir, node_path) = if cfg!(windows) {
- // (
- // std::env::var("USERPROFILE").unwrap_or_else(|_| "".into()),
- // std::env::var("NODE_PATH").unwrap_or_else(|_| "".into()),
- // )
- // } else {
- // (
- // std::env::var("HOME").unwrap_or_else(|_| "".into()),
- // std::env::var("NODE_PATH").unwrap_or_else(|_| "".into()),
- // )
- // };
-
- // let mut prefix_dir = std::env::current_exe().unwrap();
- // if cfg!(windows) {
- // prefix_dir = prefix_dir.join("..").join("..")
- // } else {
- // prefix_dir = prefix_dir.join("..")
- // }
-
- // let mut paths = vec![prefix_dir.join("lib").join("node")];
-
- // if !home_dir.is_empty() {
- // paths.insert(0, PathBuf::from(&home_dir).join(".node_libraries"));
- // paths.insert(0, PathBuf::from(&home_dir).join(".nod_modules"));
- // }
-
- // let mut paths = paths
- // .into_iter()
- // .map(|p| p.to_string_lossy().to_string())
- // .collect();
-
- // if !node_path.is_empty() {
- // let delimiter = if cfg!(windows) { ";" } else { ":" };
- // let mut node_paths: Vec<String> = node_path
- // .split(delimiter)
- // .filter(|e| !e.is_empty())
- // .map(|s| s.to_string())
- // .collect();
- // node_paths.append(&mut paths);
- // paths = node_paths;
- // }
-
- vec![]
-}
-
-#[op]
-pub fn op_require_node_module_paths<P>(
- state: &mut OpState,
- from: String,
-) -> Result<Vec<String>, AnyError>
-where
- P: NodePermissions + 'static,
-{
- // Guarantee that "from" is absolute.
- let from = deno_core::resolve_path(&from)
- .unwrap()
- .to_file_path()
- .unwrap();
-
- ensure_read_permission::<P>(state, &from)?;
-
- if cfg!(windows) {
- // return root node_modules when path is 'D:\\'.
- let from_str = from.to_str().unwrap();
- if from_str.len() >= 3 {
- let bytes = from_str.as_bytes();
- if bytes[from_str.len() - 1] == b'\\' && bytes[from_str.len() - 2] == b':'
- {
- let p = from_str.to_owned() + "node_modules";
- return Ok(vec![p]);
- }
- }
- } else {
- // Return early not only to avoid unnecessary work, but to *avoid* returning
- // an array of two items for a root: [ '//node_modules', '/node_modules' ]
- if from.to_string_lossy() == "/" {
- return Ok(vec!["/node_modules".to_string()]);
- }
- }
-
- let mut paths = vec![];
- let mut current_path = from.as_path();
- let mut maybe_parent = Some(current_path);
- while let Some(parent) = maybe_parent {
- if !parent.ends_with("/node_modules") {
- paths.push(parent.join("node_modules").to_string_lossy().to_string());
- current_path = parent;
- maybe_parent = current_path.parent();
- }
- }
-
- if !cfg!(windows) {
- // Append /node_modules to handle root paths.
- paths.push("/node_modules".to_string());
- }
-
- Ok(paths)
-}
-
-#[op]
-fn op_require_proxy_path(filename: String) -> String {
- // Allow a directory to be passed as the filename
- let trailing_slash = if cfg!(windows) {
- // Node also counts a trailing forward slash as a
- // directory for node on Windows, but not backslashes
- // on non-Windows platforms
- filename.ends_with('\\') || filename.ends_with('/')
- } else {
- filename.ends_with('/')
- };
-
- if trailing_slash {
- let p = PathBuf::from(filename);
- p.join("noop.js").to_string_lossy().to_string()
- } else {
- filename
- }
-}
-
-#[op]
-fn op_require_is_request_relative(request: String) -> bool {
- if request.starts_with("./") || request.starts_with("../") || request == ".."
- {
- return true;
- }
-
- if cfg!(windows) {
- if request.starts_with(".\\") {
- return true;
- }
-
- if request.starts_with("..\\") {
- return true;
- }
- }
-
- false
-}
-
-#[op]
-fn op_require_resolve_deno_dir(
- state: &mut OpState,
- request: String,
- parent_filename: String,
-) -> Option<String> {
- let resolver = state.borrow::<Rc<dyn RequireNpmResolver>>();
- resolver
- .resolve_package_folder_from_package(
- &request,
- &PathBuf::from(parent_filename),
- NodeResolutionMode::Execution,
- )
- .ok()
- .map(|p| p.to_string_lossy().to_string())
-}
-
-#[op]
-fn op_require_is_deno_dir_package(state: &mut OpState, path: String) -> bool {
- let resolver = state.borrow::<Rc<dyn RequireNpmResolver>>();
- resolver.in_npm_package(&PathBuf::from(path))
-}
-
-#[op]
-fn op_require_resolve_lookup_paths(
- request: String,
- maybe_parent_paths: Option<Vec<String>>,
- parent_filename: String,
-) -> Option<Vec<String>> {
- if !request.starts_with('.')
- || (request.len() > 1
- && !request.starts_with("..")
- && !request.starts_with("./")
- && (!cfg!(windows) || !request.starts_with(".\\")))
- {
- let module_paths = vec![];
- let mut paths = module_paths;
- if let Some(mut parent_paths) = maybe_parent_paths {
- if !parent_paths.is_empty() {
- paths.append(&mut parent_paths);
- }
- }
-
- if !paths.is_empty() {
- return Some(paths);
- } else {
- return None;
- }
- }
-
- // In REPL, parent.filename is null.
- // if (!parent || !parent.id || !parent.filename) {
- // // Make require('./path/to/foo') work - normally the path is taken
- // // from realpath(__filename) but in REPL there is no filename
- // const mainPaths = ['.'];
-
- // debug('looking for %j in %j', request, mainPaths);
- // return mainPaths;
- // }
-
- let p = PathBuf::from(parent_filename);
- Some(vec![p.parent().unwrap().to_string_lossy().to_string()])
-}
-
-#[op]
-fn op_require_path_is_absolute(p: String) -> bool {
- PathBuf::from(p).is_absolute()
-}
-
-#[op]
-fn op_require_stat<P>(
- state: &mut OpState,
- path: String,
-) -> Result<i32, AnyError>
-where
- P: NodePermissions + 'static,
-{
- let path = PathBuf::from(path);
- ensure_read_permission::<P>(state, &path)?;
- if let Ok(metadata) = std::fs::metadata(&path) {
- if metadata.is_file() {
- return Ok(0);
- } else {
- return Ok(1);
- }
- }
-
- Ok(-1)
-}
-
-#[op]
-fn op_require_real_path<P>(
- state: &mut OpState,
- request: String,
-) -> Result<String, AnyError>
-where
- P: NodePermissions + 'static,
-{
- let path = PathBuf::from(request);
- ensure_read_permission::<P>(state, &path)?;
- let mut canonicalized_path = path.canonicalize()?;
- if cfg!(windows) {
- canonicalized_path = PathBuf::from(
- canonicalized_path
- .display()
- .to_string()
- .trim_start_matches("\\\\?\\"),
- );
- }
- Ok(canonicalized_path.to_string_lossy().to_string())
-}
-
-fn path_resolve(parts: Vec<String>) -> String {
- assert!(!parts.is_empty());
- let mut p = PathBuf::from(&parts[0]);
- if parts.len() > 1 {
- for part in &parts[1..] {
- p = p.join(part);
- }
- }
- normalize_path(p).to_string_lossy().to_string()
-}
-
-#[op]
-fn op_require_path_resolve(parts: Vec<String>) -> String {
- path_resolve(parts)
-}
-
-#[op]
-fn op_require_path_dirname(request: String) -> Result<String, AnyError> {
- let p = PathBuf::from(request);
- if let Some(parent) = p.parent() {
- Ok(parent.to_string_lossy().to_string())
- } else {
- Err(generic_error("Path doesn't have a parent"))
- }
-}
-
-#[op]
-fn op_require_path_basename(request: String) -> Result<String, AnyError> {
- let p = PathBuf::from(request);
- if let Some(path) = p.file_name() {
- Ok(path.to_string_lossy().to_string())
- } else {
- Err(generic_error("Path doesn't have a file name"))
- }
-}
-
-#[op]
-fn op_require_try_self_parent_path<P>(
- state: &mut OpState,
- has_parent: bool,
- maybe_parent_filename: Option<String>,
- maybe_parent_id: Option<String>,
-) -> Result<Option<String>, AnyError>
-where
- P: NodePermissions + 'static,
-{
- if !has_parent {
- return Ok(None);
- }
-
- if let Some(parent_filename) = maybe_parent_filename {
- return Ok(Some(parent_filename));
- }
-
- if let Some(parent_id) = maybe_parent_id {
- if parent_id == "<repl>" || parent_id == "internal/preload" {
- if let Ok(cwd) = std::env::current_dir() {
- ensure_read_permission::<P>(state, &cwd)?;
- return Ok(Some(cwd.to_string_lossy().to_string()));
- }
- }
- }
- Ok(None)
-}
-
-#[op]
-fn op_require_try_self<P>(
- state: &mut OpState,
- parent_path: Option<String>,
- request: String,
-) -> Result<Option<String>, AnyError>
-where
- P: NodePermissions + 'static,
-{
- if parent_path.is_none() {
- return Ok(None);
- }
-
- let resolver = state.borrow::<Rc<dyn RequireNpmResolver>>().clone();
- let permissions = state.borrow_mut::<P>();
- let pkg = resolution::get_package_scope_config(
- &Url::from_file_path(parent_path.unwrap()).unwrap(),
- &*resolver,
- permissions,
- )
- .ok();
- if pkg.is_none() {
- return Ok(None);
- }
-
- let pkg = pkg.unwrap();
- if pkg.exports.is_none() {
- return Ok(None);
- }
- if pkg.name.is_none() {
- return Ok(None);
- }
-
- let pkg_name = pkg.name.as_ref().unwrap().to_string();
- let mut expansion = ".".to_string();
-
- if request == pkg_name {
- // pass
- } else if request.starts_with(&format!("{pkg_name}/")) {
- expansion += &request[pkg_name.len()..];
- } else {
- return Ok(None);
- }
-
- let referrer = deno_core::url::Url::from_file_path(&pkg.path).unwrap();
- if let Some(exports) = &pkg.exports {
- resolution::package_exports_resolve(
- &pkg.path,
- expansion,
- exports,
- &referrer,
- NodeModuleKind::Cjs,
- resolution::REQUIRE_CONDITIONS,
- NodeResolutionMode::Execution,
- &*resolver,
- permissions,
- )
- .map(|r| Some(r.to_string_lossy().to_string()))
- } else {
- Ok(None)
- }
-}
-
-#[op]
-fn op_require_read_file<P>(
- state: &mut OpState,
- file_path: String,
-) -> Result<String, AnyError>
-where
- P: NodePermissions + 'static,
-{
- let file_path = PathBuf::from(file_path);
- ensure_read_permission::<P>(state, &file_path)?;
- Ok(std::fs::read_to_string(file_path)?)
-}
-
-#[op]
-pub fn op_require_as_file_path(file_or_url: String) -> String {
- if let Ok(url) = Url::parse(&file_or_url) {
- if let Ok(p) = url.to_file_path() {
- return p.to_string_lossy().to_string();
- }
- }
-
- file_or_url
-}
-
-#[op]
-fn op_require_resolve_exports<P>(
- state: &mut OpState,
+pub async fn initialize_runtime(
+ js_runtime: &mut JsRuntime,
+ module_all_url: &str,
uses_local_node_modules_dir: bool,
- modules_path: String,
- _request: String,
- name: String,
- expansion: String,
- parent_path: String,
-) -> Result<Option<String>, AnyError>
-where
- P: NodePermissions + 'static,
-{
- let resolver = state.borrow::<Rc<dyn RequireNpmResolver>>().clone();
- let permissions = state.borrow_mut::<P>();
-
- let pkg_path = if resolver.in_npm_package(&PathBuf::from(&modules_path))
- && !uses_local_node_modules_dir
- {
- modules_path
- } else {
- path_resolve(vec![modules_path, name])
- };
- let pkg = PackageJson::load(
- &*resolver,
- permissions,
- PathBuf::from(&pkg_path).join("package.json"),
- )?;
-
- if let Some(exports) = &pkg.exports {
- let referrer = Url::from_file_path(parent_path).unwrap();
- resolution::package_exports_resolve(
- &pkg.path,
- format!(".{expansion}"),
- exports,
- &referrer,
- NodeModuleKind::Cjs,
- resolution::REQUIRE_CONDITIONS,
- NodeResolutionMode::Execution,
- &*resolver,
- permissions,
- )
- .map(|r| Some(r.to_string_lossy().to_string()))
- } else {
- Ok(None)
- }
-}
-
-#[op]
-fn op_require_read_closest_package_json<P>(
- state: &mut OpState,
- filename: String,
-) -> Result<PackageJson, AnyError>
-where
- P: NodePermissions + 'static,
-{
- ensure_read_permission::<P>(
- state,
- PathBuf::from(&filename).parent().unwrap(),
- )?;
- let resolver = state.borrow::<Rc<dyn RequireNpmResolver>>().clone();
- let permissions = state.borrow_mut::<P>();
- resolution::get_closest_package_json(
- &Url::from_file_path(filename).unwrap(),
- &*resolver,
- permissions,
- )
+) -> Result<(), AnyError> {
+ let source_code = &format!(
+ r#"(async function loadBuiltinNodeModules(moduleAllUrl, nodeGlobalThisName, usesLocalNodeModulesDir) {{
+ const moduleAll = await import(moduleAllUrl);
+ Deno[Deno.internal].node.initialize(moduleAll.default, nodeGlobalThisName);
+ if (usesLocalNodeModulesDir) {{
+ Deno[Deno.internal].require.setUsesLocalNodeModulesDir();
+ }}
+ }})('{}', '{}', {});"#,
+ module_all_url,
+ NODE_GLOBAL_THIS_NAME.as_str(),
+ uses_local_node_modules_dir,
+ );
+
+ let value =
+ js_runtime.execute_script(&located_script_name!(), source_code)?;
+ js_runtime.resolve_value(value).await?;
+ Ok(())
+}
+
+pub fn load_cjs_module(
+ js_runtime: &mut JsRuntime,
+ module: &str,
+ main: bool,
+ inspect_brk: bool,
+) -> Result<(), AnyError> {
+ fn escape_for_single_quote_string(text: &str) -> String {
+ text.replace('\\', r"\\").replace('\'', r"\'")
+ }
+
+ let source_code = &format!(
+ r#"(function loadCjsModule(module, inspectBrk) {{
+ if (inspectBrk) {{
+ Deno[Deno.internal].require.setInspectBrk();
+ }}
+ Deno[Deno.internal].require.Module._load(module, null, {main});
+ }})('{module}', {inspect_brk});"#,
+ main = main,
+ module = escape_for_single_quote_string(module),
+ inspect_brk = inspect_brk,
+ );
+
+ js_runtime.execute_script(&located_script_name!(), source_code)?;
+ Ok(())
+}
+
+pub async fn initialize_binary_command(
+ js_runtime: &mut JsRuntime,
+ binary_name: &str,
+) -> Result<(), AnyError> {
+ // overwrite what's done in deno_std in order to set the binary arg name
+ let source_code = &format!(
+ r#"(async function initializeBinaryCommand(binaryName) {{
+ const process = Deno[Deno.internal].node.globalThis.process;
+ Object.defineProperty(process.argv, "0", {{
+ get: () => binaryName,
+ }});
+ }})('{binary_name}');"#,
+ );
+
+ let value =
+ js_runtime.execute_script(&located_script_name!(), source_code)?;
+ js_runtime.resolve_value(value).await?;
+ Ok(())
}
-
-#[op]
-fn op_require_read_package_scope<P>(
- state: &mut OpState,
- package_json_path: String,
-) -> Option<PackageJson>
-where
- P: NodePermissions + 'static,
-{
- let resolver = state.borrow::<Rc<dyn RequireNpmResolver>>().clone();
- let permissions = state.borrow_mut::<P>();
- let package_json_path = PathBuf::from(package_json_path);
- PackageJson::load(&*resolver, permissions, package_json_path).ok()
-}
-
-#[op]
-fn op_require_package_imports_resolve<P>(
- state: &mut OpState,
- parent_filename: String,
- request: String,
-) -> Result<Option<String>, AnyError>
-where
- P: NodePermissions + 'static,
-{
- let parent_path = PathBuf::from(&parent_filename);
- ensure_read_permission::<P>(state, &parent_path)?;
- let resolver = state.borrow::<Rc<dyn RequireNpmResolver>>().clone();
- let permissions = state.borrow_mut::<P>();
- let pkg = PackageJson::load(
- &*resolver,
- permissions,
- parent_path.join("package.json"),
- )?;
-
- if pkg.imports.is_some() {
- let referrer =
- deno_core::url::Url::from_file_path(&parent_filename).unwrap();
- let r = resolution::package_imports_resolve(
- &request,
- &referrer,
- NodeModuleKind::Cjs,
- resolution::REQUIRE_CONDITIONS,
- NodeResolutionMode::Execution,
- &*resolver,
- permissions,
- )
- .map(|r| Some(Url::from_file_path(r).unwrap().to_string()));
- state.put(resolver);
- r
- } else {
- Ok(None)
- }
-}
-
-#[op]
-fn op_require_break_on_next_statement(state: &mut OpState) {
- let inspector = state.borrow::<Rc<RefCell<JsRuntimeInspector>>>();
- inspector
- .borrow_mut()
- .wait_for_session_and_break_on_next_statement()
-}
-
-pub enum NodeModulePolyfillSpecifier {
- /// An internal module specifier, like "internal:deno_node/assert.ts". The
- /// module must be either embedded in the binary or snapshotted.
- Embedded(&'static str),
-
- /// Specifier relative to the root of `deno_std` repo, like "node/assert.ts"
- StdNode(&'static str),
-}
-
-pub struct NodeModulePolyfill {
- /// Name of the module like "assert" or "timers/promises"
- pub name: &'static str,
- pub specifier: NodeModulePolyfillSpecifier,
-}
-
-pub static SUPPORTED_BUILTIN_NODE_MODULES: &[NodeModulePolyfill] = &[
- NodeModulePolyfill {
- name: "assert",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/assert.ts"),
- },
- NodeModulePolyfill {
- name: "assert/strict",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/assert/strict.ts"),
- },
- NodeModulePolyfill {
- name: "async_hooks",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/async_hooks.ts"),
- },
- NodeModulePolyfill {
- name: "buffer",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/buffer.ts"),
- },
- NodeModulePolyfill {
- name: "child_process",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/child_process.ts"),
- },
- NodeModulePolyfill {
- name: "cluster",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/cluster.ts"),
- },
- NodeModulePolyfill {
- name: "console",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/console.ts"),
- },
- NodeModulePolyfill {
- name: "constants",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/constants.ts"),
- },
- NodeModulePolyfill {
- name: "crypto",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/crypto.ts"),
- },
- NodeModulePolyfill {
- name: "dgram",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/dgram.ts"),
- },
- NodeModulePolyfill {
- name: "dns",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/dns.ts"),
- },
- NodeModulePolyfill {
- name: "dns/promises",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/dns/promises.ts"),
- },
- NodeModulePolyfill {
- name: "domain",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/domain.ts"),
- },
- NodeModulePolyfill {
- name: "events",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/events.ts"),
- },
- NodeModulePolyfill {
- name: "fs",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/fs.ts"),
- },
- NodeModulePolyfill {
- name: "fs/promises",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/fs/promises.ts"),
- },
- NodeModulePolyfill {
- name: "http",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/http.ts"),
- },
- NodeModulePolyfill {
- name: "https",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/https.ts"),
- },
- NodeModulePolyfill {
- name: "module",
- specifier: NodeModulePolyfillSpecifier::Embedded(
- "internal:deno_node/module_es_shim.js",
- ),
- },
- NodeModulePolyfill {
- name: "net",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/net.ts"),
- },
- NodeModulePolyfill {
- name: "os",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/os.ts"),
- },
- NodeModulePolyfill {
- name: "path",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/path.ts"),
- },
- NodeModulePolyfill {
- name: "path/posix",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/path/posix.ts"),
- },
- NodeModulePolyfill {
- name: "path/win32",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/path/win32.ts"),
- },
- NodeModulePolyfill {
- name: "perf_hooks",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/perf_hooks.ts"),
- },
- NodeModulePolyfill {
- name: "process",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/process.ts"),
- },
- NodeModulePolyfill {
- name: "querystring",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/querystring.ts"),
- },
- NodeModulePolyfill {
- name: "readline",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/readline.ts"),
- },
- NodeModulePolyfill {
- name: "stream",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/stream.ts"),
- },
- NodeModulePolyfill {
- name: "stream/consumers",
- specifier: NodeModulePolyfillSpecifier::StdNode(
- "node/stream/consumers.mjs",
- ),
- },
- NodeModulePolyfill {
- name: "stream/promises",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/stream/promises.mjs"),
- },
- NodeModulePolyfill {
- name: "stream/web",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/stream/web.ts"),
- },
- NodeModulePolyfill {
- name: "string_decoder",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/string_decoder.ts"),
- },
- NodeModulePolyfill {
- name: "sys",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/sys.ts"),
- },
- NodeModulePolyfill {
- name: "timers",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/timers.ts"),
- },
- NodeModulePolyfill {
- name: "timers/promises",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/timers/promises.ts"),
- },
- NodeModulePolyfill {
- name: "tls",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/tls.ts"),
- },
- NodeModulePolyfill {
- name: "tty",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/tty.ts"),
- },
- NodeModulePolyfill {
- name: "url",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/url.ts"),
- },
- NodeModulePolyfill {
- name: "util",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/util.ts"),
- },
- NodeModulePolyfill {
- name: "util/types",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/util/types.ts"),
- },
- NodeModulePolyfill {
- name: "v8",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/v8.ts"),
- },
- NodeModulePolyfill {
- name: "vm",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/vm.ts"),
- },
- NodeModulePolyfill {
- name: "worker_threads",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/worker_threads.ts"),
- },
- NodeModulePolyfill {
- name: "zlib",
- specifier: NodeModulePolyfillSpecifier::StdNode("node/zlib.ts"),
- },
-];
diff --git a/ext/node/ops.rs b/ext/node/ops.rs
new file mode 100644
index 000000000..046578ca5
--- /dev/null
+++ b/ext/node/ops.rs
@@ -0,0 +1,573 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::generic_error;
+use deno_core::error::AnyError;
+use deno_core::normalize_path;
+use deno_core::op;
+use deno_core::url::Url;
+use deno_core::JsRuntimeInspector;
+use deno_core::OpState;
+use std::cell::RefCell;
+use std::path::Path;
+use std::path::PathBuf;
+use std::rc::Rc;
+
+use super::resolution;
+use super::NodeModuleKind;
+use super::NodePermissions;
+use super::NodeResolutionMode;
+use super::PackageJson;
+use super::RequireNpmResolver;
+
+fn ensure_read_permission<P>(
+ state: &mut OpState,
+ file_path: &Path,
+) -> Result<(), AnyError>
+where
+ P: NodePermissions + 'static,
+{
+ let resolver = {
+ let resolver = state.borrow::<Rc<dyn RequireNpmResolver>>();
+ resolver.clone()
+ };
+ let permissions = state.borrow_mut::<P>();
+ resolver.ensure_read_permission(permissions, file_path)
+}
+
+#[op]
+pub fn op_require_init_paths() -> Vec<String> {
+ // todo(dsherret): this code is node compat mode specific and
+ // we probably don't want it for small mammal, so ignore it for now
+
+ // let (home_dir, node_path) = if cfg!(windows) {
+ // (
+ // std::env::var("USERPROFILE").unwrap_or_else(|_| "".into()),
+ // std::env::var("NODE_PATH").unwrap_or_else(|_| "".into()),
+ // )
+ // } else {
+ // (
+ // std::env::var("HOME").unwrap_or_else(|_| "".into()),
+ // std::env::var("NODE_PATH").unwrap_or_else(|_| "".into()),
+ // )
+ // };
+
+ // let mut prefix_dir = std::env::current_exe().unwrap();
+ // if cfg!(windows) {
+ // prefix_dir = prefix_dir.join("..").join("..")
+ // } else {
+ // prefix_dir = prefix_dir.join("..")
+ // }
+
+ // let mut paths = vec![prefix_dir.join("lib").join("node")];
+
+ // if !home_dir.is_empty() {
+ // paths.insert(0, PathBuf::from(&home_dir).join(".node_libraries"));
+ // paths.insert(0, PathBuf::from(&home_dir).join(".nod_modules"));
+ // }
+
+ // let mut paths = paths
+ // .into_iter()
+ // .map(|p| p.to_string_lossy().to_string())
+ // .collect();
+
+ // if !node_path.is_empty() {
+ // let delimiter = if cfg!(windows) { ";" } else { ":" };
+ // let mut node_paths: Vec<String> = node_path
+ // .split(delimiter)
+ // .filter(|e| !e.is_empty())
+ // .map(|s| s.to_string())
+ // .collect();
+ // node_paths.append(&mut paths);
+ // paths = node_paths;
+ // }
+
+ vec![]
+}
+
+#[op]
+pub fn op_require_node_module_paths<P>(
+ state: &mut OpState,
+ from: String,
+) -> Result<Vec<String>, AnyError>
+where
+ P: NodePermissions + 'static,
+{
+ // Guarantee that "from" is absolute.
+ let from = deno_core::resolve_path(&from)
+ .unwrap()
+ .to_file_path()
+ .unwrap();
+
+ ensure_read_permission::<P>(state, &from)?;
+
+ if cfg!(windows) {
+ // return root node_modules when path is 'D:\\'.
+ let from_str = from.to_str().unwrap();
+ if from_str.len() >= 3 {
+ let bytes = from_str.as_bytes();
+ if bytes[from_str.len() - 1] == b'\\' && bytes[from_str.len() - 2] == b':'
+ {
+ let p = from_str.to_owned() + "node_modules";
+ return Ok(vec![p]);
+ }
+ }
+ } else {
+ // Return early not only to avoid unnecessary work, but to *avoid* returning
+ // an array of two items for a root: [ '//node_modules', '/node_modules' ]
+ if from.to_string_lossy() == "/" {
+ return Ok(vec!["/node_modules".to_string()]);
+ }
+ }
+
+ let mut paths = vec![];
+ let mut current_path = from.as_path();
+ let mut maybe_parent = Some(current_path);
+ while let Some(parent) = maybe_parent {
+ if !parent.ends_with("/node_modules") {
+ paths.push(parent.join("node_modules").to_string_lossy().to_string());
+ current_path = parent;
+ maybe_parent = current_path.parent();
+ }
+ }
+
+ if !cfg!(windows) {
+ // Append /node_modules to handle root paths.
+ paths.push("/node_modules".to_string());
+ }
+
+ Ok(paths)
+}
+
+#[op]
+fn op_require_proxy_path(filename: String) -> String {
+ // Allow a directory to be passed as the filename
+ let trailing_slash = if cfg!(windows) {
+ // Node also counts a trailing forward slash as a
+ // directory for node on Windows, but not backslashes
+ // on non-Windows platforms
+ filename.ends_with('\\') || filename.ends_with('/')
+ } else {
+ filename.ends_with('/')
+ };
+
+ if trailing_slash {
+ let p = PathBuf::from(filename);
+ p.join("noop.js").to_string_lossy().to_string()
+ } else {
+ filename
+ }
+}
+
+#[op]
+fn op_require_is_request_relative(request: String) -> bool {
+ if request.starts_with("./") || request.starts_with("../") || request == ".."
+ {
+ return true;
+ }
+
+ if cfg!(windows) {
+ if request.starts_with(".\\") {
+ return true;
+ }
+
+ if request.starts_with("..\\") {
+ return true;
+ }
+ }
+
+ false
+}
+
+#[op]
+fn op_require_resolve_deno_dir(
+ state: &mut OpState,
+ request: String,
+ parent_filename: String,
+) -> Option<String> {
+ let resolver = state.borrow::<Rc<dyn RequireNpmResolver>>();
+ resolver
+ .resolve_package_folder_from_package(
+ &request,
+ &PathBuf::from(parent_filename),
+ NodeResolutionMode::Execution,
+ )
+ .ok()
+ .map(|p| p.to_string_lossy().to_string())
+}
+
+#[op]
+fn op_require_is_deno_dir_package(state: &mut OpState, path: String) -> bool {
+ let resolver = state.borrow::<Rc<dyn RequireNpmResolver>>();
+ resolver.in_npm_package(&PathBuf::from(path))
+}
+
+#[op]
+fn op_require_resolve_lookup_paths(
+ request: String,
+ maybe_parent_paths: Option<Vec<String>>,
+ parent_filename: String,
+) -> Option<Vec<String>> {
+ if !request.starts_with('.')
+ || (request.len() > 1
+ && !request.starts_with("..")
+ && !request.starts_with("./")
+ && (!cfg!(windows) || !request.starts_with(".\\")))
+ {
+ let module_paths = vec![];
+ let mut paths = module_paths;
+ if let Some(mut parent_paths) = maybe_parent_paths {
+ if !parent_paths.is_empty() {
+ paths.append(&mut parent_paths);
+ }
+ }
+
+ if !paths.is_empty() {
+ return Some(paths);
+ } else {
+ return None;
+ }
+ }
+
+ // In REPL, parent.filename is null.
+ // if (!parent || !parent.id || !parent.filename) {
+ // // Make require('./path/to/foo') work - normally the path is taken
+ // // from realpath(__filename) but in REPL there is no filename
+ // const mainPaths = ['.'];
+
+ // debug('looking for %j in %j', request, mainPaths);
+ // return mainPaths;
+ // }
+
+ let p = PathBuf::from(parent_filename);
+ Some(vec![p.parent().unwrap().to_string_lossy().to_string()])
+}
+
+#[op]
+fn op_require_path_is_absolute(p: String) -> bool {
+ PathBuf::from(p).is_absolute()
+}
+
+#[op]
+fn op_require_stat<P>(
+ state: &mut OpState,
+ path: String,
+) -> Result<i32, AnyError>
+where
+ P: NodePermissions + 'static,
+{
+ let path = PathBuf::from(path);
+ ensure_read_permission::<P>(state, &path)?;
+ if let Ok(metadata) = std::fs::metadata(&path) {
+ if metadata.is_file() {
+ return Ok(0);
+ } else {
+ return Ok(1);
+ }
+ }
+
+ Ok(-1)
+}
+
+#[op]
+fn op_require_real_path<P>(
+ state: &mut OpState,
+ request: String,
+) -> Result<String, AnyError>
+where
+ P: NodePermissions + 'static,
+{
+ let path = PathBuf::from(request);
+ ensure_read_permission::<P>(state, &path)?;
+ let mut canonicalized_path = path.canonicalize()?;
+ if cfg!(windows) {
+ canonicalized_path = PathBuf::from(
+ canonicalized_path
+ .display()
+ .to_string()
+ .trim_start_matches("\\\\?\\"),
+ );
+ }
+ Ok(canonicalized_path.to_string_lossy().to_string())
+}
+
+fn path_resolve(parts: Vec<String>) -> String {
+ assert!(!parts.is_empty());
+ let mut p = PathBuf::from(&parts[0]);
+ if parts.len() > 1 {
+ for part in &parts[1..] {
+ p = p.join(part);
+ }
+ }
+ normalize_path(p).to_string_lossy().to_string()
+}
+
+#[op]
+fn op_require_path_resolve(parts: Vec<String>) -> String {
+ path_resolve(parts)
+}
+
+#[op]
+fn op_require_path_dirname(request: String) -> Result<String, AnyError> {
+ let p = PathBuf::from(request);
+ if let Some(parent) = p.parent() {
+ Ok(parent.to_string_lossy().to_string())
+ } else {
+ Err(generic_error("Path doesn't have a parent"))
+ }
+}
+
+#[op]
+fn op_require_path_basename(request: String) -> Result<String, AnyError> {
+ let p = PathBuf::from(request);
+ if let Some(path) = p.file_name() {
+ Ok(path.to_string_lossy().to_string())
+ } else {
+ Err(generic_error("Path doesn't have a file name"))
+ }
+}
+
+#[op]
+fn op_require_try_self_parent_path<P>(
+ state: &mut OpState,
+ has_parent: bool,
+ maybe_parent_filename: Option<String>,
+ maybe_parent_id: Option<String>,
+) -> Result<Option<String>, AnyError>
+where
+ P: NodePermissions + 'static,
+{
+ if !has_parent {
+ return Ok(None);
+ }
+
+ if let Some(parent_filename) = maybe_parent_filename {
+ return Ok(Some(parent_filename));
+ }
+
+ if let Some(parent_id) = maybe_parent_id {
+ if parent_id == "<repl>" || parent_id == "internal/preload" {
+ if let Ok(cwd) = std::env::current_dir() {
+ ensure_read_permission::<P>(state, &cwd)?;
+ return Ok(Some(cwd.to_string_lossy().to_string()));
+ }
+ }
+ }
+ Ok(None)
+}
+
+#[op]
+fn op_require_try_self<P>(
+ state: &mut OpState,
+ parent_path: Option<String>,
+ request: String,
+) -> Result<Option<String>, AnyError>
+where
+ P: NodePermissions + 'static,
+{
+ if parent_path.is_none() {
+ return Ok(None);
+ }
+
+ let resolver = state.borrow::<Rc<dyn RequireNpmResolver>>().clone();
+ let permissions = state.borrow_mut::<P>();
+ let pkg = resolution::get_package_scope_config(
+ &Url::from_file_path(parent_path.unwrap()).unwrap(),
+ &*resolver,
+ permissions,
+ )
+ .ok();
+ if pkg.is_none() {
+ return Ok(None);
+ }
+
+ let pkg = pkg.unwrap();
+ if pkg.exports.is_none() {
+ return Ok(None);
+ }
+ if pkg.name.is_none() {
+ return Ok(None);
+ }
+
+ let pkg_name = pkg.name.as_ref().unwrap().to_string();
+ let mut expansion = ".".to_string();
+
+ if request == pkg_name {
+ // pass
+ } else if request.starts_with(&format!("{pkg_name}/")) {
+ expansion += &request[pkg_name.len()..];
+ } else {
+ return Ok(None);
+ }
+
+ let referrer = deno_core::url::Url::from_file_path(&pkg.path).unwrap();
+ if let Some(exports) = &pkg.exports {
+ resolution::package_exports_resolve(
+ &pkg.path,
+ expansion,
+ exports,
+ &referrer,
+ NodeModuleKind::Cjs,
+ resolution::REQUIRE_CONDITIONS,
+ NodeResolutionMode::Execution,
+ &*resolver,
+ permissions,
+ )
+ .map(|r| Some(r.to_string_lossy().to_string()))
+ } else {
+ Ok(None)
+ }
+}
+
+#[op]
+fn op_require_read_file<P>(
+ state: &mut OpState,
+ file_path: String,
+) -> Result<String, AnyError>
+where
+ P: NodePermissions + 'static,
+{
+ let file_path = PathBuf::from(file_path);
+ ensure_read_permission::<P>(state, &file_path)?;
+ Ok(std::fs::read_to_string(file_path)?)
+}
+
+#[op]
+pub fn op_require_as_file_path(file_or_url: String) -> String {
+ if let Ok(url) = Url::parse(&file_or_url) {
+ if let Ok(p) = url.to_file_path() {
+ return p.to_string_lossy().to_string();
+ }
+ }
+
+ file_or_url
+}
+
+#[op]
+fn op_require_resolve_exports<P>(
+ state: &mut OpState,
+ uses_local_node_modules_dir: bool,
+ modules_path: String,
+ _request: String,
+ name: String,
+ expansion: String,
+ parent_path: String,
+) -> Result<Option<String>, AnyError>
+where
+ P: NodePermissions + 'static,
+{
+ let resolver = state.borrow::<Rc<dyn RequireNpmResolver>>().clone();
+ let permissions = state.borrow_mut::<P>();
+
+ let pkg_path = if resolver.in_npm_package(&PathBuf::from(&modules_path))
+ && !uses_local_node_modules_dir
+ {
+ modules_path
+ } else {
+ path_resolve(vec![modules_path, name])
+ };
+ let pkg = PackageJson::load(
+ &*resolver,
+ permissions,
+ PathBuf::from(&pkg_path).join("package.json"),
+ )?;
+
+ if let Some(exports) = &pkg.exports {
+ let referrer = Url::from_file_path(parent_path).unwrap();
+ resolution::package_exports_resolve(
+ &pkg.path,
+ format!(".{expansion}"),
+ exports,
+ &referrer,
+ NodeModuleKind::Cjs,
+ resolution::REQUIRE_CONDITIONS,
+ NodeResolutionMode::Execution,
+ &*resolver,
+ permissions,
+ )
+ .map(|r| Some(r.to_string_lossy().to_string()))
+ } else {
+ Ok(None)
+ }
+}
+
+#[op]
+fn op_require_read_closest_package_json<P>(
+ state: &mut OpState,
+ filename: String,
+) -> Result<PackageJson, AnyError>
+where
+ P: NodePermissions + 'static,
+{
+ ensure_read_permission::<P>(
+ state,
+ PathBuf::from(&filename).parent().unwrap(),
+ )?;
+ let resolver = state.borrow::<Rc<dyn RequireNpmResolver>>().clone();
+ let permissions = state.borrow_mut::<P>();
+ resolution::get_closest_package_json(
+ &Url::from_file_path(filename).unwrap(),
+ &*resolver,
+ permissions,
+ )
+}
+
+#[op]
+fn op_require_read_package_scope<P>(
+ state: &mut OpState,
+ package_json_path: String,
+) -> Option<PackageJson>
+where
+ P: NodePermissions + 'static,
+{
+ let resolver = state.borrow::<Rc<dyn RequireNpmResolver>>().clone();
+ let permissions = state.borrow_mut::<P>();
+ let package_json_path = PathBuf::from(package_json_path);
+ PackageJson::load(&*resolver, permissions, package_json_path).ok()
+}
+
+#[op]
+fn op_require_package_imports_resolve<P>(
+ state: &mut OpState,
+ parent_filename: String,
+ request: String,
+) -> Result<Option<String>, AnyError>
+where
+ P: NodePermissions + 'static,
+{
+ let parent_path = PathBuf::from(&parent_filename);
+ ensure_read_permission::<P>(state, &parent_path)?;
+ let resolver = state.borrow::<Rc<dyn RequireNpmResolver>>().clone();
+ let permissions = state.borrow_mut::<P>();
+ let pkg = PackageJson::load(
+ &*resolver,
+ permissions,
+ parent_path.join("package.json"),
+ )?;
+
+ if pkg.imports.is_some() {
+ let referrer =
+ deno_core::url::Url::from_file_path(&parent_filename).unwrap();
+ let r = resolution::package_imports_resolve(
+ &request,
+ &referrer,
+ NodeModuleKind::Cjs,
+ resolution::REQUIRE_CONDITIONS,
+ NodeResolutionMode::Execution,
+ &*resolver,
+ permissions,
+ )
+ .map(|r| Some(Url::from_file_path(r).unwrap().to_string()));
+ state.put(resolver);
+ r
+ } else {
+ Ok(None)
+ }
+}
+
+#[op]
+fn op_require_break_on_next_statement(state: &mut OpState) {
+ let inspector = state.borrow::<Rc<RefCell<JsRuntimeInspector>>>();
+ inspector
+ .borrow_mut()
+ .wait_for_session_and_break_on_next_statement()
+}
diff --git a/ext/node/polyfill.rs b/ext/node/polyfill.rs
new file mode 100644
index 000000000..371b27be6
--- /dev/null
+++ b/ext/node/polyfill.rs
@@ -0,0 +1,215 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+pub fn find_builtin_node_module(
+ specifier: &str,
+) -> Option<&NodeModulePolyfill> {
+ SUPPORTED_BUILTIN_NODE_MODULES
+ .iter()
+ .find(|m| m.name == specifier)
+}
+
+pub fn is_builtin_node_module(specifier: &str) -> bool {
+ find_builtin_node_module(specifier).is_some()
+}
+
+pub enum NodeModulePolyfillSpecifier {
+ /// An internal module specifier, like "internal:deno_node/assert.ts". The
+ /// module must be either embedded in the binary or snapshotted.
+ Embedded(&'static str),
+
+ /// Specifier relative to the root of `deno_std` repo, like "node/assert.ts"
+ StdNode(&'static str),
+}
+
+pub struct NodeModulePolyfill {
+ /// Name of the module like "assert" or "timers/promises"
+ pub name: &'static str,
+ pub specifier: NodeModulePolyfillSpecifier,
+}
+
+pub static SUPPORTED_BUILTIN_NODE_MODULES: &[NodeModulePolyfill] = &[
+ NodeModulePolyfill {
+ name: "assert",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/assert.ts"),
+ },
+ NodeModulePolyfill {
+ name: "assert/strict",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/assert/strict.ts"),
+ },
+ NodeModulePolyfill {
+ name: "async_hooks",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/async_hooks.ts"),
+ },
+ NodeModulePolyfill {
+ name: "buffer",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/buffer.ts"),
+ },
+ NodeModulePolyfill {
+ name: "child_process",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/child_process.ts"),
+ },
+ NodeModulePolyfill {
+ name: "cluster",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/cluster.ts"),
+ },
+ NodeModulePolyfill {
+ name: "console",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/console.ts"),
+ },
+ NodeModulePolyfill {
+ name: "constants",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/constants.ts"),
+ },
+ NodeModulePolyfill {
+ name: "crypto",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/crypto.ts"),
+ },
+ NodeModulePolyfill {
+ name: "dgram",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/dgram.ts"),
+ },
+ NodeModulePolyfill {
+ name: "dns",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/dns.ts"),
+ },
+ NodeModulePolyfill {
+ name: "dns/promises",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/dns/promises.ts"),
+ },
+ NodeModulePolyfill {
+ name: "domain",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/domain.ts"),
+ },
+ NodeModulePolyfill {
+ name: "events",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/events.ts"),
+ },
+ NodeModulePolyfill {
+ name: "fs",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/fs.ts"),
+ },
+ NodeModulePolyfill {
+ name: "fs/promises",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/fs/promises.ts"),
+ },
+ NodeModulePolyfill {
+ name: "http",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/http.ts"),
+ },
+ NodeModulePolyfill {
+ name: "https",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/https.ts"),
+ },
+ NodeModulePolyfill {
+ name: "module",
+ specifier: NodeModulePolyfillSpecifier::Embedded(
+ "internal:deno_node/module_es_shim.js",
+ ),
+ },
+ NodeModulePolyfill {
+ name: "net",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/net.ts"),
+ },
+ NodeModulePolyfill {
+ name: "os",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/os.ts"),
+ },
+ NodeModulePolyfill {
+ name: "path",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/path.ts"),
+ },
+ NodeModulePolyfill {
+ name: "path/posix",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/path/posix.ts"),
+ },
+ NodeModulePolyfill {
+ name: "path/win32",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/path/win32.ts"),
+ },
+ NodeModulePolyfill {
+ name: "perf_hooks",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/perf_hooks.ts"),
+ },
+ NodeModulePolyfill {
+ name: "process",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/process.ts"),
+ },
+ NodeModulePolyfill {
+ name: "querystring",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/querystring.ts"),
+ },
+ NodeModulePolyfill {
+ name: "readline",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/readline.ts"),
+ },
+ NodeModulePolyfill {
+ name: "stream",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/stream.ts"),
+ },
+ NodeModulePolyfill {
+ name: "stream/consumers",
+ specifier: NodeModulePolyfillSpecifier::StdNode(
+ "node/stream/consumers.mjs",
+ ),
+ },
+ NodeModulePolyfill {
+ name: "stream/promises",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/stream/promises.mjs"),
+ },
+ NodeModulePolyfill {
+ name: "stream/web",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/stream/web.ts"),
+ },
+ NodeModulePolyfill {
+ name: "string_decoder",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/string_decoder.ts"),
+ },
+ NodeModulePolyfill {
+ name: "sys",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/sys.ts"),
+ },
+ NodeModulePolyfill {
+ name: "timers",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/timers.ts"),
+ },
+ NodeModulePolyfill {
+ name: "timers/promises",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/timers/promises.ts"),
+ },
+ NodeModulePolyfill {
+ name: "tls",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/tls.ts"),
+ },
+ NodeModulePolyfill {
+ name: "tty",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/tty.ts"),
+ },
+ NodeModulePolyfill {
+ name: "url",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/url.ts"),
+ },
+ NodeModulePolyfill {
+ name: "util",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/util.ts"),
+ },
+ NodeModulePolyfill {
+ name: "util/types",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/util/types.ts"),
+ },
+ NodeModulePolyfill {
+ name: "v8",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/v8.ts"),
+ },
+ NodeModulePolyfill {
+ name: "vm",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/vm.ts"),
+ },
+ NodeModulePolyfill {
+ name: "worker_threads",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/worker_threads.ts"),
+ },
+ NodeModulePolyfill {
+ name: "zlib",
+ specifier: NodeModulePolyfillSpecifier::StdNode("node/zlib.ts"),
+ },
+];