html_minifier/
lib.rs

1/*!
2# HTML Minifier
3
4This library can help you generate and minify your HTML code at the same time. It also supports to minify JS and CSS in `<style>`, `<script>` elements, and ignores the minification of `<pre>`, `<code>` and `<textarea>` elements.
5
6HTML is minified by the following rules:
7
8* ASCII control characters (0x00-0x08, 0x11-0x1F, 0x7F) are always removed.
9* Comments can be optionally removed. (removed by default)
10* **Useless** whitespaces (spaces, tabs and newlines) are removed.
11* Whitespaces (spaces, tabs and newlines) are converted to a single `'\x20'` or a single '\n', if possible.
12* Empty attribute values are collapsed. (e.g `<input readonly="">` => `<input readonly>` )
13* The inner HTML of all elements is minified except for the following elements:
14    * `<pre>`
15    * `<textarea>`
16    * `<code>` (optionally, minified by default)
17    * `<style>` (if the `type` attribute is unsupported)
18    * `<script>` (if the `type` attribute is unsupported)
19* JS code and CSS code in `<script>` and `<style>` elements are minified by [minifier](https://crates.io/crates/minifier).
20
21The original (non-minified) HTML doesn't need to be completely generated before using this library because this library doesn't do any deserialization to create DOMs.
22
23## Examples
24
25```rust
26use html_minifier::HTMLMinifier;
27
28let mut html_minifier = HTMLMinifier::new();
29
30html_minifier.digest("<!DOCTYPE html>   <html  ").unwrap();
31html_minifier.digest("lang=  en >").unwrap();
32html_minifier.digest("
33<head>
34    <meta name=viewport>
35</head>
36").unwrap();
37html_minifier.digest("
38<body class=' container   bg-light '>
39    <input type='text' value='123   456' readonly=''  />
40
41    123456
42    <b>big</b> 789
43    ab
44    c
45    中文
4647</body>
48").unwrap();
49html_minifier.digest("</html  >").unwrap();
50
51assert_eq!("<!DOCTYPE html> <html lang=en>
52<head>
53<meta name=viewport>
54</head>
55<body class='container bg-light'>
56<input type='text' value='123   456' readonly/>
57123456
58<b>big</b> 789
59ab
60c
61中文
6263</body>
64</html>".as_bytes(), html_minifier.get_html());
65```
66
67```rust
68use html_minifier::HTMLMinifier;
69
70let mut html_minifier = HTMLMinifier::new();
71
72html_minifier.digest("<pre  >   Hello  world!   </pre  >").unwrap();
73
74assert_eq!(b"<pre>   Hello  world!   </pre>", html_minifier.get_html());
75```
76
77```rust
78use html_minifier::HTMLMinifier;
79
80let mut html_minifier = HTMLMinifier::new();
81
82html_minifier.digest("<script type='  application/javascript '>   alert('Hello!')    ;   </script>").unwrap();
83
84assert_eq!("<script type='application/javascript'>alert('Hello!')</script>".as_bytes(), html_minifier.get_html());
85```
86
87## Write HTML to a Writer
88
89If you don't want to store your HTML in memory (e.g. writing to a file instead), you can use the `HTMLMinifierHelper` struct which provides a low-level API that allows you to pass your output instance when invoking the `digest` method.
90
91```rust
92use html_minifier::HTMLMinifierHelper;
93
94use std::fs::File;
95use std::io::Read;
96
97let mut input_file = File::open("tests/data/w3schools.com_tryhow_css_example_website.htm").unwrap();
98let mut output_file = File::create("tests/data/index.min.html").unwrap();
99
100let mut buffer = [0u8; 256];
101
102let mut html_minifier_helper = HTMLMinifierHelper::new();
103
104loop {
105    let c = input_file.read(&mut buffer).unwrap();
106
107    if c == 0 {
108        break;
109    }
110
111    html_minifier_helper.digest(&buffer[..c], &mut output_file).unwrap();
112}
113```
114*/
115mod errors;
116mod functions;
117mod html_minifier_helper;
118mod html_writer;
119
120use educe::Educe;
121pub use errors::*;
122pub use html_minifier_helper::*;
123pub use html_writer::*;
124
125use crate::functions::*;
126
127/// This struct helps you generate and minify your HTML code in the same time. The output destination is inside this struct.
128#[derive(Educe, Clone)]
129#[educe(Debug, Default(new))]
130pub struct HTMLMinifier {
131    helper: HTMLMinifierHelper,
132    #[educe(Debug(method = "str_bytes_fmt"))]
133    out:    Vec<u8>,
134}
135
136impl HTMLMinifier {
137    /// Set whether to remove HTML comments.
138    #[inline]
139    pub fn set_remove_comments(&mut self, remove_comments: bool) {
140        self.helper.remove_comments = remove_comments;
141    }
142
143    /// Set whether to minify the content in the `code` element.
144    #[inline]
145    pub fn set_minify_code(&mut self, minify_code: bool) {
146        self.helper.minify_code = minify_code;
147    }
148
149    /// Get whether to remove HTML comments.
150    #[inline]
151    pub const fn get_remove_comments(&self) -> bool {
152        self.helper.remove_comments
153    }
154
155    /// Get whether to minify the content in the `code` element.
156    #[inline]
157    pub const fn get_minify_code(&self) -> bool {
158        self.helper.minify_code
159    }
160}
161
162impl HTMLMinifier {
163    /// Reset this html minifier. The option settings and allocated memory will be be preserved.
164    #[inline]
165    pub fn reset(&mut self) {
166        self.helper.reset();
167        self.out.clear();
168    }
169}
170
171impl HTMLMinifier {
172    /// Input some text to generate HTML code. It is not necessary to input a full HTML text at once.
173    #[inline]
174    pub fn digest<S: AsRef<[u8]>>(&mut self, text: S) -> Result<(), HTMLMinifierError> {
175        let text = text.as_ref();
176
177        self.out.reserve(text.len());
178
179        self.helper.digest(text, &mut self.out)
180    }
181
182    /// Directly input some text to generate HTML code. The text will just be appended to the output buffer instead of being through the helper.
183    ///
184    /// # When to Use This?
185    ///
186    /// If the text has been minified, you can consider to use this method to get a better performance.
187    #[allow(clippy::missing_safety_doc)]
188    #[inline]
189    pub unsafe fn indigest<S: AsRef<[u8]>>(&mut self, text: S) {
190        self.out.extend_from_slice(text.as_ref());
191    }
192}
193
194impl HTMLMinifier {
195    /// Get HTML in a string slice.
196    #[inline]
197    pub fn get_html(&mut self) -> &[u8] {
198        self.out.as_slice()
199    }
200}
201
202/// Minify HTML.
203#[inline]
204pub fn minify<S: AsRef<str>>(html: S) -> Result<String, HTMLMinifierError> {
205    let mut minifier = HTMLMinifierHelper::new();
206
207    let html = html.as_ref();
208
209    let mut minified_html = String::with_capacity(html.len());
210
211    minifier.digest(html, unsafe { minified_html.as_mut_vec() })?;
212
213    Ok(minified_html)
214}