@esportsplus/template 0.28.3 → 0.29.1

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 (109) hide show
  1. package/build/attributes.d.ts +7 -1
  2. package/build/attributes.js +86 -33
  3. package/build/constants.d.ts +3 -11
  4. package/build/constants.js +4 -32
  5. package/build/event/constants.d.ts +3 -0
  6. package/build/event/constants.js +13 -0
  7. package/build/event/index.d.ts +9 -1
  8. package/build/event/index.js +29 -35
  9. package/build/event/ontick.js +6 -9
  10. package/build/html.d.ts +9 -0
  11. package/build/html.js +7 -0
  12. package/build/index.d.ts +8 -2
  13. package/build/index.js +8 -1
  14. package/build/render.d.ts +2 -2
  15. package/build/render.js +2 -3
  16. package/build/runtime.d.ts +1 -0
  17. package/build/runtime.js +5 -0
  18. package/build/slot/array.d.ts +3 -3
  19. package/build/slot/array.js +11 -14
  20. package/build/slot/cleanup.d.ts +1 -1
  21. package/build/slot/cleanup.js +1 -2
  22. package/build/slot/effect.js +5 -7
  23. package/build/slot/index.js +1 -7
  24. package/build/slot/render.js +6 -8
  25. package/build/svg.d.ts +1 -1
  26. package/build/svg.js +1 -1
  27. package/build/transformer/codegen.d.ts +18 -0
  28. package/build/transformer/codegen.js +316 -0
  29. package/build/transformer/index.d.ts +12 -0
  30. package/build/transformer/index.js +62 -0
  31. package/build/transformer/parser.d.ts +18 -0
  32. package/build/transformer/parser.js +166 -0
  33. package/build/transformer/plugins/esbuild.d.ts +5 -0
  34. package/build/transformer/plugins/esbuild.js +35 -0
  35. package/build/transformer/plugins/tsc.d.ts +3 -0
  36. package/build/transformer/plugins/tsc.js +4 -0
  37. package/build/transformer/plugins/vite.d.ts +5 -0
  38. package/build/transformer/plugins/vite.js +37 -0
  39. package/build/transformer/ts-parser.d.ts +21 -0
  40. package/build/transformer/ts-parser.js +72 -0
  41. package/build/transformer/type-analyzer.d.ts +7 -0
  42. package/build/transformer/type-analyzer.js +230 -0
  43. package/build/types.d.ts +2 -3
  44. package/build/utilities.d.ts +7 -0
  45. package/build/utilities.js +31 -0
  46. package/package.json +11 -4
  47. package/src/attributes.ts +115 -51
  48. package/src/constants.ts +6 -53
  49. package/src/event/constants.ts +16 -0
  50. package/src/event/index.ts +36 -42
  51. package/src/event/onconnect.ts +1 -1
  52. package/src/event/onresize.ts +1 -1
  53. package/src/event/ontick.ts +7 -11
  54. package/src/html.ts +18 -0
  55. package/src/index.ts +8 -2
  56. package/src/render.ts +6 -7
  57. package/src/runtime.ts +8 -0
  58. package/src/slot/array.ts +18 -24
  59. package/src/slot/cleanup.ts +3 -4
  60. package/src/slot/effect.ts +6 -8
  61. package/src/slot/index.ts +2 -8
  62. package/src/slot/render.ts +7 -9
  63. package/src/svg.ts +1 -1
  64. package/src/transformer/codegen.ts +518 -0
  65. package/src/transformer/index.ts +98 -0
  66. package/src/transformer/parser.ts +239 -0
  67. package/src/transformer/plugins/esbuild.ts +46 -0
  68. package/src/transformer/plugins/tsc.ts +7 -0
  69. package/src/transformer/plugins/vite.ts +49 -0
  70. package/src/transformer/ts-parser.ts +123 -0
  71. package/src/transformer/type-analyzer.ts +334 -0
  72. package/src/types.ts +3 -4
  73. package/src/utilities.ts +52 -0
  74. package/storage/rewrite-analysis-2026-01-04.md +439 -0
  75. package/test/constants.ts +69 -0
  76. package/test/effects.ts +237 -0
  77. package/test/events.ts +318 -0
  78. package/test/imported-values.ts +253 -0
  79. package/test/nested.ts +298 -0
  80. package/test/slots.ts +259 -0
  81. package/test/spread.ts +290 -0
  82. package/test/static.ts +118 -0
  83. package/test/templates.ts +473 -0
  84. package/test/tsconfig.json +17 -0
  85. package/test/vite.config.ts +50 -0
  86. package/build/html/index.d.ts +0 -9
  87. package/build/html/index.js +0 -29
  88. package/build/html/parser.d.ts +0 -5
  89. package/build/html/parser.js +0 -165
  90. package/build/utilities/element.d.ts +0 -11
  91. package/build/utilities/element.js +0 -9
  92. package/build/utilities/fragment.d.ts +0 -3
  93. package/build/utilities/fragment.js +0 -10
  94. package/build/utilities/marker.d.ts +0 -2
  95. package/build/utilities/marker.js +0 -4
  96. package/build/utilities/node.d.ts +0 -9
  97. package/build/utilities/node.js +0 -10
  98. package/build/utilities/raf.d.ts +0 -2
  99. package/build/utilities/raf.js +0 -1
  100. package/build/utilities/text.d.ts +0 -2
  101. package/build/utilities/text.js +0 -9
  102. package/src/html/index.ts +0 -48
  103. package/src/html/parser.ts +0 -235
  104. package/src/utilities/element.ts +0 -28
  105. package/src/utilities/fragment.ts +0 -19
  106. package/src/utilities/marker.ts +0 -6
  107. package/src/utilities/node.ts +0 -29
  108. package/src/utilities/raf.ts +0 -1
  109. package/src/utilities/text.ts +0 -15
