1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-21 15:04:11 -05:00

feat: binary npm commands (#15542)

This commit is contained in:
David Sherret 2022-08-23 10:39:19 -04:00 committed by GitHub
parent 362af63c6f
commit e7367044d9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 2389 additions and 196 deletions

View file

@ -112,24 +112,6 @@ pub fn try_resolve_builtin_module(specifier: &str) -> Option<Url> {
}
}
#[allow(unused)]
pub fn load_cjs_module_from_ext_node(
js_runtime: &mut JsRuntime,
module: &str,
main: bool,
) -> Result<(), AnyError> {
let source_code = &format!(
r#"(function loadCjsModule(module) {{
Deno[Deno.internal].require.Module._load(module, null, {main});
}})('{module}');"#,
main = main,
module = escape_for_single_quote_string(module),
);
js_runtime.execute_script(&located_script_name!(), source_code)?;
Ok(())
}
pub fn load_cjs_module(
js_runtime: &mut JsRuntime,
module: &str,

View file

@ -164,7 +164,8 @@ async fn test_specifier(
stdout: StdioPipe::File(sender.stdout()),
stderr: StdioPipe::File(sender.stderr()),
},
);
)
.await?;
worker.run_lsp_test_specifier(mode).await?;
}

View file

@ -91,6 +91,7 @@ use deno_runtime::permissions::Permissions;
use deno_runtime::tokio_util::run_local;
use log::debug;
use log::info;
use npm::NpmPackageReference;
use std::env;
use std::io::Read;
use std::io::Write;
@ -323,7 +324,8 @@ async fn install_command(
permissions,
vec![],
Default::default(),
);
)
.await?;
// First, fetch and compile the module; this step ensures that the module exists.
worker.preload_main_module().await?;
tools::installer::install(flags, install_flags)?;
@ -412,7 +414,8 @@ async fn eval_command(
permissions,
vec![],
Default::default(),
);
)
.await?;
// Create a dummy source file.
let source_code = if eval_flags.print {
format!("console.log({})", eval_flags.code)
@ -694,7 +697,8 @@ async fn repl_command(
Permissions::from_options(&ps.options.permissions_options())?,
vec![],
Default::default(),
);
)
.await?;
worker.setup_repl().await?;
tools::repl::run(
&ps,
@ -714,7 +718,8 @@ async fn run_from_stdin(flags: Flags) -> Result<i32, AnyError> {
Permissions::from_options(&ps.options.permissions_options())?,
vec![],
Default::default(),
);
)
.await?;
let mut source = Vec::new();
std::io::stdin().read_to_end(&mut source)?;
@ -758,7 +763,8 @@ async fn run_with_watch(flags: Flags, script: String) -> Result<i32, AnyError> {
permissions,
vec![],
Default::default(),
);
)
.await?;
worker.run_for_watcher().await?;
Ok(())
@ -797,8 +803,13 @@ async fn run_command(
// TODO(bartlomieju): actually I think it will also fail if there's an import
// map specified and bare specifier is used on the command line - this should
// probably call `ProcState::resolve` instead
let main_module = resolve_url_or_path(&run_flags.script)?;
let ps = ProcState::build(flags).await?;
let main_module = if NpmPackageReference::from_str(&run_flags.script).is_ok()
{
ModuleSpecifier::parse(&run_flags.script)?
} else {
resolve_url_or_path(&run_flags.script)?
};
let permissions =
Permissions::from_options(&ps.options.permissions_options())?;
let mut worker = create_main_worker(
@ -807,7 +818,8 @@ async fn run_command(
permissions,
vec![],
Default::default(),
);
)
.await?;
let exit_code = worker.run().await?;
Ok(exit_code)

View file

@ -74,7 +74,15 @@ pub fn esm_code_with_node_globals(
write!(result, "var {0} = {1}.{0};", global, global_this_expr).unwrap();
}
result.push_str(parsed_source.text_info().text_str());
let file_text = parsed_source.text_info().text_str();
// strip the shebang
let file_text = if file_text.starts_with("#!/") {
let start_index = file_text.find('\n').unwrap_or(file_text.len());
&file_text[start_index..]
} else {
file_text
};
result.push_str(file_text);
Ok(result)
}
@ -158,4 +166,24 @@ mod tests {
assert!(r.contains("var process = globalThis.process;"));
assert!(r.contains("export const x = 1;"));
}
#[test]
fn test_esm_code_with_node_globals_with_shebang() {
let r = esm_code_with_node_globals(
&ModuleSpecifier::parse("https://example.com/foo/bar.js").unwrap(),
"#!/usr/bin/env node\nexport const x = 1;".to_string(),
)
.unwrap();
assert_eq!(
r,
concat!(
"var globalThis = Deno[Deno.internal].node.globalThis;var Buffer = globalThis.Buffer;",
"var clearImmediate = globalThis.clearImmediate;var clearInterval = globalThis.clearInterval;",
"var clearTimeout = globalThis.clearTimeout;var global = globalThis.global;",
"var process = globalThis.process;var setImmediate = globalThis.setImmediate;",
"var setInterval = globalThis.setInterval;var setTimeout = globalThis.setTimeout;\n",
"export const x = 1;"
),
);
}
}

