mirror of
https://github.com/denoland/deno.git
synced 2025-09-26 12:19:12 +00:00
refactor: extract DenoDir
, Emitter
, EmitCache
and ParsedSourceCache
from cli crate (#29961)
This commit is contained in:
parent
45dfae18ee
commit
7a1e949c47
26 changed files with 490 additions and 406 deletions
162
cli/cache/parsed_source.rs
vendored
162
cli/cache/parsed_source.rs
vendored
|
@ -1,162 +0,0 @@
|
|||
// Copyright 2018-2025 the Deno authors. 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::ast::CapturingEsParser;
|
||||
use deno_graph::ast::DefaultEsParser;
|
||||
use deno_graph::ast::EsParser;
|
||||
use deno_graph::ast::ParseOptions;
|
||||
use deno_graph::ast::ParsedSourceStore;
|
||||
|
||||
/// Lazily parses JS/TS sources from a `deno_graph::ModuleGraph` given
|
||||
/// a `ParsedSourceCache`. Note that deno_graph doesn't necessarily cause
|
||||
/// files to end up in the `ParsedSourceCache` because it might have all
|
||||
/// the information it needs via caching in order to skip parsing.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct LazyGraphSourceParser<'a> {
|
||||
cache: &'a ParsedSourceCache,
|
||||
graph: &'a deno_graph::ModuleGraph,
|
||||
}
|
||||
|
||||
impl<'a> LazyGraphSourceParser<'a> {
|
||||
pub fn new(
|
||||
cache: &'a ParsedSourceCache,
|
||||
graph: &'a deno_graph::ModuleGraph,
|
||||
) -> Self {
|
||||
Self { cache, graph }
|
||||
}
|
||||
|
||||
#[allow(clippy::result_large_err)]
|
||||
pub fn get_or_parse_source(
|
||||
&self,
|
||||
module_specifier: &ModuleSpecifier,
|
||||
) -> Result<Option<deno_ast::ParsedSource>, deno_ast::ParseDiagnostic> {
|
||||
let Some(deno_graph::Module::Js(module)) = self.graph.get(module_specifier)
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
self
|
||||
.cache
|
||||
.get_parsed_source_from_js_module(module)
|
||||
.map(Some)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ParsedSourceCache {
|
||||
sources: Mutex<HashMap<ModuleSpecifier, ParsedSource>>,
|
||||
}
|
||||
|
||||
impl ParsedSourceCache {
|
||||
#[allow(clippy::result_large_err)]
|
||||
pub fn get_parsed_source_from_js_module(
|
||||
&self,
|
||||
module: &deno_graph::JsModule,
|
||||
) -> Result<ParsedSource, deno_ast::ParseDiagnostic> {
|
||||
let parser = self.as_capturing_parser();
|
||||
// this will conditionally parse because it's using a CapturingEsParser
|
||||
parser.parse_program(ParseOptions {
|
||||
specifier: &module.specifier,
|
||||
source: module.source.text.clone(),
|
||||
media_type: module.media_type,
|
||||
scope_analysis: false,
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(clippy::result_large_err)]
|
||||
pub fn remove_or_parse_module(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
source: Arc<str>,
|
||||
media_type: MediaType,
|
||||
) -> Result<ParsedSource, deno_ast::ParseDiagnostic> {
|
||||
if let Some(parsed_source) = self.remove_parsed_source(specifier) {
|
||||
if parsed_source.media_type() == media_type
|
||||
&& parsed_source.text().as_ref() == source.as_ref()
|
||||
{
|
||||
// note: message used tests
|
||||
log::debug!("Removed parsed source: {}", specifier);
|
||||
return Ok(parsed_source);
|
||||
}
|
||||
}
|
||||
let options = ParseOptions {
|
||||
specifier,
|
||||
source,
|
||||
media_type,
|
||||
scope_analysis: false,
|
||||
};
|
||||
DefaultEsParser.parse_program(options)
|
||||
}
|
||||
|
||||
/// Frees the parsed source from memory.
|
||||
pub fn free(&self, specifier: &ModuleSpecifier) {
|
||||
self.sources.lock().remove(specifier);
|
||||
}
|
||||
|
||||
/// Fress all parsed sources from memory.
|
||||
pub fn free_all(&self) {
|
||||
self.sources.lock().clear();
|
||||
}
|
||||
|
||||
/// Creates a parser that will reuse a ParsedSource from the store
|
||||
/// if it exists, or else parse.
|
||||
pub fn as_capturing_parser(&self) -> CapturingEsParser {
|
||||
CapturingEsParser::new(None, self)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn len(&self) -> usize {
|
||||
self.sources.lock().len()
|
||||
}
|
||||
}
|
||||
|
||||
/// 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::ast::ParsedSourceStore for ParsedSourceCache {
|
||||
fn set_parsed_source(
|
||||
&self,
|
||||
specifier: ModuleSpecifier,
|
||||
parsed_source: ParsedSource,
|
||||
) -> Option<ParsedSource> {
|
||||
self.sources.lock().insert(specifier, parsed_source)
|
||||
}
|
||||
|
||||
fn get_parsed_source(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> Option<ParsedSource> {
|
||||
self.sources.lock().get(specifier).cloned()
|
||||
}
|
||||
|
||||
fn remove_parsed_source(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> Option<ParsedSource> {
|
||||
self.sources.lock().remove(specifier)
|
||||
}
|
||||
|
||||
fn get_scope_analysis_parsed_source(
|
||||
&self,
|
||||
specifier: &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)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue