commit 743fc518732633c937c387476475434c6dd8c5c5 Author: Isaac Paul Date: Tue Sep 23 20:22:59 2025 -0400 Initial Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2564ffe --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.DS_Store +/.build +/.swiftpm +/*.xcodeproj +Sources/Old diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..cc6f753 --- /dev/null +++ b/LICENSE @@ -0,0 +1,145 @@ +# Custom license + +## Acceptance + +In order to get any license under these terms, you must agree +to them as both strict obligations and conditions to all +your licenses. + +## Copyright License + +The licensor grants you a copyright license for the +software to do everything you might do with the software +that would otherwise infringe the licensor's copyright +in it for any permitted purpose. However, you may +only distribute the software according to [Distribution +License](#distribution-license) and make changes or new works +based on the software according to [Changes and New Works +License](#changes-and-new-works-license). + +## Distribution License + +The licensor grants you an additional copyright license +to distribute copies of the software. Your license +to distribute covers distributing the software with +changes and new works permitted by [Changes and New Works +License](#changes-and-new-works-license). + +## Notices + +You must ensure that anyone who gets a copy of any part of +the software from you also gets a copy of these terms or the +URL for them above, as well as copies of any plain-text lines +beginning with `Required Notice:` that the licensor provided +with the software. For example: + +> Required Notice: Copyright Yoyodyne, Inc. (http://example.com) + +## Changes and New Works License + +The licensor grants you an additional copyright license to +make changes and new works based on the software for any +permitted purpose. + +## Patent License + +The licensor grants you a patent license for the software that +covers patent claims the licensor can license, or becomes able +to license, that you would infringe by using the software. + +## Noncommercial Purposes + +Any noncommercial purpose is a permitted purpose excluding anything +involving training or improving machine learning algorithms + +## Personal Uses + +Personal use for research, experiment, and testing for +the benefit of public knowledge, personal study, private +entertainment, hobby projects, amateur pursuits, or religious +observance, without any anticipated commercial application, +is use for a permitted purpose. + +## Noncommercial Organizations + +Use by any charitable organization, educational institution, +public research organization, public safety or health +organization, environmental protection organization, +or government institution are not exempt and is not a permitted +purpose. + +## Fair Use + +You may have "fair use" rights for the software under the +law. These terms do not limit them. + +## AI + +Training or improving machine learning algorithms, including +but not limited to artificial intelligence, natural language +processing, or data mining is not a permitted purpose. This +condition applies to any derivatives, modifications, or updates +based on the Software code. + +Any usage of the Software in an AI-training dataset is considered +a breach of this License. + +The Software may not be included in any dataset used for training +or improving machine learning algorithms, including but not limited +to artificial intelligence, natural language processing, or data mining. + +## No Other Rights + +These terms do not allow you to sublicense or transfer any of +your licenses to anyone else, or prevent the licensor from +granting licenses to anyone else. These terms do not imply +any other licenses. + +## Patent Defense + +If you make any written claim that the software infringes or +contributes to infringement of any patent, your patent license +for the software granted under these terms ends immediately. If +your company makes such a claim, your patent license ends +immediately for work on behalf of your company. + +## Violations + +The first time you are notified in writing that you have +violated any of these terms, or done anything with the software +not covered by your licenses, your licenses can nonetheless +continue if you come into full compliance with these terms, +and take practical steps to correct past violations, within +32 days of receiving notice. Otherwise, all your licenses +end immediately. + +## No Liability + +***As far as the law allows, the software comes as is, without +any warranty or condition, and the licensor will not be liable +to you for any damages arising out of these terms or the use +or nature of the software, under any kind of legal claim.*** + +## Definitions + +The **licensor** is the individual or entity offering these +terms, and the **software** is the software the licensor makes +available under these terms. + +**You** refers to the individual or entity agreeing to these +terms. + +**Your company** is any legal entity, sole proprietorship, +or other kind of organization that you work for, plus all +organizations that have control over, are under the control of, +or are under common control with that organization. **Control** +means ownership of substantially all the assets of an entity, +or the power to direct its management and policies by vote, +contract, or otherwise. Control can be direct or indirect. + +**Your licenses** are all the licenses granted to you for the +software under these terms. + +**Use** means anything you do with the software requiring one +of your licenses. + diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..f93606c --- /dev/null +++ b/Package.swift @@ -0,0 +1,34 @@ +// swift-tools-version:5.9 + +// +// Package.swift +// HRW +// +// Created by Isaac Paul on 10/15/24 +// Non-commercial license, see LICENSE.MD in the project root for details +// + +import PackageDescription + +let package = Package( + name: "HRW", + products: [ + .library( + name: "HRW", + targets: ["HRW"] + ) + ], + dependencies: [ + ], + targets: [ + .target( + name: "HRW", + dependencies: [ + ] + ), + .testTarget( + name: "HRWTests", + dependencies: ["HRW"] + ) + ] +) diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..c238f59 --- /dev/null +++ b/README.txt @@ -0,0 +1,22 @@ +## HTML Reader Writer (H-RW) + +The goal is to be able to load html into swift from a file, manipulate it, then spit out html while retaining the benefits of a type safe language which serves as an alternative to templating. + +#### Thoughts + +I suppose I wanted to be able to use plain html as is. In hindsight, it was too much effort for the payoff. This is mostly so I can build multipage websites with swift without the use of JS as a silly experiement. + +There is also a HTML library called Plot that also lets you manipulate html, but it doesn't let you load it in from a file and treats HTML generations similarly to SwiftUI. + +Perhaps this project can be repurposed as an LSP for html, but that's not much of an interest to me. It could also possibly be used to implement a renderer or translate html into a different UI format. However, I did not really take security into consideration when building this. + +There is also an HTML parser in LadyBird thats partially written in Swift. It seems to call into c++ for a handful of things, so it doesn't seem to be reusable without that. + + +## License + +The license is a modified version of the PolyForm Noncommercial License (1.0.0) to add more non-commerical and non-ai use stipulations. I am open relicensing. + +## Contributions + +All contributors must sign an CLA as I do not wish to restrict myself in the use of the code or future relicensing endevors. diff --git a/Sources/HRW/BaseComponents.swift b/Sources/HRW/BaseComponents.swift new file mode 100644 index 0000000..dc3cd7c --- /dev/null +++ b/Sources/HRW/BaseComponents.swift @@ -0,0 +1,358 @@ +// +// BaseComponents.swift +// HRW +// +// Created by Isaac Paul on 10/15/24. +// Non-commercial license, see LICENSE.MD in the project root for details +// + +public protocol IFlowContent : HTMLNode {} + +public protocol IGlobalContainer { + var globalAttributes:Dictionary { get set } + var globalEvents:Dictionary { get set } + var dataAttributes:Dictionary { get set } +} + +extension IGlobalContainer { + mutating func trySetGlobalAttribute(_ key:String, _ value:String) -> Bool { + if let attr = GlobalAttributeKey(rawValue: key) { + globalAttributes.updateValue(value, forKey: attr) + return true + } + if let attr = GlobalEventKey(rawValue: key.asSubstring()) { + globalEvents[attr] = value + return true + } + if key[.. = [:] + public var globalEvents:Dictionary = [:] + public var dataAttributes:Dictionary = [:] + + public init(globalAttributes: Dictionary, globalEvents: Dictionary, dataAttributes: Dictionary) { + self.globalAttributes = globalAttributes + self.globalEvents = globalEvents + self.dataAttributes = dataAttributes + } + + public init() { + self.globalAttributes = [:] + self.globalEvents = [:] + self.dataAttributes = [:] + } + + public init(_ attributes: [String: String]) throws { + self.globalAttributes = [:] + self.globalEvents = [:] + self.dataAttributes = [:] + for (key, value) in attributes { + if self.trySetGlobalAttribute(key, value) { + continue + } + throw AppError("Unexpected attribute: \(key)") + } + } +} + +// The problem is pausing and resuming so this is a n-squared search, but is it faster than make allocations? do we even need this? +/* +public struct HtmlIterator : IteratorProtocol { + public init(src: NHTMLRenderable) { + self._src = src + } + + public typealias Element = NHTMLRenderable + + private let _src:NHTMLRenderable + private var _index:Int = 0 + + public mutating func next() -> NHTMLRenderable? { + let (index, result) = _src.renderableAtIndex(_index) + _index += 1 + return result + } +}*/ + +public class HTMLNode : XMLNode, IGlobalContainer { + + public var globalAttributes:Dictionary = [:] + public var globalEvents:Dictionary = [:] + + public var children:[HTMLNode] = [] + + public init(expectedAttributes:[String:String]) throws { + super.init() + for (key, value) in expectedAttributes { + if let attr = GlobalAttributeKey(rawValue: key) { + globalAttributes.updateValue(value, forKey: attr) + continue + } + if let attr = GlobalEventKey(rawValue: key.asSubstring()) { + globalEvents[attr] = value + continue + } + if key[.., globalEvents:Dictionary, dataAttributes:Dictionary) { + self.globalAttributes = globalAttributes + self.globalEvents = globalEvents + super.init(dataAttributes: dataAttributes) + } + + public init(_ builder:GlobalAttributesBuilder, _ children:[HTMLNode] = []) { + self.globalAttributes = builder.globalAttributes + self.globalEvents = builder.globalEvents + self.children = children + super.init(dataAttributes: builder.dataAttributes) + } + + public func findById(_ id:String) -> HTMLNode? { + if (globalAttributes[.id] == id) { + return self + } + for eachChild in children { + if let result = eachChild.findById(id) { + return result + } + } + return nil + } + + public override func renderAttributes() -> String { + var result = super.renderAttributes() + var first = result.count == 0 + for eachAttr in globalAttributes { + if (!first) { + result += " " + } + first = false + if (eachAttr.value.count > 0) { + result += "\(eachAttr.key)='\(eachAttr.value)'" + } else { + result += "\(eachAttr.key)" + } + } + + for eachAttr in globalEvents { + if (!first) { + result += " " + } + first = false + if (eachAttr.value.count > 0) { + result += "\(eachAttr.key) = \(eachAttr.value)" + } else { + result += "\(eachAttr.key)" + } + } + + return result + } + + override var nodeName: String { + return "HTML" + } + + override var isVoidElement: Bool { + return false + } + + override public func toString(_ depth:Int = 0, spacingStrat:SpacingStrat = .tabs) -> (Int, String) { + var newDepth = depth + var result = renderTag() + if (!isVoidElement) { + for eachChild in children { + let (nextDepth, renderedChild) = eachChild.toString(newDepth, spacingStrat: spacingStrat) + newDepth = nextDepth + result += renderedChild + } + result += "<\(nodeName)/>" + } + return (newDepth, result) + } +} + +public enum SpacingStrat { + case tabs + case spaces(num:Int) +} + +public class GenericXMLNode : XMLNode { + + public var attributes:Dictionary = [:] + public var children:[XMLNode] = [] + public var name:String + + public init(_ name:String, _ attributes:[String:String], _ children:[XMLNode] = []) { + self.attributes = attributes + self.children = children + self.name = name + super.init(attributes) + } + + public init(_ name:String, _ attributes:[String:String], _ parser:XMLParser? = nil) throws { + self.attributes = attributes + self.name = name + super.init(attributes) + + + var allItems:[XMLNode] = [] + while let obj = try parser?.readObjectXml(endTag: "a") { + allItems.append(obj) + } + children = allItems + } +} + +public class XMLNode { //Not sure if should be a parent class or just protocol + + public var dataAttributes:Dictionary = [:] + public var parent:XMLNode? = nil + var nodeName: String { + return "" + } + var isVoidElement: Bool { + return true + } + /* + public func it() -> HtmlIterator { + return HtmlIterator(src: self) + }*/ + + public init() { + + } + + public init(_ attributes:[String:String]) { + for (key, value) in attributes { + if key[.. String { + var result = "" + var first = true + for eachAttr in dataAttributes { + if (!first) { + result += " " + } + first = false + if (eachAttr.value.count > 0) { + result += "\(eachAttr.key) = \(eachAttr.value)" + } else { + result += "\(eachAttr.key)" + } + } + return result + } + + /* + public func renderableAtIndex(_ index:Int) -> (Int, NHTMLRenderable?) { + if (index == 0) { + return (-1, self) + } + var nextIndex = index - 1 + for eachChild in children { + let (index, result) = eachChild.renderableAtIndex(nextIndex) + if let result = result { + return (-1, result) + } + nextIndex = index + } + return (nextIndex, nil) + }*/ + + public func toString(_ depth:Int = 0, spacingStrat:SpacingStrat = .tabs) -> (Int, String) { + var newDepth = depth + var result = "<\(nodeName) \(renderAttributes())" + if (isVoidElement) { + result += "/>" + } else { + result += ">" + } + return (newDepth, result) + } + + public func renderTag() -> String { + let closing = isVoidElement ? "/" : "" + let attributes = renderAttributes() + if (attributes.isEmpty) { + let result = "<\(nodeName)\(closing)>" + return result + } else { + let result = "<\(nodeName) \(renderAttributes()) \(closing)>" + return result + } + } + +} + +func isGlobalHTMLAttribute(_ key:String) -> Bool { + if let _ = GlobalAttributeKey(rawValue: key) { + return true + } + if let _ = GlobalEventKey(rawValue: key.asSubstring()) { + return true + } + if key[.. NHTMLRenderable? { + if let result = super.findById(id) { + return result + } + for eachChild in children { + if let result = eachChild.findById(id) { + return result + } + } + return nil + } +}*/ + diff --git a/Sources/HRW/GenHTML/Elements/A.swift b/Sources/HRW/GenHTML/Elements/A.swift new file mode 100644 index 0000000..738f7d5 --- /dev/null +++ b/Sources/HRW/GenHTML/Elements/A.swift @@ -0,0 +1,116 @@ +// +// A.swift +// HTMLStandard +// +// Generated on 09/23/2025. +// THIS FILE IS GENERATED. DO NOT EDIT. +// +import Foundation + +/// Hyperlink +public class A : HTMLNode, IFlow, IInteractive, IPalpable, IPhrasing { + + /// Whether to download the resource instead of navigating to it, and its file name if so. + public var download:String? = nil + + /// Address of the hyperlink. Valid URL potentially surrounded by spaces. + public var href:URL? = nil + + /// Language of the linked resource. Valid BCP 47 language tag. + public var hreflang:String? = nil + + /// URLs to ping. Set of space-separated tokens consisting of valid non-empty URLs. + public var ping:[URL] = [] + + /// Referrer policy for fetches initiated by the element. Referrer policy. + public var referrerpolicy:ReferrerPolicy? = nil + + /// Relationship between the location in the document containing the hyperlink and the destination resource. Unordered set of unique space-separated tokens. The actual rules are more complicated than indicated. + public var rel:[String] = [] + + /// Browsing context for hyperlink navigation. Valid browsing context name or keyword. + public var target:String? = nil + + /// Hint for the type of the referenced resource. Valid MIME type string. + public var type:String? = nil + + + public init(_ attributes:[String:String], _ parser:XMLParser? = nil) throws { + var globalAttr = GlobalAttributesBuilder() + for (key, attValue) in attributes { + switch (key) { + case "download": + download = attValue + continue + case "href": + href = try URL(expect: attValue) + continue + case "hreflang": + hreflang = attValue + continue + case "ping": + ping = try URL.parseList(attValue, " ") + continue + case "referrerpolicy": + referrerpolicy = try ReferrerPolicy(expect: attValue) + continue + case "rel": + rel = try String.parseList(attValue, " ") + continue + case "target": + target = attValue + continue + case "type": + type = attValue + continue + + default: break + } + if globalAttr.trySetGlobalAttribute(key, attValue) { + continue + } + throw AppError("Unexpected attribute: \(key)") + } + var allItems:[HTMLNode] = [] + while let obj = try parser?.readObject(endTag: "a", xmlToHtmlMapper) { + allItems.append(obj) + } + super.init(globalAttr, allItems) + } + + + public override func renderAttributes() -> String { + var result = super.renderAttributes() + if let download = download { + result += " download='\(download)'" + } + if let href = href { + result += " href='\(href.absoluteString)'" + } + if let hreflang = hreflang { + result += " hreflang='\(hreflang)'" + } + result += " ping='\(ping.toStringList(" "))'" + if let referrerpolicy = referrerpolicy { + result += " referrerpolicy='\(referrerpolicy.rawValue)'" + } + result += " rel='\(rel.toStringList(" "))'" + if let target = target { + result += " target='\(target)'" + } + if let type = type { + result += " type='\(type)'" + } + + + return result + } + + override var nodeName: String { + return "a" + } + + override var isVoidElement: Bool { + return false + } +} \ No newline at end of file diff --git a/Sources/HRW/GenHTML/Elements/Abbr.swift b/Sources/HRW/GenHTML/Elements/Abbr.swift new file mode 100644 index 0000000..900e66d --- /dev/null +++ b/Sources/HRW/GenHTML/Elements/Abbr.swift @@ -0,0 +1,56 @@ +// +// Abbr.swift +// HTMLStandard +// +// Generated on 09/23/2025. +// THIS FILE IS GENERATED. DO NOT EDIT. +// +import Foundation + +/// Abbreviation +public class Abbr : HTMLNode, IFlow, IPalpable, IPhrasing { + + + /// Full term or expansion of abbreviation. + public var title:String? { + get { return globalAttributes[.title] } + set { globalAttributes[.title] = newValue } + } + + + public init(_ attributes:[String:String], _ parser:XMLParser? = nil) throws { + var globalAttr = GlobalAttributesBuilder() + for (key, attValue) in attributes { + + if globalAttr.trySetGlobalAttribute(key, attValue) { + continue + } + throw AppError("Unexpected attribute: \(key)") + } + var allItems:[HTMLNode] = [] + while let obj = try parser?.readObject(endTag: "abbr", xmlToHtmlMapper) { + allItems.append(obj) + } + super.init(globalAttr, allItems) + } + + + public func addChild(_ someElement:IPhrasing) { + children.append(someElement) + } + + public override func renderAttributes() -> String { + var result = super.renderAttributes() + + + return result + } + + override var nodeName: String { + return "abbr" + } + + override var isVoidElement: Bool { + return false + } +} \ No newline at end of file diff --git a/Sources/HRW/GenHTML/Elements/Address.swift b/Sources/HRW/GenHTML/Elements/Address.swift new file mode 100644 index 0000000..1d9538e --- /dev/null +++ b/Sources/HRW/GenHTML/Elements/Address.swift @@ -0,0 +1,49 @@ +// +// Address.swift +// HTMLStandard +// +// Generated on 09/23/2025. +// THIS FILE IS GENERATED. DO NOT EDIT. +// +import Foundation + +///
Contact information for a page or article element +public class Address : HTMLNode, IFlow, IPalpable { + + + public init(_ attributes:[String:String], _ parser:XMLParser? = nil) throws { + var globalAttr = GlobalAttributesBuilder() + for (key, attValue) in attributes { + + if globalAttr.trySetGlobalAttribute(key, attValue) { + continue + } + throw AppError("Unexpected attribute: \(key)") + } + var allItems:[HTMLNode] = [] + while let obj = try parser?.readObject(endTag: "address", xmlToHtmlMapper) { + allItems.append(obj) + } + super.init(globalAttr, allItems) + } + + + public func addChild(_ someElement:IFlow) { + children.append(someElement) + } + + public override func renderAttributes() -> String { + var result = super.renderAttributes() + + + return result + } + + override var nodeName: String { + return "address" + } + + override var isVoidElement: Bool { + return false + } +} \ No newline at end of file diff --git a/Sources/HRW/GenHTML/Elements/Area.swift b/Sources/HRW/GenHTML/Elements/Area.swift new file mode 100644 index 0000000..250997c --- /dev/null +++ b/Sources/HRW/GenHTML/Elements/Area.swift @@ -0,0 +1,161 @@ +// +// Area.swift +// HTMLStandard +// +// Generated on 09/23/2025. +// THIS FILE IS GENERATED. DO NOT EDIT. +// +import Foundation + +/// Hyperlink or dead area on an image map +public class Area : HTMLNode, IFlow, IPhrasing { + + public enum Shape : String, CaseIterable { + + case circle + case default_ = "default" + case poly + case rect + + public init?(rawValue: Substring) { + guard + let value = Self.allCases.first(where: { $0.rawValue == rawValue }) + else + { return nil } + + self = value + } + + public init(expect: Substring) throws { + guard + let value = Self.allCases.first(where: { $0.rawValue == expect }) + else + { throw AppError("Unexpected value for Shape: \(expect)") } + + self = value + } + + public init(expect: String) throws { + guard let result = Shape(rawValue: expect) else { + throw AppError("Unexpected value for Shape: \(expect)") + } + self = result + } + + static func parseList(_ value:String?, _ separator:String = " ") throws -> [Shape] { + guard let value = value else { return [] } + var iterator = value.componentsIterator(separatedBy: separator) + let result = try iterator.map { input in + return try expect(Shape(rawValue: input), "unexpected value for Shape: \(input)") + } + return result + } + } + + /// Replacement text for use when images are not available. Text. The actual rules are more complicated than indicated. + public var alt:String? = nil + + /// Coordinates for the shape to be created in an image map. Valid list of floating-point numbers. The actual rules are more complicated than indicated. + public var coords:[Float] = [] + + /// Whether to download the resource instead of navigating to it, and its file name if so. + public var download:String? = nil + + /// Address of the hyperlink. Valid URL potentially surrounded by spaces. + public var href:URL? = nil + + /// URLs to ping. Set of space-separated tokens consisting of valid non-empty URLs. + public var ping:[URL] = [] + + /// Referrer policy for fetches initiated by the element. Referrer policy. + public var referrerpolicy:ReferrerPolicy? = nil + + /// Relationship between the location in the document containing the hyperlink and the destination resource. Unordered set of unique space-separated tokens. The actual rules are more complicated than indicated. + public var rel:[String] = [] + + /// The kind of shape to be created in an image map. + public var shape:Shape? = nil + + /// Browsing context for hyperlink navigation. Valid browsing context name or keyword. + public var target:String? = nil + + + public init(_ attributes:[String:String], _ parser:XMLParser? = nil) throws { + var globalAttr = GlobalAttributesBuilder() + for (key, attValue) in attributes { + switch (key) { + case "alt": + alt = attValue + continue + case "coords": + coords = try Float.parseList(attValue, ",") + continue + case "download": + download = attValue + continue + case "href": + href = try URL(expect: attValue) + continue + case "ping": + ping = try URL.parseList(attValue, " ") + continue + case "referrerpolicy": + referrerpolicy = try ReferrerPolicy(expect: attValue) + continue + case "rel": + rel = try String.parseList(attValue, " ") + continue + case "shape": + shape = try Shape(expect: attValue) + continue + case "target": + target = attValue + continue + + default: break + } + if globalAttr.trySetGlobalAttribute(key, attValue) { + continue + } + throw AppError("Unexpected attribute: \(key)") + } + super.init(globalAttr) + } + + + public override func renderAttributes() -> String { + var result = super.renderAttributes() + if let alt = alt { + result += " alt='\(alt)'" + } + result += " coords='\(coords.toStringList(","))'" + if let download = download { + result += " download='\(download)'" + } + if let href = href { + result += " href='\(href.absoluteString)'" + } + result += " ping='\(ping.toStringList(" "))'" + if let referrerpolicy = referrerpolicy { + result += " referrerpolicy='\(referrerpolicy.rawValue)'" + } + result += " rel='\(rel.toStringList(" "))'" + if let shape = shape { + result += " shape='\(shape.rawValue)'" + } + if let target = target { + result += " target='\(target)'" + } + + + return result + } + + override var nodeName: String { + return "area" + } + + override var isVoidElement: Bool { + return true + } +} \ No newline at end of file diff --git a/Sources/HRW/GenHTML/Elements/Article.swift b/Sources/HRW/GenHTML/Elements/Article.swift new file mode 100644 index 0000000..c10ed95 --- /dev/null +++ b/Sources/HRW/GenHTML/Elements/Article.swift @@ -0,0 +1,49 @@ +// +// Article.swift +// HTMLStandard +// +// Generated on 09/23/2025. +// THIS FILE IS GENERATED. DO NOT EDIT. +// +import Foundation + +///
Self-contained syndicatable or reusable composition +public class Article : HTMLNode, IFlow, IPalpable, ISectioning { + + + public init(_ attributes:[String:String], _ parser:XMLParser? = nil) throws { + var globalAttr = GlobalAttributesBuilder() + for (key, attValue) in attributes { + + if globalAttr.trySetGlobalAttribute(key, attValue) { + continue + } + throw AppError("Unexpected attribute: \(key)") + } + var allItems:[HTMLNode] = [] + while let obj = try parser?.readObject(endTag: "article", xmlToHtmlMapper) { + allItems.append(obj) + } + super.init(globalAttr, allItems) + } + + + public func addChild(_ someElement:IFlow) { + children.append(someElement) + } + + public override func renderAttributes() -> String { + var result = super.renderAttributes() + + + return result + } + + override var nodeName: String { + return "article" + } + + override var isVoidElement: Bool { + return false + } +} \ No newline at end of file diff --git a/Sources/HRW/GenHTML/Elements/Aside.swift b/Sources/HRW/GenHTML/Elements/Aside.swift new file mode 100644 index 0000000..367fcb9 --- /dev/null +++ b/Sources/HRW/GenHTML/Elements/Aside.swift @@ -0,0 +1,49 @@ +// +// Aside.swift +// HTMLStandard +// +// Generated on 09/23/2025. +// THIS FILE IS GENERATED. DO NOT EDIT. +// +import Foundation + +///