View file

@ -6,6 +6,7 @@ use std::path::PathBuf;
use deno_ast::MediaType;
use deno_ast::ModuleSpecifier;
use deno_core::anyhow::bail;
use deno_core::anyhow::Context;
use deno_core::error::generic_error;
use deno_core::error::AnyError;
@ -31,6 +32,7 @@ use crate::compat;
use crate::file_fetcher::FileFetcher;
use crate::npm::GlobalNpmPackageResolver;
use crate::npm::NpmPackageReference;
use crate::npm::NpmPackageReq;
use crate::npm::NpmPackageResolver;
mod analyze;
@ -185,6 +187,87 @@ pub fn node_resolve_npm_reference(
Ok(Some(resolve_response))
}
pub fn node_resolve_binary_export(
pkg_req: &NpmPackageReq,
bin_name: Option<&str>,
npm_resolver: &GlobalNpmPackageResolver,
) -> Result<ResolveResponse, AnyError> {
let pkg = npm_resolver.resolve_package_from_deno_module(pkg_req)?;
let package_folder = pkg.folder_path;
let package_json_path = package_folder.join("package.json");
let package_json = PackageJson::load(npm_resolver, package_json_path)?;
let bin = match &package_json.bin {
Some(bin) => bin,
None => bail!(
"package {} did not have a 'bin' property in its package.json",
pkg.id
),
};
let bin_entry = match bin {
Value::String(_) => {
if bin_name.is_some() && bin_name.unwrap() != pkg_req.name {
None
} else {
Some(bin)
}
}
Value::Object(o) => {
if let Some(bin_name) = bin_name {
o.get(bin_name)
} else if o.len() == 1 {
o.values().next()
} else {
o.get(&pkg_req.name)
}
},
_ => bail!("package {} did not have a 'bin' property with a string or object value in its package.json", pkg.id),
};
let bin_entry = match bin_entry {
Some(e) => e,
None => bail!(
"package {} did not have a 'bin' entry for {} in its package.json",
pkg.id,
bin_name.unwrap_or(&pkg_req.name),
),
};
let bin_entry = match bin_entry {
Value::String(s) => s,
_ => bail!(
"package {} had a non-string sub property of 'bin' in its package.json",
pkg.id
),
};
let url =
ModuleSpecifier::from_file_path(package_folder.join(bin_entry)).unwrap();
let resolve_response = url_to_resolve_response(url, npm_resolver)?;
// TODO(bartlomieju): skipped checking errors for commonJS resolution and
// "preserveSymlinksMain"/"preserveSymlinks" options.
Ok(resolve_response)
}
pub fn load_cjs_module_from_ext_node(
js_runtime: &mut JsRuntime,
module: &str,
main: 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) {{
Deno[Deno.internal].require.Module._load(module, null, {main});
}})('{module}');"#,
main = main,
module = escape_for_single_quote_string(module),
);
js_runtime.execute_script(&located_script_name!(), source_code)?;
Ok(())
}
fn package_config_resolve(
package_subpath: &str,
package_dir: &Path,

View file

@ -4,6 +4,7 @@ mod cache;
mod registry;
mod resolution;
mod tarball;
mod version_req;
use std::io::ErrorKind;
use std::path::Path;

View file

@ -23,7 +23,7 @@ use crate::fs_util;
use crate::http_cache::CACHE_PERM;
use super::cache::NpmCache;
use super::resolution::NpmVersionMatcher;
use super::version_req::NpmVersionReq;
// npm registry docs: https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md
@ -320,98 +320,3 @@ impl NpmRegistryApi {
name_folder_path.join("registry.json")
}
}
/// A version requirement found in an npm package's dependencies.
pub struct NpmVersionReq {
raw_text: String,
comparators: Vec<semver::VersionReq>,
}
impl NpmVersionReq {
pub fn parse(text: &str) -> Result<NpmVersionReq, AnyError> {
// semver::VersionReq doesn't support spaces between comparators
// and it doesn't support using || for "OR", so we pre-process
// the version requirement in order to make this work.
let raw_text = text.to_string();
let part_texts = text.split("||").collect::<Vec<_>>();
let mut comparators = Vec::with_capacity(part_texts.len());
for part in part_texts {
comparators.push(npm_version_req_parse_part(part)?);
}
Ok(NpmVersionReq {
raw_text,
comparators,
})
}
}
impl NpmVersionMatcher for NpmVersionReq {
fn matches(&self, version: &semver::Version) -> bool {
self.comparators.iter().any(|c| c.matches(version))
}
fn version_text(&self) -> String {
self.raw_text.to_string()
}
}
fn npm_version_req_parse_part(
text: &str,
) -> Result<semver::VersionReq, AnyError> {
let text = text.trim();
let text = text.strip_prefix('v').unwrap_or(text);
let mut chars = text.chars().enumerate().peekable();
let mut final_text = String::new();
while chars.peek().is_some() {
let (i, c) = chars.next().unwrap();
let is_greater_or_less_than = c == '<' || c == '>';
if is_greater_or_less_than || c == '=' {
if i > 0 {
final_text = final_text.trim().to_string();
// add a comma to make semver::VersionReq parse this
final_text.push(',');
}
final_text.push(c);
let next_char = chars.peek().map(|(_, c)| c);
if is_greater_or_less_than && matches!(next_char, Some('=')) {
let c = chars.next().unwrap().1; // skip
final_text.push(c);
}
} else {
final_text.push(c);
}
}
Ok(semver::VersionReq::parse(&final_text)?)
}
#[cfg(test)]
mod test {
use super::*;
struct NpmVersionReqTester(NpmVersionReq);
impl NpmVersionReqTester {
fn matches(&self, version: &str) -> bool {
self.0.matches(&semver::Version::parse(version).unwrap())
}
}
#[test]
pub fn npm_version_req_with_v() {
assert!(NpmVersionReq::parse("v1.0.0").is_ok());
}
#[test]
pub fn npm_version_req_ranges() {
let tester = NpmVersionReqTester(
NpmVersionReq::parse(">= 2.1.2 < 3.0.0 || 5.x").unwrap(),
);
assert!(!tester.matches("2.1.1"));
assert!(tester.matches("2.1.2"));
assert!(tester.matches("2.9.9"));
assert!(!tester.matches("3.0.0"));
assert!(tester.matches("5.0.0"));
assert!(tester.matches("5.1.0"));
assert!(!tester.matches("6.1.0"));
}
}

