mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 20:32:10 +00:00
205 lines
6.7 KiB
Rust
205 lines
6.7 KiB
Rust
use std::path::Path;
|
|
|
|
use napi::Either;
|
|
use napi_derive::napi;
|
|
|
|
use oxc::{
|
|
codegen::CodegenReturn,
|
|
diagnostics::OxcDiagnostic,
|
|
isolated_declarations::IsolatedDeclarationsOptions,
|
|
napi::{
|
|
source_map::SourceMap,
|
|
transform::{TransformOptions, TransformResult},
|
|
},
|
|
span::SourceType,
|
|
transformer::{InjectGlobalVariablesConfig, InjectImport, ReplaceGlobalDefinesConfig},
|
|
CompilerInterface,
|
|
};
|
|
|
|
use crate::errors::wrap_diagnostics;
|
|
|
|
#[derive(Default)]
|
|
struct Compiler {
|
|
transform_options: oxc::transformer::TransformOptions,
|
|
isolated_declaration_options: Option<oxc::isolated_declarations::IsolatedDeclarationsOptions>,
|
|
|
|
sourcemap: bool,
|
|
|
|
printed: String,
|
|
printed_sourcemap: Option<SourceMap>,
|
|
|
|
declaration: Option<String>,
|
|
declaration_map: Option<SourceMap>,
|
|
|
|
define: Option<ReplaceGlobalDefinesConfig>,
|
|
inject: Option<InjectGlobalVariablesConfig>,
|
|
|
|
errors: Vec<OxcDiagnostic>,
|
|
}
|
|
|
|
impl Compiler {
|
|
fn new(options: Option<TransformOptions>) -> Result<Self, Vec<OxcDiagnostic>> {
|
|
let mut options = options;
|
|
|
|
let isolated_declaration_options = options
|
|
.as_ref()
|
|
.and_then(|o| o.typescript.as_ref())
|
|
.and_then(|o| o.declaration)
|
|
.map(oxc::isolated_declarations::IsolatedDeclarationsOptions::from);
|
|
|
|
let sourcemap = options.as_ref().and_then(|o| o.sourcemap).unwrap_or_default();
|
|
|
|
let define = options
|
|
.as_mut()
|
|
.and_then(|options| options.define.take())
|
|
.map(|map| {
|
|
let define = map.into_iter().collect::<Vec<_>>();
|
|
ReplaceGlobalDefinesConfig::new(&define)
|
|
})
|
|
.transpose()?;
|
|
|
|
let inject = options
|
|
.as_mut()
|
|
.and_then(|options| options.inject.take())
|
|
.map(|map| {
|
|
map.into_iter()
|
|
.map(|(local, value)| match value {
|
|
Either::A(source) => Ok(InjectImport::default_specifier(&source, &local)),
|
|
Either::B(v) => {
|
|
if v.len() != 2 {
|
|
return Err(vec![OxcDiagnostic::error(
|
|
"Inject plugin did not receive a tuple [string, string].",
|
|
)]);
|
|
}
|
|
let source = v[0].to_string();
|
|
Ok(if v[1] == "*" {
|
|
InjectImport::namespace_specifier(&source, &local)
|
|
} else {
|
|
InjectImport::named_specifier(&source, Some(&v[1]), &local)
|
|
})
|
|
}
|
|
})
|
|
.collect::<Result<Vec<_>, _>>()
|
|
})
|
|
.transpose()?
|
|
.map(InjectGlobalVariablesConfig::new);
|
|
|
|
let transform_options = match options {
|
|
Some(options) => oxc::transformer::TransformOptions::try_from(options)
|
|
.map_err(|err| vec![OxcDiagnostic::error(err)])?,
|
|
None => oxc::transformer::TransformOptions::default(),
|
|
};
|
|
|
|
Ok(Self {
|
|
transform_options,
|
|
isolated_declaration_options,
|
|
sourcemap,
|
|
printed: String::default(),
|
|
printed_sourcemap: None,
|
|
declaration: None,
|
|
declaration_map: None,
|
|
define,
|
|
inject,
|
|
errors: vec![],
|
|
})
|
|
}
|
|
}
|
|
|
|
impl CompilerInterface for Compiler {
|
|
fn handle_errors(&mut self, errors: Vec<OxcDiagnostic>) {
|
|
self.errors.extend(errors);
|
|
}
|
|
|
|
fn enable_sourcemap(&self) -> bool {
|
|
self.sourcemap
|
|
}
|
|
|
|
fn transform_options(&self) -> Option<&oxc::transformer::TransformOptions> {
|
|
Some(&self.transform_options)
|
|
}
|
|
|
|
fn isolated_declaration_options(&self) -> Option<IsolatedDeclarationsOptions> {
|
|
self.isolated_declaration_options
|
|
}
|
|
|
|
fn define_options(&self) -> Option<ReplaceGlobalDefinesConfig> {
|
|
self.define.clone()
|
|
}
|
|
|
|
fn inject_options(&self) -> Option<InjectGlobalVariablesConfig> {
|
|
self.inject.clone()
|
|
}
|
|
|
|
fn after_codegen(&mut self, ret: CodegenReturn) {
|
|
self.printed = ret.code;
|
|
self.printed_sourcemap = ret.map.map(SourceMap::from);
|
|
}
|
|
|
|
fn after_isolated_declarations(&mut self, ret: CodegenReturn) {
|
|
self.declaration.replace(ret.code);
|
|
self.declaration_map = ret.map.map(SourceMap::from);
|
|
}
|
|
}
|
|
|
|
/// Transpile a JavaScript or TypeScript into a target ECMAScript version.
|
|
///
|
|
/// @param filename The name of the file being transformed. If this is a
|
|
/// relative path, consider setting the {@link TransformOptions#cwd} option..
|
|
/// @param sourceText the source code itself
|
|
/// @param options The options for the transformation. See {@link
|
|
/// TransformOptions} for more information.
|
|
///
|
|
/// @returns an object containing the transformed code, source maps, and any
|
|
/// errors that occurred during parsing or transformation.
|
|
#[allow(clippy::needless_pass_by_value)]
|
|
#[napi]
|
|
pub fn transform(
|
|
filename: String,
|
|
source_text: String,
|
|
options: Option<TransformOptions>,
|
|
) -> TransformResult {
|
|
let source_path = Path::new(&filename);
|
|
|
|
let source_type = match options.as_ref().and_then(|options| options.lang.as_deref()) {
|
|
Some("js") => SourceType::mjs(),
|
|
Some("jsx") => SourceType::jsx(),
|
|
Some("ts") => SourceType::ts(),
|
|
Some("tsx") => SourceType::tsx(),
|
|
Some(lang) => {
|
|
return TransformResult {
|
|
errors: vec![format!("Incorrect lang '{lang}'")],
|
|
..Default::default()
|
|
}
|
|
}
|
|
None => {
|
|
let mut source_type = SourceType::from_path(source_path).unwrap_or_default();
|
|
// Force `script` or `module`
|
|
match options.as_ref().and_then(|options| options.source_type.as_deref()) {
|
|
Some("script") => source_type = source_type.with_script(true),
|
|
Some("module") => source_type = source_type.with_module(true),
|
|
_ => {}
|
|
}
|
|
source_type
|
|
}
|
|
};
|
|
|
|
let mut compiler = match Compiler::new(options) {
|
|
Ok(compiler) => compiler,
|
|
Err(errors) => {
|
|
return TransformResult {
|
|
errors: wrap_diagnostics(source_path, source_type, &source_text, errors),
|
|
..Default::default()
|
|
}
|
|
}
|
|
};
|
|
|
|
compiler.compile(&source_text, source_type, source_path);
|
|
|
|
TransformResult {
|
|
code: compiler.printed,
|
|
map: compiler.printed_sourcemap,
|
|
declaration: compiler.declaration,
|
|
declaration_map: compiler.declaration_map,
|
|
errors: wrap_diagnostics(source_path, source_type, &source_text, compiler.errors),
|
|
}
|
|
}
|