package/package.json CHANGED
@@ -2,11 +2,17 @@
2
2
  "author": "ICJR",
3
3
  "dependencies": {
4
4
  "@esportsplus/queue": "^0.2.0",
5
- "@esportsplus/reactivity": "^0.22.0",
6
- "@esportsplus/utilities": "^0.25.0"
5
+ "@esportsplus/reactivity": "^0.23.2",
6
+ "@esportsplus/utilities": "^0.27.2",
7
+ "serve": "^14.2.5"
7
8
  },
8
9
  "devDependencies": {
9
- "@esportsplus/typescript": "^0.9.2"
10
+ "@esportsplus/typescript": "^0.13.0",
11
+ "@types/node": "^25.0.3",
12
+ "esbuild": "^0.27.2",
13
+ "typescript": "^5.9.3",
14
+ "vite": "^7.3.0",
15
+ "vite-tsconfig-paths": "^6.0.3"
10
16
  },
11
17
  "main": "./build/index.js",
12
18
  "name": "@esportsplus/template",
@@ -17,9 +23,10 @@
17
23
  },
18
24
  "type": "module",
19
25
  "types": "./build/index.d.ts",
20
- "version": "0.28.3",
26
+ "version": "0.29.1",
21
27
  "scripts": {
22
28
  "build": "tsc && tsc-alias",
29
+ "compile:test": "vite build --config test/vite.config.ts",
23
30
  "-": "-"
24
31
  }
25
32
  }
package/src/attributes.ts CHANGED
@@ -1,10 +1,9 @@
1
1
  import { effect } from '@esportsplus/reactivity';
2
2
  import { isArray, isObject } from '@esportsplus/utilities';
3
- import { STATE_HYDRATING, STATE_NONE, STATE_WAITING } from './constants';
3
+ import { DIRECT_ATTACH_EVENTS, LIFECYCLE_EVENTS, STATE_HYDRATING, STATE_NONE, STATE_WAITING } from './constants';
4
4
  import { Attributes, Element } from './types';
5
- import { className, removeAttribute, setAttribute } from './utilities/element';
5
+ import { raf } from './utilities';
6
6
  import q from '@esportsplus/queue';
7
- import raf from './utilities/raf';
8
7
  import event from './event';
9
8
 
10
9
 
