summaryrefslogtreecommitdiff
path: root/cli/cache/parsed_source.rs
blob: 77f2e59534ad174dd98edbfc41e55f846bb39fc4 (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
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.

use std::collections::HashMap;
use std::sync::Arc;

use deno_ast::MediaType;
use deno_ast::ModuleSpecifier;
use deno_ast::ParsedSource;
use deno_core::parking_lot::Mutex;
use deno_graph::CapturingModuleParser;
use deno_graph::ModuleParser;
use deno_graph::ParseOptions;

#[derive(Default)]
pub struct ParsedSourceCache {
  sources: Mutex<HashMap<ModuleSpecifier, ParsedSource>>,
}

impl ParsedSourceCache {
  pub fn get_parsed_source_from_esm_module(
    &self,
    module: &deno_graph::EsmModule,
  ) -> Result<ParsedSource, deno_ast::Diagnostic> {
    self.get_or_parse_module(
      &module.specifier,
      module.source.clone(),
      module.media_type,
    )
  }

  /// Gets the matching `ParsedSource` from the cache
  /// or parses a new one and stores that in the cache.
  pub fn get_or_parse_module(
    &self,
    specifier: &deno_graph::ModuleSpecifier,
    source: Arc<str>,
    media_type: MediaType,
  ) -> deno_core::anyhow::Result<ParsedSource, deno_ast::Diagnostic> {
    let parser = self.as_capturing_parser();
    // this will conditionally parse because it's using a CapturingModuleParser
    parser.parse_module(ParseOptions {
      specifier,
      source,
      media_type,
      // don't bother enabling because this method is currently only used for emitting
      scope_analysis: false,
    })
  }

  /// Frees the parsed source from memory.
  pub fn free(&self, specifier: &ModuleSpecifier) {
    self.sources.lock().remove(specifier);
  }

  /// Creates a parser that will reuse a ParsedSource from the store
  /// if it exists, or else parse.
  pub fn as_capturing_parser(&self) -> CapturingModuleParser {
    CapturingModuleParser::new(None, self)
  }
}

/// It's ok that this is racy since in non-LSP situations
/// this will only ever store one form of a parsed source
/// and in LSP settings the concurrency will be enforced
/// at a higher level to ensure this will have the latest
/// parsed source.
impl deno_graph::ParsedSourceStore for ParsedSourceCache {
  fn set_parsed_source(
    &self,
    specifier: deno_graph::ModuleSpecifier,
    parsed_source: ParsedSource,
  ) -> Option<ParsedSource> {
    self.sources.lock().insert(specifier, parsed_source)
  }

  fn get_parsed_source(
    &self,
    specifier: &deno_graph::ModuleSpecifier,
  ) -> Option<ParsedSource> {
    self.sources.lock().get(specifier).cloned()
  }

  fn get_scope_analysis_parsed_source(
    &self,
    specifier: &deno_graph::ModuleSpecifier,
  ) -> Option<ParsedSource> {
    let mut sources = self.sources.lock();
    let parsed_source = sources.get(specifier)?;
    if parsed_source.has_scope_analysis() {
      Some(parsed_source.clone())
    } else {
      // upgrade to have scope analysis
      let parsed_source = sources.remove(specifier).unwrap();
      let parsed_source = parsed_source.into_with_scope_analysis();
      sources.insert(specifier.clone(), parsed_source.clone());
      Some(parsed_source)
    }
  }
}