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