-
Notifications
You must be signed in to change notification settings - Fork 96
/
van-0.11.2.debug.js
117 lines (100 loc) · 4.82 KB
/
van-0.11.2.debug.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import van from "./van-0.11.2.js"
// If this variable is set to an Array, we will push the error message into the array instead of
// throwing an error. This is useful in testing, to capture the error occurred asynchronous to the initiating
// callstack. e.g.: a state change can trigger a dom update processing when idle (i.e.: dom update
// processing is set via setTimeout function, which is asynchronous to the initiating callstack).
let capturedErrors
const startCapturingErrors = () => capturedErrors = []
const stopCapturingErrors = () => capturedErrors = null
const expect = (cond, msg) => {
if (!cond) {
if (capturedErrors) capturedErrors.push(msg); else throw new Error(msg)
return false
}
return true
}
const stateProto = van.state().__proto__
const checkStateValValid = v => {
expect(!(v instanceof Node), "DOM Node is not valid value for state")
expect(v?.__proto__ !== stateProto, "State couldn't have value to other state")
return v
}
const state = initVal => new Proxy(van.state(checkStateValValid(initVal)), {
set: (s, prop, val) => {
if (prop === "val") checkStateValValid(val)
return Reflect.set(s, prop, val)
},
get: (s, prop) => {
if (prop === "onnew") return l => {
expect(typeof l === "function", "You should pass-in functions to register `onnew` handlers")
s.onnew(l)
}
return Reflect.get(s, prop)
}
})
const isValidPrimitive = v =>
typeof(v) === "string" ||
typeof(v) === "number" ||
typeof(v) === "boolean" ||
typeof(v) === "bigint"
const isDomOrPrimitive = v => v instanceof Node || isValidPrimitive(v)
const checkChildValid = child => {
expect(
isDomOrPrimitive(child) || child?.__proto__ === stateProto && isValidPrimitive(child.val),
"Only DOM Node, string, number, boolean, bigint, and state of string, number, boolean or bigint are valid child of a DOM Node"
)
expect(!child.isConnected, "You can't add a DOM Node that is already connected to document")
}
const add = (dom, ...children) => {
expect(dom instanceof Node, "1st argument of `add` function must be a DOM Node object")
children.flat(Infinity).forEach(child => checkChildValid(child))
van.add(dom, ...children)
}
const tags = new Proxy(van.tags, {
get: (vanTags, name) => {
const vanTag = vanTags[name]
return (...args) => {
let [props, ...children] = args[0]?.__proto__ === Object.prototype ? args : [{}, ...args]
const debugProps = {}
for (const [k, v] of Object.entries(props)) {
const validatePropValue = k.startsWith("on") ?
v => (expect(typeof v === "function",
`Invalid property value for ${k}: Only functions are allowed for on... handler`), v) :
v => (expect(isValidPrimitive(v),
`Invalid property value for ${k}: Only string, number, boolean, bigint are valid prop value types`), v)
if (v?.__proto__ === stateProto) {
debugProps[k] = {deps: [v], f: v => validatePropValue(v)}
} else if (v?.__proto__ === Object.prototype) {
expect(Array.isArray(v.deps) && v.deps.every(d => d.__proto__ === stateProto),
"For state-derived properties, you want specify an Array of states in `deps` field")
expect(typeof v.f === "function",
"For state-derived properties, you want specify the generation function in `f` field")
debugProps[k] = {deps: v.deps, f: (...deps) => validatePropValue(v.f(...deps))}
} else
debugProps[k] = validatePropValue(v)
}
children.flat(Infinity).forEach(child => checkChildValid(child))
return vanTag(debugProps, ...children)
}
},
})
const bind = (...args) => {
const deps = args.slice(0, -1), func = args[args.length - 1]
expect(deps.length > 0, "`bind` must be called with 1 or more states as dependencies")
deps.forEach(d => expect(d.__proto__ === stateProto, "Dependencies in `bind` must be states"))
expect(typeof func === "function", "The last argument of `bind` must be the generation function")
return van.bind(...deps, (...depArgs) => {
const result = func(...depArgs)
if (!expect(result === null || isDomOrPrimitive(result), "The result of `bind` generation function must be DOM node, primitive or null")) return null
if (depArgs.length > deps.length) {
const prevResult = depArgs[deps.length]
if (!expect(prevResult instanceof Node && prevResult.isConnected,
"The previous result of the `bind` generation function must be a DOM node connected to document")) return null
if (result !== prevResult && result instanceof Node)
expect(!result.isConnected,
"If the result of `bind` generation fucntion is not the same as previous one, it shouldn't be already connected to document")
}
return result
})
}
export default {add, tags, state, bind, startCapturingErrors, stopCapturingErrors, get capturedErrors() { return capturedErrors }}