@esportsplus/template 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/.editorconfig +9 -0
  2. package/.gitattributes +2 -0
  3. package/.github/dependabot.yml +25 -0
  4. package/.github/workflows/bump.yml +9 -0
  5. package/.github/workflows/dependabot.yml +12 -0
  6. package/.github/workflows/publish.yml +16 -0
  7. package/README.md +385 -0
  8. package/build/attributes.d.ts +5 -0
  9. package/build/attributes.js +212 -0
  10. package/build/compiler/codegen.d.ts +21 -0
  11. package/build/compiler/codegen.js +303 -0
  12. package/build/compiler/constants.d.ts +16 -0
  13. package/build/compiler/constants.js +19 -0
  14. package/build/compiler/index.d.ts +14 -0
  15. package/build/compiler/index.js +61 -0
  16. package/build/compiler/parser.d.ts +19 -0
  17. package/build/compiler/parser.js +164 -0
  18. package/build/compiler/plugins/tsc.d.ts +3 -0
  19. package/build/compiler/plugins/tsc.js +4 -0
  20. package/build/compiler/plugins/vite.d.ts +13 -0
  21. package/build/compiler/plugins/vite.js +8 -0
  22. package/build/compiler/ts-analyzer.d.ts +4 -0
  23. package/build/compiler/ts-analyzer.js +63 -0
  24. package/build/compiler/ts-parser.d.ts +24 -0
  25. package/build/compiler/ts-parser.js +67 -0
  26. package/build/constants.d.ts +12 -0
  27. package/build/constants.js +25 -0
  28. package/build/event/index.d.ts +10 -0
  29. package/build/event/index.js +90 -0
  30. package/build/event/onconnect.d.ts +3 -0
  31. package/build/event/onconnect.js +15 -0
  32. package/build/event/onresize.d.ts +3 -0
  33. package/build/event/onresize.js +26 -0
  34. package/build/event/ontick.d.ts +6 -0
  35. package/build/event/ontick.js +41 -0
  36. package/build/html.d.ts +9 -0
  37. package/build/html.js +7 -0
  38. package/build/index.d.ts +8 -0
  39. package/build/index.js +12 -0
  40. package/build/render.d.ts +3 -0
  41. package/build/render.js +8 -0
  42. package/build/slot/array.d.ts +25 -0
  43. package/build/slot/array.js +189 -0
  44. package/build/slot/cleanup.d.ts +4 -0
  45. package/build/slot/cleanup.js +23 -0
  46. package/build/slot/effect.d.ts +12 -0
  47. package/build/slot/effect.js +85 -0
  48. package/build/slot/index.d.ts +7 -0
  49. package/build/slot/index.js +14 -0
  50. package/build/slot/render.d.ts +2 -0
  51. package/build/slot/render.js +44 -0
  52. package/build/svg.d.ts +5 -0
  53. package/build/svg.js +14 -0
  54. package/build/types.d.ts +23 -0
  55. package/build/types.js +1 -0
  56. package/build/utilities.d.ts +7 -0
  57. package/build/utilities.js +31 -0
  58. package/package.json +43 -0
  59. package/src/attributes.ts +313 -0
  60. package/src/compiler/codegen.ts +492 -0
  61. package/src/compiler/constants.ts +25 -0
  62. package/src/compiler/index.ts +87 -0
  63. package/src/compiler/parser.ts +242 -0
  64. package/src/compiler/plugins/tsc.ts +6 -0
  65. package/src/compiler/plugins/vite.ts +10 -0
  66. package/src/compiler/ts-analyzer.ts +89 -0
  67. package/src/compiler/ts-parser.ts +112 -0
  68. package/src/constants.ts +44 -0
  69. package/src/event/index.ts +130 -0
  70. package/src/event/onconnect.ts +22 -0
  71. package/src/event/onresize.ts +37 -0
  72. package/src/event/ontick.ts +59 -0
  73. package/src/html.ts +18 -0
  74. package/src/index.ts +19 -0
  75. package/src/llm.txt +403 -0
  76. package/src/render.ts +13 -0
  77. package/src/slot/array.ts +257 -0
  78. package/src/slot/cleanup.ts +37 -0
  79. package/src/slot/effect.ts +114 -0
  80. package/src/slot/index.ts +17 -0
  81. package/src/slot/render.ts +61 -0
  82. package/src/svg.ts +27 -0
  83. package/src/types.ts +40 -0
  84. package/src/utilities.ts +53 -0
  85. package/storage/compiler-architecture-2026-01-13.md +420 -0
  86. package/test/dist/test.js +1912 -0
  87. package/test/dist/test.js.map +1 -0
  88. package/test/index.ts +648 -0
  89. package/test/vite.config.ts +23 -0
  90. package/tsconfig.json +8 -0
