spikijs 1.0.7 → 1.0.8

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "spikijs",
3
3
  "license": "MIT",
4
- "version": "1.0.7",
4
+ "version": "1.0.8",
5
5
  "type": "module",
6
6
  "repository": {
7
7
  "type": "git",
package/spiki.esm.js CHANGED
@@ -1,24 +1,27 @@
1
1
  var spiki = (() => {
2
- // --- State & Storage ---
2
+ // =========================================================================
3
+ // 1. STATE & STORAGE
4
+ // =========================================================================
3
5
  var componentRegistry = Object.create(null);
4
6
  var eventMetadataMap = new WeakMap();
5
- var dependencyMap = new WeakMap();
6
- var proxyCache = new WeakMap();
7
- var pathSplitCache = new Map();
8
- var schedulerQueue = new Set();
7
+ var dependencyMap = new WeakMap(); // Stores dependencies: Target -> Key -> Set<Effect>
8
+ var proxyCache = new WeakMap(); // Prevents double-wrapping objects
9
+ var pathSplitCache = new Map(); // Caches "user.name" -> ["user", "name"]
10
+ var schedulerQueue = new Set(); // Async task queue
9
11
 
10
- var loopRegex = /^\s*(.*?)\s+in\s+(.+)\s*$/;
12
+ var loopRegex = /^\s*(.*?)\s+in\s+(.+)\s*$/; // Regex for "item in items"
11
13
 
12
- // --- Global State Variables ---
14
+ // Global flags
13
15
  var currentActiveEffect;
14
16
  var isFlushingQueue;
15
17
  var globalStore;
16
18
  var shouldTriggerEffects = true;
17
19
  var resolvedPromise = Promise.resolve();
18
20
 
19
- // -------------------------------------------------------------------------
20
- // 1. Array Interceptors
21
- // -------------------------------------------------------------------------
21
+ // =========================================================================
22
+ // 2. ARRAY INSTRUMENTATION
23
+ // Intercepts array mutation methods to trigger reactivity
24
+ // =========================================================================
22
25
  var arrayInstrumentations = {};
23
26
  ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(methodName => {
24
27
  arrayInstrumentations[methodName] = function () {
@@ -38,10 +41,12 @@ var spiki = (() => {
38
41
  };
39
42
  });
40
43
 
41
- // -------------------------------------------------------------------------
42
- // 2. Helper Functions
43
- // -------------------------------------------------------------------------
44
+ // =========================================================================
45
+ // 3. HELPER FUNCTIONS
46
+ // =========================================================================
44
47
 
48
+ // Creates a child scope that inherits from parent.
49
+ // Writes bubble up to the object that actually owns the property.
45
50
  var createScope = (parentScope) => {
46
51
  var proto = Object.create(parentScope);
47
52
  return new Proxy(proto, {
@@ -60,6 +65,7 @@ var spiki = (() => {
60
65
  });
61
66
  };
62
67
 
68
+ // Resolves "user.name" from the scope
63
69
  var evaluatePath = (scope, path) => {
64
70
  if (typeof path !== 'string') return { value: path, context: scope };
65
71
 
@@ -88,6 +94,7 @@ var spiki = (() => {
88
94
  return { value: currentValue, context: currentContext };
89
95
  };
90
96
 
97
+ // Microtask Scheduler
91
98
  var nextTick = (fn) => {
92
99
  return !schedulerQueue.has(fn) &&
93
100
  schedulerQueue.add(fn) &&
@@ -100,9 +107,9 @@ var spiki = (() => {
100
107
  });
101
108
  };
102
109
 
103
- // -------------------------------------------------------------------------
104
- // 3. Reactivity System
105
- // -------------------------------------------------------------------------
110
+ // =========================================================================
111
+ // 4. REACTIVITY SYSTEM
112
+ // =========================================================================
106
113
 
107
114
  var trackDependency = (target, key) => {
108
115
  if (!currentActiveEffect) return;
@@ -134,6 +141,7 @@ var spiki = (() => {
134
141
 
135
142
  var createEffect = (fn, scheduler) => {
136
143
  var runner = () => {
144
+ // Clean up old dependencies before re-running
137
145
  runner.dependencies.forEach(depSet => depSet.delete(runner));
138
146
  runner.dependencies.clear();
139
147
 
@@ -148,7 +156,7 @@ var spiki = (() => {
148
156
 
149
157
  runner.dependencies = new Set();
150
158
  runner.scheduler = scheduler;
151
- runner();
159
+ runner(); // Run immediately
152
160
 
153
161
  return () => {
154
162
  runner.dependencies.forEach(depSet => depSet.delete(runner));
@@ -175,6 +183,7 @@ var spiki = (() => {
175
183
  trackDependency(target, key);
176
184
 
177
185
  var result = Reflect.get(target, key, receiver);
186
+ // Deep reactivity
178
187
  return result && typeof result === 'object' && !(result instanceof Node)
179
188
  ? makeReactive(result)
180
189
  : result;
@@ -214,9 +223,9 @@ var spiki = (() => {
214
223
 
215
224
  globalStore = makeReactive({});
216
225
 
217
- // -------------------------------------------------------------------------
218
- // 4. DOM Operations
219
- // -------------------------------------------------------------------------
226
+ // =========================================================================
227
+ // 5. DOM OPERATIONS
228
+ // =========================================================================
220
229
  var domOperations = {
221
230
  text: (el, value) => {
222
231
  el.textContent = (value !== null && value !== undefined) ? value : '';
@@ -255,9 +264,9 @@ var spiki = (() => {
255
264
  destroy: () => { }
256
265
  };
257
266
 
258
- // -------------------------------------------------------------------------
259
- // 5. Engine / Mounting Logic
260
- // -------------------------------------------------------------------------
267
+ // =========================================================================
268
+ // 6. MOUNTING & ENGINE
269
+ // =========================================================================
261
270
  var mountComponent = (rootElement) => {
262
271
  if (rootElement.__isMounted) return;
263
272
  rootElement.__isMounted = 1;
@@ -273,28 +282,34 @@ var spiki = (() => {
273
282
 
274
283
  var cleanupCallbacks = [];
275
284
 
285
+ // --- Global Event Delegation Handler ---
276
286
  var handleEvent = (e) => {
277
287
  var target = e.target;
278
288
 
289
+ // Handle s-model updates
279
290
  if (target.__modelPath && (e.type === 'input' || e.type === 'change')) {
280
291
  var path = target.__modelPath;
292
+ // Use tagged scope if available, else root state
293
+ var scope = target.__scope || state;
281
294
  var value = target.type === 'checkbox' ? target.checked : target.value;
282
- var evaluation = evaluatePath(target.__scope || state, path);
295
+ var evaluation = evaluatePath(scope, path);
283
296
  var parentObject = evaluation.context;
284
297
 
285
298
  if (path.indexOf('.') === -1) {
286
- (target.__scope || state)[path] = value;
299
+ scope[path] = value;
287
300
  } else if (parentObject) {
288
301
  var parts = path.split('.');
289
302
  parentObject[parts[parts.length - 1]] = value;
290
303
  }
291
304
  }
292
305
 
306
+ // Handle standard events (s-click, etc.)
293
307
  var handlerName;
294
308
  while (target && target !== rootElement.parentNode) {
295
309
  var meta = eventMetadataMap.get(target);
296
310
  if (meta && (handlerName = meta[e.type])) {
297
- var evalResult = evaluatePath(target.__scope || state, handlerName);
311
+ var targetScope = target.__scope || state;
312
+ var evalResult = evaluatePath(targetScope, handlerName);
298
313
  var handlerFn = evalResult.value;
299
314
  var handlerContext = evalResult.context;
300
315
 
@@ -306,17 +321,21 @@ var spiki = (() => {
306
321
  }
307
322
  };
308
323
 
324
+ // --- Core Recursive Walker ---
309
325
  var walkDOM = (el, currentScope, cleanupList) => {
326
+ // 1. Fast Reject: Text nodes, Comments, or s-ignore
310
327
  if (el.nodeType !== 1 || el.hasAttribute('s-ignore')) return;
311
328
 
329
+ // 2. Component Boundary Check
312
330
  if (el !== rootElement && el.hasAttribute('s-data')) {
313
331
  var childComponent = mountComponent(el);
314
332
  if (childComponent) cleanupList.push(childComponent.unmount);
315
- return;
333
+ return; // Stop recursion here, child handles itself
316
334
  }
317
335
 
318
336
  var attributeValue;
319
337
 
338
+ // 3. Structural Directive: s-if
320
339
  if ((attributeValue = el.getAttribute('s-if'))) {
321
340
  var anchor = document.createTextNode('');
322
341
  var branchCleanups = [];
@@ -338,6 +357,7 @@ var spiki = (() => {
338
357
  if (!activeNode) {
339
358
  activeNode = el.cloneNode(true);
340
359
  activeNode.removeAttribute('s-if');
360
+ // Recurse into the new branch
341
361
  walkDOM(activeNode, currentScope, branchCleanups);
342
362
  anchor.parentNode.insertBefore(activeNode, anchor);
343
363
  }
@@ -350,6 +370,7 @@ var spiki = (() => {
350
370
  }, nextTick));
351
371
  }
352
372
 
373
+ // 4. Structural Directive: s-for
353
374
  if (el.tagName === 'TEMPLATE' && (attributeValue = el.getAttribute('s-for'))) {
354
375
  var match = attributeValue.match(loopRegex);
355
376
  if (!match) return;
@@ -416,6 +437,7 @@ var spiki = (() => {
416
437
  while (childNode) {
417
438
  rowNodes.push(childNode);
418
439
  var nextSibling = childNode.nextSibling;
440
+ // Recursive walk for new DOM nodes
419
441
  walkDOM(childNode, rowScope, rowCleanups);
420
442
  childNode = nextSibling;
421
443
  }
@@ -444,82 +466,97 @@ var spiki = (() => {
444
466
  }, nextTick));
445
467
  }
446
468
 
469
+ // 5. Attribute Binding & Interactivity Check
447
470
  var attributes = el.attributes;
448
- for (var i = attributes.length - 1; i >= 0; i--) {
449
- ((attr) => {
450
- var name = attr.name;
451
- var value = attr.value;
452
-
453
- if (name[0] === ':') {
454
- cleanupList.push(createEffect(() => {
455
- var evaluation = evaluatePath(currentScope, value);
456
- var result = evaluation.value;
457
- var context = evaluation.context;
458
- var opName = name.slice(1) === 'class' ? 'class' : 'attr';
459
-
460
- domOperations[opName](
461
- el,
462
- typeof result === 'function' ? result.call(context, el) : result,
463
- name.slice(1)
464
- );
465
- }, nextTick));
466
-
467
- } else if (name[0] === 's' && name[1] === '-') {
468
- var directiveType = name.slice(2);
471
+ var isInteractive = false;
472
+
473
+ if (attributes && attributes.length > 0) {
474
+ // Reverse loop for safety
475
+ for (var i = attributes.length - 1; i >= 0; i--) {
476
+ ((attr) => {
477
+ var name = attr.name;
478
+ var value = attr.value;
469
479
 
470
- if (directiveType === 'ref') {
471
- state.$refs[value] = el;
472
- } else if (directiveType === 'model') {
473
- cleanupList.push(createEffect(() => {
474
- var evaluation = evaluatePath(currentScope, value);
475
- domOperations.value(el, evaluation.value);
476
- }, nextTick));
477
-
478
- if (/^(INPUT|SELECT|TEXTAREA)$/.test(el.tagName)) {
479
- el.__scope = currentScope;
480
- el.__modelPath = value;
481
- var eventType = (el.type === 'checkbox' || el.type === 'radio' || el.tagName === 'SELECT')
482
- ? 'change'
483
- : 'input';
484
-
485
- if (!rootElement.__listeningEvents) rootElement.__listeningEvents = new Set();
486
- if (!rootElement.__listeningEvents.has(eventType)) {
487
- rootElement.__listeningEvents.add(eventType);
488
- rootElement.addEventListener(eventType, handleEvent);
489
- }
490
- }
491
- } else if (domOperations[directiveType]) {
480
+ if (name[0] === ':') {
492
481
  cleanupList.push(createEffect(() => {
493
482
  var evaluation = evaluatePath(currentScope, value);
494
483
  var result = evaluation.value;
495
484
  var context = evaluation.context;
496
- domOperations[directiveType](
485
+ var opName = name.slice(1) === 'class' ? 'class' : 'attr';
486
+
487
+ domOperations[opName](
497
488
  el,
498
- typeof result === 'function' ? result.call(context, el) : result
489
+ typeof result === 'function' ? result.call(context, el) : result,
490
+ name.slice(1)
499
491
  );
500
492
  }, nextTick));
501
- } else {
502
- el.__scope = currentScope;
503
- var meta = eventMetadataMap.get(el);
504
- if (!meta) {
505
- meta = {};
506
- eventMetadataMap.set(el, meta);
507
- }
508
- meta[directiveType] = value;
509
493
 
510
- if (!rootElement.__listeningEvents) rootElement.__listeningEvents = new Set();
511
- if (!rootElement.__listeningEvents.has(directiveType)) {
512
- rootElement.__listeningEvents.add(directiveType);
513
- rootElement.addEventListener(directiveType, handleEvent);
494
+ } else if (name[0] === 's' && name[1] === '-') {
495
+ var directiveType = name.slice(2);
496
+
497
+ if (directiveType === 'ref') {
498
+ state.$refs[value] = el;
499
+ } else if (directiveType === 'model') {
500
+ isInteractive = true; // Flag for scope attachment
501
+ cleanupList.push(createEffect(() => {
502
+ var evaluation = evaluatePath(currentScope, value);
503
+ domOperations.value(el, evaluation.value);
504
+ }, nextTick));
505
+
506
+ if (/^(INPUT|SELECT|TEXTAREA)$/.test(el.tagName)) {
507
+ el.__modelPath = value;
508
+ var eventType = (el.type === 'checkbox' || el.type === 'radio' || el.tagName === 'SELECT')
509
+ ? 'change'
510
+ : 'input';
511
+
512
+ if (!rootElement.__listeningEvents) rootElement.__listeningEvents = new Set();
513
+ if (!rootElement.__listeningEvents.has(eventType)) {
514
+ rootElement.__listeningEvents.add(eventType);
515
+ rootElement.addEventListener(eventType, handleEvent);
516
+ }
517
+ }
518
+ } else if (domOperations[directiveType]) {
519
+ cleanupList.push(createEffect(() => {
520
+ var evaluation = evaluatePath(currentScope, value);
521
+ var result = evaluation.value;
522
+ var context = evaluation.context;
523
+ domOperations[directiveType](
524
+ el,
525
+ typeof result === 'function' ? result.call(context, el) : result
526
+ );
527
+ }, nextTick));
528
+ } else {
529
+ // Event Handlers
530
+ isInteractive = true; // Flag for scope attachment
531
+ var meta = eventMetadataMap.get(el);
532
+ if (!meta) {
533
+ meta = {};
534
+ eventMetadataMap.set(el, meta);
535
+ }
536
+ meta[directiveType] = value;
537
+
538
+ if (!rootElement.__listeningEvents) rootElement.__listeningEvents = new Set();
539
+ if (!rootElement.__listeningEvents.has(directiveType)) {
540
+ rootElement.__listeningEvents.add(directiveType);
541
+ rootElement.addEventListener(directiveType, handleEvent);
542
+ }
514
543
  }
515
544
  }
516
- }
517
- })(attributes[i]);
545
+ })(attributes[i]);
546
+ }
547
+ }
548
+
549
+ // 6. Selective Scope Attachment
550
+ // Only attach scope to DOM elements that actually need it for events/models.
551
+ // This saves memory on static elements.
552
+ if (isInteractive) {
553
+ el.__scope = currentScope;
518
554
  }
519
555
 
520
- var child = el.firstElementChild;
556
+ // 7. Recursive Traversal (Optimized Native Loop)
557
+ var child = el.firstChild;
521
558
  while (child) {
522
- var next = child.nextElementSibling;
559
+ var next = child.nextSibling;
523
560
  walkDOM(child, currentScope, cleanupList);
524
561
  child = next;
525
562
  }
@@ -542,6 +579,9 @@ var spiki = (() => {
542
579
  };
543
580
  };
544
581
 
582
+ // =========================================================================
583
+ // 7. PUBLIC API
584
+ // =========================================================================
545
585
  return {
546
586
  data: (name, factoryFn) => {
547
587
  componentRegistry[name] = factoryFn;
package/spiki.esm.min.js CHANGED
@@ -1 +1 @@
1
- var spiki=(()=>{var n,r,E=Object,A=Array,P=Reflect,e=Promise,T=Map,k=Set,t=WeakMap,a=Node,L=document,N="length",C="value",S="push",$="forEach",l="indexOf",j="split",M="slice",O="call",R="get",W="set",q="add",z="delete",B="has",s="clear",D="prototype",F="hasOwnProperty",H="create",I="isArray",U="getAttribute",X="removeAttribute",G="hasAttribute",J="tagName",K="type",Q="name",u="checked",V="parentNode",Y="nextSibling",Z="replaceWith",ee="insertBefore",re="cloneNode",te="remove",ne="createTextNode",ae="addEventListener",i="dependencies",ie="cleanups",oe="nodes",se="context",o="scheduler",f=E[H](null),le=new t,v=new t,c=new t,p=new T,d=new k,ue=/^\s*(.*?)\s+in\s+(.+)\s*$/,y=!0,m=e.resolve(),_={};[S,"pop","shift","unshift","splice","sort","reverse"][$](t=>{_[t]=function(){for(var e=[],r=0;r<arguments[N];r++)e[r]=arguments[r];y=!1;try{return A[D][t].apply(this,e)}finally{y=!0,h(this,N)}}});var fe=(e,r)=>{if("string"!=typeof r)return{value:r,context:e};if(-1===r[l]("."))return{value:e?e[r]:void 0,context:e};var t=p[R](r);t||(1e3<p.size&&p[s](),t=r[j]("."),p[W](r,t));for(var n=e,a=e,i=0;i<t[N];i++){var o=t[i];if(null==n)return{value:n,context:null};n=(a=n)[o]}return{value:n,context:a}},ve=e=>!d[B](e)&&d[q](e)&&!r&&(r=!0)&&m.then(()=>{d[$](e=>e()),d[s](),r=!1}),ce=(e,r)=>{var t;n&&((t=v[R](e))||(t=new T,v[W](e,t)),(e=t[R](r))||(e=new k,t[W](r,e)),e[q](n),n[i][q](e))},h=(e,r)=>{var t,n;y&&(t=v[R](e))&&(n=t[R](r))&&n[$](e=>{e[o]?e[o](e):e()})},pe=(r,e)=>{var t=()=>{t[i][$](e=>e[z](t)),t[i][s]();var e=n;n=t;try{r()}finally{n=e}};return t[i]=new k,t[o]=e,t(),()=>{t[i][$](e=>e[z](t)),t[i][s](),d[z](t)}},g=e=>{if(!e||"object"!=typeof e||e._ip||e instanceof a)return e;var r=c[R](e);if(r)return r;r=new Proxy(e,{get:(e,r,t)=>{if("_r"===r)return e;if("_ip"===r)return!0;if(A[I](e)&&_[F](r))return _[r];ce(e,r);t=P[R](e,r,t);return!t||"object"!=typeof t||t instanceof a?t:g(t)},set:(e,r,t,n)=>{var a=e[r],i=A[I](e)?Number(r)<e[N]:E[D][F][O](e,r),n=P[W](e,r,t,n);return y&&(i?a!==t&&h(e,r):(h(e,r),A[I](e)&&h(e,N))),n},deleteProperty:(e,r)=>{var t=E[D][F][O](e,r),n=P.deleteProperty(e,r);return n&&t&&(h(e,r),A[I](e)&&h(e,N)),n}});return c[W](e,r),r},de=g({}),ye={text:(e,r)=>{e.textContent=null!=r?r:""},html:(e,r)=>{e.innerHTML=null!=r?r:""},value:(e,r)=>{"checkbox"===e[K]?e[u]=!!r:"radio"===e[K]&&e[Q]?e[u]=e[C]==r:e[C]!=r&&(e[C]=null!=r?r:"")},attr:(e,r,t)=>{null==r||!1===r?e[X](t):e.setAttribute(t,!0===r?"":r)},class:(t,e)=>{"string"==typeof e&&e[j](/\s+/)[$](e=>{var r;e&&(e=(r="!"===e[0])?e[M](1):e,t.classList[r?te:q](e))})},init:()=>{},destroy:()=>{}},me=p=>{if(!p._im){p._im=1;var e=p[U]("s-data"),e=f[e];if(e){var b=g(e());b.$refs={},b.$root=p,b.$store=de;var r=[],w=e=>{var r,t,n,a,i=e.target;for(!i._m||"input"!==e[K]&&"change"!==e[K]||(n=i._m,r="checkbox"===i[K]?i[u]:i[C],t=fe(i._s||b,n)[se],-1===n[l](".")?(i._s||b)[n]=r:t&&(t[(n=n[j]("."))[n[N]-1]]=r));i&&i!==p[V];){var o,s=le[R](i);s&&(a=s[e[K]])&&(s=(o=fe(i._s||b,a))[C],o=o[se],"function"==typeof s&&s[O](o,e)),i=i[V]}},x=(d,y,i)=>{if(1===d.nodeType&&!d[G]("s-ignore"))if(d!==p&&d[G]("s-data")){var e=me(d);e&&i[S](e.unmount)}else{var t;if(t=d[U]("s-if")){var n,a=L[ne](""),o=[];return d[Z](a),i[S](()=>{o[$](e=>e())}),i[S](pe(()=>{var e=fe(y,t),r=e[C],e=e[se];("function"==typeof r?r[O](e,d):r)?n||((n=d[re](!0))[X]("s-if"),x(n,y,o),a[V][ee](n,a)):n&&(o[$](e=>e()),o[N]=0,n[te](),n=null)},ve))}if("TEMPLATE"===d[J]&&(t=d[U]("s-for"))){var r=t.match(ue);if(!r)return;var e=r[1].replace(/[()]/g,""),s=r[2],e=e[j](","),m=e[0].trim(),_=e[1]?e[1].trim():null,h=d[U]("s-key"),l=L[ne]("");d[Z](l);var g=new T;return i[S](()=>{g[$](e=>{e[ie][$](e=>e())})}),i[S](pe(()=>{var v=fe(y,s)[C];A[I](v)&&ce(v,N);var c=l,e=A[I](v)?v:v?E.keys(v):[],p=new T;e[$]((e,r)=>{var t,n=A[I](v)?r:e,a=A[I](v)?e:v[e],i=h&&a?a[h]:"object"==typeof a&&a?a:n+"_"+a,r=g[R](i),e=e=>{E.defineProperty(e,m,{configurable:!0,enumerable:!0,get:()=>v[n],set:e=>{v[n]=e}})};if(r)e(r.scope),_&&(r.scope[_]=n);else{var a=d.content[re](!0),o=(e=>{e=E[H](e);return new Proxy(e,{set:(e,r,t,n)=>{if(e[F](r))return P[W](e,r,t,n);for(var a=e;a&&!E[D][F][O](a,r);)a=E.getPrototypeOf(a);return P[W](a||e,r,t)}})})(y),s=[];e(o),_&&(o[_]=n);for(var l=[],u=a.firstChild;u;){l[S](u);var f=u[Y];x(u,o,s),u=f}r={nodes:l,scope:o,cleanups:s}}r[oe][0]!==c[Y]&&(t=L.createDocumentFragment(),r[oe][$](e=>t.appendChild(e)),c[V][ee](t,c[Y])),c=r[oe][r[oe][N]-1],p[W](i,r),g[z](i)}),g[$](e=>{e[ie][$](e=>e()),e[oe][$](e=>e[te]())}),g=p},ve))}for(var u=d.attributes,f=u[N]-1;0<=f;f--)(e=>{var t,r,n=e[Q],a=e[C];":"===n[0]?i[S](pe(()=>{var e=fe(y,a),r=e[C],t=e[se],e="class"===n[M](1)?"class":"attr";ye[e](d,"function"==typeof r?r[O](t,d):r,n[M](1))},ve)):"s"===n[0]&&"-"===n[1]&&("ref"===(t=n[M](2))?b.$refs[a]=d:"model"===t?(i[S](pe(()=>{var e=fe(y,a);ye[C](d,e[C])},ve)),/^(INPUT|SELECT|TEXTAREA)$/.test(d[J])&&(d._s=y,d._m=a,r="checkbox"===d[K]||"radio"===d[K]||"SELECT"===d[J]?"change":"input",p._le||(p._le=new k),p._le[B](r)||(p._le[q](r),p[ae](r,w)))):ye[t]?i[S](pe(()=>{var e=fe(y,a),r=e[C],e=e[se];ye[t](d,"function"==typeof r?r[O](e,d):r)},ve)):(d._s=y,(r=le[R](d))||(r={},le[W](d,r)),r[t]=a,p._le||(p._le=new k),p._le[B](t)||(p._le[q](t),p[ae](t,w))))})(u[f]);for(var v=d.firstElementChild;v;){var c=v.nextElementSibling;x(v,y,i),v=c}}};return x(p,b,r),b.init&&b.init(),{unmount:()=>{b.destroy&&b.destroy[O](b),r[$](e=>e()),p._le&&p._le[$](e=>{p.removeEventListener(e,w)}),p._im=0}}}}};return{data:(e,r)=>{f[e]=r},start:()=>{for(var e=L.querySelectorAll("[s-data]"),r=0;r<e[N];r++)me(e[r])},store:(e,r)=>void 0===r?de[e]:de[e]=r,raw:e=>e&&e._r||e}})();export default spiki;
1
+ var spiki=(()=>{var n,r,A=Object,P=Array,T=Reflect,e=Promise,L=Map,N=Set,t=WeakMap,a=Node,k=document,$="length",C="value",S="push",M="forEach",u="indexOf",O="split",j="slice",R="call",W="get",q="set",z="add",B="delete",D="has",i="clear",F="prototype",H="hasOwnProperty",I="create",U="isArray",X="getAttribute",G="removeAttribute",J="hasAttribute",K="tagName",Q="type",V="name",v="checked",Y="parentNode",Z="nextSibling",ee="firstChild",re="replaceWith",te="insertBefore",ne="cloneNode",ae="remove",oe="createTextNode",se="addEventListener",ie="checkbox",le="function",ue="object",o="dependencies",ve="cleanups",fe="nodes",ce="context",s="scheduler",l=A[I](null),pe=new t,f=new t,c=new t,p=new L,d=new N,de=/^\s*(.*?)\s+in\s+(.+)\s*$/,y=!0,_=e.resolve(),h={};[S,"pop","shift","unshift","splice","sort","reverse"][M](t=>{h[t]=function(){for(var e=[],r=0;r<arguments[$];r++)e[r]=arguments[r];y=!1;try{return P[F][t].apply(this,e)}finally{y=!0,m(this,$)}}});var ye=(e,r)=>{if("string"!=typeof r)return{value:r,context:e};if(-1===r[u]("."))return{value:e?e[r]:void 0,context:e};var t=p[W](r);t||(1e3<p.size&&p[i](),t=r[O]("."),p[q](r,t));for(var n=e,a=e,o=0;o<t[$];o++){var s=t[o];if(null==n)return{value:n,context:null};n=(a=n)[s]}return{value:n,context:a}},_e=e=>!d[D](e)&&d[z](e)&&!r&&(r=!0)&&_.then(()=>{d[M](e=>e()),d[i](),r=!1}),he=(e,r)=>{var t;n&&((t=f[W](e))||(t=new L,f[q](e,t)),(e=t[W](r))||(e=new N,t[q](r,e)),e[z](n),n[o][z](e))},m=(e,r)=>{var t,n;y&&(t=f[W](e))&&(n=t[W](r))&&n[M](e=>{e[s]?e[s](e):e()})},me=(r,e)=>{var t=()=>{t[o][M](e=>e[B](t)),t[o][i]();var e=n;n=t;try{r()}finally{n=e}};return t[o]=new N,t[s]=e,t(),()=>{t[o][M](e=>e[B](t)),t[o][i](),d[B](t)}},g=e=>{if(!e||typeof e!=ue||e._p||e instanceof a)return e;var r=c[W](e);if(r)return r;r=new Proxy(e,{get:(e,r,t)=>{if("_r"===r)return e;if("_p"===r)return!0;if(P[U](e)&&h[H](r))return h[r];he(e,r);t=T[W](e,r,t);return!t||typeof t!=ue||t instanceof a?t:g(t)},set:(e,r,t,n)=>{var a=e[r],o=P[U](e)?Number(r)<e[$]:A[F][H][R](e,r),n=T[q](e,r,t,n);return y&&(o?a!==t&&m(e,r):(m(e,r),P[U](e)&&m(e,$))),n},deleteProperty:(e,r)=>{var t=A[F][H][R](e,r),n=T.deleteProperty(e,r);return n&&t&&(m(e,r),P[U](e)&&m(e,$)),n}});return c[q](e,r),r},ge=g({}),we={text:(e,r)=>{e.textContent=null!=r?r:""},html:(e,r)=>{e.innerHTML=null!=r?r:""},value:(e,r)=>{e[Q]===ie?e[v]=!!r:"radio"===e[Q]&&e[V]?e[v]=e[C]==r:e[C]!=r&&(e[C]=null!=r?r:"")},attr:(e,r,t)=>{null==r||!1===r?e[G](t):e.setAttribute(t,!0===r?"":r)},class:(t,e)=>{"string"==typeof e&&e[O](/\s+/)[M](e=>{var r;e&&(e=(r="!"===e[0])?e[j](1):e,t.classList[r?ae:z](e))})},init:()=>{},destroy:()=>{}},xe=w=>{if(!w._m){w._m=1;var e=w[X]("s-data"),e=l[e];if(e){var x=g(e());x.$refs={},x.$root=w,x.$store=ge;var r=[],b=e=>{var r,t,n,a,o,s=e.target;for(!s._t||"input"!==e[Q]&&"change"!==e[Q]||(a=s._t,r=s._s||x,t=s[Q]===ie?s[v]:s[C],n=ye(r,a)[ce],-1===a[u](".")?r[a]=t:n&&(n[(a=a[O]("."))[a[$]-1]]=t));s&&s!==w[Y];){var i,l=pe[W](s);l&&(o=l[e[Q]])&&(i=s._s||x,i=(l=ye(i,o))[C],l=l[ce],typeof i==le&&i[R](l,e)),s=s[Y]}},E=(d,y,o)=>{if(1===d.nodeType&&!d[J]("s-ignore"))if(d!==w&&d[J]("s-data")){var e=xe(d);e&&o[S](e.unmount)}else{var t;if(t=d[X]("s-if")){var n,a=k[oe](""),s=[];return d[re](a),o[S](()=>{s[M](e=>e())}),o[S](me(()=>{var e=ye(y,t),r=e[C],e=e[ce];(typeof r==le?r[R](e,d):r)?n||((n=d[ne](!0))[G]("s-if"),E(n,y,s),a[Y][te](n,a)):n&&(s[M](e=>e()),s[$]=0,n[ae](),n=null)},_e))}if("TEMPLATE"===d[K]&&(t=d[X]("s-for"))){var r=t.match(de);if(!r)return;var e=r[1].replace(/[()]/g,""),i=r[2],e=e[O](","),_=e[0].trim(),h=e[1]?e[1].trim():null,m=d[X]("s-key"),l=k[oe]("");d[re](l);var g=new L;return o[S](()=>{g[M](e=>{e[ve][M](e=>e())})}),o[S](me(()=>{var f=ye(y,i)[C];P[U](f)&&he(f,$);var c=l,e=P[U](f)?f:f?A.keys(f):[],p=new L;e[M]((e,r)=>{var t,n=P[U](f)?r:e,a=P[U](f)?e:f[e],o=m&&a?a[m]:typeof a==ue&&a?a:n+"_"+a,r=g[W](o),e=e=>{A.defineProperty(e,_,{configurable:!0,enumerable:!0,get:()=>f[n],set:e=>{f[n]=e}})};if(r)e(r.scope),h&&(r.scope[h]=n);else{var a=d.content[ne](!0),s=(e=>{e=A[I](e);return new Proxy(e,{set:(e,r,t,n)=>{if(e[H](r))return T[q](e,r,t,n);for(var a=e;a&&!A[F][H][R](a,r);)a=A.getPrototypeOf(a);return T[q](a||e,r,t)}})})(y),i=[];e(s),h&&(s[h]=n);for(var l=[],u=a[ee];u;){l[S](u);var v=u[Z];E(u,s,i),u=v}r={nodes:l,scope:s,cleanups:i}}r[fe][0]!==c[Z]&&(t=k.createDocumentFragment(),r[fe][M](e=>t.appendChild(e)),c[Y][te](t,c[Z])),c=r[fe][r[fe][$]-1],p[q](o,r),g[B](o)}),g[M](e=>{e[ve][M](e=>e()),e[fe][M](e=>e[ae]())}),g=p},_e))}var u=d.attributes,v=!1;if(u&&0<u[$])for(var f=u[$]-1;0<=f;f--)(e=>{var t,r,n=e[V],a=e[C];":"===n[0]?o[S](me(()=>{var e=ye(y,a),r=e[C],t=e[ce],e="class"===n[j](1)?"class":"attr";we[e](d,typeof r==le?r[R](t,d):r,n[j](1))},_e)):"s"===n[0]&&"-"===n[1]&&("ref"===(t=n[j](2))?x.$refs[a]=d:"model"===t?(v=!0,o[S](me(()=>{var e=ye(y,a);we[C](d,e[C])},_e)),/^(INPUT|SELECT|TEXTAREA)$/.test(d[K])&&(d._t=a,r=d[Q]===ie||"radio"===d[Q]||"SELECT"===d[K]?"change":"input",w._e||(w._e=new N),w._e[D](r)||(w._e[z](r),w[se](r,b)))):we[t]?o[S](me(()=>{var e=ye(y,a),r=e[C],e=e[ce];we[t](d,typeof r==le?r[R](e,d):r)},_e)):(v=!0,(r=pe[W](d))||(r={},pe[q](d,r)),r[t]=a,w._e||(w._e=new N),w._e[D](t)||(w._e[z](t),w[se](t,b))))})(u[f]);v&&(d._s=y);for(var c=d[ee];c;){var p=c[Z];E(c,y,o),c=p}}};return E(w,x,r),x.init&&x.init(),{unmount:()=>{x.destroy&&x.destroy[R](x),r[M](e=>e()),w._e&&w._e[M](e=>{w.removeEventListener(e,b)}),w._m=0}}}}};return{data:(e,r)=>{l[e]=r},start:()=>{for(var e=k.querySelectorAll("[s-data]"),r=0;r<e[$];r++)xe(e[r])},store:(e,r)=>void 0===r?ge[e]:ge[e]=r,raw:e=>e&&e._r||e}})();export default spiki;
package/spiki.js CHANGED
@@ -2,26 +2,29 @@
2
2
  "use strict";
3
3
 
4
4
  var spiki = (() => {
5
- // --- State & Storage ---
5
+ // =========================================================================
6
+ // 1. STATE & STORAGE
7
+ // =========================================================================
6
8
  var componentRegistry = Object.create(null);
7
9
  var eventMetadataMap = new WeakMap();
8
- var dependencyMap = new WeakMap();
9
- var proxyCache = new WeakMap();
10
- var pathSplitCache = new Map();
11
- var schedulerQueue = new Set();
10
+ var dependencyMap = new WeakMap(); // Stores dependencies: Target -> Key -> Set<Effect>
11
+ var proxyCache = new WeakMap(); // Prevents double-wrapping objects
12
+ var pathSplitCache = new Map(); // Caches "user.name" -> ["user", "name"]
13
+ var schedulerQueue = new Set(); // Async task queue
12
14
 
13
- var loopRegex = /^\s*(.*?)\s+in\s+(.+)\s*$/;
15
+ var loopRegex = /^\s*(.*?)\s+in\s+(.+)\s*$/; // Regex for "item in items"
14
16
 
15
- // --- Global State Variables ---
17
+ // Global flags
16
18
  var currentActiveEffect;
17
19
  var isFlushingQueue;
18
20
  var globalStore;
19
21
  var shouldTriggerEffects = true;
20
22
  var resolvedPromise = Promise.resolve();
21
23
 
22
- // -------------------------------------------------------------------------
23
- // 1. Array Interceptors
24
- // -------------------------------------------------------------------------
24
+ // =========================================================================
25
+ // 2. ARRAY INSTRUMENTATION
26
+ // Intercepts array mutation methods to trigger reactivity
27
+ // =========================================================================
25
28
  var arrayInstrumentations = {};
26
29
  ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(methodName => {
27
30
  arrayInstrumentations[methodName] = function () {
@@ -41,10 +44,12 @@ var spiki = (() => {
41
44
  };
42
45
  });
43
46
 
44
- // -------------------------------------------------------------------------
45
- // 2. Helper Functions
46
- // -------------------------------------------------------------------------
47
+ // =========================================================================
48
+ // 3. HELPER FUNCTIONS
49
+ // =========================================================================
47
50
 
51
+ // Creates a child scope that inherits from parent.
52
+ // Writes bubble up to the object that actually owns the property.
48
53
  var createScope = (parentScope) => {
49
54
  var proto = Object.create(parentScope);
50
55
  return new Proxy(proto, {
@@ -63,6 +68,7 @@ var spiki = (() => {
63
68
  });
64
69
  };
65
70
 
71
+ // Resolves "user.name" from the scope
66
72
  var evaluatePath = (scope, path) => {
67
73
  if (typeof path !== 'string') return { value: path, context: scope };
68
74
 
@@ -91,6 +97,7 @@ var spiki = (() => {
91
97
  return { value: currentValue, context: currentContext };
92
98
  };
93
99
 
100
+ // Microtask Scheduler
94
101
  var nextTick = (fn) => {
95
102
  return !schedulerQueue.has(fn) &&
96
103
  schedulerQueue.add(fn) &&
@@ -103,9 +110,9 @@ var spiki = (() => {
103
110
  });
104
111
  };
105
112
 
106
- // -------------------------------------------------------------------------
107
- // 3. Reactivity System
108
- // -------------------------------------------------------------------------
113
+ // =========================================================================
114
+ // 4. REACTIVITY SYSTEM
115
+ // =========================================================================
109
116
 
110
117
  var trackDependency = (target, key) => {
111
118
  if (!currentActiveEffect) return;
@@ -137,6 +144,7 @@ var spiki = (() => {
137
144
 
138
145
  var createEffect = (fn, scheduler) => {
139
146
  var runner = () => {
147
+ // Clean up old dependencies before re-running
140
148
  runner.dependencies.forEach(depSet => depSet.delete(runner));
141
149
  runner.dependencies.clear();
142
150
 
@@ -151,7 +159,7 @@ var spiki = (() => {
151
159
 
152
160
  runner.dependencies = new Set();
153
161
  runner.scheduler = scheduler;
154
- runner();
162
+ runner(); // Run immediately
155
163
 
156
164
  return () => {
157
165
  runner.dependencies.forEach(depSet => depSet.delete(runner));
@@ -178,6 +186,7 @@ var spiki = (() => {
178
186
  trackDependency(target, key);
179
187
 
180
188
  var result = Reflect.get(target, key, receiver);
189
+ // Deep reactivity
181
190
  return result && typeof result === 'object' && !(result instanceof Node)
182
191
  ? makeReactive(result)
183
192
  : result;
@@ -217,9 +226,9 @@ var spiki = (() => {
217
226
 
218
227
  globalStore = makeReactive({});
219
228
 
220
- // -------------------------------------------------------------------------
221
- // 4. DOM Operations
222
- // -------------------------------------------------------------------------
229
+ // =========================================================================
230
+ // 5. DOM OPERATIONS
231
+ // =========================================================================
223
232
  var domOperations = {
224
233
  text: (el, value) => {
225
234
  el.textContent = (value !== null && value !== undefined) ? value : '';
@@ -258,9 +267,9 @@ var spiki = (() => {
258
267
  destroy: () => { }
259
268
  };
260
269
 
261
- // -------------------------------------------------------------------------
262
- // 5. Engine / Mounting Logic
263
- // -------------------------------------------------------------------------
270
+ // =========================================================================
271
+ // 6. MOUNTING & ENGINE
272
+ // =========================================================================
264
273
  var mountComponent = (rootElement) => {
265
274
  if (rootElement.__isMounted) return;
266
275
  rootElement.__isMounted = 1;
@@ -276,28 +285,34 @@ var spiki = (() => {
276
285
 
277
286
  var cleanupCallbacks = [];
278
287
 
288
+ // --- Global Event Delegation Handler ---
279
289
  var handleEvent = (e) => {
280
290
  var target = e.target;
281
291
 
292
+ // Handle s-model updates
282
293
  if (target.__modelPath && (e.type === 'input' || e.type === 'change')) {
283
294
  var path = target.__modelPath;
295
+ // Use tagged scope if available, else root state
296
+ var scope = target.__scope || state;
284
297
  var value = target.type === 'checkbox' ? target.checked : target.value;
285
- var evaluation = evaluatePath(target.__scope || state, path);
298
+ var evaluation = evaluatePath(scope, path);
286
299
  var parentObject = evaluation.context;
287
300
 
288
301
  if (path.indexOf('.') === -1) {
289
- (target.__scope || state)[path] = value;
302
+ scope[path] = value;
290
303
  } else if (parentObject) {
291
304
  var parts = path.split('.');
292
305
  parentObject[parts[parts.length - 1]] = value;
293
306
  }
294
307
  }
295
308
 
309
+ // Handle standard events (s-click, etc.)
296
310
  var handlerName;
297
311
  while (target && target !== rootElement.parentNode) {
298
312
  var meta = eventMetadataMap.get(target);
299
313
  if (meta && (handlerName = meta[e.type])) {
300
- var evalResult = evaluatePath(target.__scope || state, handlerName);
314
+ var targetScope = target.__scope || state;
315
+ var evalResult = evaluatePath(targetScope, handlerName);
301
316
  var handlerFn = evalResult.value;
302
317
  var handlerContext = evalResult.context;
303
318
 
@@ -309,17 +324,21 @@ var spiki = (() => {
309
324
  }
310
325
  };
311
326
 
327
+ // --- Core Recursive Walker ---
312
328
  var walkDOM = (el, currentScope, cleanupList) => {
329
+ // 1. Fast Reject: Text nodes, Comments, or s-ignore
313
330
  if (el.nodeType !== 1 || el.hasAttribute('s-ignore')) return;
314
331
 
332
+ // 2. Component Boundary Check
315
333
  if (el !== rootElement && el.hasAttribute('s-data')) {
316
334
  var childComponent = mountComponent(el);
317
335
  if (childComponent) cleanupList.push(childComponent.unmount);
318
- return;
336
+ return; // Stop recursion here, child handles itself
319
337
  }
320
338
 
321
339
  var attributeValue;
322
340
 
341
+ // 3. Structural Directive: s-if
323
342
  if ((attributeValue = el.getAttribute('s-if'))) {
324
343
  var anchor = document.createTextNode('');
325
344
  var branchCleanups = [];
@@ -341,6 +360,7 @@ var spiki = (() => {
341
360
  if (!activeNode) {
342
361
  activeNode = el.cloneNode(true);
343
362
  activeNode.removeAttribute('s-if');
363
+ // Recurse into the new branch
344
364
  walkDOM(activeNode, currentScope, branchCleanups);
345
365
  anchor.parentNode.insertBefore(activeNode, anchor);
346
366
  }
@@ -353,6 +373,7 @@ var spiki = (() => {
353
373
  }, nextTick));
354
374
  }
355
375
 
376
+ // 4. Structural Directive: s-for
356
377
  if (el.tagName === 'TEMPLATE' && (attributeValue = el.getAttribute('s-for'))) {
357
378
  var match = attributeValue.match(loopRegex);
358
379
  if (!match) return;
@@ -419,6 +440,7 @@ var spiki = (() => {
419
440
  while (childNode) {
420
441
  rowNodes.push(childNode);
421
442
  var nextSibling = childNode.nextSibling;
443
+ // Recursive walk for new DOM nodes
422
444
  walkDOM(childNode, rowScope, rowCleanups);
423
445
  childNode = nextSibling;
424
446
  }
@@ -447,82 +469,97 @@ var spiki = (() => {
447
469
  }, nextTick));
448
470
  }
449
471
 
472
+ // 5. Attribute Binding & Interactivity Check
450
473
  var attributes = el.attributes;
451
- for (var i = attributes.length - 1; i >= 0; i--) {
452
- ((attr) => {
453
- var name = attr.name;
454
- var value = attr.value;
455
-
456
- if (name[0] === ':') {
457
- cleanupList.push(createEffect(() => {
458
- var evaluation = evaluatePath(currentScope, value);
459
- var result = evaluation.value;
460
- var context = evaluation.context;
461
- var opName = name.slice(1) === 'class' ? 'class' : 'attr';
462
-
463
- domOperations[opName](
464
- el,
465
- typeof result === 'function' ? result.call(context, el) : result,
466
- name.slice(1)
467
- );
468
- }, nextTick));
469
-
470
- } else if (name[0] === 's' && name[1] === '-') {
471
- var directiveType = name.slice(2);
474
+ var isInteractive = false;
475
+
476
+ if (attributes && attributes.length > 0) {
477
+ // Reverse loop for safety
478
+ for (var i = attributes.length - 1; i >= 0; i--) {
479
+ ((attr) => {
480
+ var name = attr.name;
481
+ var value = attr.value;
472
482
 
473
- if (directiveType === 'ref') {
474
- state.$refs[value] = el;
475
- } else if (directiveType === 'model') {
476
- cleanupList.push(createEffect(() => {
477
- var evaluation = evaluatePath(currentScope, value);
478
- domOperations.value(el, evaluation.value);
479
- }, nextTick));
480
-
481
- if (/^(INPUT|SELECT|TEXTAREA)$/.test(el.tagName)) {
482
- el.__scope = currentScope;
483
- el.__modelPath = value;
484
- var eventType = (el.type === 'checkbox' || el.type === 'radio' || el.tagName === 'SELECT')
485
- ? 'change'
486
- : 'input';
487
-
488
- if (!rootElement.__listeningEvents) rootElement.__listeningEvents = new Set();
489
- if (!rootElement.__listeningEvents.has(eventType)) {
490
- rootElement.__listeningEvents.add(eventType);
491
- rootElement.addEventListener(eventType, handleEvent);
492
- }
493
- }
494
- } else if (domOperations[directiveType]) {
483
+ if (name[0] === ':') {
495
484
  cleanupList.push(createEffect(() => {
496
485
  var evaluation = evaluatePath(currentScope, value);
497
486
  var result = evaluation.value;
498
487
  var context = evaluation.context;
499
- domOperations[directiveType](
488
+ var opName = name.slice(1) === 'class' ? 'class' : 'attr';
489
+
490
+ domOperations[opName](
500
491
  el,
501
- typeof result === 'function' ? result.call(context, el) : result
492
+ typeof result === 'function' ? result.call(context, el) : result,
493
+ name.slice(1)
502
494
  );
503
495
  }, nextTick));
504
- } else {
505
- el.__scope = currentScope;
506
- var meta = eventMetadataMap.get(el);
507
- if (!meta) {
508
- meta = {};
509
- eventMetadataMap.set(el, meta);
510
- }
511
- meta[directiveType] = value;
512
496
 
513
- if (!rootElement.__listeningEvents) rootElement.__listeningEvents = new Set();
514
- if (!rootElement.__listeningEvents.has(directiveType)) {
515
- rootElement.__listeningEvents.add(directiveType);
516
- rootElement.addEventListener(directiveType, handleEvent);
497
+ } else if (name[0] === 's' && name[1] === '-') {
498
+ var directiveType = name.slice(2);
499
+
500
+ if (directiveType === 'ref') {
501
+ state.$refs[value] = el;
502
+ } else if (directiveType === 'model') {
503
+ isInteractive = true; // Flag for scope attachment
504
+ cleanupList.push(createEffect(() => {
505
+ var evaluation = evaluatePath(currentScope, value);
506
+ domOperations.value(el, evaluation.value);
507
+ }, nextTick));
508
+
509
+ if (/^(INPUT|SELECT|TEXTAREA)$/.test(el.tagName)) {
510
+ el.__modelPath = value;
511
+ var eventType = (el.type === 'checkbox' || el.type === 'radio' || el.tagName === 'SELECT')
512
+ ? 'change'
513
+ : 'input';
514
+
515
+ if (!rootElement.__listeningEvents) rootElement.__listeningEvents = new Set();
516
+ if (!rootElement.__listeningEvents.has(eventType)) {
517
+ rootElement.__listeningEvents.add(eventType);
518
+ rootElement.addEventListener(eventType, handleEvent);
519
+ }
520
+ }
521
+ } else if (domOperations[directiveType]) {
522
+ cleanupList.push(createEffect(() => {
523
+ var evaluation = evaluatePath(currentScope, value);
524
+ var result = evaluation.value;
525
+ var context = evaluation.context;
526
+ domOperations[directiveType](
527
+ el,
528
+ typeof result === 'function' ? result.call(context, el) : result
529
+ );
530
+ }, nextTick));
531
+ } else {
532
+ // Event Handlers
533
+ isInteractive = true; // Flag for scope attachment
534
+ var meta = eventMetadataMap.get(el);
535
+ if (!meta) {
536
+ meta = {};
537
+ eventMetadataMap.set(el, meta);
538
+ }
539
+ meta[directiveType] = value;
540
+
541
+ if (!rootElement.__listeningEvents) rootElement.__listeningEvents = new Set();
542
+ if (!rootElement.__listeningEvents.has(directiveType)) {
543
+ rootElement.__listeningEvents.add(directiveType);
544
+ rootElement.addEventListener(directiveType, handleEvent);
545
+ }
517
546
  }
518
547
  }
519
- }
520
- })(attributes[i]);
548
+ })(attributes[i]);
549
+ }
550
+ }
551
+
552
+ // 6. Selective Scope Attachment
553
+ // Only attach scope to DOM elements that actually need it for events/models.
554
+ // This saves memory on static elements.
555
+ if (isInteractive) {
556
+ el.__scope = currentScope;
521
557
  }
522
558
 
523
- var child = el.firstElementChild;
559
+ // 7. Recursive Traversal (Optimized Native Loop)
560
+ var child = el.firstChild;
524
561
  while (child) {
525
- var next = child.nextElementSibling;
562
+ var next = child.nextSibling;
526
563
  walkDOM(child, currentScope, cleanupList);
527
564
  child = next;
528
565
  }
@@ -545,6 +582,9 @@ var spiki = (() => {
545
582
  };
546
583
  };
547
584
 
585
+ // =========================================================================
586
+ // 7. PUBLIC API
587
+ // =========================================================================
548
588
  return {
549
589
  data: (name, factoryFn) => {
550
590
  componentRegistry[name] = factoryFn;
package/spiki.min.js CHANGED
@@ -1 +1 @@
1
- (()=>{"use strict";var e=(()=>{var n,t,E=Object,A=Array,P=Reflect,e=Promise,T=Map,k=Set,r=WeakMap,a=Node,L=document,N="length",C="value",S="push",$="forEach",l="indexOf",j="split",M="slice",O="call",R="get",W="set",q="add",z="delete",B="has",s="clear",D="prototype",F="hasOwnProperty",H="create",I="isArray",U="getAttribute",X="removeAttribute",G="hasAttribute",J="tagName",K="type",Q="name",u="checked",V="parentNode",Y="nextSibling",Z="replaceWith",ee="insertBefore",te="cloneNode",re="remove",ne="createTextNode",ae="addEventListener",o="dependencies",oe="cleanups",ie="nodes",se="context",i="scheduler",c=E[H](null),le=new r,f=new r,v=new r,p=new T,d=new k,ue=/^\s*(.*?)\s+in\s+(.+)\s*$/,y=!0,m=e.resolve(),_={};[S,"pop","shift","unshift","splice","sort","reverse"][$](r=>{_[r]=function(){for(var e=[],t=0;t<arguments[N];t++)e[t]=arguments[t];y=!1;try{return A[D][r].apply(this,e)}finally{y=!0,h(this,N)}}});var ce=(e,t)=>{if("string"!=typeof t)return{value:t,context:e};if(-1===t[l]("."))return{value:e?e[t]:void 0,context:e};var r=p[R](t);r||(1e3<p.size&&p[s](),r=t[j]("."),p[W](t,r));for(var n=e,a=e,o=0;o<r[N];o++){var i=r[o];if(null==n)return{value:n,context:null};n=(a=n)[i]}return{value:n,context:a}},fe=e=>!d[B](e)&&d[q](e)&&!t&&(t=!0)&&m.then(()=>{d[$](e=>e()),d[s](),t=!1}),ve=(e,t)=>{var r;n&&((r=f[R](e))||(r=new T,f[W](e,r)),(e=r[R](t))||(e=new k,r[W](t,e)),e[q](n),n[o][q](e))},h=(e,t)=>{var r,n;y&&(r=f[R](e))&&(n=r[R](t))&&n[$](e=>{e[i]?e[i](e):e()})},pe=(t,e)=>{var r=()=>{r[o][$](e=>e[z](r)),r[o][s]();var e=n;n=r;try{t()}finally{n=e}};return r[o]=new k,r[i]=e,r(),()=>{r[o][$](e=>e[z](r)),r[o][s](),d[z](r)}},g=e=>{if(!e||"object"!=typeof e||e._ip||e instanceof a)return e;var t=v[R](e);return t||(t=new Proxy(e,{get:(e,t,r)=>"_r"===t?e:"_ip"===t||(A[I](e)&&_[F](t)?_[t]:(ve(e,t),!(r=P[R](e,t,r))||"object"!=typeof r||r instanceof a?r:g(r))),set:(e,t,r,n)=>{var a=e[t],o=A[I](e)?Number(t)<e[N]:E[D][F][O](e,t),n=P[W](e,t,r,n);return y&&(o?a!==r&&h(e,t):(h(e,t),A[I](e)&&h(e,N))),n},deleteProperty:(e,t)=>{var r=E[D][F][O](e,t),n=P.deleteProperty(e,t);return n&&r&&(h(e,t),A[I](e)&&h(e,N)),n}}),v[W](e,t),t)},w=g({}),de={text:(e,t)=>{e.textContent=null!=t?t:""},html:(e,t)=>{e.innerHTML=null!=t?t:""},value:(e,t)=>{"checkbox"===e[K]?e[u]=!!t:"radio"===e[K]&&e[Q]?e[u]=e[C]==t:e[C]!=t&&(e[C]=null!=t?t:"")},attr:(e,t,r)=>{null==t||!1===t?e[X](r):e.setAttribute(r,!0===t?"":t)},class:(r,e)=>{"string"==typeof e&&e[j](/\s+/)[$](e=>{var t;e&&(e=(t="!"===e[0])?e[M](1):e,r.classList[t?re:q](e))})},init:()=>{},destroy:()=>{}},ye=p=>{if(!p._im){p._im=1;var e=p[U]("s-data");if(e=c[e]){var d=g(e());d.$refs={},d.$root=p,d.$store=w;var t=[],b=e=>{var t,r,n,a,o=e.target;for(!o._m||"input"!==e[K]&&"change"!==e[K]||(n=o._m,t="checkbox"===o[K]?o[u]:o[C],r=ce(o._s||d,n)[se],-1===n[l](".")?(o._s||d)[n]=t:r&&(r[(n=n[j]("."))[n[N]-1]]=t));o&&o!==p[V];){var i,s=le[R](o);s&&(a=s[e[K]])&&(s=(i=ce(o._s||d,a))[C],i=i[se],"function"==typeof s&&s[O](i,e)),o=o[V]}},x=(y,m,o)=>{if(1===y.nodeType&&!y[G]("s-ignore"))if(y!==p&&y[G]("s-data"))(t=ye(y))&&o[S](t.unmount);else{var r;if(r=y[U]("s-if")){var n,a=L[ne](""),i=[];return y[Z](a),o[S](()=>{i[$](e=>e())}),o[S](pe(()=>{var e=(t=ce(m,r))[C],t=t[se];("function"==typeof e?e[O](t,y):e)?n||((n=y[te](!0))[X]("s-if"),x(n,m,i),a[V][ee](n,a)):n&&(i[$](e=>e()),i[N]=0,n[re](),n=null)},fe))}if("TEMPLATE"===y[J]&&(r=y[U]("s-for"))){var e=r.match(ue);if(!e)return;var t=e[1].replace(/[()]/g,""),s=e[2],_=(t=t[j](","))[0].trim(),h=t[1]?t[1].trim():null,g=y[U]("s-key"),l=L[ne]("");y[Z](l);var w=new T;return o[S](()=>{w[$](e=>{e[oe][$](e=>e())})}),o[S](pe(()=>{var v=ce(m,s)[C];A[I](v)&&ve(v,N);var p=l,e=A[I](v)?v:v?E.keys(v):[],d=new T;e[$]((e,t)=>{var r,n,a=A[I](v)?t:e,o=A[I](v)?e:v[e],i=g&&o?o[g]:"object"==typeof o&&o?o:a+"_"+o,e=e=>{E.defineProperty(e,_,{configurable:!0,enumerable:!0,get:()=>v[a],set:e=>{v[a]=e}})};if(t=w[R](i))e(t.scope),h&&(t.scope[h]=a);else{var o=y.content[te](!0),s=(n=m,n=E[H](n),new Proxy(n,{set:(e,t,r,n)=>{if(e[F](t))return P[W](e,t,r,n);for(var a=e;a&&!E[D][F][O](a,t);)a=E.getPrototypeOf(a);return P[W](a||e,t,r)}})),l=[];e(s),h&&(s[h]=a);for(var u=[],c=o.firstChild;c;){u[S](c);var f=c[Y];x(c,s,l),c=f}t={nodes:u,scope:s,cleanups:l}}t[ie][0]!==p[Y]&&(r=L.createDocumentFragment(),t[ie][$](e=>r.appendChild(e)),p[V][ee](r,p[Y])),p=t[ie][t[ie][N]-1],d[W](i,t),w[z](i)}),w[$](e=>{e[oe][$](e=>e()),e[ie][$](e=>e[re]())}),w=d},fe))}for(var u=y.attributes,c=u[N]-1;0<=c;c--)(e=>{var r,t,n=e[Q],a=e[C];":"===n[0]?o[S](pe(()=>{var e=(r=ce(m,a))[C],t=r[se],r="class"===n[M](1)?"class":"attr";de[r](y,"function"==typeof e?e[O](t,y):e,n[M](1))},fe)):"s"===n[0]&&"-"===n[1]&&("ref"===(r=n[M](2))?d.$refs[a]=y:"model"===r?(o[S](pe(()=>{var e=ce(m,a);de[C](y,e[C])},fe)),/^(INPUT|SELECT|TEXTAREA)$/.test(y[J])&&(y._s=m,y._m=a,t="checkbox"===y[K]||"radio"===y[K]||"SELECT"===y[J]?"change":"input",p._le||(p._le=new k),p._le[B](t)||(p._le[q](t),p[ae](t,b)))):de[r]?o[S](pe(()=>{var e=(t=ce(m,a))[C],t=t[se];de[r](y,"function"==typeof e?e[O](t,y):e)},fe)):(y._s=m,(t=le[R](y))||(t={},le[W](y,t)),t[r]=a,p._le||(p._le=new k),p._le[B](r)||(p._le[q](r),p[ae](r,b))))})(u[c]);for(var f=y.firstElementChild;f;){var v=f.nextElementSibling;x(f,m,o),f=v}}};return x(p,d,t),d.init&&d.init(),{unmount:()=>{d.destroy&&d.destroy[O](d),t[$](e=>e()),p._le&&p._le[$](e=>{p.removeEventListener(e,b)}),p._im=0}}}}};return{data:(e,t)=>{c[e]=t},start:()=>{for(var e=L.querySelectorAll("[s-data]"),t=0;t<e[N];t++)ye(e[t])},store:(e,t)=>void 0===t?w[e]:w[e]=t,raw:e=>e&&e._r||e}})();window.spiki=e})();
1
+ (()=>{"use strict";var e=(()=>{var n,r,A=Object,P=Array,T=Reflect,e=Promise,L=Map,N=Set,t=WeakMap,a=Node,k=document,$="length",C="value",S="push",M="forEach",u="indexOf",O="split",j="slice",R="call",W="get",q="set",z="add",B="delete",D="has",i="clear",F="prototype",H="hasOwnProperty",I="create",U="isArray",X="getAttribute",G="removeAttribute",J="hasAttribute",K="tagName",Q="type",V="name",v="checked",Y="parentNode",Z="nextSibling",ee="firstChild",re="replaceWith",te="insertBefore",ne="cloneNode",ae="remove",oe="createTextNode",se="addEventListener",ie="checkbox",le="function",ue="object",o="dependencies",ve="cleanups",fe="nodes",ce="context",s="scheduler",l=A[I](null),pe=new t,f=new t,c=new t,p=new L,d=new N,de=/^\s*(.*?)\s+in\s+(.+)\s*$/,y=!0,_=e.resolve(),h={};[S,"pop","shift","unshift","splice","sort","reverse"][M](t=>{h[t]=function(){for(var e=[],r=0;r<arguments[$];r++)e[r]=arguments[r];y=!1;try{return P[F][t].apply(this,e)}finally{y=!0,m(this,$)}}});var ye=(e,r)=>{if("string"!=typeof r)return{value:r,context:e};if(-1===r[u]("."))return{value:e?e[r]:void 0,context:e};var t=p[W](r);t||(1e3<p.size&&p[i](),t=r[O]("."),p[q](r,t));for(var n=e,a=e,o=0;o<t[$];o++){var s=t[o];if(null==n)return{value:n,context:null};n=(a=n)[s]}return{value:n,context:a}},_e=e=>!d[D](e)&&d[z](e)&&!r&&(r=!0)&&_.then(()=>{d[M](e=>e()),d[i](),r=!1}),he=(e,r)=>{var t;n&&((t=f[W](e))||(t=new L,f[q](e,t)),(e=t[W](r))||(e=new N,t[q](r,e)),e[z](n),n[o][z](e))},m=(e,r)=>{var t,n;y&&(t=f[W](e))&&(n=t[W](r))&&n[M](e=>{e[s]?e[s](e):e()})},me=(r,e)=>{var t=()=>{t[o][M](e=>e[B](t)),t[o][i]();var e=n;n=t;try{r()}finally{n=e}};return t[o]=new N,t[s]=e,t(),()=>{t[o][M](e=>e[B](t)),t[o][i](),d[B](t)}},w=e=>{if(!e||typeof e!=ue||e._p||e instanceof a)return e;var r=c[W](e);return r||(r=new Proxy(e,{get:(e,r,t)=>"_r"===r?e:"_p"===r||(P[U](e)&&h[H](r)?h[r]:(he(e,r),!(t=T[W](e,r,t))||typeof t!=ue||t instanceof a?t:w(t))),set:(e,r,t,n)=>{var a=e[r],o=P[U](e)?Number(r)<e[$]:A[F][H][R](e,r),n=T[q](e,r,t,n);return y&&(o?a!==t&&m(e,r):(m(e,r),P[U](e)&&m(e,$))),n},deleteProperty:(e,r)=>{var t=A[F][H][R](e,r),n=T.deleteProperty(e,r);return n&&t&&(m(e,r),P[U](e)&&m(e,$)),n}}),c[q](e,r),r)},g=w({}),we={text:(e,r)=>{e.textContent=null!=r?r:""},html:(e,r)=>{e.innerHTML=null!=r?r:""},value:(e,r)=>{e[Q]===ie?e[v]=!!r:"radio"===e[Q]&&e[V]?e[v]=e[C]==r:e[C]!=r&&(e[C]=null!=r?r:"")},attr:(e,r,t)=>{null==r||!1===r?e[G](t):e.setAttribute(t,!0===r?"":r)},class:(t,e)=>{"string"==typeof e&&e[O](/\s+/)[M](e=>{var r;e&&(e=(r="!"===e[0])?e[j](1):e,t.classList[r?ae:z](e))})},init:()=>{},destroy:()=>{}},ge=d=>{if(!d._m){d._m=1;var e=d[X]("s-data");if(e=l[e]){var x=w(e());x.$refs={},x.$root=d,x.$store=g;var r=[],b=e=>{var r,t,n,a,o,s=e.target;for(!s._t||"input"!==e[Q]&&"change"!==e[Q]||(a=s._t,r=s._s||x,t=s[Q]===ie?s[v]:s[C],n=ye(r,a)[ce],-1===a[u](".")?r[a]=t:n&&(n[(a=a[O]("."))[a[$]-1]]=t));s&&s!==d[Y];){var i,l=pe[W](s);l&&(o=l[e[Q]])&&(i=s._s||x,i=(l=ye(i,o))[C],l=l[ce],typeof i==le&&i[R](l,e)),s=s[Y]}},E=(y,_,o)=>{if(1===y.nodeType&&!y[J]("s-ignore"))if(y!==d&&y[J]("s-data"))(r=ge(y))&&o[S](r.unmount);else{var t;if(t=y[X]("s-if")){var n,a=k[oe](""),s=[];return y[re](a),o[S](()=>{s[M](e=>e())}),o[S](me(()=>{var e=(r=ye(_,t))[C],r=r[ce];(typeof e==le?e[R](r,y):e)?n||((n=y[ne](!0))[G]("s-if"),E(n,_,s),a[Y][te](n,a)):n&&(s[M](e=>e()),s[$]=0,n[ae](),n=null)},_e))}if("TEMPLATE"===y[K]&&(t=y[X]("s-for"))){var e=t.match(de);if(!e)return;var r=e[1].replace(/[()]/g,""),i=e[2],h=(r=r[O](","))[0].trim(),m=r[1]?r[1].trim():null,w=y[X]("s-key"),l=k[oe]("");y[re](l);var g=new L;return o[S](()=>{g[M](e=>{e[ve][M](e=>e())})}),o[S](me(()=>{var c=ye(_,i)[C];P[U](c)&&he(c,$);var p=l,e=P[U](c)?c:c?A.keys(c):[],d=new L;e[M]((e,r)=>{var t,n,a=P[U](c)?r:e,o=P[U](c)?e:c[e],s=w&&o?o[w]:typeof o==ue&&o?o:a+"_"+o,e=e=>{A.defineProperty(e,h,{configurable:!0,enumerable:!0,get:()=>c[a],set:e=>{c[a]=e}})};if(r=g[W](s))e(r.scope),m&&(r.scope[m]=a);else{var o=y.content[ne](!0),i=(n=_,n=A[I](n),new Proxy(n,{set:(e,r,t,n)=>{if(e[H](r))return T[q](e,r,t,n);for(var a=e;a&&!A[F][H][R](a,r);)a=A.getPrototypeOf(a);return T[q](a||e,r,t)}})),l=[];e(i),m&&(i[m]=a);for(var u=[],v=o[ee];v;){u[S](v);var f=v[Z];E(v,i,l),v=f}r={nodes:u,scope:i,cleanups:l}}r[fe][0]!==p[Z]&&(t=k.createDocumentFragment(),r[fe][M](e=>t.appendChild(e)),p[Y][te](t,p[Z])),p=r[fe][r[fe][$]-1],d[q](s,r),g[B](s)}),g[M](e=>{e[ve][M](e=>e()),e[fe][M](e=>e[ae]())}),g=d},_e))}var u=y.attributes,v=!1;if(u&&0<u[$])for(var f=u[$]-1;0<=f;f--)(e=>{var t,r,n=e[V],a=e[C];":"===n[0]?o[S](me(()=>{var e=(t=ye(_,a))[C],r=t[ce],t="class"===n[j](1)?"class":"attr";we[t](y,typeof e==le?e[R](r,y):e,n[j](1))},_e)):"s"===n[0]&&"-"===n[1]&&("ref"===(t=n[j](2))?x.$refs[a]=y:"model"===t?(v=!0,o[S](me(()=>{var e=ye(_,a);we[C](y,e[C])},_e)),/^(INPUT|SELECT|TEXTAREA)$/.test(y[K])&&(y._t=a,r=y[Q]===ie||"radio"===y[Q]||"SELECT"===y[K]?"change":"input",d._e||(d._e=new N),d._e[D](r)||(d._e[z](r),d[se](r,b)))):we[t]?o[S](me(()=>{var e=(r=ye(_,a))[C],r=r[ce];we[t](y,typeof e==le?e[R](r,y):e)},_e)):(v=!0,(r=pe[W](y))||(r={},pe[q](y,r)),r[t]=a,d._e||(d._e=new N),d._e[D](t)||(d._e[z](t),d[se](t,b))))})(u[f]);v&&(y._s=_);for(var c=y[ee];c;){var p=c[Z];E(c,_,o),c=p}}};return E(d,x,r),x.init&&x.init(),{unmount:()=>{x.destroy&&x.destroy[R](x),r[M](e=>e()),d._e&&d._e[M](e=>{d.removeEventListener(e,b)}),d._m=0}}}}};return{data:(e,r)=>{l[e]=r},start:()=>{for(var e=k.querySelectorAll("[s-data]"),r=0;r<e[$];r++)ge(e[r])},store:(e,r)=>void 0===r?g[e]:g[e]=r,raw:e=>e&&e._r||e}})();window.spiki=e})();