use std::{borrow::Cow, rc::Rc};
use deno_ast::{MediaType, ParseDiagnosticsError, ParseParams, SourceTextInfo, TranspileError};
use deno_core::{FastString, ModuleSpecifier, SourceMapData};
use deno_error::JsErrorBox;
pub type ModuleContents = (String, Option<SourceMapData>);
fn should_transpile(media_type: MediaType) -> bool {
matches!(
media_type,
MediaType::Jsx
| MediaType::TypeScript
| MediaType::Mts
| MediaType::Cts
| MediaType::Dts
| MediaType::Dmts
| MediaType::Dcts
| MediaType::Tsx
)
}
pub fn transpile(
module_specifier: &ModuleSpecifier,
code: &str,
) -> Result<ModuleContents, TranspileError> {
let mut media_type = MediaType::from_specifier(module_specifier);
if media_type == MediaType::Unknown && module_specifier.as_str().contains("/node:") {
media_type = MediaType::TypeScript;
}
let should_transpile = should_transpile(media_type);
let code = if should_transpile {
let sti = SourceTextInfo::from_string(code.to_string());
let text = sti.text();
let parsed = deno_ast::parse_module(ParseParams {
specifier: module_specifier.clone(),
text,
media_type,
capture_tokens: false,
scope_analysis: false,
maybe_syntax: None,
})
.map_err(|e| TranspileError::ParseErrors(ParseDiagnosticsError(vec![e])))?;
let transpile_options = deno_ast::TranspileOptions {
..Default::default()
};
let transpile_mod_options = deno_ast::TranspileModuleOptions {
..Default::default()
};
let emit_options = deno_ast::EmitOptions {
remove_comments: false,
source_map: deno_ast::SourceMapOption::Separate,
inline_sources: false,
..Default::default()
};
let res = parsed
.transpile(&transpile_options, &transpile_mod_options, &emit_options)?
.into_source();
let text = res.text;
let source_map: Option<SourceMapData> = res.source_map.map(|sm| sm.into_bytes().into());
(text, source_map)
} else {
(code.to_string(), None)
};
Ok(code)
}
#[allow(clippy::type_complexity)]
pub fn transpile_extension(
specifier: &ModuleSpecifier,
code: &str,
) -> Result<(FastString, Option<Cow<'static, [u8]>>), JsErrorBox> {
let (code, source_map) = transpile(specifier, code).map_err(JsErrorBox::from_err)?;
let code = FastString::from(code);
Ok((code, source_map))
}
pub type ExtensionTranspiler = Rc<
dyn Fn(FastString, FastString) -> Result<(FastString, Option<Cow<'static, [u8]>>), JsErrorBox>,
>;
pub type ExtensionTranspilation = (FastString, Option<Cow<'static, [u8]>>);