summaryrefslogtreecommitdiff
path: root/cli/npm/installer.rs
blob: 41d85a9b933ed4ccde3a4778b5bb9f8fc6bcb131 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.

use std::sync::Arc;

use deno_core::error::AnyError;
use deno_core::futures::stream::FuturesOrdered;
use deno_core::futures::StreamExt;
use deno_npm::registry::NpmRegistryApi;

use crate::args::package_json::PackageJsonDeps;
use crate::util::sync::AtomicFlag;

use super::NpmRegistry;
use super::NpmResolution;

#[derive(Debug)]
struct PackageJsonDepsInstallerInner {
  has_installed_flag: AtomicFlag,
  npm_registry_api: NpmRegistry,
  npm_resolution: NpmResolution,
  package_deps: PackageJsonDeps,
}

/// Holds and controls installing dependencies from package.json.
#[derive(Debug, Clone, Default)]
pub struct PackageJsonDepsInstaller(Option<Arc<PackageJsonDepsInstallerInner>>);

impl PackageJsonDepsInstaller {
  pub fn new(
    npm_registry_api: NpmRegistry,
    npm_resolution: NpmResolution,
    deps: Option<PackageJsonDeps>,
  ) -> Self {
    Self(deps.map(|package_deps| {
      Arc::new(PackageJsonDepsInstallerInner {
        has_installed_flag: Default::default(),
        npm_registry_api,
        npm_resolution,
        package_deps,
      })
    }))
  }

  pub fn package_deps(&self) -> Option<&PackageJsonDeps> {
    self.0.as_ref().map(|inner| &inner.package_deps)
  }

  /// Installs the top level dependencies in the package.json file
  /// without going through and resolving the descendant dependencies yet.
  pub async fn ensure_top_level_install(&self) -> Result<(), AnyError> {
    let inner = match &self.0 {
      Some(inner) => inner,
      None => return Ok(()),
    };

    if !inner.has_installed_flag.raise() {
      return Ok(()); // already installed by something else
    }

    let mut package_reqs = inner
      .package_deps
      .values()
      .filter_map(|r| r.as_ref().ok())
      .collect::<Vec<_>>();
    package_reqs.sort(); // deterministic resolution

    let mut req_with_infos =
      FuturesOrdered::from_iter(package_reqs.into_iter().map(|req| {
        let api = inner.npm_registry_api.clone();
        async move {
          let info = api.package_info(&req.name).await?;
          Ok::<_, AnyError>((req, info))
        }
      }));

    while let Some(result) = req_with_infos.next().await {
      let (req, info) = result?;
      inner
        .npm_resolution
        .resolve_package_req_as_pending_with_info(req, &info)?;
    }

    Ok(())
  }
}