summaryrefslogtreecommitdiff
path: root/cli/lsp/cache.rs
blob: 941b6d9cfc2a0fb3259cbb312fbd3f2e6841a428 (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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.

use crate::cache::CacherLoader;
use crate::cache::FetchCacher;
use crate::config_file::ConfigFile;
use crate::flags::Flags;
use crate::graph_util::graph_valid;
use crate::proc_state::ProcState;
use crate::resolver::ImportMapResolver;
use crate::resolver::JsxResolver;

use deno_core::anyhow::anyhow;
use deno_core::error::AnyError;
use deno_core::ModuleSpecifier;
use deno_runtime::permissions::Permissions;
use deno_runtime::tokio_util::create_basic_runtime;
use import_map::ImportMap;
use std::path::PathBuf;
use std::sync::Arc;
use std::thread;
use tokio::sync::mpsc;
use tokio::sync::oneshot;

type Request = (Vec<ModuleSpecifier>, oneshot::Sender<Result<(), AnyError>>);

/// A "server" that handles requests from the language server to cache modules
/// in its own thread.
#[derive(Debug)]
pub(crate) struct CacheServer(mpsc::UnboundedSender<Request>);

impl CacheServer {
  pub async fn new(
    maybe_cache_path: Option<PathBuf>,
    maybe_import_map: Option<Arc<ImportMap>>,
    maybe_config_file: Option<ConfigFile>,
  ) -> Self {
    let (tx, mut rx) = mpsc::unbounded_channel::<Request>();
    let _join_handle = thread::spawn(move || {
      let runtime = create_basic_runtime();
      runtime.block_on(async {
        let ps = ProcState::build(Flags {
          cache_path: maybe_cache_path,
          ..Default::default()
        })
        .await
        .unwrap();
        let maybe_import_map_resolver =
          maybe_import_map.map(ImportMapResolver::new);
        let maybe_jsx_resolver = maybe_config_file
          .as_ref()
          .map(|cf| {
            cf.to_maybe_jsx_import_source_module()
              .map(|im| JsxResolver::new(im, maybe_import_map_resolver.clone()))
          })
          .flatten();
        let maybe_resolver = if maybe_jsx_resolver.is_some() {
          maybe_jsx_resolver.as_ref().map(|jr| jr.as_resolver())
        } else {
          maybe_import_map_resolver
            .as_ref()
            .map(|im| im.as_resolver())
        };
        let maybe_imports = maybe_config_file
          .map(|cf| cf.to_maybe_imports().ok())
          .flatten()
          .flatten();
        let mut cache = FetchCacher::new(
          ps.dir.gen_cache.clone(),
          ps.file_fetcher.clone(),
          Permissions::allow_all(),
          Permissions::allow_all(),
        );

        while let Some((roots, tx)) = rx.recv().await {
          let graph = deno_graph::create_graph(
            roots,
            false,
            maybe_imports.clone(),
            cache.as_mut_loader(),
            maybe_resolver,
            None,
            None,
          )
          .await;

          if tx.send(graph_valid(&graph, true, false)).is_err() {
            log::warn!("cannot send to client");
          }
        }
      })
    });

    Self(tx)
  }

  /// Attempt to cache the supplied module specifiers and their dependencies in
  /// the current DENO_DIR, returning any errors, so they can be returned to the
  /// client.
  pub async fn cache(
    &self,
    roots: Vec<ModuleSpecifier>,
  ) -> Result<(), AnyError> {
    let (tx, rx) = oneshot::channel::<Result<(), AnyError>>();
    if self.0.send((roots, tx)).is_err() {
      return Err(anyhow!("failed to send request to cache thread"));
    }
    rx.await?
  }
}