next_swc_napi/
minify.rs

1/*
2Copyright (c) 2017 The swc Project Developers
3
4Permission is hereby granted, free of charge, to any
5person obtaining a copy of this software and associated
6documentation files (the "Software"), to deal in the
7Software without restriction, including without
8limitation the rights to use, copy, modify, merge,
9publish, distribute, sublicense, and/or sell copies of
10the Software, and to permit persons to whom the Software
11is furnished to do so, subject to the following
12conditions:
13
14The above copyright notice and this permission notice
15shall be included in all copies or substantial portions
16of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
19ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
20TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
21PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
22SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
23CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
25IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26DEALINGS IN THE SOFTWARE.
27*/
28
29use 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// Same as the swc_core::base::TransformOutput, but using our napi-rs v2's derived #[napi], while
46// swc is already on napi-rs v3.
47#[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}