1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-25 15:29:32 -05:00
denoland-deno/cli/tools/run/mod.rs
Nathan Whitaker 04ae1a5517
fix(cli): set npm_config_user_agent when running npm packages or tasks (#26639)
Fixes #25342.

Still not sure on the exact user agent to set (should it include
`node`?).

After this PR, here's the state of running some `create-*` packages
(just ones I could think of off the top of my head):

| package                  | prints/runs/suggests deno install | notes |
| ---------------- | ------------- | ------ |
| `create-next-app` |  | falls back to npm, needs a PR
([code](c32e280209/packages/create-next-app/helpers/get-pkg-manager.ts (L3)))
| `sv create` |  | uses `package-manager-detector`, needs a PR
([code](https://github.com/antfu-collective/package-manager-detector/tree/main))
| `create-qwik` |  | runs `deno install` but suggests `deno start`
which doesn't work (should be `deno task start` or `deno run start`)
| `create-astro` |  | runs `deno install` but suggests `npm run dev`
later in output, probably needs a PR
| `nuxi init` |  | deno not an option in dialog, needs a PR
([code](f04e2e8944/src/commands/init.ts (L96-L102)))
| `create-react-app` |  | uses npm
| `ng new` (`@angular/cli`) |  | uses npm
| `create-vite` |  | suggests working deno commands 🎉 
| `create-solid` |   | suggests npm commands, needs PR

It's possible that fixing `package-manager-detector` or other packages
might make some of these just work, but haven't looked too carefully at
each
2024-10-31 22:19:19 -07:00

210 lines
5.9 KiB
Rust

// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use std::io::Read;
use std::sync::Arc;
use deno_config::deno_json::NodeModulesDirMode;
use deno_core::error::AnyError;
use deno_runtime::WorkerExecutionMode;
use crate::args::EvalFlags;
use crate::args::Flags;
use crate::args::WatchFlagsWithPaths;
use crate::factory::CliFactory;
use crate::file_fetcher::File;
use crate::util;
use crate::util::file_watcher::WatcherRestartMode;
pub mod hmr;
pub fn check_permission_before_script(flags: &Flags) {
if !flags.has_permission() && flags.has_permission_in_argv() {
log::warn!(
"{}",
crate::colors::yellow(
r#"Permission flags have likely been incorrectly set after the script argument.
To grant permissions, set them before the script argument. For example:
deno run --allow-read=. main.js"#
)
);
}
}
fn set_npm_user_agent() {
static ONCE: std::sync::Once = std::sync::Once::new();
ONCE.call_once(|| {
std::env::set_var(
crate::npm::NPM_CONFIG_USER_AGENT_ENV_VAR,
crate::npm::get_npm_config_user_agent(),
);
});
}
pub async fn run_script(
mode: WorkerExecutionMode,
flags: Arc<Flags>,
watch: Option<WatchFlagsWithPaths>,
) -> Result<i32, AnyError> {
check_permission_before_script(&flags);
if let Some(watch_flags) = watch {
return run_with_watch(mode, flags, watch_flags).await;
}
// 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
let factory = CliFactory::from_flags(flags);
let cli_options = factory.cli_options()?;
let deno_dir = factory.deno_dir()?;
let http_client = factory.http_client_provider();
// Run a background task that checks for available upgrades or output
// if an earlier run of this background task found a new version of Deno.
#[cfg(feature = "upgrade")]
super::upgrade::check_for_upgrades(
http_client.clone(),
deno_dir.upgrade_check_file_path(),
);
let main_module = cli_options.resolve_main_module()?;
if main_module.scheme() == "npm" {
set_npm_user_agent();
}
maybe_npm_install(&factory).await?;
let worker_factory = factory.create_cli_main_worker_factory().await?;
let mut worker = worker_factory
.create_main_worker(mode, main_module.clone())
.await?;
let exit_code = worker.run().await?;
Ok(exit_code)
}
pub async fn run_from_stdin(flags: Arc<Flags>) -> Result<i32, AnyError> {
let factory = CliFactory::from_flags(flags);
let cli_options = factory.cli_options()?;
let main_module = cli_options.resolve_main_module()?;
maybe_npm_install(&factory).await?;
let file_fetcher = factory.file_fetcher()?;
let worker_factory = factory.create_cli_main_worker_factory().await?;
let mut source = Vec::new();
std::io::stdin().read_to_end(&mut source)?;
// Save a fake file into file fetcher cache
// to allow module access by TS compiler
file_fetcher.insert_memory_files(File {
specifier: main_module.clone(),
maybe_headers: None,
source: source.into(),
});
let mut worker = worker_factory
.create_main_worker(WorkerExecutionMode::Run, main_module.clone())
.await?;
let exit_code = worker.run().await?;
Ok(exit_code)
}
// TODO(bartlomieju): this function is not handling `exit_code` set by the runtime
// code properly.
async fn run_with_watch(
mode: WorkerExecutionMode,
flags: Arc<Flags>,
watch_flags: WatchFlagsWithPaths,
) -> Result<i32, AnyError> {
util::file_watcher::watch_recv(
flags,
util::file_watcher::PrintConfig::new_with_banner(
if watch_flags.hmr { "HMR" } else { "Watcher" },
"Process",
!watch_flags.no_clear_screen,
),
WatcherRestartMode::Automatic,
move |flags, watcher_communicator, _changed_paths| {
Ok(async move {
let factory = CliFactory::from_flags_for_watcher(
flags,
watcher_communicator.clone(),
);
let cli_options = factory.cli_options()?;
let main_module = cli_options.resolve_main_module()?;
if main_module.scheme() == "npm" {
set_npm_user_agent();
}
maybe_npm_install(&factory).await?;
let _ = watcher_communicator.watch_paths(cli_options.watch_paths());
let mut worker = factory
.create_cli_main_worker_factory()
.await?
.create_main_worker(mode, main_module.clone())
.await?;
if watch_flags.hmr {
worker.run().await?;
} else {
worker.run_for_watcher().await?;
}
Ok(())
})
},
)
.await?;
Ok(0)
}
pub async fn eval_command(
flags: Arc<Flags>,
eval_flags: EvalFlags,
) -> Result<i32, AnyError> {
let factory = CliFactory::from_flags(flags);
let cli_options = factory.cli_options()?;
let file_fetcher = factory.file_fetcher()?;
let main_module = cli_options.resolve_main_module()?;
maybe_npm_install(&factory).await?;
// Create a dummy source file.
let source_code = if eval_flags.print {
format!("console.log({})", eval_flags.code)
} else {
eval_flags.code
};
// Save a fake file into file fetcher cache
// to allow module access by TS compiler.
file_fetcher.insert_memory_files(File {
specifier: main_module.clone(),
maybe_headers: None,
source: source_code.into_bytes().into(),
});
let worker_factory = factory.create_cli_main_worker_factory().await?;
let mut worker = worker_factory
.create_main_worker(WorkerExecutionMode::Eval, main_module.clone())
.await?;
let exit_code = worker.run().await?;
Ok(exit_code)
}
pub async fn maybe_npm_install(factory: &CliFactory) -> Result<(), AnyError> {
// ensure an "npm install" is done if the user has explicitly
// opted into using a managed node_modules directory
if factory.cli_options()?.node_modules_dir()?
== Some(NodeModulesDirMode::Auto)
{
if let Some(npm_resolver) = factory.npm_resolver().await?.as_managed() {
npm_resolver.ensure_top_level_package_json_install().await?;
}
}
Ok(())
}