diff --git a/README.md b/README.md
index 66abd72..250c1b6 100644
--- a/README.md
+++ b/README.md
@@ -154,7 +154,7 @@ Sample code:
_Requires Deno `1.35` or later._
```typescript
-import van from "https://deno.land/x/minivan@0.5.7/src/van-plate.js"
+import van from "https://deno.land/x/minivan@0.6.0/src/van-plate.js"
const {a, body, li, p, ul} = van.tags
@@ -195,7 +195,7 @@ _Requires Deno `1.35` or later._
```typescript
import { DOMParser } from "https://deno.land/x/deno_dom@v0.1.38/deno-dom-wasm.ts"
-import van from "https://deno.land/x/minivan@0.5.7/src/mini-van.js"
+import van from "https://deno.land/x/minivan@0.6.0/src/mini-van.js"
const document = new DOMParser().parseFromString("", "text/html")!
const {tags, html} = van.vanWithDoc(document)
@@ -235,16 +235,16 @@ Preview via [CodeSandbox](https://codesandbox.io/p/sandbox/github/vanjs-org/vanj
To get started on the client side, add the line below to your script:
```javascript
-import van from "https://cdn.jsdelivr.net/gh/vanjs-org/mini-van/public/mini-van-0.5.7.min.js"
+import van from "https://cdn.jsdelivr.net/gh/vanjs-org/mini-van/public/mini-van-0.6.0.min.js"
```
To code without ES6 modules, add the following line to your HTML file instead:
```html
-
+
```
-Alternative, you can download the files ([`mini-van-0.5.7.min.js`](https://vanjs.org/autodownload?file=mini-van-0.5.7.min.js), [`mini-van-0.5.7.nomodule.min.js`](https://vanjs.org/autodownload?file=mini-van-0.5.7.nomodule.min.js)) and serve them locally.
+Alternative, you can download the files ([`mini-van-0.6.0.min.js`](https://vanjs.org/autodownload?file=mini-van-0.6.0.min.js), [`mini-van-0.6.0.nomodule.min.js`](https://vanjs.org/autodownload?file=mini-van-0.6.0.nomodule.min.js)) and serve them locally.
You can find all relevant **Mini-Van** files in this [Download Table](https://vanjs.org/minivan#download-table).
diff --git a/package-lock.json b/package-lock.json
index d124b92..7f5624a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "mini-van-plate",
- "version": "0.5.7",
+ "version": "0.6.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "mini-van-plate",
- "version": "0.5.7",
+ "version": "0.6.0",
"license": "MIT",
"devDependencies": {
"esbuild": "^0.17.19",
diff --git a/package.json b/package.json
index 43d4457..383bdc8 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "mini-van-plate",
- "version": "0.5.7",
+ "version": "0.6.0",
"description": "A minimalist template engine for DOM generation and manipulation, working for both client-side and server-side rendering",
"files": [
"src/mini-van.js",
diff --git a/public/mini-van-0.6.0.d.ts b/public/mini-van-0.6.0.d.ts
new file mode 100644
index 0000000..2796707
--- /dev/null
+++ b/public/mini-van-0.6.0.d.ts
@@ -0,0 +1,75 @@
+export interface State {
+ val: T
+ readonly oldVal: T
+ readonly rawVal: T
+}
+
+// Defining readonly view of State for covariance.
+// Basically we want StateView to implement StateView
+export type StateView = Readonly>
+
+export type Primitive = string | number | boolean | bigint
+
+export type PropValue = Primitive | ((e: any) => void) | null
+
+export type Props = Record | (() => PropValue)>
+
+interface HasFirstChild {firstChild?: unknown}
+
+type NodeType =
+ Omit
+
+export type ValidChildDomValue =
+ Primitive | ElementType | NodeType | TextNodeType | null | undefined
+
+export type BindingFunc =
+ | ((dom?: ElementType | TextNodeType) => ValidChildDomValue)
+ | ((dom?: ElementType) => ElementType)
+
+export type ChildDom =
+ | ValidChildDomValue
+ | StateView
+ | BindingFunc
+ | readonly ChildDom[]
+
+type AddFunc =
+ (dom: ElementType, ...children: readonly ChildDom[]) => ElementType
+
+export type TagFunc =
+ (first?: Props | ChildDom,
+ ...rest: readonly ChildDom[]) => ResultType
+
+type Tags =
+ Readonly>>
+
+// Tags type in browser context, which contains the signatures to tag functions that return
+// specialized DOM elements.
+type BrowserTags = Tags & {
+ [K in keyof HTMLElementTagNameMap]: TagFunc
+}
+
+declare function state(): State
+declare function state(initVal: T): State
+
+export interface VanObj {
+ readonly state: typeof state
+ readonly derive: (f: () => T) => State
+ readonly add: AddFunc
+ readonly tags: Tags & ((namespaceURI: string) => Tags)
+
+ // Mini-Van specific API
+ html: (first?: Props | ChildDom,
+ ...rest: readonly ChildDom[]) => string
+}
+
+export interface Van extends VanObj {
+ readonly vanWithDoc: (doc: {
+ createElement(s: any): ElementType,
+ createTextNode(s: any): TextNodeType,
+ }) => VanObj
+ readonly tags: BrowserTags & ((namespaceURI: string) => Tags)
+}
+
+declare const van: Van
+
+export default van
diff --git a/public/mini-van-0.6.0.js b/public/mini-van-0.6.0.js
new file mode 100644
index 0000000..ae2613c
--- /dev/null
+++ b/public/mini-van-0.6.0.js
@@ -0,0 +1,48 @@
+///
+
+// This file consistently uses `let` keyword instead of `const` for reducing the bundle size.
+
+// Aliasing some builtin symbols to reduce the bundle size.
+let protoOf = Object.getPrototypeOf, _undefined, funcProto = protoOf(protoOf)
+
+let stateProto = {get oldVal() { return this.val }, get rawVal() { return this.val }}
+let objProto = protoOf(stateProto)
+
+let state = initVal => ({__proto__: stateProto, val: initVal})
+
+let plainValue = (k, v) => {
+ let protoOfV = protoOf(v ?? 0)
+ return protoOfV === stateProto ? v.val :
+ protoOfV !== funcProto || k?.startsWith("on") ? v : v()
+}
+
+let add = (dom, ...children) =>
+ (dom.append(...children.flat(Infinity)
+ .map(plainValue.bind(_undefined, _undefined))
+ .filter(c => c != _undefined)),
+ dom)
+
+let vanWithDoc = doc => {
+ let tag = (ns, name, ...args) => {
+ let [props, ...children] = protoOf(args[0] ?? 0) === objProto ? args : [{}, ...args]
+ let dom = ns ? doc.createElementNS(ns, name) : doc.createElement(name)
+ for (let [k, v] of Object.entries(props)) {
+ let plainV = plainValue(k, v)
+ // Disable setting attribute for function-valued properties (mostly event handlers),
+ // as they're usually not useful for SSR (server-side rendering).
+ protoOf(plainV) !== funcProto && dom.setAttribute(k, plainV)
+ }
+ return add(dom, ...children)
+ }
+
+ let handler = ns => ({get: (_, name) => tag.bind(_undefined, ns, name)})
+ let tags = new Proxy(ns => new Proxy(tag, handler(ns)), handler())
+
+ return {
+ add, tags, state, derive: f => state(f()),
+ html: (...args) => "" + tags.html(...args).outerHTML,
+ }
+}
+
+export default {"vanWithDoc": vanWithDoc,
+ ...vanWithDoc(typeof window !== "undefined" ? window.document : null)}
diff --git a/public/mini-van-0.6.0.min.d.ts b/public/mini-van-0.6.0.min.d.ts
new file mode 100644
index 0000000..2796707
--- /dev/null
+++ b/public/mini-van-0.6.0.min.d.ts
@@ -0,0 +1,75 @@
+export interface State {
+ val: T
+ readonly oldVal: T
+ readonly rawVal: T
+}
+
+// Defining readonly view of State for covariance.
+// Basically we want StateView to implement StateView
+export type StateView = Readonly>
+
+export type Primitive = string | number | boolean | bigint
+
+export type PropValue = Primitive | ((e: any) => void) | null
+
+export type Props = Record | (() => PropValue)>
+
+interface HasFirstChild {firstChild?: unknown}
+
+type NodeType =
+ Omit
+
+export type ValidChildDomValue =
+ Primitive | ElementType | NodeType | TextNodeType | null | undefined
+
+export type BindingFunc =
+ | ((dom?: ElementType | TextNodeType) => ValidChildDomValue)
+ | ((dom?: ElementType) => ElementType)
+
+export type ChildDom =
+ | ValidChildDomValue
+ | StateView
+ | BindingFunc
+ | readonly ChildDom[]
+
+type AddFunc =
+ (dom: ElementType, ...children: readonly ChildDom[]) => ElementType
+
+export type TagFunc =
+ (first?: Props | ChildDom,
+ ...rest: readonly ChildDom[]) => ResultType
+
+type Tags =
+ Readonly>>
+
+// Tags type in browser context, which contains the signatures to tag functions that return
+// specialized DOM elements.
+type BrowserTags = Tags & {
+ [K in keyof HTMLElementTagNameMap]: TagFunc
+}
+
+declare function state(): State
+declare function state(initVal: T): State
+
+export interface VanObj {
+ readonly state: typeof state
+ readonly derive: (f: () => T) => State
+ readonly add: AddFunc
+ readonly tags: Tags & ((namespaceURI: string) => Tags)
+
+ // Mini-Van specific API
+ html: (first?: Props | ChildDom,
+ ...rest: readonly ChildDom[]) => string
+}
+
+export interface Van extends VanObj {
+ readonly vanWithDoc: (doc: {
+ createElement(s: any): ElementType,
+ createTextNode(s: any): TextNodeType,
+ }) => VanObj
+ readonly tags: BrowserTags & ((namespaceURI: string) => Tags)
+}
+
+declare const van: Van
+
+export default van
diff --git a/public/mini-van-0.6.0.min.js b/public/mini-van-0.6.0.min.js
new file mode 100644
index 0000000..ed82d89
--- /dev/null
+++ b/public/mini-van-0.6.0.min.js
@@ -0,0 +1 @@
+let t,e=Object.getPrototypeOf,r=e(e),l={get oldVal(){return this.val},get rawVal(){return this.val}},n=e(l),o=t=>({__proto__:l,val:t}),a=(t,n)=>{let o=e(n??0);return o===l?n.val:o!==r||t?.startsWith("on")?n:n()},d=(e,...r)=>(e.append(...r.flat(1/0).map(a.bind(t,t)).filter(e=>e!=t)),e),u=l=>{let u=(t,o,...u)=>{let[i,...w]=e(u[0]??0)===n?u:[{},...u],f=t?l.createElementNS(t,o):l.createElement(o);for(let[t,l]of Object.entries(i)){let n=a(t,l);e(n)!==r&&f.setAttribute(t,n)}return d(f,...w)},i=e=>({get:(r,l)=>u.bind(t,e,l)}),w=new Proxy(t=>new Proxy(u,i(t)),i());return{add:d,tags:w,state:o,derive:t=>o(t()),html:(...t)=>""+w.html(...t).outerHTML}};export default{vanWithDoc:u,...u("undefined"!=typeof window?window.document:null)};
\ No newline at end of file
diff --git a/public/mini-van-0.6.0.nomodule.js b/public/mini-van-0.6.0.nomodule.js
new file mode 100644
index 0000000..dfaa60a
--- /dev/null
+++ b/public/mini-van-0.6.0.nomodule.js
@@ -0,0 +1,45 @@
+(() => {
+ // mini-van.js
+ var protoOf = Object.getPrototypeOf;
+ var _undefined;
+ var funcProto = protoOf(protoOf);
+ var stateProto = { get oldVal() {
+ return this.val;
+ }, get rawVal() {
+ return this.val;
+ } };
+ var objProto = protoOf(stateProto);
+ var state = (initVal) => ({ __proto__: stateProto, val: initVal });
+ var plainValue = (k, v) => {
+ let protoOfV = protoOf(v ?? 0);
+ return protoOfV === stateProto ? v.val : protoOfV !== funcProto || k?.startsWith("on") ? v : v();
+ };
+ var add = (dom, ...children) => (dom.append(...children.flat(Infinity).map(plainValue.bind(_undefined, _undefined)).filter((c) => c != _undefined)), dom);
+ var vanWithDoc = (doc) => {
+ let tag = (ns, name, ...args) => {
+ let [props, ...children] = protoOf(args[0] ?? 0) === objProto ? args : [{}, ...args];
+ let dom = ns ? doc.createElementNS(ns, name) : doc.createElement(name);
+ for (let [k, v] of Object.entries(props)) {
+ let plainV = plainValue(k, v);
+ protoOf(plainV) !== funcProto && dom.setAttribute(k, plainV);
+ }
+ return add(dom, ...children);
+ };
+ let handler = (ns) => ({ get: (_, name) => tag.bind(_undefined, ns, name) });
+ let tags = new Proxy((ns) => new Proxy(tag, handler(ns)), handler());
+ return {
+ add,
+ tags,
+ state,
+ derive: (f) => state(f()),
+ html: (...args) => "" + tags.html(...args).outerHTML
+ };
+ };
+ var mini_van_default = {
+ "vanWithDoc": vanWithDoc,
+ ...vanWithDoc(typeof window !== "undefined" ? window.document : null)
+ };
+
+ // mini-van.forbundle.js
+ window.van = mini_van_default;
+})();
diff --git a/public/mini-van-0.6.0.nomodule.min.js b/public/mini-van-0.6.0.nomodule.min.js
new file mode 100644
index 0000000..c1beea4
--- /dev/null
+++ b/public/mini-van-0.6.0.nomodule.min.js
@@ -0,0 +1 @@
+{let t,e,r,n,l,o,a,d,w,i;e=Object.getPrototypeOf,r=e(e),l=e(n={get oldVal(){return this.val},get rawVal(){return this.val}}),o=t=>({__proto__:n,val:t}),a=(t,l)=>{let o=e(l??0);return o===n?l.val:o!==r||t?.startsWith("on")?l:l()},d=(e,...r)=>(e.append(...r.flat(1/0).map(a.bind(t,t)).filter(e=>e!=t)),e),i={vanWithDoc:w=n=>{let w=(t,o,...w)=>{let[i,...u]=e(w[0]??0)===l?w:[{},...w],h=t?n.createElementNS(t,o):n.createElement(o);for(let[t,n]of Object.entries(i)){let l=a(t,n);e(l)!==r&&h.setAttribute(t,l)}return d(h,...u)},i=e=>({get:(r,n)=>w.bind(t,e,n)}),u=new Proxy(t=>new Proxy(w,i(t)),i());return{add:d,tags:u,state:o,derive:t=>o(t()),html:(...t)=>""+u.html(...t).outerHTML}},...w("undefined"!=typeof window?window.document:null)},window.van=i;}
\ No newline at end of file
diff --git a/public/mini-van.version b/public/mini-van.version
index dc2b74e..09a3acf 100644
--- a/public/mini-van.version
+++ b/public/mini-van.version
@@ -1 +1 @@
-0.5.7
\ No newline at end of file
+0.6.0
\ No newline at end of file
diff --git a/src/shared.d.ts b/src/shared.d.ts
index 8100ebd..88e49c3 100644
--- a/src/shared.d.ts
+++ b/src/shared.d.ts
@@ -14,4 +14,27 @@ export interface VanObj {
readonly add: Function;
readonly tags: Record & ((namespaceURI: string) => Record);
}
+export type StateOf = {
+ readonly [K in keyof T]: State;
+};
+export type ValueType = T extends (infer V)[] ? V : T[keyof T];
+export type KeyType = T extends unknown[] ? number : string;
+export type ReplacementFunc = T extends (infer V)[] ? (items: V[]) => readonly V[] : (items: [string, T[keyof T]][]) => readonly [string, T[keyof T]][];
+export interface VanXObj {
+ readonly calc: (f: () => R) => R;
+ readonly reactive: (obj: T) => T;
+ readonly noreactive: (obj: T) => T;
+ readonly stateFields: (obj: T) => StateOf;
+ readonly raw: (obj: T) => T;
+ readonly list: (container: any, items: T, itemFunc: (v: State>, deleter: () => void, k: KeyType) => any) => any;
+ readonly replace: (obj: T, replacement: ReplacementFunc | T) => T;
+ readonly compact: (obj: T) => T;
+}
+export interface EnvObj {
+ readonly van: VanObj;
+ readonly vanX: VanXObj;
+}
+export declare const env: EnvObj;
+export declare const registerEnv: (input: Partial) => void;
+export declare const dummyVanX: VanXObj;
export {};
diff --git a/src/shared.js b/src/shared.js
index cb0ff5c..7312db2 100644
--- a/src/shared.js
+++ b/src/shared.js
@@ -1 +1,29 @@
-export {};
+export const env = {};
+export const registerEnv = (input) => {
+ if (input.van)
+ env.van = input.van;
+ if (input.vanX)
+ env.vanX = input.vanX;
+};
+export const dummyVanX = {
+ calc: f => f(),
+ reactive: obj => obj,
+ noreactive: obj => obj,
+ stateFields: (obj) => {
+ const states = Array.isArray(obj) ? [] : { __proto__: Object.getPrototypeOf(obj) };
+ for (const [k, v] of Object.entries(obj))
+ states[k] = env.van.state(v);
+ return states;
+ },
+ raw: obj => obj,
+ list: (container, items, itemFunc) => {
+ if (container instanceof Function)
+ container = container();
+ const isArray = Array.isArray(items);
+ for (const [k, v] of Object.entries(items))
+ env.van.add(container, itemFunc(env.van.state(v), () => { }, (isArray ? Number(k) : k)));
+ return container;
+ },
+ replace: obj => obj,
+ compact: obj => obj,
+};
diff --git a/src/shared.ts b/src/shared.ts
index 2fade6e..49b709d 100644
--- a/src/shared.ts
+++ b/src/shared.ts
@@ -24,3 +24,56 @@ export interface VanObj {
readonly add: Function
readonly tags: Record & ((namespaceURI: string) => Record)
}
+
+export type StateOf = { readonly [K in keyof T]: State }
+export type ValueType = T extends (infer V)[] ? V : T[keyof T]
+export type KeyType = T extends unknown[] ? number : string
+export type ReplacementFunc =
+ T extends (infer V)[] ? (items: V[]) => readonly V[] :
+ (items: [string, T[keyof T]][]) => readonly [string, T[keyof T]][]
+
+export interface VanXObj {
+ readonly calc: (f: () => R) => R
+ readonly reactive: (obj: T) => T
+ readonly noreactive: (obj: T) => T
+ readonly stateFields: (obj: T) => StateOf
+ readonly raw: (obj: T) => T
+ readonly list:
+ (container: any, items: T,
+ itemFunc: (v: State>, deleter: () => void, k: KeyType) => any) => any
+ readonly replace: (obj: T, replacement: ReplacementFunc | T) => T
+ readonly compact: (obj: T) => T
+}
+
+export interface EnvObj {
+ readonly van: VanObj
+ readonly vanX: VanXObj
+}
+
+export const env: EnvObj = {}
+
+export const registerEnv = (input: Partial) => {
+ if (input.van) (env).van = input.van
+ if (input.vanX) (env).vanX = input.vanX
+}
+
+export const dummyVanX: VanXObj = {
+ calc: f => f(),
+ reactive: obj => obj,
+ noreactive: obj => obj,
+ stateFields: (obj: T) => {
+ const states = Array.isArray(obj) ? [] : {__proto__: Object.getPrototypeOf(obj)}
+ for (const [k, v] of Object.entries(obj)) states[k] = env.van.state(v)
+ return >states
+ },
+ raw: obj => obj,
+ list: (container, items, itemFunc) => {
+ if (container instanceof Function) container = container()
+ const isArray = Array.isArray(items)
+ for (const [k, v] of Object.entries(items))
+ env.van.add(container, itemFunc(env.van.state(v), () => {}, (isArray ? Number(k) : k)))
+ return container
+ },
+ replace: obj => obj,
+ compact: obj => obj,
+}