View file

@ -8,12 +8,14 @@ use deno_ast::ModuleSpecifier;
use deno_core::anyhow::bail;
use deno_core::anyhow::Context;
use deno_core::error::AnyError;
use deno_core::futures;
use deno_core::parking_lot::RwLock;
use super::registry::NpmPackageInfo;
use super::registry::NpmPackageVersionDistInfo;
use super::registry::NpmPackageVersionInfo;
use super::registry::NpmRegistryApi;
use super::version_req::SpecifierVersionReq;
/// The version matcher used for npm schemed urls is more strict than
/// the one used by npm packages.
@ -28,10 +30,55 @@ pub struct NpmPackageReference {
pub sub_path: Option<String>,
}
impl NpmPackageReference {
pub fn from_specifier(
specifier: &ModuleSpecifier,
) -> Result<NpmPackageReference, AnyError> {
Self::from_str(specifier.as_str())
}
pub fn from_str(specifier: &str) -> Result<NpmPackageReference, AnyError> {
let specifier = match specifier.strip_prefix("npm:") {
Some(s) => s,
None => {
bail!("Not an npm specifier: '{}'", specifier);
}
};
let (name, version_req) = match specifier.rsplit_once('@') {
Some((name, version_req)) => (
name,
match SpecifierVersionReq::parse(version_req) {
Ok(v) => Some(v),
Err(_) => None, // not a version requirement
},
),
None => (specifier, None),
};
Ok(NpmPackageReference {
req: NpmPackageReq {
name: name.to_string(),
version_req,
},
// todo: implement and support this
sub_path: None,
})
}
}
impl std::fmt::Display for NpmPackageReference {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(sub_path) = &self.sub_path {
write!(f, "{}/{}", self.req, sub_path)
} else {
write!(f, "{}", self.req)
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct NpmPackageReq {
pub name: String,
pub version_req: Option<semver::VersionReq>,
pub version_req: Option<SpecifierVersionReq>,
}
impl std::fmt::Display for NpmPackageReq {
@ -60,51 +107,6 @@ impl NpmVersionMatcher for NpmPackageReq {
}
}
impl NpmPackageReference {
pub fn from_specifier(
specifier: &ModuleSpecifier,
) -> Result<NpmPackageReference, AnyError> {
Self::from_str(specifier.as_str())
}
pub fn from_str(specifier: &str) -> Result<NpmPackageReference, AnyError> {
let specifier = match specifier.strip_prefix("npm:") {
Some(s) => s,
None => {
bail!("Not an npm specifier: '{}'", specifier);
}
};
let (name, version_req) = match specifier.rsplit_once('@') {
Some((name, version_req)) => (
name,
match semver::VersionReq::parse(version_req) {
Ok(v) => Some(v),
Err(_) => None, // not a version requirement
},
),
None => (specifier, None),
};
Ok(NpmPackageReference {
req: NpmPackageReq {
name: name.to_string(),
version_req,
},
// todo: implement and support this
sub_path: None,
})
}
}
impl std::fmt::Display for NpmPackageReference {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(sub_path) = &self.sub_path {
write!(f, "{}/{}", self.req, sub_path)
} else {
write!(f, "{}", self.req)
}
}
}
#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub struct NpmPackageId {
pub name: String,
@ -314,6 +316,27 @@ impl NpmResolution {
ordering => ordering,
});
// cache all the dependencies' registry infos in parallel when this env var isn't set
if std::env::var("DENO_UNSTABLE_NPM_SYNC_DOWNLOAD") != Ok("1".to_string())
{
let handles = deps
.iter()
.map(|dep| {
let name = dep.name.clone();
let api = self.api.clone();
tokio::task::spawn(async move {
// it's ok to call this without storing the result, because
// NpmRegistryApi will cache the package info in memory
api.package_info(&name).await
})
})
.collect::<Vec<_>>();
let results = futures::future::join_all(handles).await;
for result in results {
result??; // surface the first error
}
}
// now resolve them
for dep in deps {
// check if an existing dependency matches this

219
cli/npm/version_req.rs Normal file
View file

@ -0,0 +1,219 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
use std::borrow::Cow;
use deno_core::anyhow::bail;
use deno_core::error::AnyError;
use once_cell::sync::Lazy;
use regex::Regex;
use super::resolution::NpmVersionMatcher;
static MINOR_SPECIFIER_RE: Lazy<Regex> =
Lazy::new(|| Regex::new(r#"^[0-9]+\.[0-9]+$"#).unwrap());
/// Version requirement found in npm specifiers.
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct SpecifierVersionReq(semver::VersionReq);
impl std::fmt::Display for SpecifierVersionReq {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl SpecifierVersionReq {
// in order to keep using semver, we do some pre-processing to change the behavior
pub fn parse(text: &str) -> Result<Self, AnyError> {
// for now, we don't support these scenarios
if text.contains("||") {
bail!("not supported '||'");
}
if text.contains(',') {
bail!("not supported ','");
}
// force exact versions to be matched exactly
let text = if semver::Version::parse(text).is_ok() {
Cow::Owned(format!("={}", text))
} else {
Cow::Borrowed(text)
};
// force requirements like 1.2 to be ~1.2 instead of ^1.2
let text = if MINOR_SPECIFIER_RE.is_match(&text) {
Cow::Owned(format!("~{}", text))
} else {
text
};
Ok(Self(semver::VersionReq::parse(&text)?))
}
pub fn matches(&self, version: &semver::Version) -> bool {
self.0.matches(version)
}
}
/// A version requirement found in an npm package's dependencies.
pub struct NpmVersionReq {
raw_text: String,
comparators: Vec<semver::VersionReq>,
}
impl NpmVersionReq {
pub fn parse(text: &str) -> Result<NpmVersionReq, AnyError> {
// semver::VersionReq doesn't support spaces between comparators
// and it doesn't support using || for "OR", so we pre-process
// the version requirement in order to make this work.
let raw_text = text.to_string();
let part_texts = text.split("||").collect::<Vec<_>>();
let mut comparators = Vec::with_capacity(part_texts.len());
for part in part_texts {
comparators.push(npm_version_req_parse_part(part)?);
}
Ok(NpmVersionReq {
raw_text,
comparators,
})
}
}
impl NpmVersionMatcher for NpmVersionReq {
fn matches(&self, version: &semver::Version) -> bool {
self.comparators.iter().any(|c| c.matches(version))
}
fn version_text(&self) -> String {
self.raw_text.to_string()
}
}
fn npm_version_req_parse_part(
text: &str,
) -> Result<semver::VersionReq, AnyError> {
let text = text.trim();
let text = text.strip_prefix('v').unwrap_or(text);
// force exact versions to be matched exactly
let text = if semver::Version::parse(text).is_ok() {
Cow::Owned(format!("={}", text))
} else {
Cow::Borrowed(text)
};
// force requirements like 1.2 to be ~1.2 instead of ^1.2
let text = if MINOR_SPECIFIER_RE.is_match(&text) {
Cow::Owned(format!("~{}", text))
} else {
text
};
let mut chars = text.chars().enumerate().peekable();
let mut final_text = String::new();
while chars.peek().is_some() {
let (i, c) = chars.next().unwrap();
let is_greater_or_less_than = c == '<' || c == '>';
if is_greater_or_less_than || c == '=' {
if i > 0 {
final_text = final_text.trim().to_string();
// add a comma to make semver::VersionReq parse this
final_text.push(',');
}
final_text.push(c);
let next_char = chars.peek().map(|(_, c)| c);
if is_greater_or_less_than && matches!(next_char, Some('=')) {
let c = chars.next().unwrap().1; // skip
final_text.push(c);
}
} else {
final_text.push(c);
}
}
Ok(semver::VersionReq::parse(&final_text)?)
}
#[cfg(test)]
mod tests {
use super::*;
struct VersionReqTester(SpecifierVersionReq);
impl VersionReqTester {
fn new(text: &str) -> Self {
Self(SpecifierVersionReq::parse(text).unwrap())
}
fn matches(&self, version: &str) -> bool {
self.0.matches(&semver::Version::parse(version).unwrap())
}
}
#[test]
fn version_req_exact() {
let tester = VersionReqTester::new("1.0.1");
assert!(!tester.matches("1.0.0"));
assert!(tester.matches("1.0.1"));
assert!(!tester.matches("1.0.2"));
assert!(!tester.matches("1.1.1"));
}
#[test]
fn version_req_minor() {
let tester = VersionReqTester::new("1.1");
assert!(!tester.matches("1.0.0"));
assert!(tester.matches("1.1.0"));
assert!(tester.matches("1.1.1"));
assert!(!tester.matches("1.2.0"));
assert!(!tester.matches("1.2.1"));
}
struct NpmVersionReqTester(NpmVersionReq);
impl NpmVersionReqTester {
fn new(text: &str) -> Self {
Self(NpmVersionReq::parse(text).unwrap())
}
fn matches(&self, version: &str) -> bool {
self.0.matches(&semver::Version::parse(version).unwrap())
}
}
#[test]
pub fn npm_version_req_with_v() {
assert!(NpmVersionReq::parse("v1.0.0").is_ok());
}
#[test]
pub fn npm_version_req_exact() {
let tester = NpmVersionReqTester::new("2.1.2");
assert!(!tester.matches("2.1.1"));
assert!(tester.matches("2.1.2"));
assert!(!tester.matches("2.1.3"));
let tester = NpmVersionReqTester::new("2.1.2 || 2.1.5");
assert!(!tester.matches("2.1.1"));
assert!(tester.matches("2.1.2"));
assert!(!tester.matches("2.1.3"));
assert!(!tester.matches("2.1.4"));
assert!(tester.matches("2.1.5"));
assert!(!tester.matches("2.1.6"));
}
#[test]
pub fn npm_version_req_minor() {
let tester = NpmVersionReqTester::new("1.1");
assert!(!tester.matches("1.0.0"));
assert!(tester.matches("1.1.0"));
assert!(tester.matches("1.1.1"));
assert!(!tester.matches("1.2.0"));
assert!(!tester.matches("1.2.1"));
}
#[test]
pub fn npm_version_req_ranges() {
let tester = NpmVersionReqTester::new(">= 2.1.2 < 3.0.0 || 5.x");
assert!(!tester.matches("2.1.1"));
assert!(tester.matches("2.1.2"));
assert!(tester.matches("2.9.9"));
assert!(!tester.matches("3.0.0"));
assert!(tester.matches("5.0.0"));
assert!(tester.matches("5.1.0"));
assert!(!tester.matches("6.1.0"));
}
}

View file

@ -443,12 +443,7 @@ impl ProcState {
.add_package_reqs(npm_package_references)
.await?;
self.npm_resolver.cache_packages().await?;
// add the builtin node modules to the graph data
let node_std_graph = self
.create_graph(vec![(compat::MODULE_ALL_URL.clone(), ModuleKind::Esm)])
.await?;
self.graph_data.write().add_graph(&node_std_graph, false);
self.prepare_node_std_graph().await?;
}
// type check if necessary
@ -492,6 +487,15 @@ impl ProcState {
Ok(())
}
/// Add the builtin node modules to the graph data.
pub async fn prepare_node_std_graph(&self) -> Result<(), AnyError> {
let node_std_graph = self
.create_graph(vec![(compat::MODULE_ALL_URL.clone(), ModuleKind::Esm)])
.await?;
self.graph_data.write().add_graph(&node_std_graph, false);
Ok(())
}
fn handle_node_resolve_result(
&self,
result: Result<Option<ResolveResponse>, AnyError>,

View file

@ -151,18 +151,47 @@ fn cached_only_after_first_run() {
.spawn()
.unwrap();
eprintln!("DENO DIR: {}", deno_dir.path().display());
std::mem::forget(deno_dir);
let output = deno.wait_with_output().unwrap();
let stderr = String::from_utf8_lossy(&output.stderr);
let stdout = String::from_utf8_lossy(&output.stdout);
eprintln!("stderr {}", stderr);
eprintln!("stdout {}", stdout);
assert!(output.status.success());
assert!(stderr.is_empty());
assert_contains!(stdout, "createChalk: chalk");
}
#[test]
fn deno_run_cjs_module() {
let _server = http_server();
let deno_dir = util::new_deno_dir();
let deno = util::deno_cmd_with_deno_dir(&deno_dir)
.current_dir(deno_dir.path())
.arg("run")
.arg("--unstable")
.arg("--allow-read")
.arg("--allow-env")
.arg("--allow-write")
.arg("npm:mkdirp@1.0.4")
.arg("test_dir")
.env("NO_COLOR", "1")
.envs(env_vars())
.spawn()
.unwrap();
let output = deno.wait_with_output().unwrap();
assert!(output.status.success());
assert!(deno_dir.path().join("test_dir").exists());
}
itest!(deno_run_non_existent {
args: "run --unstable npm:mkdirp@0.5.125",
output: "npm/deno_run_non_existent.out",
envs: env_vars(),
http_server: true,
exit_code: 1,
});
#[test]
fn ensure_registry_files_local() {
// ensures the registry files all point at local tarballs

View file

@ -2,7 +2,7 @@
// and also get the parent directory index.js file using require("..")
import Ajv from "npm:ajv@~8.11";
import addFormats from "npm:ajv-formats@2.1.1";
import { expect } from "npm:chai@4.2";
import { expect } from "npm:chai@4.3";
const ajv = new Ajv();
addFormats(ajv);

View file

@ -1,5 +1,5 @@
import chalk from "npm:chalk@4";
import { expect } from "npm:chai@4.2";
import { expect } from "npm:chai@4.3";
console.log(chalk.green("chalk cjs loads"));

View file

@ -0,0 +1,2 @@
Download http://localhost:4545/npm/registry/mkdirp
error: Could not find npm package 'mkdirp' matching =0.5.125. Try retreiving the latest npm package information by running with --reload

Binary file not shown.

File diff suppressed because it is too large Load diff

View file

@ -362,7 +362,8 @@ async fn bench_specifier(
ps.options.unstable(),
)],
Default::default(),
);
)
.await?;
worker.run_bench_specifier().await
}

View file

@ -723,7 +723,8 @@ async fn test_specifier(
stdout: StdioPipe::File(sender.stdout()),
stderr: StdioPipe::File(sender.stderr()),
},
);
)
.await?;
worker.run_test_specifier(mode).await
}