@@ -32,13 +31,13 @@ let delimiters: Record<string, string> = {
32
31
 
33
32
  function apply(element: Element, name: string, value: unknown) {
34
33
  if (value == null || value === false || value === '') {
35
- removeAttribute.call(element, name);
34
+ element.removeAttribute(name);
36
35
  }
37
36
  else if (name === 'class') {
38
- className.call(element, value as string);
37
+ element.className = value as string;
39
38
  }
40
39
  else if (name === 'style' || (name[0] === 'd' && name.startsWith('data-')) || element['ownerSVGElement']) {
41
- setAttribute.call(element, name, value as string);
40
+ element.setAttribute(name, value as string);
42
41
  }
43
42
  else {
44
43
  element[name] = value;
@@ -62,6 +61,7 @@ function list(
62
61
  }
63
62
 
64
63
  let base = name + '.static',
64
+ changed = false,
65
65
  delimiter = delimiters[name],
66
66
  store = (ctx ??= context(element)).store ??= {},
67
67
  dynamic = store[name] as Set<string> | undefined;
@@ -75,10 +75,17 @@ function list(
75
75
 
76
76
  if (id === null) {
77
77
  if (value && typeof value === 'string') {
78
+ changed = true;
78
79
  store[base] += (store[base] ? delimiter : '') + value;
79
80
  }
80
81
  }
81
82
  else {
83
+ if (store[id + '.raw'] === value) {
84
+ return;
85
+ }
86
+
87
+ store[id + '.raw'] = value;
88
+
82
89
  let hot: Record<PropertyKey, true> = {};
83
90
 
84
91
  if (value && typeof value === 'string') {
@@ -92,7 +99,11 @@ function list(
92
99
  continue;
93
100
  }
94
101
 
95
- dynamic.add(part);
102
+ if (!dynamic.has(part)) {
103
+ changed = true;
104
+ dynamic.add(part);
105
+ }
106
+
96
107
  hot[part] = true;
97
108
  }
98
109
  }
@@ -105,6 +116,7 @@ function list(
105
116
  continue;
106
117
  }
107
118
 
119
+ changed = true;
108
120
  dynamic.delete(part);
109
121
  }
110
122
  }
@@ -112,6 +124,10 @@ function list(
112
124
  store[id] = hot;
113
125
  }
114
126
 
127
+ if (!changed) {
128
+ return;
129
+ }
130
+
115
131
  value = store[base];
116
132
 
117
133
  for (let key of dynamic) {
@@ -156,6 +172,41 @@ function property(
156
172
  }
157
173
  }
158
174
 
175
+ function reactive(element: Element, name: string, state: State, value: unknown) {
176
+ let ctx = context(element),
177
+ fn = name === 'class' || name === 'style' ? list : property;
178
+
179
+ ctx.effect ??= 0;
180
+
181
+ let id = (ctx.effect as number)++;
182
+
183
+ effect(() => {
184
+ let v = (value as Function)(element);
185
+
186
+ if (v == null || typeof v !== 'object') {
187
+ fn(ctx, element, id, name, state, v);
188
+ }
189
+ else if (isArray(v)) {
190
+ let last = v.length - 1;
191
+
192
+ for (let i = 0, n = v.length; i < n; i++) {
193
+ fn(
194
+ ctx,
195
+ element,
196
+ id,
197
+ name,
198
+ state === STATE_HYDRATING
199
+ ? state
200
+ : i !== last ? STATE_WAITING : state,
201
+ v[i],
202
+ );
203
+ }
204
+ }
205
+ });
206
+
207
+ state = STATE_NONE;
208
+ }
209
+
159
210
  function schedule(ctx: Context | null, element: Element, name: string, state: State, value: unknown) {
160
211
  ctx ??= context(element);
161
212
  (ctx.updates ??= {})[name] = value;
@@ -198,50 +249,27 @@ function task() {
198
249
 
199
250
 
200
251
  const set = (element: Element, name: string, value: unknown) => {
201
- let fn = name === 'class' || name === 'style' ? list : property,
202
- state: State = STATE_HYDRATING;
203
-
204
252
  if (typeof value === 'function') {
205
253
  if (name[0] === 'o' && name[1] === 'n') {
206
- return event(element, name as `on${string}`, value as Function);
207
- }
254
+ let e = name.slice(2).toLowerCase(),
255
+ key = name.toLowerCase();
208
256
 
209
- let ctx = context(element);
210
-
211
- ctx.effect ??= 0;
212
-
213
- let id = (ctx.effect as number)++;
214
-
215
- effect(() => {
216
- let v = (value as Function)(element);
217
-
218
- if (v == null || typeof v !== 'object') {
219
- fn(ctx, element, id, name, state, v);
257
+ if (LIFECYCLE_EVENTS.has(key)) {
258
+ (event as any)[key](element, value as Function);
220
259
  }
221
- else if (isArray(v)) {
222
- let last = v.length - 1;
223
-
224
- for (let i = 0, n = v.length; i < n; i++) {
225
- fn(
226
- ctx,
227
- element,
228
- id,
229
- name,
230
- state === STATE_HYDRATING
231
- ? state
232
- : i !== last ? STATE_WAITING : state,
233
- v[i],
234
- );
235
- }
260
+ else if (DIRECT_ATTACH_EVENTS.has(key)) {
261
+ event.direct(element, e, value as Function);
236
262
  }
237
- });
238
-
239
- state = STATE_NONE;
240
-
263
+ else {
264
+ event.delegate(element, e, value as Function);
265
+ }
266
+ }
267
+ else {
268
+ reactive(element, name, STATE_HYDRATING, value);
269
+ }
241
270
  return;
242
271
  }
243
-
244
- if (typeof value !== 'object') {
272
+ else if (typeof value !== 'object') {
245
273
  // Skip isArray when possible
246
274
  }
247
275
  else if (isArray(value)) {
@@ -257,15 +285,51 @@ const set = (element: Element, name: string, value: unknown) => {
257
285
  return;
258
286
  }
259
287
 
260
- fn(null, element, null, name, state, value);
288
+ (name === 'class' || name === 'style' ? list : property)(null, element, null, name, STATE_HYDRATING, value);
289
+ };
290
+
291
+ const setClass = (element: Element, classlist: false | string | undefined, value: unknown) => {
292
+ let ctx = context(element),
293
+ store = ctx.store ??= {};
294
+
295
+ store['class.static'] = classlist || '';
296
+ store['class'] ??= new Set<string>();
297
+
298
+ if (typeof value === 'function') {
299
+ reactive(element, 'class', STATE_HYDRATING, value);
300
+ }
301
+ else {
302
+ list(ctx, element, null, 'class', STATE_HYDRATING, value);
303
+ }
304
+ };
305
+
306
+ const setProperty = (element: Element, name: string, value: unknown) => {
307
+ if (typeof value === 'function') {
308
+ reactive(element, name, STATE_HYDRATING, value);
309
+ }
310
+ else {
311
+ property(null, element, null, name, STATE_HYDRATING, value);
312
+ }
313
+ };
314
+
315
+ const setStyle = (element: Element, styles: false | string | undefined, value: unknown) => {
316
+ let ctx = context(element),
317
+ store = ctx.store ??= {};
318
+
319
+ store['style.static'] = styles || '';
320
+ store['style'] ??= new Set<string>();
321
+
322
+ if (typeof value === 'function') {
323
+ reactive(element, 'style', STATE_HYDRATING, value);
324
+ }
325
+ else {
326
+ list(ctx, element, null, 'style', STATE_HYDRATING, value);
327
+ }
261
328
  };
262
329
 
263
330
  const spread = function (element: Element, value: Attributes | Attributes[]) {
264
331
  if (isObject(value)) {
265
- let names = Object.keys(value),
266
- name;
267
-
268
- while (name = names.pop()) {
332
+ for (let name in value) {
269
333
  let v = value[name];
270
334
 
271
335
  if (v == null || v === false || v === '') {
@@ -283,5 +347,5 @@ const spread = function (element: Element, value: Attributes | Attributes[]) {
283
347
  };
284
348
 
285
349
 
286
- export default { set, spread };
287
- export { set, spread };
350
+ export default { set, setClass, setProperty, setStyle, spread };
351
+ export { set, setClass, setProperty, setStyle, spread };
package/src/constants.ts CHANGED
@@ -1,74 +1,27 @@
1
- import { fragment } from './utilities/fragment';
1
+ import { fragment } from './utilities';
2
2
 
3
3
 
4
4
  const ARRAY_SLOT = Symbol('template.array.slot');
5
5
 
6
-
7
6
  const CLEANUP = Symbol('template.cleanup');
8
7
 
9
-
10
8
  const EMPTY_FRAGMENT = fragment('');
11
9
 
12
-
13
- const NODE_CLOSING = 1;
14
-
15
- const NODE_COMMENT = 2;
16
-
17
- const NODE_ELEMENT = 3;
18
-
19
- const NODE_SLOT = 4;
20
-
21
- const NODE_VOID = 5;
22
-
23
- const NODE_WHITELIST: Record<string, number> = {
24
- '/': NODE_CLOSING,
25
- '!': NODE_COMMENT,
26
-
27
- 'area': NODE_VOID,
28
- 'base': NODE_VOID,
29
- 'br': NODE_VOID,
30
- 'col': NODE_VOID,
31
- 'embed': NODE_VOID,
32
- 'hr': NODE_VOID,
33
- 'img': NODE_VOID,
34
- 'input': NODE_VOID,
35
- 'keygen': NODE_VOID,
36
- 'link': NODE_VOID,
37
- 'menuitem': NODE_VOID,
38
- 'meta': NODE_VOID,
39
- 'param': NODE_VOID,
40
- 'source': NODE_VOID,
41
- 'track': NODE_VOID,
42
- 'wbr': NODE_VOID
43
- };
44
-
45
-
46
- const REGEX_EMPTY_TEXT_NODES = /(>|}|\s)\s+(<|{|\s)/g;
47
-
48
- const REGEX_EVENTS = /(?:\s*on[\w-:]+\s*=(?:\s*["'][^"']*["'])*)/g;
49
-
50
- const REGEX_SLOT_ATTRIBUTES = /<[\w-]+([^><]*{{\$}}[^><]*)>/g;
51
-
52
- const REGEX_SLOT_NODES = /<([\w-]+|[\/!])(?:([^><]*{{\$}}[^><]*)|(?:[^><]*))?>|{{\$}}/g;
53
-
54
-
55
10
  const SLOT_HTML = '<!--$-->';
56
11
 
57
- const SLOT_MARKER = '{{$}}';
58
-
59
-
60
12
  const STATE_HYDRATING = 0;
61
13
 
62
14
  const STATE_NONE = 1;
63
15
 
64
16
  const STATE_WAITING = 2;
65
17
 
18
+ const STORE = Symbol('template.store');
19
+
66
20
 
67
21
  export {
68
22
  ARRAY_SLOT,
69
23
  CLEANUP,
70
24
  EMPTY_FRAGMENT,
71
- NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST,
72
- REGEX_EMPTY_TEXT_NODES, REGEX_EVENTS, REGEX_SLOT_ATTRIBUTES, REGEX_SLOT_NODES,
73
- SLOT_HTML, STATE_HYDRATING, SLOT_MARKER, STATE_NONE, STATE_WAITING
74
- };
25
+ SLOT_HTML, STATE_HYDRATING, STATE_NONE, STATE_WAITING, STORE
26
+ };
27
+ export * from './event/constants';
@@ -0,0 +1,16 @@
1
+ const DIRECT_ATTACH_EVENTS = new Set<string>([
2
+ 'onblur',
3
+ 'onerror',
4
+ 'onfocus', 'onfocusin', 'onfocusout',
5
+ 'onload',
6
+ 'onplay', 'onpause', 'onended', 'ontimeupdate',
7
+ 'onreset',
8
+ 'onscroll', 'onsubmit'
9
+ ]);
10
+
11
+ const LIFECYCLE_EVENTS = new Set<string>([
12
+ 'onconnect', 'ondisconnect', 'onrender', 'onresize', 'ontick'
13
+ ]);
14
+
15
+
16
+ export { DIRECT_ATTACH_EVENTS, LIFECYCLE_EVENTS };
@@ -1,36 +1,31 @@
1
1
  import { root } from '@esportsplus/reactivity';
2
2
  import { defineProperty } from '@esportsplus/utilities';
3
3
  import { Element } from '~/types';
4
- import { addEventListener } from '~/utilities/element';
5
- import { parentElement } from '~/utilities/node';
6
- import { ondisconnect } from '~/slot/cleanup';
4
+ import { ondisconnect as disconnect } from '~/slot/cleanup';
7
5
  import onconnect from './onconnect';
8
6
  import onresize from './onresize';
9
7
  import ontick from './ontick';
10
8
 
11
9
 
12
- let capture = new Set<`on${string}`>(['onblur', 'onfocus', 'onscroll']),
13
- controllers = new Map<
14
- `on${string}`,
15
- (AbortController & { listeners: number }) | null
16
- >(),
10
+ let controllers = new Map<string, (AbortController & { listeners: number }) | null>(),
11
+ host = window.document,
17
12
  keys: Record<string, symbol> = {},
18
- passive = new Set<`on${string}`>([
19
- 'onanimationend', 'onanimationiteration', 'onanimationstart',
20
- 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel',
21
- 'onpointerenter', 'onpointerleave', 'onpointermove', 'onpointerout', 'onpointerover',
22
- 'onscroll',
23
- 'ontouchcancel', 'ontouchend', 'ontouchleave', 'ontouchmove', 'ontouchstart', 'ontransitionend',
24
- 'onwheel'
13
+ passive = new Set<string>([
14
+ 'animationend', 'animationiteration', 'animationstart',
15
+ 'mousedown', 'mouseenter', 'mouseleave', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'mousewheel',
16
+ 'pointerenter', 'pointerleave', 'pointermove', 'pointerout', 'pointerover',
17
+ 'scroll',
18
+ 'touchcancel', 'touchend', 'touchleave', 'touchmove', 'touchstart', 'transitionend',
19
+ 'wheel'
25
20
  ]);
26
21
 
27
22
 
28
- (['onmousemove', 'onmousewheel', 'onscroll', 'ontouchend', 'ontouchmove', 'ontouchstart', 'onwheel'] as `on${string}`[]).map(event => {
23
+ (['mousemove', 'mousewheel', 'scroll', 'touchend', 'touchmove', 'touchstart', 'wheel'] as string[]).map(event => {
29
24
  controllers.set(event, null);
30
25
  });
31
26
 
32
27
 
33
- function register(element: Element, event: `on${string}`) {
28
+ function register(element: Element, event: string) {
34
29
  let controller = controllers.get(event),
35
30
  signal: AbortController['signal'] | undefined;
36
31
 
@@ -63,7 +58,7 @@ function register(element: Element, event: `on${string}`) {
63
58
 
64
59
  let key = keys[event] = Symbol();
65
60
 
66
- addEventListener.call(window.document, event.slice(2), (e) => {
61
+ host.addEventListener(event.slice(2), (e) => {
67
62
  let fn,
68
63
  node = e.target as Element | null;
69
64
 
@@ -81,10 +76,9 @@ function register(element: Element, event: `on${string}`) {
81
76
  return fn.call(node, e);
82
77
  }
83
78
 
84
- node = parentElement.call(node);
79
+ node = node.parentElement as Element | null;
85
80
  }
86
81
  }, {
87
- capture: capture.has(event),
88
82
  passive: passive.has(event),
89
83
  signal
90
84
  });
@@ -93,30 +87,30 @@ function register(element: Element, event: `on${string}`) {
93
87
  }
94
88
 
95
89
 
96
- export default (element: Element, event: `on${string}`, listener: Function): void => {
97
- switch (event) {
98
- case 'onconnect':
99
- onconnect(element, listener);
100
- return;
90
+ const delegate = (element: Element, event: string, listener: Function): void => {
91
+ element[ keys[event] || register(element, event) ] = listener;
92
+ };
101
93
 
102
- case 'ondisconnect':
103
- ondisconnect(element, () => listener(element));
104
- return;
94
+ // DIRECT_ATTACH_EVENTS in ./constants.ts tells compiler to use this function
95
+ const direct = (element: Element, event: string, listener: Function): void => {
96
+ let handler = (e: Event) => listener.call(element, e);
105
97
 
106
- case 'onrender':
107
- root(() => listener(element));
108
- return;
98
+ element.addEventListener(event, handler, {
99
+ passive: passive.has(event)
100
+ });
109
101
 
110
- case 'onresize':
111
- onresize(element, listener);
112
- return;
102
+ ondisconnect(element, () => {
103
+ element.removeEventListener(event, handler);
104
+ });
105
+ };
113
106
 
114
- case 'ontick':
115
- ontick(element, listener);
116
- return;
107
+ const ondisconnect = (element: Element, listener: Function) => {
108
+ disconnect(element, () => listener(element));
109
+ };
117
110
 
118
- default:
119
- element[ keys[event] || register(element, event) ] = listener;
120
- return;
121
- }
122
- };
111
+ const onrender = (element: Element, listener: Function) => {
112
+ root(() => listener(element));
113
+ };
114
+
115
+
116
+ export default { delegate, direct, onconnect, ondisconnect, onrender, onresize, ontick };
@@ -18,4 +18,4 @@ export default (element: Element, listener: Function) => {
18
18
  retry = 60;
19
19
 
20
20
  add(fn);
21
- };
21
+ };
@@ -34,4 +34,4 @@ export default (element: Element, listener: Function) => {
34
34
  window.addEventListener('resize', onresize);
35
35
  registered = true;
36
36
  }
37
- };
37
+ };
@@ -1,5 +1,4 @@
1
- import { STATE_HYDRATING, STATE_NONE } from '~/constants';
2
- import raf from '~/utilities/raf';
1
+ import { raf } from '~/utilities';
3
2
 
4
3
 
5
4
  let tasks = Object.assign(new Set<VoidFunction>(), { running: false });
@@ -34,13 +33,11 @@ const remove = (task: VoidFunction) => {
34
33
 
35
34
 
36
35
  export default (element: Element, listener: Function) => {
37
- let dispose = () => {
38
- remove(fn);
39
- },
36
+ let connected = false,
40
37
  fn = () => {
41
- if (state === STATE_HYDRATING) {
38
+ if (connected === false) {
42
39
  if (element.isConnected) {
43
- state = STATE_NONE;
40
+ connected = true;
44
41
  }
45
42
  else if (retry--) {
46
43
  return;
@@ -52,11 +49,10 @@ export default (element: Element, listener: Function) => {
52
49
  return;
53
50
  }
54
51
 
55
- listener(dispose, element);
52
+ listener(() => remove(fn), element);
56
53
  },
57
- retry = 60,
58
- state = STATE_HYDRATING;
54
+ retry = 60;
59
55
 
60
56
  add(fn);
61
57
  };
62
- export { add, remove };
58
+ export { add, remove };
package/src/html.ts ADDED
@@ -0,0 +1,18 @@
1
+ import { ReactiveArray } from '@esportsplus/reactivity';
2
+ import { Attribute, Attributes, Renderable } from '~/types';
3
+ import { ArraySlot } from '~/slot/array';
4
+
5
+
6
+ type Values<T> = Attribute | Attributes<any> | ArraySlot<T> | Renderable<T>;
7
+
8
+
9
+ const html = <T>(_literals: TemplateStringsArray, ..._values: (Values<T> | Values<T>[])[]): DocumentFragment => {
10
+ throw new Error('html`` templates must be compiled. Ensure vite-plugin is configured.');
11
+ };
12
+
13
+ html.reactive = <T>(_arr: ReactiveArray<T>, _template: (value: T) => DocumentFragment): ArraySlot<T> => {
14
+ throw new Error('html.reactive() must be compiled. Ensure vite-plugin is configured.');
15
+ };
16
+
17
+
18
+ export default html;
package/src/index.ts CHANGED
@@ -1,5 +1,11 @@
1
+ import './runtime';
2
+ export { default as attributes } from './attributes';
3
+ export { default as event } from './event';
1
4
  export { default as html } from './html';
2
5
  export { default as render } from './render';
6
+ export { default as slot } from './slot';
3
7
  export { default as svg } from './svg';
4
- export type { ArraySlot } from './slot/array';
5
- export type { Attributes, Element, Renderable } from './types';
8
+ export { ArraySlot } from './slot/array';
9
+ export { EffectSlot } from './slot/effect';
10
+ export type { Attributes, Element, Renderable } from './types';
11
+ export * from './utilities';
package/src/render.ts CHANGED
@@ -1,14 +1,13 @@
1
- import { Renderable } from './types';
2
- import { nodeValue } from './utilities/node';
3
- import marker from './utilities/marker';
1
+ import { Effect, Element, Renderable } from './types';
2
+ import { marker } from './utilities';
4
3
  import slot from './slot';
5
4
 
6
5
 
7
- export default <T>(parent: HTMLElement, renderable: Renderable<T>) => {
8
- let anchor = marker.cloneNode();
6
+ export default <T>(parent: HTMLElement, renderable: Exclude<Renderable<T>, Effect<T>>) => {
7
+ let anchor = marker.cloneNode() as unknown as Element;
9
8
 
10
- nodeValue.call(parent, '');
9
+ parent.nodeValue = '';
11
10
  parent.append(anchor);
12
11
 
13
12
  slot(anchor, renderable);
14
- };
13
+ };
package/src/runtime.ts ADDED
@@ -0,0 +1,8 @@
1
+ import { CLEANUP, STORE } from './constants';
2
+
3
+
4
+ // Pre-allocate on Node prototype to optimize property access
5
+ if (typeof Node !== 'undefined') {
6
+ (Node.prototype as any)[CLEANUP] = null;
7
+ (Node.prototype as any)[STORE] = null;
8
+ }