55// Created by Evan Anderson on 3/31/25.
66//
77
8- import SwiftSyntax
9-
108extension HTMLKitUtilities {
11- static let defaultPreservedWhitespaceTags : Set < String > = Set ( [
12- " a " , " abbr " ,
13- " b " , " bdi " , " bdo " , " button " ,
14- " cite " , " code " ,
15- " data " , " dd " , " dfn " , " dt " ,
16- " em " ,
17- " h1 " , " h2 " , " h3 " , " h4 " , " h5 " , " h6 " ,
18- " i " ,
19- " kbd " ,
20- " label " , " li " ,
21- " mark " ,
22- " p " ,
23- " q " ,
24- " rp " ,
25- " rt " ,
26- " ruby " ,
27- " s " , " samp " , " small " , " span " , " strong " , " sub " , " sup " ,
28- " td " , " time " , " title " , " tr " ,
29- " u " ,
30- " var " ,
31- " wbr "
32- ] . map { " < " + $0 + " > " } )
9+ static let defaultPreservedWhitespaceTags : Set < Substring > = Set ( Array < HTMLElementType > ( arrayLiteral :
10+ . a , . abbr,
11+ . b , . bdi, . bdo, . button,
12+ . cite, . code,
13+ . data, . dd , . dfn, . dt ,
14+ . em ,
15+ . h1 , . h2 , . h3 , . h4 , . h5 , . h6 ,
16+ . i ,
17+ . kbd,
18+ . label, . li ,
19+ . mark,
20+ . p ,
21+ . q ,
22+ . rp ,
23+ . rt ,
24+ . ruby,
25+ . s , . samp, . small, . span, . strong, . sub, . sup,
26+ . td , . time, . title, . tr ,
27+ . u ,
28+ . variable ,
29+ . wbr
30+ ) . map { " < " + $0. tagName + " > " } )
3331
3432 /// Removes whitespace between elements.
3533 public static func minify(
3634 html: String ,
37- preservingWhitespaceForTags: Set < String > = [ ]
35+ preservingWhitespaceForTags: Set < Substring > = [ ]
3836 ) -> String {
39- var preservedWhitespaceTags : Set < String > = Self . defaultPreservedWhitespaceTags
40- preservedWhitespaceTags. formUnion ( preservingWhitespaceForTags)
4137 var result : String = " "
4238 result. reserveCapacity ( html. count)
4339 let tagRegex = " [^/>]+ "
44- let openElementRegex = " (< \( tagRegex) >) "
45- let openElementRanges = html. ranges ( of: try ! Regex ( openElementRegex) )
46-
47- let closeElementRegex = " (</ \( tagRegex) >) "
48- let closeElementRanges = html. ranges ( of: try ! Regex ( closeElementRegex) )
40+ let openElementRanges = html. ranges ( of: try ! Regex ( " (< \( tagRegex) >) " ) )
41+ let closeElementRanges = html. ranges ( of: try ! Regex ( " (</ \( tagRegex) >) " ) )
4942
5043 var openingRangeIndex = 0
5144 var ignoredClosingTags : Set < Range < String . Index > > = [ ]
5245 for openingRange in openElementRanges {
5346 let tag = html [ openingRange]
5447 result += tag
55- let closure : ( Character ) -> Bool = preservedWhitespaceTags. contains ( String ( tag) ) ? { _ in true } : {
56- !( $0. isWhitespace || $0. isNewline)
57- }
48+ let closure = Self . defaultPreservedWhitespaceTags. contains ( tag) || preservingWhitespaceForTags. contains ( tag) ? appendAll : appendIfPreserved
5849 let closestClosingRange = closeElementRanges. first ( where: { $0. lowerBound > openingRange. upperBound } )
59- if let nextOpeningRange = openElementRanges. get ( openingRangeIndex + 1 ) {
50+ if let nextOpeningRange = openElementRanges. getPositive ( openingRangeIndex + 1 ) {
6051 var i = openingRange. upperBound
6152 var lowerBound = nextOpeningRange. lowerBound
6253 if let closestClosingRange {
@@ -68,13 +59,7 @@ extension HTMLKitUtilities {
6859 }
6960 }
7061 // anything after the opening tag, upto the end of the next closing tag
71- while i < lowerBound {
72- let char = html [ i]
73- if closure ( char) {
74- result. append ( char)
75- }
76- html. formIndex ( after: & i)
77- }
62+ closure ( html, & i, lowerBound, & result)
7863 // anything after the closing tag and before the next opening tag
7964 while i < nextOpeningRange. lowerBound {
8065 let char = html [ i]
@@ -85,12 +70,8 @@ extension HTMLKitUtilities {
8570 }
8671 } else if let closestClosingRange {
8772 // anything after the opening tag and before the next closing tag
88- let slice = html [ openingRange. upperBound..< closestClosingRange. lowerBound]
89- for char in slice {
90- if closure ( char) {
91- result. append ( char)
92- }
93- }
73+ var i = openingRange. upperBound
74+ closure ( html, & i, closestClosingRange. lowerBound, & result)
9475 }
9576 openingRangeIndex += 1
9677 }
@@ -101,4 +82,31 @@ extension HTMLKitUtilities {
10182 }
10283 return result
10384 }
85+ }
86+
87+ // MARK: append
88+ extension HTMLKitUtilities {
89+ fileprivate static func appendAll(
90+ html: String ,
91+ i: inout String . Index ,
92+ bound: String . Index ,
93+ result: inout String
94+ ) {
95+ result += html [ i..< bound]
96+ i = bound
97+ }
98+ fileprivate static func appendIfPreserved(
99+ html: String ,
100+ i: inout String . Index ,
101+ bound: String . Index ,
102+ result: inout String
103+ ) {
104+ while i < bound {
105+ let char = html [ i]
106+ if !( char. isWhitespace || char. isNewline) {
107+ result. append ( char)
108+ }
109+ html. formIndex ( after: & i)
110+ }
111+ }
104112}
0 commit comments