@esportsplus/template 0.16.0 → 0.16.3

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 (53) hide show
  1. package/.editorconfig +9 -9
  2. package/.gitattributes +2 -2
  3. package/.github/dependabot.yml +24 -24
  4. package/.github/workflows/bump.yml +8 -8
  5. package/.github/workflows/dependabot.yml +11 -11
  6. package/.github/workflows/publish.yml +16 -16
  7. package/README.md +385 -385
  8. package/build/compiler/codegen.js +9 -9
  9. package/build/compiler/index.js +3 -3
  10. package/{src/llm.txt → llm.txt} +403 -403
  11. package/package.json +11 -4
  12. package/src/attributes.ts +312 -312
  13. package/src/compiler/codegen.ts +492 -492
  14. package/src/compiler/constants.ts +24 -24
  15. package/src/compiler/index.ts +87 -87
  16. package/src/compiler/parser.ts +242 -242
  17. package/src/compiler/plugins/tsc.ts +6 -6
  18. package/src/compiler/plugins/vite.ts +10 -10
  19. package/src/compiler/ts-analyzer.ts +89 -89
  20. package/src/compiler/ts-parser.ts +112 -112
  21. package/src/constants.ts +44 -44
  22. package/src/event/index.ts +130 -130
  23. package/src/event/onconnect.ts +22 -22
  24. package/src/event/onresize.ts +37 -37
  25. package/src/event/ontick.ts +59 -59
  26. package/src/html.ts +18 -18
  27. package/src/index.ts +18 -18
  28. package/src/render.ts +13 -13
  29. package/src/slot/array.ts +257 -257
  30. package/src/slot/cleanup.ts +37 -37
  31. package/src/slot/effect.ts +114 -114
  32. package/src/slot/index.ts +16 -16
  33. package/src/slot/render.ts +61 -61
  34. package/src/svg.ts +27 -27
  35. package/src/types.ts +40 -40
  36. package/src/utilities.ts +53 -53
  37. package/test/attributes.test.ts +311 -0
  38. package/test/compiler/parser.test.ts +402 -0
  39. package/test/compiler/ts-analyzer.test.ts +296 -0
  40. package/test/constants.test.ts +153 -0
  41. package/test/event/index.test.ts +359 -0
  42. package/test/html.test.ts +33 -0
  43. package/test/index.ts +648 -648
  44. package/test/render.test.ts +154 -0
  45. package/test/slot/array.test.ts +475 -0
  46. package/test/slot/cleanup.test.ts +243 -0
  47. package/test/slot/effect.test.ts +263 -0
  48. package/test/slot/index.test.ts +176 -0
  49. package/test/slot/render.test.ts +216 -0
  50. package/test/svg.test.ts +92 -0
  51. package/test/utilities.test.ts +242 -0
  52. package/tsconfig.json +8 -8
  53. package/vitest.config.ts +21 -0
package/package.json CHANGED
@@ -3,10 +3,15 @@
3
3
  "dependencies": {
4
4
  "@esportsplus/pipeline": "^1.2.2",
5
5
  "@esportsplus/queue": "^0.2.0",
6
- "@esportsplus/reactivity": "^0.29.19",
7
- "@esportsplus/typescript": "^0.28.2",
6
+ "@esportsplus/reactivity": "^0.29.21",
7
+ "@esportsplus/typescript": "^0.28.3",
8
8
  "@esportsplus/utilities": "^0.27.2"
9
9
  },
10
+ "devDependencies": {
11
+ "@vitest/coverage-v8": "^4.0.17",
12
+ "jsdom": "^27.4.0",
13
+ "vitest": "^4.0.17"
14
+ },
10
15
  "exports": {
11
16
  "./compiler": {
12
17
  "types": "./build/compiler/index.d.ts",
@@ -34,10 +39,12 @@
34
39
  },
35
40
  "type": "module",
36
41
  "types": "./build/index.d.ts",
37
- "version": "0.16.0",
42
+ "version": "0.16.3",
38
43
  "scripts": {
39
44
  "build": "tsc",
40
- "build:test": "vite build --config test/vite.config.ts",
45
+ "test": "vitest run",
46
+ "test:watch": "vitest",
47
+ "test:coverage": "vitest run --coverage",
41
48
  "-": "-"
42
49
  }
43
50
  }
package/src/attributes.ts CHANGED
@@ -1,313 +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
-
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
313
  export { setList, setProperty, setProperties };