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' | 'commonjs' | '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>,
116
117 #[napi(ts_type = "'preserve' | JsxOptions")]
120 pub jsx: Option<Either<String, JsxOptions>>,
121
122 pub target: Option<Either<String, Vec<String>>>,
135
136 pub helpers: Option<Helpers>,
138
139 #[napi(ts_type = "Record<string, string>")]
142 pub define: Option<FxHashMap<String, String>>,
143
144 #[napi(ts_type = "Record<string, string | [string, string]>")]
147 pub inject: Option<FxHashMap<String, Either<String, Vec<String>>>>,
148
149 pub decorator: Option<DecoratorOptions>,
151
152 pub plugins: Option<PluginsOptions>,
155}
156
157impl TryFrom<TransformOptions> for oxc::transformer::TransformOptions {
158 type Error = String;
159
160 fn try_from(options: TransformOptions) -> Result<Self, Self::Error> {
161 let env = match options.target {
162 Some(Either::A(s)) => EnvOptions::from_target(&s)?,
163 Some(Either::B(list)) => EnvOptions::from_target_list(&list)?,
164 _ => EnvOptions::default(),
165 };
166 Ok(Self {
167 cwd: options.cwd.map(PathBuf::from).unwrap_or_default(),
168 assumptions: options.assumptions.map(Into::into).unwrap_or_default(),
169 typescript: options
170 .typescript
171 .map(oxc::transformer::TypeScriptOptions::from)
172 .unwrap_or_default(),
173 decorator: options
174 .decorator
175 .map(oxc::transformer::DecoratorOptions::from)
176 .unwrap_or_default(),
177 jsx: match options.jsx {
178 Some(Either::A(s)) => {
179 if s == "preserve" {
180 oxc::transformer::JsxOptions::disable()
181 } else {
182 return Err(format!("Invalid jsx option: `{s}`."));
183 }
184 }
185 Some(Either::B(options)) => oxc::transformer::JsxOptions::from(options),
186 None => oxc::transformer::JsxOptions::enable(),
187 },
188 env,
189 proposals: ProposalOptions::default(),
190 helper_loader: options
191 .helpers
192 .map_or_else(HelperLoaderOptions::default, HelperLoaderOptions::from),
193 plugins: options
194 .plugins
195 .map(oxc::transformer::PluginsOptions::from)
196 .unwrap_or_default(),
197 })
198 }
199}
200
201#[napi(object)]
202#[derive(Default, Debug)]
203pub struct CompilerAssumptions {
204 pub ignore_function_length: Option<bool>,
205 pub no_document_all: Option<bool>,
206 pub object_rest_no_symbols: Option<bool>,
207 pub pure_getters: Option<bool>,
208 pub set_public_class_fields: Option<bool>,
248}
249
250impl From<CompilerAssumptions> for oxc::transformer::CompilerAssumptions {
251 fn from(value: CompilerAssumptions) -> Self {
252 let ops = oxc::transformer::CompilerAssumptions::default();
253 Self {
254 ignore_function_length: value
255 .ignore_function_length
256 .unwrap_or(ops.ignore_function_length),
257 no_document_all: value.no_document_all.unwrap_or(ops.no_document_all),
258 object_rest_no_symbols: value
259 .object_rest_no_symbols
260 .unwrap_or(ops.object_rest_no_symbols),
261 pure_getters: value.pure_getters.unwrap_or(ops.pure_getters),
262 set_public_class_fields: value
263 .set_public_class_fields
264 .unwrap_or(ops.set_public_class_fields),
265 ..ops
266 }
267 }
268}
269
270#[napi(object)]
271#[derive(Default)]
272pub struct TypeScriptOptions {
273 pub jsx_pragma: Option<String>,
274 pub jsx_pragma_frag: Option<String>,
275 pub only_remove_type_imports: Option<bool>,
276 pub allow_namespaces: Option<bool>,
277 pub allow_declare_fields: Option<bool>,
284 pub remove_class_fields_without_initializer: Option<bool>,
317 pub declaration: Option<IsolatedDeclarationsOptions>,
325 #[napi(ts_type = "'rewrite' | 'remove' | boolean")]
334 pub rewrite_import_extensions: Option<Either<bool, String>>,
335}
336
337impl From<TypeScriptOptions> for oxc::transformer::TypeScriptOptions {
338 fn from(options: TypeScriptOptions) -> Self {
339 let ops = oxc::transformer::TypeScriptOptions::default();
340 oxc::transformer::TypeScriptOptions {
341 jsx_pragma: options.jsx_pragma.map(Into::into).unwrap_or(ops.jsx_pragma),
342 jsx_pragma_frag: options.jsx_pragma_frag.map(Into::into).unwrap_or(ops.jsx_pragma_frag),
343 only_remove_type_imports: options
344 .only_remove_type_imports
345 .unwrap_or(ops.only_remove_type_imports),
346 allow_namespaces: options.allow_namespaces.unwrap_or(ops.allow_namespaces),
347 allow_declare_fields: options.allow_declare_fields.unwrap_or(ops.allow_declare_fields),
348 optimize_const_enums: false,
349 remove_class_fields_without_initializer: options
350 .remove_class_fields_without_initializer
351 .unwrap_or(ops.remove_class_fields_without_initializer),
352 rewrite_import_extensions: options.rewrite_import_extensions.and_then(|value| {
353 match value {
354 Either::A(v) => {
355 if v {
356 Some(RewriteExtensionsMode::Rewrite)
357 } else {
358 None
359 }
360 }
361 Either::B(v) => match v.as_str() {
362 "rewrite" => Some(RewriteExtensionsMode::Rewrite),
363 "remove" => Some(RewriteExtensionsMode::Remove),
364 _ => None,
365 },
366 }
367 }),
368 }
369 }
370}
371
372#[napi(object)]
373#[derive(Default)]
374pub struct DecoratorOptions {
375 pub legacy: Option<bool>,
383
384 pub emit_decorator_metadata: Option<bool>,
392}
393
394impl From<DecoratorOptions> for oxc::transformer::DecoratorOptions {
395 fn from(options: DecoratorOptions) -> Self {
396 oxc::transformer::DecoratorOptions {
397 legacy: options.legacy.unwrap_or_default(),
398 emit_decorator_metadata: options.emit_decorator_metadata.unwrap_or_default(),
399 }
400 }
401}
402
403#[napi(object)]
407#[derive(Default)]
408pub struct StyledComponentsOptions {
409 pub display_name: Option<bool>,
414
415 pub file_name: Option<bool>,
420
421 pub ssr: Option<bool>,
426
427 pub transpile_template_literals: Option<bool>,
432
433 pub minify: Option<bool>,
438
439 pub css_prop: Option<bool>,
445
446 pub pure: Option<bool>,
450
451 pub namespace: Option<String>,
455
456 pub meaningless_file_names: Option<Vec<String>>,
464
465 pub top_level_import_paths: Option<Vec<String>>,
469}
470
471#[napi(object)]
472#[derive(Default)]
473pub struct PluginsOptions {
474 pub styled_components: Option<StyledComponentsOptions>,
475 pub tagged_template_escape: Option<bool>,
476}
477
478impl From<PluginsOptions> for oxc::transformer::PluginsOptions {
479 fn from(options: PluginsOptions) -> Self {
480 oxc::transformer::PluginsOptions {
481 styled_components: options
482 .styled_components
483 .map(oxc::transformer::StyledComponentsOptions::from),
484 tagged_template_transform: options.tagged_template_escape.unwrap_or(false),
485 }
486 }
487}
488
489impl From<StyledComponentsOptions> for oxc::transformer::StyledComponentsOptions {
490 fn from(options: StyledComponentsOptions) -> Self {
491 let ops = oxc::transformer::StyledComponentsOptions::default();
492 oxc::transformer::StyledComponentsOptions {
493 display_name: options.display_name.unwrap_or(ops.display_name),
494 file_name: options.file_name.unwrap_or(ops.file_name),
495 ssr: options.ssr.unwrap_or(ops.ssr),
496 transpile_template_literals: options
497 .transpile_template_literals
498 .unwrap_or(ops.transpile_template_literals),
499 minify: options.minify.unwrap_or(ops.minify),
500 css_prop: options.css_prop.unwrap_or(ops.css_prop),
501 pure: options.pure.unwrap_or(ops.pure),
502 namespace: options.namespace,
503 meaningless_file_names: options
504 .meaningless_file_names
505 .unwrap_or(ops.meaningless_file_names),
506 top_level_import_paths: options
507 .top_level_import_paths
508 .unwrap_or(ops.top_level_import_paths),
509 }
510 }
511}
512
513#[napi(object)]
517pub struct JsxOptions {
518 #[napi(ts_type = "'classic' | 'automatic'")]
525 pub runtime: Option<String>,
526
527 pub development: Option<bool>,
531
532 pub throw_if_namespace: Option<bool>,
540
541 pub pure: Option<bool>,
545
546 pub import_source: Option<String>,
550
551 pub pragma: Option<String>,
559
560 pub pragma_frag: Option<String>,
567
568 pub refresh: Option<Either<bool, ReactRefreshOptions>>,
574}
575
576impl From<JsxOptions> for oxc::transformer::JsxOptions {
577 fn from(options: JsxOptions) -> Self {
578 let ops = oxc::transformer::JsxOptions::default();
579 oxc::transformer::JsxOptions {
580 runtime: match options.runtime.as_deref() {
581 Some("classic") => JsxRuntime::Classic,
582 _ => JsxRuntime::Automatic,
583 },
584 development: options.development.unwrap_or(ops.development),
585 throw_if_namespace: options.throw_if_namespace.unwrap_or(ops.throw_if_namespace),
586 pure: options.pure.unwrap_or(ops.pure),
587 import_source: options.import_source,
588 pragma: options.pragma,
589 pragma_frag: options.pragma_frag,
590 use_built_ins: None,
591 use_spread: None,
592 refresh: options.refresh.and_then(|value| match value {
593 Either::A(b) => b.then(oxc::transformer::ReactRefreshOptions::default),
594 Either::B(options) => Some(oxc::transformer::ReactRefreshOptions::from(options)),
595 }),
596 ..Default::default()
597 }
598 }
599}
600
601#[napi(object)]
602pub struct ReactRefreshOptions {
603 pub refresh_reg: Option<String>,
607
608 pub refresh_sig: Option<String>,
612
613 pub emit_full_signatures: Option<bool>,
614}
615
616impl From<ReactRefreshOptions> for oxc::transformer::ReactRefreshOptions {
617 fn from(options: ReactRefreshOptions) -> Self {
618 let ops = oxc::transformer::ReactRefreshOptions::default();
619 oxc::transformer::ReactRefreshOptions {
620 refresh_reg: options.refresh_reg.unwrap_or(ops.refresh_reg),
621 refresh_sig: options.refresh_sig.unwrap_or(ops.refresh_sig),
622 emit_full_signatures: options.emit_full_signatures.unwrap_or(ops.emit_full_signatures),
623 }
624 }
625}
626
627#[napi(object)]
628pub struct ArrowFunctionsOptions {
629 pub spec: Option<bool>,
636}
637
638impl From<ArrowFunctionsOptions> for oxc::transformer::ArrowFunctionsOptions {
639 fn from(options: ArrowFunctionsOptions) -> Self {
640 oxc::transformer::ArrowFunctionsOptions { spec: options.spec.unwrap_or_default() }
641 }
642}
643
644#[napi(object)]
645pub struct Es2015Options {
646 pub arrow_function: Option<ArrowFunctionsOptions>,
648}
649
650impl From<Es2015Options> for oxc::transformer::ES2015Options {
651 fn from(options: Es2015Options) -> Self {
652 oxc::transformer::ES2015Options { arrow_function: options.arrow_function.map(Into::into) }
653 }
654}
655
656#[napi(object)]
657#[derive(Default)]
658pub struct Helpers {
659 pub mode: Option<HelperMode>,
660}
661
662#[derive(Default, Clone, Copy)]
663#[napi(string_enum)]
664pub enum HelperMode {
665 #[default]
674 Runtime,
675 External,
683}
684
685impl From<Helpers> for HelperLoaderOptions {
686 fn from(value: Helpers) -> Self {
687 Self {
688 mode: value.mode.map(HelperLoaderMode::from).unwrap_or_default(),
689 ..HelperLoaderOptions::default()
690 }
691 }
692}
693
694impl From<HelperMode> for HelperLoaderMode {
695 fn from(value: HelperMode) -> Self {
696 match value {
697 HelperMode::Runtime => Self::Runtime,
698 HelperMode::External => Self::External,
699 }
700 }
701}
702
703#[derive(Default)]
704struct Compiler {
705 transform_options: oxc::transformer::TransformOptions,
706 isolated_declaration_options: Option<oxc::isolated_declarations::IsolatedDeclarationsOptions>,
707
708 sourcemap: bool,
709
710 printed: String,
711 printed_sourcemap: Option<SourceMap>,
712
713 declaration: Option<String>,
714 declaration_map: Option<SourceMap>,
715
716 define: Option<ReplaceGlobalDefinesConfig>,
717 inject: Option<InjectGlobalVariablesConfig>,
718
719 helpers_used: FxHashMap<String, String>,
720 errors: Vec<OxcDiagnostic>,
721}
722
723impl Compiler {
724 fn new(options: Option<TransformOptions>) -> Result<Self, Vec<OxcDiagnostic>> {
725 let mut options = options;
726
727 let isolated_declaration_options = options
728 .as_ref()
729 .and_then(|o| o.typescript.as_ref())
730 .and_then(|o| o.declaration)
731 .map(oxc::isolated_declarations::IsolatedDeclarationsOptions::from);
732
733 let sourcemap = options.as_ref().and_then(|o| o.sourcemap).unwrap_or_default();
734
735 let define = options
736 .as_mut()
737 .and_then(|options| options.define.take())
738 .map(|map| {
739 let define = map.into_iter().collect::<Vec<_>>();
740 ReplaceGlobalDefinesConfig::new(&define)
741 })
742 .transpose()?;
743
744 let inject = options
745 .as_mut()
746 .and_then(|options| options.inject.take())
747 .map(|map| {
748 map.into_iter()
749 .map(|(local, value)| match value {
750 Either::A(source) => Ok(InjectImport::default_specifier(&source, &local)),
751 Either::B(v) => {
752 if v.len() != 2 {
753 return Err(vec![OxcDiagnostic::error(
754 "Inject plugin did not receive a tuple [string, string].",
755 )]);
756 }
757 let source = &v[0];
758 Ok(if v[1] == "*" {
759 InjectImport::namespace_specifier(source, &local)
760 } else {
761 InjectImport::named_specifier(source, Some(&v[1]), &local)
762 })
763 }
764 })
765 .collect::<Result<Vec<_>, _>>()
766 })
767 .transpose()?
768 .map(InjectGlobalVariablesConfig::new);
769
770 let transform_options = match options {
771 Some(options) => oxc::transformer::TransformOptions::try_from(options)
772 .map_err(|err| vec![OxcDiagnostic::error(err)])?,
773 None => oxc::transformer::TransformOptions::default(),
774 };
775
776 Ok(Self {
777 transform_options,
778 isolated_declaration_options,
779 sourcemap,
780 printed: String::default(),
781 printed_sourcemap: None,
782 declaration: None,
783 declaration_map: None,
784 define,
785 inject,
786 helpers_used: FxHashMap::default(),
787 errors: vec![],
788 })
789 }
790}
791
792impl CompilerInterface for Compiler {
793 fn handle_errors(&mut self, errors: Vec<OxcDiagnostic>) {
794 self.errors.extend(errors);
795 }
796
797 fn enable_sourcemap(&self) -> bool {
798 self.sourcemap
799 }
800
801 fn transform_options(&self) -> Option<&oxc::transformer::TransformOptions> {
802 Some(&self.transform_options)
803 }
804
805 fn isolated_declaration_options(
806 &self,
807 ) -> Option<oxc::isolated_declarations::IsolatedDeclarationsOptions> {
808 self.isolated_declaration_options
809 }
810
811 fn define_options(&self) -> Option<ReplaceGlobalDefinesConfig> {
812 self.define.clone()
813 }
814
815 fn inject_options(&self) -> Option<InjectGlobalVariablesConfig> {
816 self.inject.clone()
817 }
818
819 fn after_codegen(&mut self, ret: CodegenReturn) {
820 self.printed = ret.code;
821 self.printed_sourcemap = ret.map.map(SourceMap::from);
822 }
823
824 fn after_isolated_declarations(&mut self, ret: CodegenReturn) {
825 self.declaration.replace(ret.code);
826 self.declaration_map = ret.map.map(SourceMap::from);
827 }
828
829 #[expect(deprecated)]
830 fn after_transform(
831 &mut self,
832 _program: &mut oxc::ast::ast::Program<'_>,
833 transformer_return: &mut oxc::transformer::TransformerReturn,
834 ) -> ControlFlow<()> {
835 self.helpers_used = transformer_return
836 .helpers_used
837 .drain()
838 .map(|(helper, source)| (helper.name().to_string(), source))
839 .collect();
840 ControlFlow::Continue(())
841 }
842}
843
844#[allow(clippy::needless_pass_by_value, clippy::allow_attributes)]
855#[napi]
856pub fn transform_sync(
857 filename: String,
858 source_text: String,
859 options: Option<TransformOptions>,
860) -> TransformResult {
861 let source_path = Path::new(&filename);
862
863 let source_type = get_source_type(
864 &filename,
865 options.as_ref().and_then(|options| options.lang.as_deref()),
866 options.as_ref().and_then(|options| options.source_type.as_deref()),
867 );
868
869 let mut compiler = match Compiler::new(options) {
870 Ok(compiler) => compiler,
871 Err(errors) => {
872 return TransformResult {
873 errors: OxcError::from_diagnostics(&filename, &source_text, errors),
874 ..Default::default()
875 };
876 }
877 };
878
879 compiler.compile(&source_text, source_type, source_path);
880
881 TransformResult {
882 code: compiler.printed,
883 map: compiler.printed_sourcemap,
884 declaration: compiler.declaration,
885 declaration_map: compiler.declaration_map,
886 helpers_used: compiler.helpers_used,
887 errors: OxcError::from_diagnostics(&filename, &source_text, compiler.errors),
888 }
889}
890
891pub struct TransformTask {
892 filename: String,
893 source_text: String,
894 options: Option<TransformOptions>,
895}
896
897#[napi]
898impl Task for TransformTask {
899 type JsValue = TransformResult;
900 type Output = TransformResult;
901
902 fn compute(&mut self) -> napi::Result<Self::Output> {
903 let source_path = Path::new(&self.filename);
904
905 let source_type = get_source_type(
906 &self.filename,
907 self.options.as_ref().and_then(|options| options.lang.as_deref()),
908 self.options.as_ref().and_then(|options| options.source_type.as_deref()),
909 );
910
911 let mut compiler = match Compiler::new(self.options.take()) {
912 Ok(compiler) => compiler,
913 Err(errors) => {
914 return Ok(TransformResult {
915 errors: OxcError::from_diagnostics(&self.filename, &self.source_text, errors),
916 ..Default::default()
917 });
918 }
919 };
920
921 compiler.compile(&self.source_text, source_type, source_path);
922
923 Ok(TransformResult {
924 code: compiler.printed,
925 map: compiler.printed_sourcemap,
926 declaration: compiler.declaration,
927 declaration_map: compiler.declaration_map,
928 helpers_used: compiler.helpers_used,
929 errors: OxcError::from_diagnostics(&self.filename, &self.source_text, compiler.errors),
930 })
931 }
932
933 fn resolve(&mut self, _: napi::Env, result: Self::Output) -> napi::Result<Self::JsValue> {
934 Ok(result)
935 }
936}
937
938#[napi]
951pub fn transform(
952 filename: String,
953 source_text: String,
954 options: Option<TransformOptions>,
955) -> AsyncTask<TransformTask> {
956 AsyncTask::new(TransformTask { filename, source_text, options })
957}
958
959#[derive(Default)]
960#[napi(object)]
961pub struct ModuleRunnerTransformOptions {
962 pub sourcemap: Option<bool>,
970}
971
972#[derive(Default)]
973#[napi(object)]
974pub struct ModuleRunnerTransformResult {
975 pub code: String,
979
980 pub map: Option<SourceMap>,
984
985 pub deps: Vec<String>,
987
988 pub dynamic_deps: Vec<String>,
990
991 pub errors: Vec<OxcError>,
997}
998
999fn module_runner_transform_impl(
1010 filename: &str,
1011 source_text: &str,
1012 options: Option<ModuleRunnerTransformOptions>,
1013) -> ModuleRunnerTransformResult {
1014 let file_path = Path::new(filename);
1015 let source_type = SourceType::from_path(file_path);
1016 let source_type = match source_type {
1017 Ok(s) => s,
1018 Err(err) => {
1019 return ModuleRunnerTransformResult {
1020 code: String::default(),
1021 map: None,
1022 deps: vec![],
1023 dynamic_deps: vec![],
1024 errors: vec![OxcError::new(err.to_string())],
1025 };
1026 }
1027 };
1028
1029 let allocator = Allocator::default();
1030 let mut parser_ret = Parser::new(&allocator, source_text, source_type).parse();
1031 let mut program = parser_ret.program;
1032
1033 let SemanticBuilderReturn { semantic, errors } =
1034 SemanticBuilder::new().with_check_syntax_error(true).build(&program);
1035 parser_ret.errors.extend(errors);
1036
1037 let scoping = semantic.into_scoping();
1038 let (deps, dynamic_deps) =
1039 ModuleRunnerTransform::default().transform(&allocator, &mut program, scoping);
1040
1041 let CodegenReturn { code, map, .. } = Codegen::new()
1042 .with_options(CodegenOptions {
1043 source_map_path: options.and_then(|opts| {
1044 opts.sourcemap.as_ref().and_then(|s| s.then(|| file_path.to_path_buf()))
1045 }),
1046 ..Default::default()
1047 })
1048 .build(&program);
1049
1050 ModuleRunnerTransformResult {
1051 code,
1052 map: map.map(Into::into),
1053 deps: deps.into_iter().collect::<Vec<String>>(),
1054 dynamic_deps: dynamic_deps.into_iter().collect::<Vec<String>>(),
1055 errors: OxcError::from_diagnostics(filename, source_text, parser_ret.errors),
1056 }
1057}
1058
1059#[allow(clippy::needless_pass_by_value, clippy::allow_attributes)]
1061#[napi]
1062pub fn module_runner_transform_sync(
1063 filename: String,
1064 source_text: String,
1065 options: Option<ModuleRunnerTransformOptions>,
1066) -> ModuleRunnerTransformResult {
1067 module_runner_transform_impl(&filename, &source_text, options)
1068}
1069
1070pub struct ModuleRunnerTransformTask {
1071 filename: String,
1072 source_text: String,
1073 options: Option<ModuleRunnerTransformOptions>,
1074}
1075
1076#[napi]
1077impl Task for ModuleRunnerTransformTask {
1078 type JsValue = ModuleRunnerTransformResult;
1079 type Output = ModuleRunnerTransformResult;
1080
1081 fn compute(&mut self) -> napi::Result<Self::Output> {
1082 Ok(module_runner_transform_impl(&self.filename, &self.source_text, self.options.take()))
1083 }
1084
1085 fn resolve(&mut self, _: napi::Env, result: Self::Output) -> napi::Result<Self::JsValue> {
1086 Ok(result)
1087 }
1088}
1089
1090#[napi]
1104pub fn module_runner_transform(
1105 filename: String,
1106 source_text: String,
1107 options: Option<ModuleRunnerTransformOptions>,
1108) -> AsyncTask<ModuleRunnerTransformTask> {
1109 AsyncTask::new(ModuleRunnerTransformTask { filename, source_text, options })
1110}