mirror of
https://github.com/denoland/deno.git
synced 2024-12-27 01:29:14 -05:00
124b28ab63
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
203 lines
6.1 KiB
Rust
203 lines
6.1 KiB
Rust
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
|
|
|
mod byonm;
|
|
mod common;
|
|
mod managed;
|
|
|
|
use std::path::Path;
|
|
use std::path::PathBuf;
|
|
use std::sync::Arc;
|
|
|
|
use common::maybe_auth_header_for_npm_registry;
|
|
use dashmap::DashMap;
|
|
use deno_ast::ModuleSpecifier;
|
|
use deno_core::error::AnyError;
|
|
use deno_core::serde_json;
|
|
use deno_npm::npm_rc::ResolvedNpmRc;
|
|
use deno_npm::registry::NpmPackageInfo;
|
|
use deno_resolver::npm::ByonmNpmResolver;
|
|
use deno_resolver::npm::ByonmResolvePkgFolderFromDenoReqError;
|
|
use deno_runtime::deno_node::NodeRequireResolver;
|
|
use deno_runtime::ops::process::NpmProcessStateProvider;
|
|
use deno_semver::package::PackageNv;
|
|
use deno_semver::package::PackageReq;
|
|
use managed::cache::registry_info::get_package_url;
|
|
use node_resolver::NpmResolver;
|
|
use thiserror::Error;
|
|
|
|
use crate::file_fetcher::FileFetcher;
|
|
|
|
pub use self::byonm::CliByonmNpmResolver;
|
|
pub use self::byonm::CliByonmNpmResolverCreateOptions;
|
|
pub use self::managed::CliNpmResolverManagedCreateOptions;
|
|
pub use self::managed::CliNpmResolverManagedSnapshotOption;
|
|
pub use self::managed::ManagedCliNpmResolver;
|
|
|
|
#[derive(Debug, Error)]
|
|
pub enum ResolvePkgFolderFromDenoReqError {
|
|
#[error(transparent)]
|
|
Managed(deno_core::error::AnyError),
|
|
#[error(transparent)]
|
|
Byonm(#[from] ByonmResolvePkgFolderFromDenoReqError),
|
|
}
|
|
|
|
pub enum CliNpmResolverCreateOptions {
|
|
Managed(CliNpmResolverManagedCreateOptions),
|
|
Byonm(CliByonmNpmResolverCreateOptions),
|
|
}
|
|
|
|
pub async fn create_cli_npm_resolver_for_lsp(
|
|
options: CliNpmResolverCreateOptions,
|
|
) -> Arc<dyn CliNpmResolver> {
|
|
use CliNpmResolverCreateOptions::*;
|
|
match options {
|
|
Managed(options) => {
|
|
managed::create_managed_npm_resolver_for_lsp(options).await
|
|
}
|
|
Byonm(options) => Arc::new(ByonmNpmResolver::new(options)),
|
|
}
|
|
}
|
|
|
|
pub async fn create_cli_npm_resolver(
|
|
options: CliNpmResolverCreateOptions,
|
|
) -> Result<Arc<dyn CliNpmResolver>, AnyError> {
|
|
use CliNpmResolverCreateOptions::*;
|
|
match options {
|
|
Managed(options) => managed::create_managed_npm_resolver(options).await,
|
|
Byonm(options) => Ok(Arc::new(ByonmNpmResolver::new(options))),
|
|
}
|
|
}
|
|
|
|
pub enum InnerCliNpmResolverRef<'a> {
|
|
Managed(&'a ManagedCliNpmResolver),
|
|
#[allow(dead_code)]
|
|
Byonm(&'a CliByonmNpmResolver),
|
|
}
|
|
|
|
pub trait CliNpmResolver: NpmResolver {
|
|
fn into_npm_resolver(self: Arc<Self>) -> Arc<dyn NpmResolver>;
|
|
fn into_require_resolver(self: Arc<Self>) -> Arc<dyn NodeRequireResolver>;
|
|
fn into_process_state_provider(
|
|
self: Arc<Self>,
|
|
) -> Arc<dyn NpmProcessStateProvider>;
|
|
|
|
fn clone_snapshotted(&self) -> Arc<dyn CliNpmResolver>;
|
|
|
|
fn as_inner(&self) -> InnerCliNpmResolverRef;
|
|
|
|
fn as_managed(&self) -> Option<&ManagedCliNpmResolver> {
|
|
match self.as_inner() {
|
|
InnerCliNpmResolverRef::Managed(inner) => Some(inner),
|
|
InnerCliNpmResolverRef::Byonm(_) => None,
|
|
}
|
|
}
|
|
|
|
fn as_byonm(&self) -> Option<&CliByonmNpmResolver> {
|
|
match self.as_inner() {
|
|
InnerCliNpmResolverRef::Managed(_) => None,
|
|
InnerCliNpmResolverRef::Byonm(inner) => Some(inner),
|
|
}
|
|
}
|
|
|
|
fn root_node_modules_path(&self) -> Option<&Path>;
|
|
|
|
fn resolve_pkg_folder_from_deno_module_req(
|
|
&self,
|
|
req: &PackageReq,
|
|
referrer: &ModuleSpecifier,
|
|
) -> Result<PathBuf, ResolvePkgFolderFromDenoReqError>;
|
|
|
|
/// Returns a hash returning the state of the npm resolver
|
|
/// or `None` if the state currently can't be determined.
|
|
fn check_state_hash(&self) -> Option<u64>;
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct NpmFetchResolver {
|
|
nv_by_req: DashMap<PackageReq, Option<PackageNv>>,
|
|
info_by_name: DashMap<String, Option<Arc<NpmPackageInfo>>>,
|
|
file_fetcher: Arc<FileFetcher>,
|
|
npmrc: Arc<ResolvedNpmRc>,
|
|
}
|
|
|
|
impl NpmFetchResolver {
|
|
pub fn new(
|
|
file_fetcher: Arc<FileFetcher>,
|
|
npmrc: Arc<ResolvedNpmRc>,
|
|
) -> Self {
|
|
Self {
|
|
nv_by_req: Default::default(),
|
|
info_by_name: Default::default(),
|
|
file_fetcher,
|
|
npmrc,
|
|
}
|
|
}
|
|
|
|
pub async fn req_to_nv(&self, req: &PackageReq) -> Option<PackageNv> {
|
|
if let Some(nv) = self.nv_by_req.get(req) {
|
|
return nv.value().clone();
|
|
}
|
|
let maybe_get_nv = || async {
|
|
let name = req.name.clone();
|
|
let package_info = self.package_info(&name).await?;
|
|
if let Some(dist_tag) = req.version_req.tag() {
|
|
let version = package_info.dist_tags.get(dist_tag)?.clone();
|
|
return Some(PackageNv { name, version });
|
|
}
|
|
// Find the first matching version of the package.
|
|
let mut versions = package_info.versions.keys().collect::<Vec<_>>();
|
|
versions.sort();
|
|
let version = versions
|
|
.into_iter()
|
|
.rev()
|
|
.find(|v| req.version_req.tag().is_none() && req.version_req.matches(v))
|
|
.cloned()?;
|
|
Some(PackageNv { name, version })
|
|
};
|
|
let nv = maybe_get_nv().await;
|
|
self.nv_by_req.insert(req.clone(), nv.clone());
|
|
nv
|
|
}
|
|
|
|
pub async fn package_info(&self, name: &str) -> Option<Arc<NpmPackageInfo>> {
|
|
if let Some(info) = self.info_by_name.get(name) {
|
|
return info.value().clone();
|
|
}
|
|
let fetch_package_info = || async {
|
|
let info_url = get_package_url(&self.npmrc, name);
|
|
let file_fetcher = self.file_fetcher.clone();
|
|
let registry_config = self.npmrc.get_registry_config(name);
|
|
// TODO(bartlomieju): this should error out, not use `.ok()`.
|
|
let maybe_auth_header =
|
|
maybe_auth_header_for_npm_registry(registry_config).ok()?;
|
|
// spawn due to the lsp's `Send` requirement
|
|
let file = deno_core::unsync::spawn(async move {
|
|
file_fetcher
|
|
.fetch_bypass_permissions_with_maybe_auth(
|
|
&info_url,
|
|
maybe_auth_header,
|
|
)
|
|
.await
|
|
.ok()
|
|
})
|
|
.await
|
|
.ok()??;
|
|
serde_json::from_slice::<NpmPackageInfo>(&file.source).ok()
|
|
};
|
|
let info = fetch_package_info().await.map(Arc::new);
|
|
self.info_by_name.insert(name.to_string(), info.clone());
|
|
info
|
|
}
|
|
}
|
|
|
|
pub const NPM_CONFIG_USER_AGENT_ENV_VAR: &str = "npm_config_user_agent";
|
|
|
|
pub fn get_npm_config_user_agent() -> String {
|
|
format!(
|
|
"deno/{} npm/? deno/{} {} {}",
|
|
env!("CARGO_PKG_VERSION"),
|
|
env!("CARGO_PKG_VERSION"),
|
|
std::env::consts::OS,
|
|
std::env::consts::ARCH
|
|
)
|
|
}
|