Skip to content

Commit d42d97b

Browse files
committed
feat: enhance HTMLMinifierOptions with complete TypeScript interface support
- Add all missing properties based on html-minifier-next TypeScript interface - Add comprehensive documentation and default value comments for each property - Change all properties from let to var for better flexibility - Update optionsDict in HTMLMinifier to support all new properties - Support dynamic setting of optional properties (maxInputLength, maxLineLength, quoteCharacter) New properties added: - caseSensitive: treat attributes in case sensitive manner - collapseInlineTagWhitespace: collapse whitespace between inline elements - continueOnParseError: handle parse errors instead of aborting - decodeEntities: use direct Unicode characters - html5: parse input according to HTML5 specifications - includeAutoGeneratedTags: insert tags generated by HTML parser - keepClosingSlash: keep trailing slash on singleton elements - noNewlinesBeforeTagClose: never add newline before closing tag - preventAttributesEscaping: prevent escaping of attribute values - processConditionalComments: process conditional comments - removeOptionalTags: remove optional tags - removeTagWhitespace: remove space between attributes - sortAttributes: sort attributes by frequency - sortClassName: sort style classes by frequency
1 parent 9d3256f commit d42d97b

3 files changed

Lines changed: 260 additions & 77 deletions

File tree

Sources/HTMLMinifier/HTMLMinifier.swift

Lines changed: 38 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,6 @@
11
import JavaScriptCore
22
import Foundation
33

