Skip to content

Commit a7f472e

Browse files
committed
fix: correct boolean attribute handling
1 parent a0dcaad commit a7f472e

File tree

2 files changed

+87
-70
lines changed

2 files changed

+87
-70
lines changed

generator/web/src/main/kotlin/me/kcra/takenaka/generator/web/util/html.kt

Lines changed: 87 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,51 @@
1+
/*
2+
* This file is part of takenaka, licensed under the Apache License, Version 2.0 (the "License").
3+
*
4+
* Copyright (c) 2023-2024 Matous Kucera
5+
*
6+
* You may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
118
package me.kcra.takenaka.generator.web.util
219

20+
/**
21+
* A `<` (less than) character encoded for HTML usage.
22+
*/
23+
const val HTML_LT = "&lt;"
24+
25+
/**
26+
* A `>` (greater than) character encoded for HTML usage.
27+
*/
28+
const val HTML_GT = "&gt;"
29+
30+
/**
31+
* Escapes `<` and `>` characters in a string for HTML usage.
32+
*
33+
* @return the escaped string
34+
*/
35+
fun String.escapeHtml(): String = replace("<", HTML_LT).replace(">", HTML_GT)
36+
37+
/**
38+
* A `"` (double quotation mark) character encoded for HTML usage.
39+
*/
40+
const val HTML_QUOT = "&quot;"
41+
42+
/**
43+
* Escapes the `"` character in a string for usage in an HTML tag attribute value.
44+
*
45+
* @return the escaped string
46+
*/
47+
fun String.escapeHtmlAttribute(): String = replace("\"", HTML_QUOT)
48+
349
/**
450
* Void element tag names.
551
*
@@ -12,21 +58,24 @@ val VOID_ELEMENTS = setOf("area", "base", "br", "col", "embed", "hr", "img", "in
1258
*/
1359
typealias HTMLBuilder = StringBuilder
1460

