2024-01-01 19:58:21 +00:00
|
|
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
2021-10-10 21:26:22 +00:00
|
|
|
|
2022-07-12 22:58:39 +00:00
|
|
|
use crate::cache::EmitCache;
|
2022-07-19 15:58:18 +00:00
|
|
|
use crate::cache::FastInsecureHasher;
|
2022-08-22 16:14:59 +00:00
|
|
|
use crate::cache::ParsedSourceCache;
|
2021-10-10 21:26:22 +00:00
|
|
|
|
2024-04-11 23:00:17 +00:00
|
|
|
use deno_ast::SourceMapOption;
|
2024-04-17 19:15:02 +00:00
|
|
|
use deno_ast::TranspileResult;
|
2021-10-10 21:26:22 +00:00
|
|
|
use deno_core::error::AnyError;
|
2024-01-10 04:18:40 +00:00
|
|
|
use deno_core::ModuleCodeString;
|
2021-10-10 21:26:22 +00:00
|
|
|
use deno_core::ModuleSpecifier;
|
|
|
|
use deno_graph::MediaType;
|
2023-04-13 18:03:07 +00:00
|
|
|
use deno_graph::Module;
|
|
|
|
use deno_graph::ModuleGraph;
|
2021-10-10 21:26:22 +00:00
|
|
|
use std::sync::Arc;
|
|
|
|
|
2023-04-13 18:03:07 +00:00
|
|
|
pub struct Emitter {
|
|
|
|
emit_cache: EmitCache,
|
2023-04-14 20:22:33 +00:00
|
|
|
parsed_source_cache: Arc<ParsedSourceCache>,
|
2024-04-11 23:00:17 +00:00
|
|
|
transpile_options: deno_ast::TranspileOptions,
|
2023-04-13 18:03:07 +00:00
|
|
|
emit_options: deno_ast::EmitOptions,
|
2024-04-11 23:00:17 +00:00
|
|
|
// cached hash of the transpile and emit options
|
|
|
|
transpile_and_emit_options_hash: u64,
|
2021-10-10 21:26:22 +00:00
|
|
|
}
|
|
|
|
|
2023-04-13 18:03:07 +00:00
|
|
|
impl Emitter {
|
|
|
|
pub fn new(
|
|
|
|
emit_cache: EmitCache,
|
2023-04-14 20:22:33 +00:00
|
|
|
parsed_source_cache: Arc<ParsedSourceCache>,
|
2024-04-11 23:00:17 +00:00
|
|
|
transpile_options: deno_ast::TranspileOptions,
|
2023-04-13 18:03:07 +00:00
|
|
|
emit_options: deno_ast::EmitOptions,
|
|
|
|
) -> Self {
|
2024-04-11 23:00:17 +00:00
|
|
|
let transpile_and_emit_options_hash = {
|
|
|
|
let mut hasher = FastInsecureHasher::default();
|
|
|
|
hasher.write_hashable(&transpile_options);
|
2024-04-17 19:15:02 +00:00
|
|
|
hasher.write_hashable(&emit_options);
|
2024-04-11 23:00:17 +00:00
|
|
|
hasher.finish()
|
|
|
|
};
|
2023-04-13 18:03:07 +00:00
|
|
|
Self {
|
|
|
|
emit_cache,
|
|
|
|
parsed_source_cache,
|
|
|
|
emit_options,
|
2024-04-11 23:00:17 +00:00
|
|
|
transpile_options,
|
|
|
|
transpile_and_emit_options_hash,
|
2023-04-13 18:03:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn cache_module_emits(
|
|
|
|
&self,
|
|
|
|
graph: &ModuleGraph,
|
|
|
|
) -> Result<(), AnyError> {
|
|
|
|
for module in graph.modules() {
|
2024-02-01 03:15:22 +00:00
|
|
|
if let Module::Js(module) = module {
|
2023-04-13 18:03:07 +00:00
|
|
|
let is_emittable = matches!(
|
|
|
|
module.media_type,
|
|
|
|
MediaType::TypeScript
|
|
|
|
| MediaType::Mts
|
|
|
|
| MediaType::Cts
|
|
|
|
| MediaType::Jsx
|
|
|
|
| MediaType::Tsx
|
|
|
|
);
|
|
|
|
if is_emittable {
|
|
|
|
self.emit_parsed_source(
|
|
|
|
&module.specifier,
|
|
|
|
module.media_type,
|
|
|
|
&module.source,
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-04-14 20:22:33 +00:00
|
|
|
/// Gets a cached emit if the source matches the hash found in the cache.
|
2023-06-26 13:10:27 +00:00
|
|
|
pub fn maybe_cached_emit(
|
2023-04-14 20:22:33 +00:00
|
|
|
&self,
|
|
|
|
specifier: &ModuleSpecifier,
|
|
|
|
source: &str,
|
|
|
|
) -> Option<String> {
|
|
|
|
let source_hash = self.get_source_hash(source);
|
|
|
|
self.emit_cache.get_emit_code(specifier, source_hash)
|
|
|
|
}
|
|
|
|
|
2023-04-13 18:03:07 +00:00
|
|
|
pub fn emit_parsed_source(
|
|
|
|
&self,
|
|
|
|
specifier: &ModuleSpecifier,
|
|
|
|
media_type: MediaType,
|
|
|
|
source: &Arc<str>,
|
2024-01-10 04:18:40 +00:00
|
|
|
) -> Result<ModuleCodeString, AnyError> {
|
2023-04-13 18:03:07 +00:00
|
|
|
let source_hash = self.get_source_hash(source);
|
|
|
|
|
|
|
|
if let Some(emit_code) =
|
|
|
|
self.emit_cache.get_emit_code(specifier, source_hash)
|
|
|
|
{
|
|
|
|
Ok(emit_code.into())
|
|
|
|
} else {
|
2024-04-14 21:15:17 +00:00
|
|
|
// nothing else needs the parsed source at this point, so remove from
|
|
|
|
// the cache in order to not transpile owned
|
|
|
|
let parsed_source = self.parsed_source_cache.remove_or_parse_module(
|
2023-04-13 18:03:07 +00:00
|
|
|
specifier,
|
|
|
|
source.clone(),
|
|
|
|
media_type,
|
|
|
|
)?;
|
2024-04-14 21:15:17 +00:00
|
|
|
let transpiled_source = match parsed_source
|
2024-04-17 19:15:02 +00:00
|
|
|
.transpile(&self.transpile_options, &self.emit_options)?
|
2024-04-14 21:15:17 +00:00
|
|
|
{
|
2024-04-17 19:15:02 +00:00
|
|
|
TranspileResult::Owned(source) => source,
|
|
|
|
TranspileResult::Cloned(source) => {
|
2024-04-14 21:15:17 +00:00
|
|
|
debug_assert!(false, "Transpile owned failed.");
|
2024-04-17 19:15:02 +00:00
|
|
|
source
|
2024-04-14 21:15:17 +00:00
|
|
|
}
|
|
|
|
};
|
2023-04-13 18:03:07 +00:00
|
|
|
debug_assert!(transpiled_source.source_map.is_none());
|
|
|
|
self.emit_cache.set_emit_code(
|
|
|
|
specifier,
|
|
|
|
source_hash,
|
|
|
|
&transpiled_source.text,
|
|
|
|
);
|
|
|
|
Ok(transpiled_source.text.into())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-31 00:25:58 +00:00
|
|
|
/// Expects a file URL, panics otherwise.
|
|
|
|
pub async fn load_and_emit_for_hmr(
|
|
|
|
&self,
|
|
|
|
specifier: &ModuleSpecifier,
|
|
|
|
) -> Result<String, AnyError> {
|
|
|
|
let media_type = MediaType::from_specifier(specifier);
|
|
|
|
let source_code = tokio::fs::read_to_string(
|
|
|
|
ModuleSpecifier::to_file_path(specifier).unwrap(),
|
|
|
|
)
|
|
|
|
.await?;
|
|
|
|
let source_arc: Arc<str> = source_code.into();
|
|
|
|
let parsed_source = self
|
|
|
|
.parsed_source_cache
|
2024-04-14 21:15:17 +00:00
|
|
|
.remove_or_parse_module(specifier, source_arc, media_type)?;
|
2024-04-17 19:15:02 +00:00
|
|
|
let mut options = self.emit_options.clone();
|
2024-04-11 23:00:17 +00:00
|
|
|
options.source_map = SourceMapOption::None;
|
2024-04-14 21:15:17 +00:00
|
|
|
let transpiled_source = parsed_source
|
2024-04-17 19:15:02 +00:00
|
|
|
.transpile(&self.transpile_options, &options)?
|
|
|
|
.into_source();
|
2023-10-31 00:25:58 +00:00
|
|
|
Ok(transpiled_source.text)
|
|
|
|
}
|
|
|
|
|
2023-04-13 18:03:07 +00:00
|
|
|
/// A hashing function that takes the source code and uses the global emit
|
|
|
|
/// options then generates a string hash which can be stored to
|
|
|
|
/// determine if the cached emit is valid or not.
|
2023-04-14 20:22:33 +00:00
|
|
|
fn get_source_hash(&self, source_text: &str) -> u64 {
|
2023-04-13 18:03:07 +00:00
|
|
|
FastInsecureHasher::new()
|
|
|
|
.write_str(source_text)
|
2024-04-11 23:00:17 +00:00
|
|
|
.write_u64(self.transpile_and_emit_options_hash)
|
2023-04-13 18:03:07 +00:00
|
|
|
.finish()
|
2021-10-10 21:26:22 +00:00
|
|
|
}
|
|
|
|
}
|