4-
/// HTML minification options
5-
public struct HTMLMinifierOptions: Sendable {
6-
public let removeAttributeQuotes: Bool
7-
public let removeComments: Bool
8-
public let removeEmptyAttributes: Bool
9-
public let removeRedundantAttributes: Bool
10-
public let removeScriptTypeAttributes: Bool
11-
public let removeStyleLinkTypeAttributes: Bool
12-
public let trimCustomFragments: Bool
13-
public let useShortDoctype: Bool
14-
public let collapseWhitespace: Bool
15-
public let conservativeCollapse: Bool
16-
public let preserveLineBreaks: Bool
17-
public let collapseBooleanAttributes: Bool
18-
public let removeEmptyElements: Bool
19-
public let minifyJS: Bool
20-
public let minifyCSS: Bool
21-
public let minifyURLs: Bool
22-
23-
public init(
24-
removeAttributeQuotes: Bool = false,
25-
removeComments: Bool = false,
26-
removeEmptyAttributes: Bool = false,
27-
removeRedundantAttributes: Bool = false,
28-
removeScriptTypeAttributes: Bool = false,
29-
removeStyleLinkTypeAttributes: Bool = false,
30-
trimCustomFragments: Bool = false,
31-
useShortDoctype: Bool = false,
32-
collapseWhitespace: Bool = false,
33-
conservativeCollapse: Bool = false,
34-
preserveLineBreaks: Bool = false,
35-
collapseBooleanAttributes: Bool = false,
36-
removeEmptyElements: Bool = false,
37-
minifyJS: Bool = false,
38-
minifyCSS: Bool = false,
39-
minifyURLs: Bool = false
40-
) {
41-
self.removeAttributeQuotes = removeAttributeQuotes
42-
self.removeComments = removeComments
43-
self.removeEmptyAttributes = removeEmptyAttributes
44-
self.removeRedundantAttributes = removeRedundantAttributes
45-
self.removeScriptTypeAttributes = removeScriptTypeAttributes
46-
self.removeStyleLinkTypeAttributes = removeStyleLinkTypeAttributes
47-
self.trimCustomFragments = trimCustomFragments
48-
self.useShortDoctype = useShortDoctype
49-
self.collapseWhitespace = collapseWhitespace
50-
self.conservativeCollapse = conservativeCollapse
51-
self.preserveLineBreaks = preserveLineBreaks
52-
self.collapseBooleanAttributes = collapseBooleanAttributes
53-
self.removeEmptyElements = removeEmptyElements
54-
self.minifyJS = minifyJS
55-
self.minifyCSS = minifyCSS
56-
self.minifyURLs = minifyURLs
57-
}
58-
}
59-
604
/// HTML Minifier error types
615
public enum HTMLMinifierError: Error {
626
case jsContextCreationFailed
@@ -145,25 +89,52 @@ public class HTMLMinifier {
14589
}
14690

14791
// Convert options to JavaScript object
148-
let optionsDict: [String: Any] = [
92+
var optionsDict: [String: Any] = [
93+
"caseSensitive": options.caseSensitive,
94+
"collapseBooleanAttributes": options.collapseBooleanAttributes,
95+
"collapseInlineTagWhitespace": options.collapseInlineTagWhitespace,
96+
"collapseWhitespace": options.collapseWhitespace,
97+
"conservativeCollapse": options.conservativeCollapse,
98+
"continueOnParseError": options.continueOnParseError,
99+
"decodeEntities": options.decodeEntities,
100+
"html5": options.html5,
101+
"includeAutoGeneratedTags": options.includeAutoGeneratedTags,
102+
"keepClosingSlash": options.keepClosingSlash,
103+
"minifyCSS": options.minifyCSS,
104+
"minifyJS": options.minifyJS,
105+
"minifyURLs": options.minifyURLs,
106+
"noNewlinesBeforeTagClose": options.noNewlinesBeforeTagClose,
107+
"preserveLineBreaks": options.preserveLineBreaks,
108+
"preventAttributesEscaping": options.preventAttributesEscaping,
109+
"processConditionalComments": options.processConditionalComments,
149110
"removeAttributeQuotes": options.removeAttributeQuotes,
150111
"removeComments": options.removeComments,
151112
"removeEmptyAttributes": options.removeEmptyAttributes,
113+
"removeEmptyElements": options.removeEmptyElements,
114+
"removeOptionalTags": options.removeOptionalTags,
152115
"removeRedundantAttributes": options.removeRedundantAttributes,
153116
"removeScriptTypeAttributes": options.removeScriptTypeAttributes,
154117
"removeStyleLinkTypeAttributes": options.removeStyleLinkTypeAttributes,
118+
"removeTagWhitespace": options.removeTagWhitespace,
119+
"sortAttributes": options.sortAttributes,
120+
"sortClassName": options.sortClassName,
155121
"trimCustomFragments": options.trimCustomFragments,
156-
"useShortDoctype": options.useShortDoctype,
157-
"collapseWhitespace": options.collapseWhitespace,
158-
"conservativeCollapse": options.conservativeCollapse,
159-
"preserveLineBreaks": options.preserveLineBreaks,
160-
"collapseBooleanAttributes": options.collapseBooleanAttributes,
161-
"removeEmptyElements": options.removeEmptyElements,
162-
"minifyJS": options.minifyJS,
163-
"minifyCSS": options.minifyCSS,
164-
"minifyURLs": options.minifyURLs
122+
"useShortDoctype": options.useShortDoctype
165123
]
166124

125+
// Add optional properties only if they have values
126+
if let maxInputLength = options.maxInputLength {
127+
optionsDict["maxInputLength"] = maxInputLength
128+
}
129+
130+
if let maxLineLength = options.maxLineLength {
131+
optionsDict["maxLineLength"] = maxLineLength
132+
}
133+
134+
if let quoteCharacter = options.quoteCharacter {
135+
optionsDict["quoteCharacter"] = quoteCharacter
136+
}
137+
167138
// Call the minify function
168139
guard let htmlMinifier = jsContext.objectForKeyedSubscript("HTMLMinifier") else {
169140
throw HTMLMinifierError.minificationFailed("HTMLMinifier object not found")
@@ -237,17 +208,7 @@ public class HTMLMinifier {
237208
/// - Returns: Minified HTML string
238209
/// - Throws: HTMLMinifierError if minification fails
239210
public func minify(_ html: String) throws -> String {
240-
let defaultOptions = HTMLMinifierOptions(
241-
removeComments: true,
242-
removeEmptyAttributes: true,
243-
removeRedundantAttributes: true,
244-
removeScriptTypeAttributes: true,
245-
removeStyleLinkTypeAttributes: true,
246-
useShortDoctype: true,
247-
collapseWhitespace: true,
248-
collapseBooleanAttributes: true
249-
)
250-
211+
let defaultOptions: HTMLMinifierOptions = .init()
251212
return try minify(html, options: defaultOptions)
252213
}
253214
}
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
//
2+
// HTMLMinifierOptions.swift
3+
// HTMLMinifier
4+
//
5+
// Created by wong on 8/23/25.
6+
//
7+
8+
9+
/// HTML minification options
10+
/// Most of the options are disabled by default
11+
public struct HTMLMinifierOptions: Sendable {
12+
13+
/// Treat attributes in case sensitive manner (useful for custom HTML tags)
14+
/// @default false
15+
public var caseSensitive: Bool
16+
17+
/// Omit attribute values from boolean attributes
18+
/// @default false
19+
public var collapseBooleanAttributes: Bool
20+
21+
/// Don't leave any spaces between `display:inline;` elements when collapsing.
22+
/// Must be used in conjunction with `collapseWhitespace=true`
23+
/// @default false
24+
public var collapseInlineTagWhitespace: Bool
25+
26+
/// Collapse white space that contributes to text nodes in a document tree
27+
/// @default false
28+
public var collapseWhitespace: Bool
29+
30+
/// Always collapse to 1 space (never remove it entirely).
31+
/// Must be used in conjunction with `collapseWhitespace=true`
32+
/// @default false
33+
public var conservativeCollapse: Bool
34+
35+
/// Handle parse errors instead of aborting
36+
/// @default false
37+
public var continueOnParseError: Bool
38+
39+
/// Use direct Unicode characters whenever possible
40+
/// @default false
41+
public var decodeEntities: Bool
42+
43+
/// Parse input according to HTML5 specifications
44+
/// @default true
45+
public var html5: Bool
46+
47+
/// Insert tags generated by HTML parser
48+
/// @default true
49+
public var includeAutoGeneratedTags: Bool
50+
51+
/// Keep the trailing slash on singleton elements
52+
/// @default false
53+
public var keepClosingSlash: Bool
54+
55+
/// Maximum input length to prevent ReDoS attacks (disabled by default)
56+
/// @default nil
57+
public var maxInputLength: Int?
58+
59+
/// Specify a maximum line length.
60+
/// Compressed output will be split by newlines at valid HTML split-points
61+
/// @default nil
62+
public var maxLineLength: Int?
63+
64+
/// Minify CSS in style elements and style attributes
65+
/// @default false
66+
public var minifyCSS: Bool
67+
68+
/// Minify JavaScript in script elements and event attributes
69+
/// @default false
70+
public var minifyJS: Bool
71+
72+
/// Minify URLs in various attributes
73+
/// @default false
74+
public var minifyURLs: Bool
75+
76+
/// Never add a newline before a tag that closes an element
77+
/// @default false
78+
public var noNewlinesBeforeTagClose: Bool
79+
80+
/// Always collapse to 1 line break (never remove it entirely) when whitespace between tags include a line break.
81+
/// Must be used in conjunction with `collapseWhitespace=true`
82+
/// @default false
83+
public var preserveLineBreaks: Bool
84+
85+
/// Prevents the escaping of the values of attributes
86+
/// @default false
87+
public var preventAttributesEscaping: Bool
88+
89+
/// Process contents of conditional comments through minifier
90+
/// @default false
91+
public var processConditionalComments: Bool
92+
93+
/// Type of quote to use for attribute values ("'" or """)
94+
/// @default nil
95+
public var quoteCharacter: String?
96+
97+
/// Remove quotes around attributes when possible
98+
/// @default false
99+
public var removeAttributeQuotes: Bool
100+
101+
/// Strip HTML comments
102+
/// @default false
103+
public var removeComments: Bool
104+
105+
/// Remove all attributes with whitespace-only values
106+
/// @default false
107+
public var removeEmptyAttributes: Bool
108+
109+
/// Remove all elements with empty contents
110+
/// @default false
111+
public var removeEmptyElements: Bool
112+
113+
/// Remove optional tags
114+
/// @default false
115+
public var removeOptionalTags: Bool
116+
117+
/// Remove attributes when value matches default
118+
/// @default false
119+
public var removeRedundantAttributes: Bool
120+
121+
/// Remove `type="text/javascript"` from `script` tags.
122+
/// Other `type` attribute values are left intact
123+
/// @default false
124+
public var removeScriptTypeAttributes: Bool
125+
126+
/// Remove `type="text/css"` from `style` and `link` tags.
127+
/// Other `type` attribute values are left intact
128+
/// @default false
129+
public var removeStyleLinkTypeAttributes: Bool
130+
131+
/// Remove space between attributes whenever possible.
132+
/// **Note that this will result in invalid HTML!**
133+
/// @default false
134+
public var removeTagWhitespace: Bool
135+
136+
/// Sort attributes by frequency
137+
/// @default false
138+
public var sortAttributes: Bool
139+
140+
/// Sort style classes by frequency
141+
/// @default false
142+
public var sortClassName: Bool
143+
144+
/// Trim white space around `ignoreCustomFragments`
145+
/// @default false
146+
public var trimCustomFragments: Bool
147+
148+
/// Replaces the `doctype` with the short (HTML5) doctype
149+
/// @default false
150+
public var useShortDoctype: Bool
151+
152+
public init(
153+
caseSensitive: Bool = false,
154+
collapseBooleanAttributes: Bool = false,
155+
collapseInlineTagWhitespace: Bool = false,
156+
collapseWhitespace: Bool = false,
157+
conservativeCollapse: Bool = false,
158+
continueOnParseError: Bool = false,
159+
decodeEntities: Bool = false,
160+
html5: Bool = true,
161+
includeAutoGeneratedTags: Bool = true,
162+
keepClosingSlash: Bool = false,
163+
maxInputLength: Int? = nil,
164+
maxLineLength: Int? = nil,
165+
minifyCSS: Bool = false,
166+
minifyJS: Bool = false,
167+
minifyURLs: Bool = false,
168+
noNewlinesBeforeTagClose: Bool = false,
169+
preserveLineBreaks: Bool = false,
170+
preventAttributesEscaping: Bool = false,
171+
processConditionalComments: Bool = false,
172+
quoteCharacter: String? = nil,
173+
removeAttributeQuotes: Bool = false,
174+
removeComments: Bool = false,
175+
removeEmptyAttributes: Bool = false,
176+
removeEmptyElements: Bool = false,
177+
removeOptionalTags: Bool = false,
178+
removeRedundantAttributes: Bool = false,
179+
removeScriptTypeAttributes: Bool = false,
180+
removeStyleLinkTypeAttributes: Bool = false,
181+
removeTagWhitespace: Bool = false,
182+
sortAttributes: Bool = false,
183+
sortClassName: Bool = false,
184+
trimCustomFragments: Bool = false,
185+
useShortDoctype: Bool = false
186+
) {
187+
self.caseSensitive = caseSensitive
188+
self.collapseBooleanAttributes = collapseBooleanAttributes
189+
self.collapseInlineTagWhitespace = collapseInlineTagWhitespace
190+
self.collapseWhitespace = collapseWhitespace
191+
self.conservativeCollapse = conservativeCollapse
192+
self.continueOnParseError = continueOnParseError
193+
self.decodeEntities = decodeEntities
194+
self.html5 = html5
195+
self.includeAutoGeneratedTags = includeAutoGeneratedTags
196+
self.keepClosingSlash = keepClosingSlash
197+
self.maxInputLength = maxInputLength
198+
self.maxLineLength = maxLineLength
199+
self.minifyCSS = minifyCSS
200+
self.minifyJS = minifyJS
201+
self.minifyURLs = minifyURLs
202+
self.noNewlinesBeforeTagClose = noNewlinesBeforeTagClose
203+
self.preserveLineBreaks = preserveLineBreaks
204+
self.preventAttributesEscaping = preventAttributesEscaping
205+
self.processConditionalComments = processConditionalComments
206+
self.quoteCharacter = quoteCharacter
207+
self.removeAttributeQuotes = removeAttributeQuotes
208+
self.removeComments = removeComments
209+
self.removeEmptyAttributes = removeEmptyAttributes
210+
self.removeEmptyElements = removeEmptyElements
211+
self.removeOptionalTags = removeOptionalTags
212+
self.removeRedundantAttributes = removeRedundantAttributes
213+
self.removeScriptTypeAttributes = removeScriptTypeAttributes
214+
self.removeStyleLinkTypeAttributes = removeStyleLinkTypeAttributes
215+
self.removeTagWhitespace = removeTagWhitespace
216+
self.sortAttributes = sortAttributes
217+
self.sortClassName = sortClassName
218+
self.trimCustomFragments = trimCustomFragments
219+
self.useShortDoctype = useShortDoctype
220+
}
221+
}

scripts/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"start": "cp node_modules/html-minifier-next/dist/htmlminifier.umd.bundle.min.js ../Sources/HTMLMinifier/Resources"
55
},
66
"dependencies": {
7+
"@types/html-minifier-next": "1.1.0",
78
"html-minifier-next": "1.2.1"
89
}
910
}

0 commit comments

Comments
 (0)