1#![expect(rustdoc::bare_urls)]
3
4use std::{
5 ops::ControlFlow,
6 path::{Path, PathBuf},
7};
8
9use napi::{Either, Task, bindgen_prelude::AsyncTask};
10use napi_derive::napi;
11use rustc_hash::FxHashMap;
12
13use oxc::{
14 CompilerInterface,
15 allocator::Allocator,
16 codegen::{Codegen, CodegenOptions, CodegenReturn},
17 diagnostics::OxcDiagnostic,
18 parser::Parser,
19 semantic::{SemanticBuilder, SemanticBuilderReturn},
20 span::SourceType,
21 transformer::{
22 EnvOptions, HelperLoaderMode, HelperLoaderOptions, JsxRuntime, ProposalOptions,
23 RewriteExtensionsMode,
24 },
25 transformer_plugins::{
26 InjectGlobalVariablesConfig, InjectImport, ModuleRunnerTransform,
27 ReplaceGlobalDefinesConfig,
28 },
29};
30use oxc_napi::{OxcError, get_source_type};
31use oxc_sourcemap::napi::SourceMap;
32
33use crate::IsolatedDeclarationsOptions;
34
35#[derive(Default)]
36#[napi(object)]
37pub struct TransformResult {
38 pub code: String,
42
43 pub map: Option<SourceMap>,
47
48 pub declaration: Option<String>,
57
58 pub declaration_map: Option<SourceMap>,
62
63 #[napi(ts_type = "Record<string, string>")]
73 pub helpers_used: FxHashMap<String, String>,
74
75 pub errors: Vec<OxcError>,
81}
82
83#[napi(object)]
87#[derive(Default)]
88pub struct TransformOptions {
89 #[napi(ts_type = "'js' | 'jsx' | 'ts' | 'tsx' | 'dts'")]
91 pub lang: Option<String>,
92
93 #[napi(ts_type = "'script' | 'module' | 'unambiguous' | undefined")]
95 pub source_type: Option<String>,
96
97 pub cwd: Option<String>,
100
101 pub sourcemap: Option<bool>,
109
110 pub assumptions: Option<CompilerAssumptions>,
112
113 pub typescript: Option<TypeScriptOptions>,
115
116 #[napi(ts_type = "'preserve' | JsxOptions")]
118 pub jsx: Option<Either<String, JsxOptions>>,
119
120 pub target: Option<Either<String, Vec<String>>>,
133
134 pub helpers: Option<Helpers>,
136
137 #[napi(ts_type = "Record<string, string>")]
139 pub define: Option<FxHashMap<String, String>>,
140
141 #[napi(ts_type = "Record<string, string | [string, string]>")]
143 pub inject: Option<FxHashMap<String, Either<String, Vec<String>>>>,
144
145 pub decorator: Option<DecoratorOptions>,
147
148 pub plugins: Option<PluginsOptions>,
150}
151
152impl TryFrom<TransformOptions> for oxc::transformer::TransformOptions {
153 type Error = String;
154
155 fn try_from(options: TransformOptions) -> Result<Self, Self::Error> {
156 let env = match options.target {
157 Some(Either::A(s)) => EnvOptions::from_target(&s)?,
158 Some(Either::B(list)) => EnvOptions::from_target_list(&list)?,
159 _ => EnvOptions::default(),
160 };
161 Ok(Self {
162 cwd: options.cwd.map(PathBuf::from).unwrap_or_default(),
163 assumptions: options.assumptions.map(Into::into).unwrap_or_default(),
164 typescript: options
165 .typescript
166 .map(oxc::transformer::TypeScriptOptions::from)
167 .unwrap_or_default(),
168 decorator: options
169 .decorator
170 .map(oxc::transformer::DecoratorOptions::from)
171 .unwrap_or_default(),
172 jsx: match options.jsx {
173 Some(Either::A(s)) => {
174 if s == "preserve" {
175 oxc::transformer::JsxOptions::disable()
176 } else {
177 return Err(format!("Invalid jsx option: `{s}`."));
178 }
179 }
180 Some(Either::B(options)) => oxc::transformer::JsxOptions::from(options),
181 None => oxc::transformer::JsxOptions::enable(),
182 },
183 env,
184 proposals: ProposalOptions::default(),
185 helper_loader: options
186 .helpers
187 .map_or_else(HelperLoaderOptions::default, HelperLoaderOptions::from),
188 plugins: options
189 .plugins
190 .map(oxc::transformer::PluginsOptions::from)
191 .unwrap_or_default(),
192 })
193 }
194}
195
196#[napi(object)]
197#[derive(Default, Debug)]
198pub struct CompilerAssumptions {
199 pub ignore_function_length: Option<bool>,
200 pub no_document_all: Option<bool>,
201 pub object_rest_no_symbols: Option<bool>,
202 pub pure_getters: Option<bool>,
203 pub set_public_class_fields: Option<bool>,
243}
244
245impl From<CompilerAssumptions> for oxc::transformer::CompilerAssumptions {
246 fn from(value: CompilerAssumptions) -> Self {
247 let ops = oxc::transformer::CompilerAssumptions::default();
248 Self {
249 ignore_function_length: value
250 .ignore_function_length
251 .unwrap_or(ops.ignore_function_length),
252 no_document_all: value.no_document_all.unwrap_or(ops.no_document_all),
253 object_rest_no_symbols: value
254 .object_rest_no_symbols
255 .unwrap_or(ops.object_rest_no_symbols),
256 pure_getters: value.pure_getters.unwrap_or(ops.pure_getters),
257 set_public_class_fields: value
258 .set_public_class_fields
259 .unwrap_or(ops.set_public_class_fields),
260 ..ops
261 }
262 }
263}
264
265#[napi(object)]
266#[derive(Default)]
267pub struct TypeScriptOptions {
268 pub jsx_pragma: Option<String>,
269 pub jsx_pragma_frag: Option<String>,
270 pub only_remove_type_imports: Option<bool>,
271 pub allow_namespaces: Option<bool>,
272 pub allow_declare_fields: Option<bool>,
279 pub remove_class_fields_without_initializer: Option<bool>,
312 pub declaration: Option<IsolatedDeclarationsOptions>,
320 #[napi(ts_type = "'rewrite' | 'remove' | boolean")]
329 pub rewrite_import_extensions: Option<Either<bool, String>>,
330}
331
332impl From<TypeScriptOptions> for oxc::transformer::TypeScriptOptions {
333 fn from(options: TypeScriptOptions) -> Self {
334 let ops = oxc::transformer::TypeScriptOptions::default();
335 oxc::transformer::TypeScriptOptions {
336 jsx_pragma: options.jsx_pragma.map(Into::into).unwrap_or(ops.jsx_pragma),
337 jsx_pragma_frag: options.jsx_pragma_frag.map(Into::into).unwrap_or(ops.jsx_pragma_frag),
338 only_remove_type_imports: options
339 .only_remove_type_imports
340 .unwrap_or(ops.only_remove_type_imports),
341 allow_namespaces: options.allow_namespaces.unwrap_or(ops.allow_namespaces),
342 allow_declare_fields: options.allow_declare_fields.unwrap_or(ops.allow_declare_fields),
343 optimize_const_enums: false,
344 remove_class_fields_without_initializer: options
345 .remove_class_fields_without_initializer
346 .unwrap_or(ops.remove_class_fields_without_initializer),
347 rewrite_import_extensions: options.rewrite_import_extensions.and_then(|value| {
348 match value {
349 Either::A(v) => {
350 if v {
351 Some(RewriteExtensionsMode::Rewrite)
352 } else {
353 None
354 }
355 }
356 Either::B(v) => match v.as_str() {
357 "rewrite" => Some(RewriteExtensionsMode::Rewrite),
358 "remove" => Some(RewriteExtensionsMode::Remove),
359 _ => None,
360 },
361 }
362 }),
363 }
364 }
365}
366
367#[napi(object)]
368#[derive(Default)]
369pub struct DecoratorOptions {
370 pub legacy: Option<bool>,
378
379 pub emit_decorator_metadata: Option<bool>,
387}
388
389impl From<DecoratorOptions> for oxc::transformer::DecoratorOptions {
390 fn from(options: DecoratorOptions) -> Self {
391 oxc::transformer::DecoratorOptions {
392 legacy: options.legacy.unwrap_or_default(),
393 emit_decorator_metadata: options.emit_decorator_metadata.unwrap_or_default(),
394 }
395 }
396}
397
398#[napi(object)]
402#[derive(Default)]
403pub struct StyledComponentsOptions {
404 pub display_name: Option<bool>,
409
410 pub file_name: Option<bool>,
415
416 pub ssr: Option<bool>,
421
422 pub transpile_template_literals: Option<bool>,
427
428 pub minify: Option<bool>,
433
434 pub css_prop: Option<bool>,
440
441 pub pure: Option<bool>,
445
446 pub namespace: Option<String>,
450
451 pub meaningless_file_names: Option<Vec<String>>,
459
460 pub top_level_import_paths: Option<Vec<String>>,
464}
465
466#[napi(object)]
467#[derive(Default)]
468pub struct PluginsOptions {
469 pub styled_components: Option<StyledComponentsOptions>,
470 pub tagged_template_escape: Option<bool>,
471}
472
473impl From<PluginsOptions> for oxc::transformer::PluginsOptions {
474 fn from(options: PluginsOptions) -> Self {
475 oxc::transformer::PluginsOptions {
476 styled_components: options
477 .styled_components
478 .map(oxc::transformer::StyledComponentsOptions::from),
479 tagged_template_transform: options.tagged_template_escape.unwrap_or(false),
480 }
481 }
482}
483
484impl From<StyledComponentsOptions> for oxc::transformer::StyledComponentsOptions {
485 fn from(options: StyledComponentsOptions) -> Self {
486 let ops = oxc::transformer::StyledComponentsOptions::default();
487 oxc::transformer::StyledComponentsOptions {
488 display_name: options.display_name.unwrap_or(ops.display_name),
489 file_name: options.file_name.unwrap_or(ops.file_name),
490 ssr: options.ssr.unwrap_or(ops.ssr),
491 transpile_template_literals: options
492 .transpile_template_literals
493 .unwrap_or(ops.transpile_template_literals),
494 minify: options.minify.unwrap_or(ops.minify),
495 css_prop: options.css_prop.unwrap_or(ops.css_prop),
496 pure: options.pure.unwrap_or(ops.pure),
497 namespace: options.namespace,
498 meaningless_file_names: options
499 .meaningless_file_names
500 .unwrap_or(ops.meaningless_file_names),
501 top_level_import_paths: options
502 .top_level_import_paths
503 .unwrap_or(ops.top_level_import_paths),
504 }
505 }
506}
507
508#[napi(object)]
512pub struct JsxOptions {
513 #[napi(ts_type = "'classic' | 'automatic'")]
520 pub runtime: Option<String>,
521
522 pub development: Option<bool>,
528
529 pub throw_if_namespace: Option<bool>,
537
538 pub pure: Option<bool>,
546
547 pub import_source: Option<String>,
551
552 pub pragma: Option<String>,
560
561 pub pragma_frag: Option<String>,
568
569 pub use_built_ins: Option<bool>,
575
576 pub use_spread: Option<bool>,
583
584 pub refresh: Option<Either<bool, ReactRefreshOptions>>,
590}
591
592impl From<JsxOptions> for oxc::transformer::JsxOptions {
593 fn from(options: JsxOptions) -> Self {
594 let ops = oxc::transformer::JsxOptions::default();
595 oxc::transformer::JsxOptions {
596 runtime: match options.runtime.as_deref() {
597 Some("classic") => JsxRuntime::Classic,
598 _ => JsxRuntime::Automatic,
599 },
600 development: options.development.unwrap_or(ops.development),
601 throw_if_namespace: options.throw_if_namespace.unwrap_or(ops.throw_if_namespace),
602 pure: options.pure.unwrap_or(ops.pure),
603 import_source: options.import_source,
604 pragma: options.pragma,
605 pragma_frag: options.pragma_frag,
606 use_built_ins: options.use_built_ins,
607 use_spread: options.use_spread,
608 refresh: options.refresh.and_then(|value| match value {
609 Either::A(b) => b.then(oxc::transformer::ReactRefreshOptions::default),
610 Either::B(options) => Some(oxc::transformer::ReactRefreshOptions::from(options)),
611 }),
612 ..Default::default()
613 }
614 }
615}
616
617#[napi(object)]
618pub struct ReactRefreshOptions {
619 pub refresh_reg: Option<String>,
623
624 pub refresh_sig: Option<String>,
628
629 pub emit_full_signatures: Option<bool>,
630}
631
632impl From<ReactRefreshOptions> for oxc::transformer::ReactRefreshOptions {
633 fn from(options: ReactRefreshOptions) -> Self {
634 let ops = oxc::transformer::ReactRefreshOptions::default();
635 oxc::transformer::ReactRefreshOptions {
636 refresh_reg: options.refresh_reg.unwrap_or(ops.refresh_reg),
637 refresh_sig: options.refresh_sig.unwrap_or(ops.refresh_sig),
638 emit_full_signatures: options.emit_full_signatures.unwrap_or(ops.emit_full_signatures),
639 }
640 }
641}
642
643#[napi(object)]
644pub struct ArrowFunctionsOptions {
645 pub spec: Option<bool>,
652}
653
654impl From<ArrowFunctionsOptions> for oxc::transformer::ArrowFunctionsOptions {
655 fn from(options: ArrowFunctionsOptions) -> Self {
656 oxc::transformer::ArrowFunctionsOptions { spec: options.spec.unwrap_or_default() }
657 }
658}
659
660#[napi(object)]
661pub struct Es2015Options {
662 pub arrow_function: Option<ArrowFunctionsOptions>,
664}
665
666impl From<Es2015Options> for oxc::transformer::ES2015Options {
667 fn from(options: Es2015Options) -> Self {
668 oxc::transformer::ES2015Options { arrow_function: options.arrow_function.map(Into::into) }
669 }
670}
671
672#[napi(object)]
673#[derive(Default)]
674pub struct Helpers {
675 pub mode: Option<HelperMode>,
676}
677
678#[derive(Default, Clone, Copy)]
679#[napi(string_enum)]
680pub enum HelperMode {
681 #[default]
690 Runtime,
691 External,
699}
700
701impl From<Helpers> for HelperLoaderOptions {
702 fn from(value: Helpers) -> Self {
703 Self {
704 mode: value.mode.map(HelperLoaderMode::from).unwrap_or_default(),
705 ..HelperLoaderOptions::default()
706 }
707 }
708}
709
710impl From<HelperMode> for HelperLoaderMode {
711 fn from(value: HelperMode) -> Self {
712 match value {
713 HelperMode::Runtime => Self::Runtime,
714 HelperMode::External => Self::External,
715 }
716 }
717}
718
719#[derive(Default)]
720struct Compiler {
721 transform_options: oxc::transformer::TransformOptions,
722 isolated_declaration_options: Option<oxc::isolated_declarations::IsolatedDeclarationsOptions>,
723
724 sourcemap: bool,
725
726 printed: String,
727 printed_sourcemap: Option<SourceMap>,
728
729 declaration: Option<String>,
730 declaration_map: Option<SourceMap>,
731
732 define: Option<ReplaceGlobalDefinesConfig>,
733 inject: Option<InjectGlobalVariablesConfig>,
734
735 helpers_used: FxHashMap<String, String>,
736 errors: Vec<OxcDiagnostic>,
737}
738
739impl Compiler {
740 fn new(options: Option<TransformOptions>) -> Result<Self, Vec<OxcDiagnostic>> {
741 let mut options = options;
742
743 let isolated_declaration_options = options
744 .as_ref()
745 .and_then(|o| o.typescript.as_ref())
746 .and_then(|o| o.declaration)
747 .map(oxc::isolated_declarations::IsolatedDeclarationsOptions::from);
748
749 let sourcemap = options.as_ref().and_then(|o| o.sourcemap).unwrap_or_default();
750
751 let define = options
752 .as_mut()
753 .and_then(|options| options.define.take())
754 .map(|map| {
755 let define = map.into_iter().collect::<Vec<_>>();
756 ReplaceGlobalDefinesConfig::new(&define)
757 })
758 .transpose()?;
759
760 let inject = options
761 .as_mut()
762 .and_then(|options| options.inject.take())
763 .map(|map| {
764 map.into_iter()
765 .map(|(local, value)| match value {
766 Either::A(source) => Ok(InjectImport::default_specifier(&source, &local)),
767 Either::B(v) => {
768 if v.len() != 2 {
769 return Err(vec![OxcDiagnostic::error(
770 "Inject plugin did not receive a tuple [string, string].",
771 )]);
772 }
773 let source = &v[0];
774 Ok(if v[1] == "*" {
775 InjectImport::namespace_specifier(source, &local)
776 } else {
777 InjectImport::named_specifier(source, Some(&v[1]), &local)
778 })
779 }
780 })
781 .collect::<Result<Vec<_>, _>>()
782 })
783 .transpose()?
784 .map(InjectGlobalVariablesConfig::new);
785
786 let transform_options = match options {
787 Some(options) => oxc::transformer::TransformOptions::try_from(options)
788 .map_err(|err| vec![OxcDiagnostic::error(err)])?,
789 None => oxc::transformer::TransformOptions::default(),
790 };
791
792 Ok(Self {
793 transform_options,
794 isolated_declaration_options,
795 sourcemap,
796 printed: String::default(),
797 printed_sourcemap: None,
798 declaration: None,
799 declaration_map: None,
800 define,
801 inject,
802 helpers_used: FxHashMap::default(),
803 errors: vec![],
804 })
805 }
806}
807
808impl CompilerInterface for Compiler {
809 fn handle_errors(&mut self, errors: Vec<OxcDiagnostic>) {
810 self.errors.extend(errors);
811 }
812
813 fn enable_sourcemap(&self) -> bool {
814 self.sourcemap
815 }
816
817 fn transform_options(&self) -> Option<&oxc::transformer::TransformOptions> {
818 Some(&self.transform_options)
819 }
820
821 fn isolated_declaration_options(
822 &self,
823 ) -> Option<oxc::isolated_declarations::IsolatedDeclarationsOptions> {
824 self.isolated_declaration_options
825 }
826
827 fn define_options(&self) -> Option<ReplaceGlobalDefinesConfig> {
828 self.define.clone()
829 }
830
831 fn inject_options(&self) -> Option<InjectGlobalVariablesConfig> {
832 self.inject.clone()
833 }
834
835 fn after_codegen(&mut self, ret: CodegenReturn) {
836 self.printed = ret.code;
837 self.printed_sourcemap = ret.map.map(SourceMap::from);
838 }
839
840 fn after_isolated_declarations(&mut self, ret: CodegenReturn) {
841 self.declaration.replace(ret.code);
842 self.declaration_map = ret.map.map(SourceMap::from);
843 }
844
845 #[expect(deprecated)]
846 fn after_transform(
847 &mut self,
848 _program: &mut oxc::ast::ast::Program<'_>,
849 transformer_return: &mut oxc::transformer::TransformerReturn,
850 ) -> ControlFlow<()> {
851 self.helpers_used = transformer_return
852 .helpers_used
853 .drain()
854 .map(|(helper, source)| (helper.name().to_string(), source))
855 .collect();
856 ControlFlow::Continue(())
857 }
858}
859
860#[allow(clippy::needless_pass_by_value, clippy::allow_attributes)]
871#[napi]
872pub fn transform_sync(
873 filename: String,
874 source_text: String,
875 options: Option<TransformOptions>,
876) -> TransformResult {
877 let source_path = Path::new(&filename);
878
879 let source_type = get_source_type(
880 &filename,
881 options.as_ref().and_then(|options| options.lang.as_deref()),
882 options.as_ref().and_then(|options| options.source_type.as_deref()),
883 );
884
885 let mut compiler = match Compiler::new(options) {
886 Ok(compiler) => compiler,
887 Err(errors) => {
888 return TransformResult {
889 errors: OxcError::from_diagnostics(&filename, &source_text, errors),
890 ..Default::default()
891 };
892 }
893 };
894
895 compiler.compile(&source_text, source_type, source_path);
896
897 TransformResult {
898 code: compiler.printed,
899 map: compiler.printed_sourcemap,
900 declaration: compiler.declaration,
901 declaration_map: compiler.declaration_map,
902 helpers_used: compiler.helpers_used,
903 errors: OxcError::from_diagnostics(&filename, &source_text, compiler.errors),
904 }
905}
906
907pub struct TransformTask {
908 filename: String,
909 source_text: String,
910 options: Option<TransformOptions>,
911}
912
913#[napi]
914impl Task for TransformTask {
915 type JsValue = TransformResult;
916 type Output = TransformResult;
917
918 fn compute(&mut self) -> napi::Result<Self::Output> {
919 let source_path = Path::new(&self.filename);
920
921 let source_type = get_source_type(
922 &self.filename,
923 self.options.as_ref().and_then(|options| options.lang.as_deref()),
924 self.options.as_ref().and_then(|options| options.source_type.as_deref()),
925 );
926
927 let mut compiler = match Compiler::new(self.options.take()) {
928 Ok(compiler) => compiler,
929 Err(errors) => {
930 return Ok(TransformResult {
931 errors: OxcError::from_diagnostics(&self.filename, &self.source_text, errors),
932 ..Default::default()
933 });
934 }
935 };
936
937 compiler.compile(&self.source_text, source_type, source_path);
938
939 Ok(TransformResult {
940 code: compiler.printed,
941 map: compiler.printed_sourcemap,
942 declaration: compiler.declaration,
943 declaration_map: compiler.declaration_map,
944 helpers_used: compiler.helpers_used,
945 errors: OxcError::from_diagnostics(&self.filename, &self.source_text, compiler.errors),
946 })
947 }
948
949 fn resolve(&mut self, _: napi::Env, result: Self::Output) -> napi::Result<Self::JsValue> {
950 Ok(result)
951 }
952}
953
954#[napi]
967pub fn transform(
968 filename: String,
969 source_text: String,
970 options: Option<TransformOptions>,
971) -> AsyncTask<TransformTask> {
972 AsyncTask::new(TransformTask { filename, source_text, options })
973}
974
975#[derive(Default)]
976#[napi(object)]
977pub struct ModuleRunnerTransformOptions {
978 pub sourcemap: Option<bool>,
986}
987
988#[derive(Default)]
989#[napi(object)]
990pub struct ModuleRunnerTransformResult {
991 pub code: String,
995
996 pub map: Option<SourceMap>,
1000
1001 pub deps: Vec<String>,
1003
1004 pub dynamic_deps: Vec<String>,
1006
1007 pub errors: Vec<OxcError>,
1013}
1014
1015fn module_runner_transform_impl(
1026 filename: &str,
1027 source_text: &str,
1028 options: Option<ModuleRunnerTransformOptions>,
1029) -> ModuleRunnerTransformResult {
1030 let file_path = Path::new(filename);
1031 let source_type = SourceType::from_path(file_path);
1032 let source_type = match source_type {
1033 Ok(s) => s,
1034 Err(err) => {
1035 return ModuleRunnerTransformResult {
1036 code: String::default(),
1037 map: None,
1038 deps: vec![],
1039 dynamic_deps: vec![],
1040 errors: vec![OxcError::new(err.to_string())],
1041 };
1042 }
1043 };
1044
1045 let allocator = Allocator::default();
1046 let mut parser_ret = Parser::new(&allocator, source_text, source_type).parse();
1047 let mut program = parser_ret.program;
1048
1049 let SemanticBuilderReturn { semantic, errors } =
1050 SemanticBuilder::new().with_check_syntax_error(true).build(&program);
1051 parser_ret.errors.extend(errors);
1052
1053 let scoping = semantic.into_scoping();
1054 let (deps, dynamic_deps) =
1055 ModuleRunnerTransform::default().transform(&allocator, &mut program, scoping);
1056
1057 let CodegenReturn { code, map, .. } = Codegen::new()
1058 .with_options(CodegenOptions {
1059 source_map_path: options.and_then(|opts| {
1060 opts.sourcemap.as_ref().and_then(|s| s.then(|| file_path.to_path_buf()))
1061 }),
1062 ..Default::default()
1063 })
1064 .build(&program);
1065
1066 ModuleRunnerTransformResult {
1067 code,
1068 map: map.map(Into::into),
1069 deps: deps.into_iter().collect::<Vec<String>>(),
1070 dynamic_deps: dynamic_deps.into_iter().collect::<Vec<String>>(),
1071 errors: OxcError::from_diagnostics(filename, source_text, parser_ret.errors),
1072 }
1073}
1074
1075#[allow(clippy::needless_pass_by_value, clippy::allow_attributes)]
1077#[napi]
1078pub fn module_runner_transform_sync(
1079 filename: String,
1080 source_text: String,
1081 options: Option<ModuleRunnerTransformOptions>,
1082) -> ModuleRunnerTransformResult {
1083 module_runner_transform_impl(&filename, &source_text, options)
1084}
1085
1086pub struct ModuleRunnerTransformTask {
1087 filename: String,
1088 source_text: String,
1089 options: Option<ModuleRunnerTransformOptions>,
1090}
1091
1092#[napi]
1093impl Task for ModuleRunnerTransformTask {
1094 type JsValue = ModuleRunnerTransformResult;
1095 type Output = ModuleRunnerTransformResult;
1096
1097 fn compute(&mut self) -> napi::Result<Self::Output> {
1098 Ok(module_runner_transform_impl(&self.filename, &self.source_text, self.options.take()))
1099 }
1100
1101 fn resolve(&mut self, _: napi::Env, result: Self::Output) -> napi::Result<Self::JsValue> {
1102 Ok(result)
1103 }
1104}
1105
1106#[napi]
1120pub fn module_runner_transform(
1121 filename: String,
1122 source_text: String,
1123 options: Option<ModuleRunnerTransformOptions>,
1124) -> AsyncTask<ModuleRunnerTransformTask> {
1125 AsyncTask::new(ModuleRunnerTransformTask { filename, source_text, options })
1126}