1use anyhow::Context;
30use napi::bindgen_prelude::*;
31use napi_derive::napi;
32use swc_core::{
33 base::{config::JsMinifyOptions, try_with_handler},
34 common::{FileName, GLOBALS, errors::ColorConfig},
35};
36
37use crate::{get_compiler, util::MapErr};
38
39pub struct MinifyTask {
40 c: swc_core::base::Compiler,
41 code: Option<String>,
42 opts: JsMinifyOptions,
43}
44
45#[napi_derive::napi(object)]
48#[derive(Debug)]
49pub struct TransformOutput {
50 pub code: String,
51 pub map: Option<String>,
52
53 pub output: Option<String>,
54 pub diagnostics: std::vec::Vec<String>,
55}
56
57impl From<swc_core::base::TransformOutput> for TransformOutput {
58 fn from(other: swc_core::base::TransformOutput) -> Self {
59 Self {
60 code: other.code,
61 map: other.map,
62 output: other.output,
63 diagnostics: other.diagnostics,
64 }
65 }
66}
67
68#[napi]
69impl Task for MinifyTask {
70 type Output = TransformOutput;
71
72 type JsValue = TransformOutput;
73
74 fn compute(&mut self) -> napi::Result<Self::Output> {
75 let code = self.code.take().unwrap_or_default();
76
77 try_with_handler(
78 self.c.cm.clone(),
79 swc_core::base::HandlerOpts {
80 color: ColorConfig::Never,
81 skip_filename: true,
82 },
83 |handler| {
84 GLOBALS.set(&Default::default(), || {
85 let fm = self.c.cm.new_source_file(FileName::Anon.into(), code);
86
87 self.c.minify(fm, handler, &self.opts, Default::default())
88 })
89 },
90 )
91 .map(TransformOutput::from)
92 .map_err(|e| e.to_pretty_error())
93 .convert_err()
94 }
95
96 fn resolve(&mut self, _: napi::Env, output: Self::Output) -> napi::Result<Self::JsValue> {
97 Ok(output)
98 }
99}
100
101#[napi]
102pub fn minify(
103 input: Buffer,
104 opts: Buffer,
105 signal: Option<AbortSignal>,
106) -> napi::Result<AsyncTask<MinifyTask>> {
107 let code = String::from_utf8(input.into())
108 .context("failed to convert input to string")
109 .convert_err()?;
110 let opts = serde_json::from_slice(&opts)?;
111
112 let c = get_compiler();
113
114 let task = MinifyTask {
115 c,
116 code: Some(code),
117 opts,
118 };
119
120 Ok(AsyncTask::with_optional_signal(task, signal))
121}
122
123#[napi]
124pub fn minify_sync(input: Buffer, opts: Buffer) -> napi::Result<TransformOutput> {
125 let code = String::from_utf8(input.into())
126 .context("failed to convert input to string")
127 .convert_err()?;
128 let opts = serde_json::from_slice(&opts)?;
129
130 let c = get_compiler();
131
132 let fm = c.cm.new_source_file(FileName::Anon.into(), code);
133
134 try_with_handler(
135 c.cm.clone(),
136 swc_core::base::HandlerOpts {
137 color: ColorConfig::Never,
138 skip_filename: true,
139 },
140 |handler| {
141 GLOBALS.set(&Default::default(), || {
142 c.minify(fm, handler, &opts, Default::default())
143 })
144 },
145 )
146 .map(TransformOutput::from)
147 .map_err(|e| e.to_pretty_error())
148 .convert_err()
149}