61+
/**
62+
* An HTML builder action.
63+
*/
64+
typealias HTMLBuilderBlock = HTMLBuilder.() -> Unit
65+
1566
/**
1667
* Builds an HTML document.
1768
*
1869
* @param docType whether a DOCTYPE should be prepended
1970
* @param block the builder action
2071
* @return the built document
2172
*/
22-
inline fun buildHTML(docType: Boolean = true, block: HTMLBuilder.() -> Unit): String {
23-
return buildString {
24-
if (docType) {
25-
append("<!DOCTYPE html>")
26-
}
27-
28-
block(this)
73+
inline fun buildHTML(docType: Boolean = true, block: HTMLBuilderBlock): String = buildString {
74+
if (docType) {
75+
append("<!DOCTYPE html>")
2976
}
77+
78+
block(this)
3079
}
3180

3281
/**
@@ -39,12 +88,15 @@ inline fun buildHTML(docType: Boolean = true, block: HTMLBuilder.() -> Unit): St
3988
inline fun HTMLBuilder.tag(
4089
name: String,
4190
attrs: Map<String, Any?> = emptyMap(),
42-
block: HTMLBuilder.() -> Unit = {},
91+
block: HTMLBuilderBlock = {},
4392
) {
44-
append("<${name}")
93+
append("<$name")
4594
attrs.forEach { (key, value) ->
46-
if (value != null) {
47-
append(" ${key}=\"${value}\"")
95+
if (value == null || value == false) return@forEach
96+
97+
append(" $key") // https://developer.mozilla.org/en-US/docs/Glossary/Boolean/HTML
98+
if (value !is Boolean) {
99+
append("=\"${value.toString().escapeHtmlAttribute()}\"")
48100
}
49101
}
50102

@@ -54,7 +106,7 @@ inline fun HTMLBuilder.tag(
54106
append(">")
55107

56108
block(this)
57-
append("</${name}>")
109+
append("</$name>")
58110
}
59111
}
60112

@@ -64,57 +116,57 @@ inline fun HTMLBuilder.tag(
64116
* @param lang the `lang` attribute
65117
* @param block the builder action
66118
*/
67-
inline fun HTMLBuilder.html(lang: String? = null, block: HTMLBuilder.() -> Unit = {}) =
119+
inline fun HTMLBuilder.html(lang: String? = null, block: HTMLBuilderBlock = {}) =
68120
tag("html", mapOf("lang" to lang), block)
69121

70122
/**
71123
* Appends a `<head>` tag.
72124
*
73125
* @param block the builder action
74126
*/
75-
inline fun HTMLBuilder.head(block: HTMLBuilder.() -> Unit = {}) = tag("head", block = block)
127+
inline fun HTMLBuilder.head(block: HTMLBuilderBlock = {}) = tag("head", block = block)
76128

77129
/**
78130
* Appends a `<body>` tag.
79131
*
80132
* @param block the builder action
81133
*/
82-
inline fun HTMLBuilder.body(block: HTMLBuilder.() -> Unit = {}) = tag("body", block = block)
134+
inline fun HTMLBuilder.body(block: HTMLBuilderBlock = {}) = tag("body", block = block)
83135

84136
/**
85137
* Appends a `<title>` tag.
86138
*
87139
* @param block the builder action
88140
*/
89-
inline fun HTMLBuilder.title(block: HTMLBuilder.() -> Unit = {}) = tag("title", block = block)
141+
inline fun HTMLBuilder.title(block: HTMLBuilderBlock = {}) = tag("title", block = block)
90142

91143
/**
92144
* Appends a `<main>` tag.
93145
*
94146
* @param block the builder action
95147
*/
96-
inline fun HTMLBuilder.main(block: HTMLBuilder.() -> Unit = {}) = tag("main", block = block)
148+
inline fun HTMLBuilder.main(block: HTMLBuilderBlock = {}) = tag("main", block = block)
97149

98150
/**
99151
* Appends a `<h1>` tag.
100152
*
101153
* @param block the builder action
102154
*/
103-
inline fun HTMLBuilder.h1(block: HTMLBuilder.() -> Unit = {}) = tag("h1", block = block)
155+
inline fun HTMLBuilder.h1(block: HTMLBuilderBlock = {}) = tag("h1", block = block)
104156

105157
/**
106158
* Appends a `<h3>` tag.
107159
*
108160
* @param block the builder action
109161
*/
110-
inline fun HTMLBuilder.h3(block: HTMLBuilder.() -> Unit = {}) = tag("h3", block = block)
162+
inline fun HTMLBuilder.h3(block: HTMLBuilderBlock = {}) = tag("h3", block = block)
111163

112164
/**
113165
* Appends a `<h4>` tag.
114166
*
115167
* @param block the builder action
116168
*/
117-
inline fun HTMLBuilder.h4(block: HTMLBuilder.() -> Unit = {}) = tag("h4", block = block)
169+
inline fun HTMLBuilder.h4(block: HTMLBuilderBlock = {}) = tag("h4", block = block)
118170

119171
/**
120172
* Appends a `<p>` tag.
@@ -124,52 +176,52 @@ inline fun HTMLBuilder.h4(block: HTMLBuilder.() -> Unit = {}) = tag("h4", block
124176
* @param style the `style` attribute
125177
* @param block the builder action
126178
*/
127-
inline fun HTMLBuilder.p(onClick: String? = null, classes: String? = null, style: String? = null, block: HTMLBuilder.() -> Unit = {}) =
179+
inline fun HTMLBuilder.p(onClick: String? = null, classes: String? = null, style: String? = null, block: HTMLBuilderBlock = {}) =
128180
tag("p", mapOf("onclick" to onClick, "class" to classes, "style" to style), block)
129181

130182
/**
131183
* Appends a `<pre>` tag.
132184
*
133185
* @param block the builder action
134186
*/
135-
inline fun HTMLBuilder.pre(block: HTMLBuilder.() -> Unit = {}) = tag("pre", block = block)
187+
inline fun HTMLBuilder.pre(block: HTMLBuilderBlock = {}) = tag("pre", block = block)
136188

137189
/**
138190
* Appends a `<table>` tag.
139191
*
140192
* @param classes the `class` attribute
141193
* @param block the builder action
142194
*/
143-
inline fun HTMLBuilder.table(classes: String? = null, block: HTMLBuilder.() -> Unit = {}) =
195+
inline fun HTMLBuilder.table(classes: String? = null, block: HTMLBuilderBlock = {}) =
144196
tag("table", mapOf("class" to classes), block)
145197

146198
/**
147199
* Appends a `<thead>` tag.
148200
*
149201
* @param block the builder action
150202
*/
151-
inline fun HTMLBuilder.thead(block: HTMLBuilder.() -> Unit = {}) = tag("thead", block = block)
203+
inline fun HTMLBuilder.thead(block: HTMLBuilderBlock = {}) = tag("thead", block = block)
152204

153205
/**
154206
* Appends a `<tbody>` tag.
155207
*
156208
* @param block the builder action
157209
*/
158-
inline fun HTMLBuilder.tbody(block: HTMLBuilder.() -> Unit = {}) = tag("tbody", block = block)
210+
inline fun HTMLBuilder.tbody(block: HTMLBuilderBlock = {}) = tag("tbody", block = block)
159211

160212
/**
161213
* Appends a `<tr>` tag.
162214
*
163215
* @param block the builder action
164216
*/
165-
inline fun HTMLBuilder.tr(block: HTMLBuilder.() -> Unit = {}) = tag("tr", block = block)
217+
inline fun HTMLBuilder.tr(block: HTMLBuilderBlock = {}) = tag("tr", block = block)
166218

167219
/**
168220
* Appends a `<th>` tag.
169221
*
170222
* @param block the builder action
171223
*/
172-
inline fun HTMLBuilder.th(block: HTMLBuilder.() -> Unit = {}) = tag("th", block = block)
224+
inline fun HTMLBuilder.th(block: HTMLBuilderBlock = {}) = tag("th", block = block)
173225

174226
/**
175227
* Appends a `<td>` tag.
@@ -178,7 +230,7 @@ inline fun HTMLBuilder.th(block: HTMLBuilder.() -> Unit = {}) = tag("th", block
178230
* @param style the `style` attribute
179231
* @param block the builder action
180232
*/
181-
inline fun HTMLBuilder.td(classes: String? = null, style: String? = null, block: HTMLBuilder.() -> Unit = {}) =
233+
inline fun HTMLBuilder.td(classes: String? = null, style: String? = null, block: HTMLBuilderBlock = {}) =
182234
tag("td", mapOf("class" to classes, "style" to style), block)
183235

184236
/**
@@ -190,7 +242,7 @@ inline fun HTMLBuilder.td(classes: String? = null, style: String? = null, block:
190242
* @param style the `style` attribute
191243
* @param block the builder action
192244
*/
193-
inline fun HTMLBuilder.div(id: String? = null, onClick: String? = null, classes: String? = null, style: String? = null, block: HTMLBuilder.() -> Unit = {}) =
245+
inline fun HTMLBuilder.div(id: String? = null, onClick: String? = null, classes: String? = null, style: String? = null, block: HTMLBuilderBlock = {}) =
194246
tag("div", mapOf("id" to id, "onclick" to onClick, "class" to classes, "style" to style), block)
195247

196248
/**
@@ -200,7 +252,7 @@ inline fun HTMLBuilder.div(id: String? = null, onClick: String? = null, classes:
200252
* @param style the `style` attribute
201253
* @param block the builder action
202254
*/
203-
inline fun HTMLBuilder.span(classes: String? = null, style: String? = null, block: HTMLBuilder.() -> Unit = {}) =
255+
inline fun HTMLBuilder.span(classes: String? = null, style: String? = null, block: HTMLBuilderBlock = {}) =
204256
tag("span", mapOf("class" to classes, "style" to style), block)
205257

206258
/**
@@ -212,7 +264,7 @@ inline fun HTMLBuilder.span(classes: String? = null, style: String? = null, bloc
212264
* @param ariaLabel the `aria-label` attribute
213265
* @param block the builder action
214266
*/
215-
inline fun HTMLBuilder.a(id: String? = null, href: String? = null, classes: String? = null, ariaLabel: String? = null, block: HTMLBuilder.() -> Unit = {}) =
267+
inline fun HTMLBuilder.a(id: String? = null, href: String? = null, classes: String? = null, ariaLabel: String? = null, block: HTMLBuilderBlock = {}) =
216268
tag("a", mapOf("id" to id, "href" to href, "class" to classes, "aria-label" to ariaLabel), block)
217269

218270
/**
@@ -224,22 +276,22 @@ inline fun HTMLBuilder.a(id: String? = null, href: String? = null, classes: Stri
224276
* @param spellCheck the `spellcheck` attribute
225277
* @param placeholder the `placeholder` attribute
226278
*/
227-
fun HTMLBuilder.input(id: String? = null, type: String? = null, classes: String? = null, spellCheck: Boolean? = null, placeholder: String? = null) =
279+
fun HTMLBuilder.input(id: String? = null, type: String? = null, classes: String? = null, spellCheck: Boolean = false, placeholder: String? = null) =
228280
tag("input", mapOf("id" to id, "type" to type, "class" to classes, "spellcheck" to spellCheck, "placeholder" to placeholder))
229281

230282
/**
231283
* Appends a `<nav>` tag.
232284
*
233285
* @param block the builder action
234286
*/
235-
inline fun HTMLBuilder.nav(block: HTMLBuilder.() -> Unit = {}) = tag("nav", block = block)
287+
inline fun HTMLBuilder.nav(block: HTMLBuilderBlock = {}) = tag("nav", block = block)
236288

237289
/**
238290
* Appends a `<footer>` tag.
239291
*
240292
* @param block the builder action
241293
*/
242-
inline fun HTMLBuilder.footer(block: HTMLBuilder.() -> Unit = {}) = tag("footer", block = block)
294+
inline fun HTMLBuilder.footer(block: HTMLBuilderBlock = {}) = tag("footer", block = block)
243295

244296
/**
245297
* Appends a `<meta>` tag.
@@ -256,7 +308,7 @@ fun HTMLBuilder.meta(name: String? = null, content: String? = null) =
256308
* @param src the `src` attribute
257309
* @param block the builder action
258310
*/
259-
inline fun HTMLBuilder.script(src: String? = null, block: HTMLBuilder.() -> Unit = {}) =
311+
inline fun HTMLBuilder.script(src: String? = null, block: HTMLBuilderBlock = {}) =
260312
tag("script", mapOf("src" to src), block)
261313

262314
/**

generator/web/src/main/kotlin/me/kcra/takenaka/generator/web/util/strings.kt

Lines changed: 0 additions & 35 deletions
This file was deleted.

0 commit comments

Comments
 (0)