Skip to content

Commit

Permalink
feat(set): add *set directive (#17)
Browse files Browse the repository at this point in the history
  • Loading branch information
lowlighter authored Oct 8, 2024
1 parent 536cdd6 commit d642ffc
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 1 deletion.
7 changes: 7 additions & 0 deletions @mizu/set/deno.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "@mizu/set",
"version": "0.1.0",
"exports": {
".": "./mod.ts"
}
}
17 changes: 17 additions & 0 deletions @mizu/set/mod.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<mizu-directive id="set" directory="set">
<code #name><span class="hljs-keyword">*set</span><wbr /><span class="muted">="context"</span></code>
<p #description>
Set a context for an element and its children.
</p>
<code #example *skip>
<div *set="{ foo: 'bar' }">
<!--<span *text="foo"></span>-->
</div>
</code>
<mizu-restriction #note>
Context must resolve to a <a href="https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object">JavaScript <code>Object</code></a>.
</mizu-restriction>
<mizu-note #note>
Context is only initialized once and is not reset upon subsequent renderings, and can be updated by other directives.
</mizu-note>
</mizu-directive>
26 changes: 26 additions & 0 deletions @mizu/set/mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Imports
import { type Cache, type Context, type Directive, type Nullable, Phase } from "@mizu/mizu/core/engine"
export type * from "@mizu/mizu/core/engine"

/** `*set` directive. */
export const _set = {
name: "*set",
phase: Phase.CONTEXT,
init(renderer) {
renderer.cache<Cache<typeof _set>>(this.name, new WeakMap())
},
async execute(renderer, element, { attributes: [attribute], cache, ...options }) {
if (!cache.has(element)) {
const context = await renderer.evaluate(element, attribute.value, options)
if (typeof context !== "object") {
renderer.warn(`[${this.name}] expects an object but got ${typeof context}, ignoring`, element)
return
}
cache.set(element, context ? options.context.with(context as Record<PropertyKey, unknown>) : null)
}
return { context: cache.get(element) }
},
} as Directive<WeakMap<HTMLElement | Comment, Nullable<Context>>>

/** Default exports. */
export default _set
101 changes: 101 additions & 0 deletions @mizu/set/mod_test.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<load directives="@mizu/set,@mizu/test"></load>

<test name="[*set] sets context for element">
<script>
context.foo = "foo"
</script>
<render>
<p *set="{foo:'bar'}" ~test[content].text="foo"></p>
<p ~test[content].text="foo"></p>
</render>
<expect>
<p>bar</p>
<p>foo</p>
</expect>
</test>

<test name="[*set] sets context that is inherited by children">
<render>
<p *set="{foo:'foo'}">
<span *set="{bar:'bar'}" ~test[content].text="`${foo}${bar}`"></span>
</p>
</render>
<expect>
<p>
<span>foobar</span>
</p>
</expect>
</test>

<test name="[*set] sets context that is overrideable">
<render>
<p *set="{foo:'foo'}">
<span ~test[content].text="foo"></span>
<span *set="{foo:'bar'}" ~test[content].text="foo"></span>
</p>
</render>
<expect>
<p>
<span>foo</span>
<span>bar</span>
</p>
</expect>
</test>

<test name="[*set] supports context with callable members">
<render>
<p *set="{now(){return new Date(0).toISOString()}}" ~test[content].text="now()"></p>
</render>
<expect>
<p>1970-01-01T00:00:00.000Z</p>
</expect>
</test>

<test name="[*set] handles commented elements">
<script>
context.value = false
</script>
<render>
<p *set="{foo:value}" ~test[toggle].comment="!foo" ~test[content].text="foo"></p>
</render>
<expect>
<!--[~test[toggle].comment="!foo"] [~test[content].text="foo"]-->
</expect>
<script>
context.value = true
</script>
<render></render>
<expect>
<p>true</p>
</expect>
</test>

<test name="[*set] keeps the same context across different rendering executions">
<render>
<p *set="{foo:'bar'}">
<span ~test.eval="foo = 'baz'" ~test[content].text="foo"></span>
</p>
</render>
<render></render>
<expect>
<p><span>baz</span></p>
</expect>
</test>

<test name="[*set] handles `null` contexts">
<render>
<p *set="null"></p>
</render>
<expect>
<p></p>
</expect>
</test>

<test name="[*set] (error) expects an object">
<render>
<p *set="'foo'"></p>
</render>
<expect>
<p *warn="[*set] expects an object but got string, ignoring"></p>
</expect>
</test>
1 change: 1 addition & 0 deletions @mizu/set/mod_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
await import("@mizu/mizu/core/testing").then(({ test }) => test(import.meta))
2 changes: 1 addition & 1 deletion deno.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@
"@mizu/once",
"@mizu/ref",
// "@mizu/refresh",
// "@mizu/set",
"@mizu/set",
"@mizu/show",
"@mizu/skip",
"@mizu/test",
Expand Down

0 comments on commit d642ffc

Please sign in to comment.