deno.land / x / deno@v1.28.2 / ext / node / lib.rs
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633// Copyright 2018-2022 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::Extension;use deno_core::OpState;use once_cell::sync::Lazy;use std::collections::HashSet;use std::path::Path;use std::path::PathBuf;use std::rc::Rc;
pub mod errors;mod package_json;mod path;mod resolution;
pub use package_json::PackageJson;pub use path::PathClean;pub use resolution::get_closest_package_json;pub use resolution::get_package_scope_config;pub use resolution::legacy_main_resolve;pub use resolution::package_exports_resolve;pub use resolution::package_imports_resolve;pub use resolution::package_resolve;pub use resolution::path_to_declaration_path;pub use resolution::NodeModuleKind;pub use resolution::DEFAULT_CONDITIONS;pub use resolution::TYPES_CONDITIONS;
pub trait NodePermissions { fn check_read(&mut self, path: &Path) -> Result<(), AnyError>;}
pub trait RequireNpmResolver { fn resolve_package_folder_from_package( &self, specifier: &str, referrer: &Path, conditions: &[&str], ) -> Result<PathBuf, AnyError>;
fn resolve_package_folder_from_path( &self, path: &Path, ) -> Result<PathBuf, AnyError>;
fn in_npm_package(&self, path: &Path) -> bool;
fn ensure_read_permission(&self, path: &Path) -> Result<(), AnyError>;}
pub const MODULE_ES_SHIM: &str = include_str!("./module_es_shim.js");
pub static NODE_GLOBAL_THIS_NAME: Lazy<String> = Lazy::new(|| { let now = std::time::SystemTime::now(); let seconds = now .duration_since(std::time::SystemTime::UNIX_EPOCH) .unwrap() .as_secs(); // use a changing variable name to make it hard to depend on this format!("__DENO_NODE_GLOBAL_THIS_{}__", seconds)});
pub static NODE_ENV_VAR_ALLOWLIST: Lazy<HashSet<String>> = Lazy::new(|| { // The full list of environment variables supported by Node.js is available // at https://nodejs.org/api/cli.html#environment-variables let mut set = HashSet::new(); set.insert("NODE_DEBUG".to_string()); set.insert("NODE_OPTIONS".to_string()); set});
pub fn init<P: NodePermissions + 'static>( maybe_npm_resolver: Option<Rc<dyn RequireNpmResolver>>,) -> Extension { Extension::builder() .js(include_js_files!( prefix "deno:ext/node", "01_node.js", "02_require.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(), 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(), op_require_read_closest_package_json::decl::<P>(), op_require_read_package_scope::decl(), op_require_package_imports_resolve::decl::<P>(), ]) .state(move |state| { if let Some(npm_resolver) = maybe_npm_resolver.clone() { state.put(npm_resolver); } Ok(()) }) .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() }; if resolver.ensure_read_permission(file_path).is_ok() { return Ok(()); }
state.borrow_mut::<P>().check_read(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) { 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), DEFAULT_CONDITIONS, ) .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( state: &mut OpState, parent_path: Option<String>, request: String,) -> Result<Option<String>, AnyError> { if parent_path.is_none() { return Ok(None); }
let resolver = state.borrow::<Rc<dyn RequireNpmResolver>>().clone(); let pkg = resolution::get_package_scope_config( &Url::from_file_path(parent_path.unwrap()).unwrap(), &*resolver, ) .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, &*resolver, ) .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( state: &mut OpState, modules_path: String, _request: String, name: String, expansion: String, parent_path: String,) -> Result<Option<String>, AnyError> { let resolver = state.borrow::<Rc<dyn RequireNpmResolver>>().clone();
let pkg_path = if resolver.in_npm_package(&PathBuf::from(&modules_path)) { modules_path } else { path_resolve(vec![modules_path, name]) }; let pkg = PackageJson::load( &*resolver, 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, &*resolver, ) .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(); resolution::get_closest_package_json( &Url::from_file_path(filename).unwrap(), &*resolver, )}
#[op]fn op_require_read_package_scope( state: &mut OpState, package_json_path: String,) -> Option<PackageJson> { let resolver = state.borrow::<Rc<dyn RequireNpmResolver>>().clone(); let package_json_path = PathBuf::from(package_json_path); PackageJson::load(&*resolver, 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 pkg = PackageJson::load(&*resolver, 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, &*resolver, ) .map(|r| Some(Url::from_file_path(r).unwrap().to_string())); state.put(resolver); r } else { Ok(None) }}
Version Info