Skip to content

Commit acae9c3

Browse files
authored
feat(http): add .swap modifier (#40)
1 parent 38a22f7 commit acae9c3

File tree

4 files changed

+67
-13
lines changed

4 files changed

+67
-13
lines changed

@mizu/http/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,3 +220,8 @@ Alias for `.consume[json]`.
220220
### `.xml[boolean]`
221221

222222
Alias for `.consume[xml]`.
223+
224+
### `.swap[boolean]`
225+
226+
Consume body using [`response.text()`](https://developer.mozilla.org/docs/Web/API/Response/text) and set target's [`outerHTML`](https://developer.mozilla.org/docs/Web/API/Element/outerHTML). This modifier takes precedence over [`.consume`](#consume) modifier and makes it
227+
effectless, although if `.consume[text]` is set then swapped content will be escaped.

@mizu/http/mod.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,4 +232,9 @@
232232
<mizu-modifier #modifier name="xml">
233233
Alias for <code>.consume[xml]</code>.
234234
</mizu-modifier>
235+
<mizu-variable #modifier name="swap">
236+
Consume body using <a href="https://developer.mozilla.org/docs/Web/API/Response/text"><code>response.text()</code></a> and set target's <a href="https://developer.mozilla.org/docs/Web/API/Element/outerHTML"><var>outerHTML</var></a>. This modifier takes precedence over the <a
237+
href="#consume"
238+
><code>.consume</code></a> modifier and makes it effectless, although if <code>.consume[text]</code> is set swapped content will be escaped.
239+
</mizu-variable>
235240
</mizu-directive>

@mizu/http/mod.ts

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Imports
22
import { type Arg, type Cache, type Directive, type Nullable, type Optional, Phase } from "@mizu/mizu/core/engine"
3+
import { escape } from "@std/html"
34
export type * from "@mizu/mizu/core/engine"
45

56
/** `%header` directive. */
@@ -179,6 +180,7 @@ export const _response_typings = {
179180
html: { type: Boolean },
180181
json: { type: Boolean },
181182
xml: { type: Boolean },
183+
swap: { type: Boolean },
182184
},
183185
} as const
184186

@@ -206,11 +208,11 @@ export const _response = {
206208

207209
// Process response callbacks
208210
for (const attribute of attributes) {
209-
const parsed = renderer.parseAttribute(attribute, this.typings, { prefix: this.prefix, modifiers: true })
211+
const { tag, modifiers, value: expression } = renderer.parseAttribute(attribute, this.typings, { prefix: this.prefix, modifiers: true })
210212

211213
// Verify status code if applicable
212-
if (parsed.tag) {
213-
const match = parsed.tag.replace(/\s/g, "").split(",").map((code) => {
214+
if (tag) {
215+
const match = tag.replace(/\s/g, "").split(",").map((code) => {
214216
if (/^[2-5]xx$/i.test(code)) {
215217
return [Number(code.at(0)) * 100, Number(code.at(0)) * 100 + 99]
216218
}
@@ -224,43 +226,53 @@ export const _response = {
224226
}
225227
}
226228

227-
// Parse response
228-
let expression = parsed.value
229229
let $content = null
230-
switch (true) {
230+
let consume = true as Nullable<boolean>
231+
// Apply swap modifier
232+
if (modifiers.swap) {
233+
$content = await $response.text()
234+
if ((modifiers.consume === "text") || (modifiers.text)) {
235+
$content = escape($content)
236+
}
237+
element.outerHTML = $content
238+
consume = null
239+
}
240+
// Parse response
241+
switch (consume) {
231242
// Void response
232-
case (parsed.modifiers.consume === "void") || (parsed.modifiers.void): {
243+
case (modifiers.consume === "void") || (modifiers.void): {
233244
await $response.body?.cancel()
234245
break
235246
}
236247
// Text response
237-
case (parsed.modifiers.consume === "text") || (parsed.modifiers.text): {
248+
case (modifiers.consume === "text") || (modifiers.text): {
238249
$content = await $response.text()
239250
if (!expression) {
240-
expression = "this.textContent = $content"
251+
element.textContent = $content
241252
}
242253
break
243254
}
244255
// HTML response
245-
case (parsed.modifiers.consume === "html") || (parsed.modifiers.html): {
256+
case (modifiers.consume === "html") || (modifiers.html): {
246257
$content = await $response.text()
247258
if (!expression) {
248-
expression = "this.innerHTML = $content"
259+
element.innerHTML = $content
249260
}
250261
break
251262
}
252263
// JSON response
253-
case (parsed.modifiers.consume === "json") || (parsed.modifiers.json): {
264+
case (modifiers.consume === "json") || (modifiers.json): {
254265
$content = await $response.json()
255266
break
256267
}
257268
// XML response
258-
case (parsed.modifiers.consume === "xml") || (parsed.modifiers.xml): {
269+
case (modifiers.consume === "xml") || (modifiers.xml): {
259270
const { parse } = await import("./import/xml/parse.ts")
260271
$content = parse(await $response.text())
261272
break
262273
}
263274
}
275+
264276
await renderer.evaluate(element, arguments[2]._expression?.value ?? (expression || this.default), { state: { ...state, $response, $content }, ...options, args: arguments[2]._expression?.args })
265277
}
266278
},

@mizu/http/mod_test.html

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,38 @@
530530
</expect>
531531
</test>
532532

533+
<test name="[%response] with `.swap` modifier set `outerHTML`">
534+
<http-server>
535+
<response path="/foo" status="OK"><foo>bar</foo></response>
536+
</http-server>
537+
<render>
538+
<section>
539+
<div %http="/foo" %response.swap></div>
540+
</section>
541+
</render>
542+
<expect>
543+
<section>
544+
<foo>bar</foo>
545+
</section>
546+
</expect>
547+
</test>
548+
549+
<test name="[%response] with `.swap.text` modifiers set `outerHTML` and escapes html">
550+
<http-server>
551+
<response path="/foo" status="OK"><foo>bar</foo></response>
552+
</http-server>
553+
<render>
554+
<section>
555+
<div %http="/foo" %response.swap.text></div>
556+
</section>
557+
</render>
558+
<expect>
559+
<section>
560+
&lt;foo&gt;bar&lt;/foo&gt;
561+
</section>
562+
</expect>
563+
</test>
564+
533565
<test name="[%response] skips commented elements">
534566
<http-server>
535567
<response path="/foo" status="OK"></response>

0 commit comments

Comments
 (0)