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 中文
46 字
47</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中文
62字
63</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}