diff --git a/Sources/Html/Render.swift b/Sources/Html/Render.swift index f5b3e64..fcae634 100644 --- a/Sources/Html/Render.swift +++ b/Sources/Html/Render.swift @@ -11,9 +11,9 @@ public func render(_ nodes: [Node]) -> String { public func render(_ node: Node) -> String { switch node { case let .comment(string): - return "", with: "-->")) -->" + return "" case let .doctype(string): - return "", with: ">"))>" + return "" case let .element(tag, attribs, children): let renderedAttribs = render(attribs) guard !children.isEmpty else { @@ -21,7 +21,7 @@ public func render(_ node: Node) -> String { } return "<" + tag + renderedAttribs + ">" + render(children) + "" case let .text(string): - return escape(html: string) + return escapeTextNode(text: string) case let .raw(string): return string } @@ -39,20 +39,32 @@ private func render(_ attribs: [(String, String?)]) -> String { return attribs .compactMap { key, value in value.map { - " " + key + ($0.isEmpty ? "" : "=\"\($0.replacingOccurrences(of: "\"", with: """))\"") + " " + key + ($0.isEmpty ? "" : "=\"\(escapeAttributeValue($0))\"") } } .joined() } -private func escape(html: String) -> String { - return html +public func escapeAttributeValue(_ value: String) -> String { + return value.replacingOccurrences(of: "\"", with: """) +} + +public func escapeTextNode(text: String) -> String { + return text .replacingOccurrences(of: "&", with: "&") .replacingOccurrences(of: "<", with: "<") } +public func escapeDoctype(_ doctype: String) -> String { + return doctype.replacingOccurrences(of: ">", with: ">") +} + +public func escapeHtmlComment(_ comment: String) -> String { + return comment.replacingOccurrences(of: "-->", with: "-->") +} + /// A set of self-closing "void" elements that should not contain child nodes. -private let voidElements: Set = [ +public let voidElements: Set = [ "area", "base", "br",