View file

@ -10,6 +10,7 @@ use deno_core::located_script_name;
use deno_core::serde_json::json;
use deno_core::Extension;
use deno_core::ModuleId;
use deno_graph::source::ResolveResponse;
use deno_runtime::colors;
use deno_runtime::ops::worker_host::CreateWebWorkerCb;
use deno_runtime::ops::worker_host::WorkerEventCb;
@ -26,6 +27,7 @@ use crate::errors;
use crate::fmt_errors::format_js_error;
use crate::module_loader::CliModuleLoader;
use crate::node;
use crate::npm::NpmPackageReference;
use crate::ops;
use crate::proc_state::ProcState;
use crate::tools;
@ -35,6 +37,7 @@ use crate::version;
pub struct CliMainWorker {
main_module: ModuleSpecifier,
is_main_cjs: bool,
worker: MainWorker,
ps: ProcState,
}
@ -99,8 +102,14 @@ impl CliMainWorker {
true,
)?;
}
} else if self.is_main_cjs {
node::initialize_runtime(&mut self.worker.js_runtime).await?;
node::load_cjs_module_from_ext_node(
&mut self.worker.js_runtime,
&self.main_module.to_file_path().unwrap().to_string_lossy(),
true,
)?;
} else {
// Regular ES module execution
self.execute_main_module_possibly_with_npm().await?;
}
@ -438,13 +447,31 @@ impl CliMainWorker {
}
}
pub fn create_main_worker(
pub async fn create_main_worker(
ps: &ProcState,
main_module: ModuleSpecifier,
permissions: Permissions,
mut custom_extensions: Vec<Extension>,
stdio: deno_runtime::ops::io::Stdio,
) -> CliMainWorker {
) -> Result<CliMainWorker, AnyError> {
let (main_module, is_main_cjs) = if let Ok(package_ref) =
NpmPackageReference::from_specifier(&main_module)
{
ps.npm_resolver
.add_package_reqs(vec![package_ref.req.clone()])
.await?;
ps.npm_resolver.cache_packages().await?;
ps.prepare_node_std_graph().await?;
let resolve_response = node::node_resolve_binary_export(
&package_ref.req,
package_ref.sub_path.as_deref(),
&ps.npm_resolver,
)?;
let is_main_cjs = matches!(resolve_response, ResolveResponse::CommonJs(_));
(resolve_response.to_result()?, is_main_cjs)
} else {
(main_module, false)
};
let module_loader = CliModuleLoader::new(ps.clone());
let maybe_inspector_server = ps.maybe_inspector_server.clone();
@ -518,11 +545,12 @@ pub fn create_main_worker(
permissions,
options,
);
CliMainWorker {
Ok(CliMainWorker {
main_module,
is_main_cjs,
worker,
ps: ps.clone(),
}
})
}
fn create_web_worker_preload_module_callback(

View file

@ -403,11 +403,14 @@
paths.push(denoDirPath);
}
}
paths.push(...ops.op_require_resolve_lookup_paths(
const lookupPathsResult = ops.op_require_resolve_lookup_paths(
request,
parent?.paths,
parent?.filename ?? "",
));
);
if (lookupPathsResult) {
paths.push(...lookupPathsResult);
}
return paths;
};

View file

@ -963,7 +963,7 @@ async fn main_server(
return Ok(file_resp);
} else if should_download_npm_packages() {
if let Err(err) =
download_npm_registry_file(&file_path, is_tarball).await
download_npm_registry_file(req.uri(), &file_path, is_tarball).await
{
return Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
@ -992,15 +992,21 @@ fn should_download_npm_packages() -> bool {
}
async fn download_npm_registry_file(
uri: &hyper::Uri,
file_path: &PathBuf,
is_tarball: bool,
) -> Result<(), anyhow::Error> {
let package_name = file_path
.parent()
let url_parts = uri
.path()
.strip_prefix("/npm/registry/")
.unwrap()
.file_name()
.unwrap()
.to_string_lossy();
.split('/')
.collect::<Vec<_>>();
let package_name = if url_parts[0].starts_with('@') {
url_parts.into_iter().take(2).collect::<Vec<_>>().join("/")
} else {
url_parts.into_iter().take(1).collect::<Vec<_>>().join("/")
};
let url = if is_tarball {
let file_name = file_path.file_name().unwrap().to_string_lossy();
format!(