@@ -0,0 +1,4 @@
1
+ import { Element, SlotGroup } from '../types.js';
2
+ declare const ondisconnect: (element: Element, fn: VoidFunction) => void;
3
+ declare const remove: (...groups: SlotGroup[]) => void;
4
+ export { ondisconnect, remove };
@@ -0,0 +1,23 @@
1
+ import { CLEANUP } from '../constants.js';
2
+ const ondisconnect = (element, fn) => {
3
+ (element[CLEANUP] ??= []).push(fn);
4
+ };
5
+ const remove = (...groups) => {
6
+ for (let i = 0, n = groups.length; i < n; i++) {
7
+ let fns, fn, group = groups[i], head = group.head, next, tail = group.tail || head;
8
+ while (tail) {
9
+ if (fns = tail[CLEANUP]) {
10
+ while (fn = fns.pop()) {
11
+ fn();
12
+ }
13
+ }
14
+ next = tail.previousSibling;
15
+ tail.remove();
16
+ if (head === tail) {
17
+ break;
18
+ }
19
+ tail = next;
20
+ }
21
+ }
22
+ };
23
+ export { ondisconnect, remove };
@@ -0,0 +1,12 @@
1
+ import { Element, Renderable, SlotGroup } from '../types.js';
2
+ declare class EffectSlot {
3
+ anchor: Element;
4
+ disposer: VoidFunction;
5
+ group: SlotGroup | null;
6
+ scheduled: boolean;
7
+ textnode: Node | null;
8
+ constructor(anchor: Element, fn: (dispose?: VoidFunction) => Renderable<any>);
9
+ dispose(): void;
10
+ update(value: unknown): void;
11
+ }
12
+ export { EffectSlot };
@@ -0,0 +1,85 @@
1
+ import { effect } from '@esportsplus/reactivity';
2
+ import { raf, text } from '../utilities.js';
3
+ import { remove } from './cleanup.js';
4
+ import render from './render.js';
5
+ function read(value) {
6
+ if (typeof value === 'function') {
7
+ return read(value());
8
+ }
9
+ if (value == null || value === false) {
10
+ return '';
11
+ }
12
+ return value;
13
+ }
14
+ class EffectSlot {
15
+ anchor;
16
+ disposer;
17
+ group = null;
18
+ scheduled = false;
19
+ textnode = null;
20
+ constructor(anchor, fn) {
21
+ let dispose = fn.length ? () => this.dispose() : undefined, value;
22
+ this.anchor = anchor;
23
+ this.disposer = effect(() => {
24
+ value = read(fn(dispose));
25
+ if (!this.disposer) {
26
+ this.update(value);
27
+ }
28
+ else if (!this.scheduled) {
29
+ this.scheduled = true;
30
+ raf(() => {
31
+ this.scheduled = false;
32
+ this.update(value);
33
+ });
34
+ }
35
+ });
36
+ }
37
+ dispose() {
38
+ let { anchor, group, textnode } = this;
39
+ if (textnode) {
40
+ group = { head: anchor, tail: textnode };
41
+ }
42
+ else if (group) {
43
+ group.head = anchor;
44
+ }
45
+ this.disposer();
46
+ if (group) {
47
+ remove(group);
48
+ }
49
+ }
50
+ update(value) {
51
+ let { anchor, group, textnode } = this;
52
+ if (group) {
53
+ remove(group);
54
+ this.group = null;
55
+ }
56
+ if (typeof value !== 'object') {
57
+ if (typeof value !== 'string') {
58
+ value = String(value);
59
+ }
60
+ if (textnode) {
61
+ textnode.nodeValue = value;
62
+ if (!textnode.isConnected) {
63
+ anchor.after(textnode);
64
+ }
65
+ }
66
+ else {
67
+ anchor.after(this.textnode = text(value));
68
+ }
69
+ }
70
+ else {
71
+ let fragment = render(anchor, value), head = fragment.firstChild;
72
+ if (textnode?.isConnected) {
73
+ remove({ head: textnode, tail: textnode });
74
+ }
75
+ if (head) {
76
+ this.group = {
77
+ head: head,
78
+ tail: fragment.lastChild
79
+ };
80
+ anchor.after(fragment);
81
+ }
82
+ }
83
+ }
84
+ }
85
+ export { EffectSlot };
@@ -0,0 +1,7 @@
1
+ import { Element, Renderable } from '../types.js';
2
+ declare const _default: <T>(anchor: Element, renderable: Renderable<T>) => void;
3
+ export default _default;
4
+ export * from './array.js';
5
+ export * from './cleanup.js';
6
+ export * from './effect.js';
7
+ export { default as render } from './render.js';
@@ -0,0 +1,14 @@
1
+ import { EffectSlot } from './effect.js';
2
+ import render from './render.js';
3
+ export default (anchor, renderable) => {
4
+ if (typeof renderable === 'function') {
5
+ new EffectSlot(anchor, renderable);
6
+ }
7
+ else {
8
+ anchor.after(render(anchor, renderable));
9
+ }
10
+ };
11
+ export * from './array.js';
12
+ export * from './cleanup.js';
13
+ export * from './effect.js';
14
+ export { default as render } from './render.js';
@@ -0,0 +1,2 @@
1
+ import { Element } from '../types.js';
2
+ export default function render(anchor: Element, value: unknown): Node;
@@ -0,0 +1,44 @@
1
+ import { isArray } from '@esportsplus/utilities';
2
+ import { ARRAY_SLOT } from '../constants.js';
3
+ import { clone, fragment, text } from '../utilities.js';
4
+ const EMPTY_FRAGMENT = fragment('');
5
+ export default function render(anchor, value) {
6
+ if (value == null || value === false || value === '') {
7
+ return EMPTY_FRAGMENT;
8
+ }
9
+ if (typeof value !== 'object') {
10
+ return text(value);
11
+ }
12
+ if (value[ARRAY_SLOT] === true) {
13
+ return value.fragment;
14
+ }
15
+ if (value.nodeType !== undefined) {
16
+ return value;
17
+ }
18
+ let n = value.length;
19
+ if (typeof n === 'number') {
20
+ if (n === 0) {
21
+ return EMPTY_FRAGMENT;
22
+ }
23
+ else if (n === 1) {
24
+ return render(anchor, value[0]);
25
+ }
26
+ }
27
+ if (isArray(value)) {
28
+ let fragment = clone(EMPTY_FRAGMENT);
29
+ for (let i = 0; i < n; i++) {
30
+ fragment.append(render(anchor, value[i]));
31
+ anchor = fragment.lastChild;
32
+ }
33
+ return fragment;
34
+ }
35
+ if (value instanceof NodeList) {
36
+ let fragment = clone(EMPTY_FRAGMENT);
37
+ for (let i = 0; i < n; i++) {
38
+ fragment.append(value[i]);
39
+ }
40
+ return fragment;
41
+ }
42
+ return text(value);
43
+ }
44
+ ;
package/build/svg.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import html from './html.js';
2
+ declare const svg: typeof html & {
3
+ sprite: (href: string) => DocumentFragment;
4
+ };
5
+ export default svg;
package/build/svg.js ADDED
@@ -0,0 +1,14 @@
1
+ import { setProperty } from './attributes.js';
2
+ import { template } from './utilities.js';
3
+ import html from './html.js';
4
+ let factory = template('<svg><use /></svg>');
5
+ const svg = html.bind(null);
6
+ svg.sprite = (href) => {
7
+ if (href[0] !== '#') {
8
+ href = '#' + href;
9
+ }
10
+ let root = factory();
11
+ setProperty(root.firstChild.firstChild, 'href', href);
12
+ return root;
13
+ };
14
+ export default svg;
@@ -0,0 +1,23 @@
1
+ import { ArraySlot } from './slot/index.js';
2
+ type Attribute = Effect<Primitive | Primitive[]> | ((...args: any[]) => void) | Primitive;
3
+ type Attributes<T extends HTMLElement = Element> = {
4
+ class?: Attribute | Attribute[];
5
+ onconnect?: (element: T) => void;
6
+ ondisconnect?: (element: T) => void;
7
+ onrender?: (element: T) => void;
8
+ ontick?: (dispose: VoidFunction, element: T) => void;
9
+ style?: Attribute | Attribute[];
10
+ [key: `aria-${string}`]: string | number | boolean | undefined;
11
+ [key: `data-${string}`]: string | undefined;
12
+ } & {
13
+ [K in keyof GlobalEventHandlersEventMap as `on${string & K}`]?: (this: T, event: GlobalEventHandlersEventMap[K]) => void;
14
+ } & Record<PropertyKey, unknown>;
15
+ type Effect<T> = () => T extends [] ? Renderable<T>[] : Renderable<T>;
16
+ type Element = HTMLElement & Attributes<any>;
17
+ type Primitive = bigint | boolean | null | number | string | undefined;
18
+ type Renderable<T> = ArraySlot<T> | DocumentFragment | Effect<T> | Node | NodeList | Primitive | Renderable<T>[];
19
+ type SlotGroup = {
20
+ head: Element;
21
+ tail: Element;
22
+ };
23
+ export type { Attribute, Attributes, Effect, Element, Renderable, SlotGroup };
package/build/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ declare const clone: <T extends Node>(node: T, options?: boolean | ImportNodeOptions) => T;
2
+ declare const fragment: (html: string) => DocumentFragment;
3
+ declare const marker: ChildNode;
4
+ declare const raf: typeof requestAnimationFrame;
5
+ declare const template: (html: string) => () => DocumentFragment;
6
+ declare const text: (value: string) => Node;
7
+ export { clone, fragment, template, marker, raf, text };
@@ -0,0 +1,31 @@
1
+ import { SLOT_HTML } from './constants.js';
2
+ let tmpl = document.createElement('template'), txt = document.createTextNode('');
3
+ const clone = typeof navigator !== 'undefined' && navigator.userAgent.includes('Firefox')
4
+ ? document.importNode.bind(document)
5
+ : (node, deep = true) => node.cloneNode(deep);
6
+ const fragment = (html) => {
7
+ let element = tmpl.cloneNode();
8
+ element.innerHTML = html;
9
+ return element.content;
10
+ };
11
+ const marker = fragment(SLOT_HTML).firstChild;
12
+ const raf = globalThis?.requestAnimationFrame;
13
+ const template = (html) => {
14
+ let cached;
15
+ return () => {
16
+ if (!cached) {
17
+ let element = tmpl.cloneNode();
18
+ element.innerHTML = html;
19
+ cached = element.content;
20
+ }
21
+ return clone(cached, true);
22
+ };
23
+ };
24
+ const text = (value) => {
25
+ let element = txt.cloneNode();
26
+ if (value !== '') {
27
+ element.nodeValue = value;
28
+ }
29
+ return element;
30
+ };
31
+ export { clone, fragment, template, marker, raf, text };
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "author": "ICJR",
3
+ "dependencies": {
4
+ "@esportsplus/pipeline": "^1.2.2",
5
+ "@esportsplus/queue": "^0.2.0",
6
+ "@esportsplus/reactivity": "^0.29.19",
7
+ "@esportsplus/typescript": "^0.28.2",
8
+ "@esportsplus/utilities": "^0.27.2"
9
+ },
10
+ "exports": {
11
+ "./compiler": {
12
+ "types": "./build/compiler/index.d.ts",
13
+ "default": "./build/compiler/index.js"
14
+ },
15
+ "./compiler/tsc": {
16
+ "types": "./build/compiler/plugins/tsc.d.ts",
17
+ "default": "./build/compiler/plugins/tsc.js"
18
+ },
19
+ "./compiler/vite": {
20
+ "types": "./build/compiler/plugins/vite.d.ts",
21
+ "default": "./build/compiler/plugins/vite.js"
22
+ },
23
+ ".": {
24
+ "types": "./build/index.d.ts",
25
+ "default": "./build/index.js"
26
+ }
27
+ },
28
+ "main": "./build/index.js",
29
+ "name": "@esportsplus/template",
30
+ "private": false,
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "https://github.com/esportsplus/template"
34
+ },
35
+ "type": "module",
36
+ "types": "./build/index.d.ts",
37
+ "version": "0.16.0",
38
+ "scripts": {
39
+ "build": "tsc",
40
+ "build:test": "vite build --config test/vite.config.ts",
41
+ "-": "-"
42
+ }
43
+ }
@@ -0,0 +1,313 @@
1
+ import { effect } from '@esportsplus/reactivity';
2
+ import { isArray, isObject } from '@esportsplus/utilities';
3
+ import { ATTRIBUTE_DELIMITERS, STATE_HYDRATING, STATE_NONE, STATE_WAITING, STORE } from './constants';
4
+ import { Attributes, Element } from './types';
5
+ import { raf } from './utilities';
6
+ import { runtime } from './event';
7
+ import q from '@esportsplus/queue';
8
+
9
+
10
+ type Context = {
11
+ effect?: 0,
12
+ element: Element;
13
+ store?: Record<string, unknown>;
14
+ updates?: Record<PropertyKey, unknown>;
15
+ updating?: boolean;
16
+ } & Record<PropertyKey, unknown>;
17
+
18
+ type State = typeof STATE_HYDRATING | typeof STATE_NONE | typeof STATE_WAITING;
19
+
20
+
21
+ let queue = q<Context>(64),
22
+ scheduled = false;
23
+
24
+
25
+ function apply(element: Element, name: string, value: unknown) {
26
+ if (value == null || value === false || value === '') {
27
+ element.removeAttribute(name);
28
+ }
29
+ else if (name === 'class') {
30
+ element.className = value as string;
31
+ }
32
+ else if (name === 'style' || (name[0] === 'd' && name.startsWith('data-')) || element['ownerSVGElement']) {
33
+ element.setAttribute(name, value as string);
34
+ }
35
+ else {
36
+ element[name] = value;
37
+ }
38
+ }
39
+
40
+ function context(element: Element) {
41
+ return (element[STORE] ??= { element }) as Context;
42
+ }
43
+
44
+ function list(
45
+ ctx: Context | null,
46
+ element: Element,
47
+ id: null | number,
48
+ name: string,
49
+ state: State,
50
+ value: unknown
51
+ ) {
52
+ if (value == null || value === false || value === '') {
53
+ value = '';
54
+ }
55
+
56
+ let changed = false,
57
+ delimiter = ATTRIBUTE_DELIMITERS[name],
58
+ store = (ctx ??= context(element)).store ??= {},
59
+ dynamic = store[name] as Set<string>;
60
+
61
+ // Runtime fallback
62
+ if (!dynamic) {
63
+ store[name + '.static'] = (element.getAttribute(name) || '').trim();
64
+ store[name] = dynamic = new Set();
65
+ }
66
+
67
+ if (id === null) {
68
+ if (value && typeof value === 'string') {
69
+ changed = true;
70
+ store[name + '.static'] += (store[name + '.static'] ? delimiter : '') + value;
71
+ }
72
+ }
73
+ else if (store[id + '.raw'] !== value) {
74
+ let hot: Record<PropertyKey, true> = {};
75
+
76
+ if (value && typeof value === 'string') {
77
+ let part: string | undefined,
78
+ parts = (value as string).split(delimiter);
79
+
80
+ while (part = parts.pop()) {
81
+ part = part.trim();
82
+
83
+ if (part === '') {
84
+ continue;
85
+ }
86
+
87
+ if (!dynamic.has(part)) {
88
+ changed = true;
89
+ dynamic.add(part);
90
+ }
91
+
92
+ hot[part] = true;
93
+ }
94
+ }
95
+
96
+ let cold = store[id] as Record<PropertyKey, true>;
97
+
98
+ if (cold !== undefined) {
99
+ for (let part in cold) {
100
+ if (hot[part] === true) {
101
+ continue;
102
+ }
103
+
104
+ changed = true;
105
+ dynamic.delete(part);
106
+ }
107
+ }
108
+
109
+ store[id + '.raw'] = value;
110
+ store[id] = hot;
111
+ }
112
+
113
+ if (!changed) {
114
+ return;
115
+ }
116
+
117
+ value = store[name + '.static'];
118
+
119
+ for (let key of dynamic) {
120
+ value += (value ? delimiter : '') + key;
121
+ }
122
+
123
+ if (state === STATE_HYDRATING) {
124
+ apply(element, name, value);
125
+ }
126
+ else {
127
+ schedule(ctx, element, name, state, value);
128
+ }
129
+ }
130
+
131
+ function property(
132
+ ctx: Context | null,
133
+ element: Element,
134
+ id: null | number,
135
+ name: string,
136
+ state: State,
137
+ value: unknown
138
+ ) {
139
+ if (value == null || value === false || value === '') {
140
+ value = '';
141
+ }
142
+
143
+ if (id !== null) {
144
+ ctx ??= context(element);
145
+
146
+ if (ctx[name] === value) {
147
+ return;
148
+ }
149
+
150
+ ctx[name] = value as string;
151
+ }
152
+
153
+ if (state === STATE_HYDRATING) {
154
+ apply(element, name, value);
155
+ }
156
+ else {
157
+ schedule(ctx, element, name, state, value);
158
+ }
159
+ }
160
+
161
+ function reactive(element: Element, name: string, state: State, value: unknown) {
162
+ let ctx = context(element),
163
+ fn = (name === 'class' || name === 'style') ? list : property;
164
+
165
+ ctx.effect ??= 0;
166
+
167
+ let id = (ctx.effect as number)++;
168
+
169
+ effect(() => {
170
+ let v = (value as Function)(element);
171
+
172
+ if (v == null || typeof v !== 'object') {
173
+ fn(ctx, element, id, name, state, v);
174
+ }
175
+ else if (isArray(v)) {
176
+ let last = v.length - 1;
177
+
178
+ for (let i = 0, n = v.length; i < n; i++) {
179
+ fn(
180
+ ctx,
181
+ element,
182
+ id,
183
+ name,
184
+ state === STATE_HYDRATING
185
+ ? state
186
+ : i !== last ? STATE_WAITING : state,
187
+ v[i],
188
+ );
189
+ }
190
+ }
191
+ });
192
+
193
+ state = STATE_NONE;
194
+ }
195
+
196
+ function schedule(ctx: Context | null, element: Element, name: string, state: State, value: unknown) {
197
+ ctx ??= context(element);
198
+ (ctx.updates ??= {})[name] = value;
199
+
200
+ if (state === STATE_NONE && !ctx.updating) {
201
+ ctx.updating = true;
202
+ queue.add(ctx);
203
+ }
204
+
205
+ if (scheduled) {
206
+ return;
207
+ }
208
+
209
+ scheduled = true;
210
+ raf(task);
211
+ }
212
+
213
+ function task() {
214
+ let context,
215
+ n = queue.length;
216
+
217
+ while ((context = queue.next()) && n--) {
218
+ let { element, updates } = context;
219
+
220
+ for (let name in updates) {
221
+ apply(element, name, updates[name]);
222
+ }
223
+
224
+ context.updates = {};
225
+ context.updating = false;
226
+ }
227
+
228
+ if (queue.length) {
229
+ raf(task);
230
+ }
231
+ else {
232
+ scheduled = false;
233
+ }
234
+ }
235
+
236
+
237
+ const setList = (element: Element, name: 'class' | 'style', value: unknown, attributes: Record<string, string> = {}) => {
238
+ let ctx = context(element),
239
+ store = ctx.store ??= {};
240
+
241
+ store[name] ??= new Set<string>();
242
+ store[name + '.static'] ??= '';
243
+ store[name + '.static'] += `${attributes[name] && store[name + '.static'] ? ATTRIBUTE_DELIMITERS[name] : ''}${attributes[name]}`;
244
+
245
+ if (typeof value === 'function') {
246
+ reactive(element, name, STATE_HYDRATING, value);
247
+ }
248
+ else if (typeof value !== 'object') {
249
+ list(ctx, element, null, name, STATE_HYDRATING, value);
250
+ }
251
+ else if (isArray(value)) {
252
+ for (let i = 0, n = value.length; i < n; i++) {
253
+ let v = value[i];
254
+
255
+ if (v == null || v === false || v === '') {
256
+ continue;
257
+ }
258
+
259
+ list(ctx, element, null, name, STATE_HYDRATING, v);
260
+ }
261
+ }
262
+ };
263
+
264
+ const setProperty = (element: Element, name: string, value: unknown) => {
265
+ if (typeof value === 'function') {
266
+ reactive(element, name, STATE_HYDRATING, value);
267
+ }
268
+ else {
269
+ property(null, element, null, name, STATE_HYDRATING, value);
270
+ }
271
+ };
272
+
273
+ const setProperties = function (
274
+ element: Element,
275
+ properties: Attributes | Attributes[] | false | null | undefined,
276
+ attributes: Record<string, string> = {}
277
+ ) {
278
+ if (!properties) {
279
+ return;
280
+ }
281
+ else if (isObject(properties)) {
282
+ for (let name in properties) {
283
+ let value = properties[name];
284
+
285
+ if (value == null || value === false || value === '') {
286
+ continue;
287
+ }
288
+
289
+ if (name === 'class' || name === 'style') {
290
+ setList(element, name, value, attributes);
291
+ }
292
+ else if (typeof value === 'function') {
293
+ if (name[0] === 'o' && name[1] === 'n') {
294
+ runtime(element, name as `on${string}`, value as Function);
295
+ }
296
+ else {
297
+ reactive(element, name, STATE_HYDRATING, value);
298
+ }
299
+ }
300
+ else {
301
+ property(null, element, null, name, STATE_HYDRATING, value);
302
+ }
303
+ }
304
+ }
305
+ else if (isArray(properties)) {
306
+ for (let i = 0, n = properties.length; i < n; i++) {
307
+ setProperties(element, properties[i], attributes);
308
+ }
309
+ }
310
+ };
311
+
312
+
313
+ export { setList, setProperty, setProperties };