1use napi::bindgen_prelude::*;
30use rustc_hash::FxHashMap;
31use serde::Deserialize;
32use swc_core::{
33 base::{TransformOutput, config::JsMinifyOptions, try_with_handler},
34 common::{FileName, GLOBALS, SourceFile, SourceMap, errors::ColorConfig, sync::Lrc},
35};
36
37use crate::{get_compiler, util::MapErr};
38
39pub struct MinifyTask {
40 c: swc_core::base::Compiler,
41 code: MinifyTarget,
42 opts: JsMinifyOptions,
43}
44
45#[derive(Deserialize)]
46#[serde(untagged)]
47enum MinifyTarget {
48 Single(String),
50 Map(FxHashMap<String, String>),
52}
53
54impl MinifyTarget {
55 fn to_file(&self, cm: Lrc<SourceMap>) -> Lrc<SourceFile> {
56 match self {
57 MinifyTarget::Single(code) => cm.new_source_file(FileName::Anon.into(), code.clone()),
58 MinifyTarget::Map(codes) => {
59 assert_eq!(
60 codes.len(),
61 1,
62 "swc.minify does not support concatenating multiple files yet"
63 );
64
65 let (filename, code) = codes.iter().next().unwrap();
66
67 cm.new_source_file(FileName::Real(filename.clone().into()).into(), code.clone())
68 }
69 }
70 }
71}
72
73#[napi]
74impl Task for MinifyTask {
75 type Output = TransformOutput;
76
77 type JsValue = TransformOutput;
78
79 fn compute(&mut self) -> napi::Result<Self::Output> {
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.code.to_file(self.c.cm.clone());
89
90 self.c.minify(fm, handler, &self.opts, Default::default())
91 })
92 },
93 )
94 .map_err(|e| e.to_pretty_error())
95 .convert_err()
96 }
97
98 fn resolve(&mut self, _: napi::Env, output: Self::Output) -> napi::Result<Self::JsValue> {
99 Ok(output)
100 }
101}
102
103#[napi]
104pub fn minify(
105 input: Buffer,
106 opts: Buffer,
107 signal: Option<AbortSignal>,
108) -> napi::Result<AsyncTask<MinifyTask>> {
109 let code = serde_json::from_slice(&input)?;
110 let opts = serde_json::from_slice(&opts)?;
111
112 let c = get_compiler();
113
114 let task = MinifyTask { c, code, opts };
115
116 Ok(AsyncTask::with_optional_signal(task, signal))
117}
118
119#[napi]
120pub fn minify_sync(input: Buffer, opts: Buffer) -> napi::Result<TransformOutput> {
121 let code: MinifyTarget = serde_json::from_slice(&input)?;
122 let opts = serde_json::from_slice(&opts)?;
123
124 let c = get_compiler();
125
126 let fm = code.to_file(c.cm.clone());
127
128 try_with_handler(
129 c.cm.clone(),
130 swc_core::base::HandlerOpts {
131 color: ColorConfig::Never,
132 skip_filename: true,
133 },
134 |handler| {
135 GLOBALS.set(&Default::default(), || {
136 c.minify(fm, handler, &opts, Default::default())
137 })
138 },
139 )
140 .map_err(|e| e.to_pretty_error())
141 .convert_err()
142}