mirror of
https://github.com/denoland/deno.git
synced 2025-07-07 21:35:07 +00:00

Some checks failed
ci / pre-build (push) Has been cancelled
ci / build libs (push) Has been cancelled
ci / test debug linux-aarch64 (push) Has been cancelled
ci / test release linux-aarch64 (push) Has been cancelled
ci / test debug macos-aarch64 (push) Has been cancelled
ci / test release macos-aarch64 (push) Has been cancelled
ci / bench release linux-x86_64 (push) Has been cancelled
ci / lint debug linux-x86_64 (push) Has been cancelled
ci / lint debug macos-x86_64 (push) Has been cancelled
ci / lint debug windows-x86_64 (push) Has been cancelled
ci / test debug linux-x86_64 (push) Has been cancelled
ci / test release linux-x86_64 (push) Has been cancelled
ci / test debug macos-x86_64 (push) Has been cancelled
ci / test release macos-x86_64 (push) Has been cancelled
ci / test debug windows-x86_64 (push) Has been cancelled
ci / test release windows-x86_64 (push) Has been cancelled
ci / publish canary (push) Has been cancelled
171 lines
4.9 KiB
Rust
171 lines
4.9 KiB
Rust
// Copyright 2018-2025 the Deno authors. MIT license.
|
|
|
|
use deno_ast::ParsedSource;
|
|
use deno_graph::ast::CapturingEsParser;
|
|
use deno_graph::ast::DefaultEsParser;
|
|
use deno_graph::ast::EsParser;
|
|
use deno_graph::ast::ParsedSourceStore;
|
|
use deno_media_type::MediaType;
|
|
use url::Url;
|
|
|
|
/// 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: &Url,
|
|
) -> Result<Option<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)
|
|
}
|
|
}
|
|
|
|
#[allow(clippy::disallowed_types)] // ok because we always store source text as Arc<str>
|
|
type ArcStr = std::sync::Arc<str>;
|
|
|
|
#[allow(clippy::disallowed_types)]
|
|
pub type ParsedSourceCacheRc = crate::sync::MaybeArc<ParsedSourceCache>;
|
|
|
|
#[derive(Debug, Default)]
|
|
pub struct ParsedSourceCache {
|
|
sources: crate::sync::MaybeDashMap<Url, 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> {
|
|
self.get_matching_parsed_source(
|
|
&module.specifier,
|
|
module.media_type,
|
|
module.source.text.clone(),
|
|
)
|
|
}
|
|
|
|
#[allow(clippy::result_large_err)]
|
|
pub fn get_matching_parsed_source(
|
|
&self,
|
|
specifier: &Url,
|
|
media_type: MediaType,
|
|
source: ArcStr,
|
|
) -> Result<ParsedSource, deno_ast::ParseDiagnostic> {
|
|
let parser = self.as_capturing_parser();
|
|
// this will conditionally parse because it's using a CapturingEsParser
|
|
parser.parse_program(deno_graph::ast::ParseOptions {
|
|
specifier,
|
|
source,
|
|
media_type,
|
|
scope_analysis: false,
|
|
})
|
|
}
|
|
|
|
#[allow(clippy::result_large_err, clippy::disallowed_types)]
|
|
pub fn remove_or_parse_module(
|
|
&self,
|
|
specifier: &Url,
|
|
media_type: MediaType,
|
|
source: ArcStr,
|
|
) -> 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 = deno_graph::ast::ParseOptions {
|
|
specifier,
|
|
source,
|
|
media_type,
|
|
scope_analysis: false,
|
|
};
|
|
DefaultEsParser.parse_program(options)
|
|
}
|
|
|
|
/// Frees the parsed source from memory.
|
|
pub fn free(&self, specifier: &Url) {
|
|
self.sources.remove(specifier);
|
|
}
|
|
|
|
/// Fress all parsed sources from memory.
|
|
pub fn free_all(&self) {
|
|
self.sources.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)
|
|
}
|
|
|
|
#[allow(clippy::len_without_is_empty)]
|
|
pub fn len(&self) -> usize {
|
|
self.sources.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 ParsedSourceStore for ParsedSourceCache {
|
|
fn set_parsed_source(
|
|
&self,
|
|
specifier: Url,
|
|
parsed_source: ParsedSource,
|
|
) -> Option<ParsedSource> {
|
|
self.sources.insert(specifier, parsed_source)
|
|
}
|
|
|
|
fn get_parsed_source(&self, specifier: &Url) -> Option<ParsedSource> {
|
|
self.sources.get(specifier).map(|p| p.clone())
|
|
}
|
|
|
|
fn remove_parsed_source(&self, specifier: &Url) -> Option<ParsedSource> {
|
|
self.sources.remove(specifier).map(|(_, p)| p)
|
|
}
|
|
|
|
fn get_scope_analysis_parsed_source(
|
|
&self,
|
|
specifier: &Url,
|
|
) -> Option<ParsedSource> {
|
|
{
|
|
let parsed_source = self.sources.get(specifier)?;
|
|
if parsed_source.has_scope_analysis() {
|
|
return Some(parsed_source.clone());
|
|
}
|
|
}
|
|
// upgrade to have scope analysis
|
|
let (specifier, parsed_source) = self.sources.remove(specifier)?;
|
|
let parsed_source = parsed_source.into_with_scope_analysis();
|
|
self.sources.insert(specifier, parsed_source.clone());
|
|
Some(parsed_source)
|
|
}
|
|
}
|