@ynck/rendux 0.93.2 → 0.94.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,11 @@
1
1
  # Changelog
2
2
 
3
- ## v0.93.2
3
+ ## v0.94.0
4
+
5
+ - Fixed x-for context propagation: reused nodes with x-key now update context on all descendants, not just the root node
6
+ - Fixed x-for DOM reordering: iterate backwards to ensure correct node positioning when items are moved
7
+
8
+ ## v0.93.3
4
9
 
5
10
  - fixed nested x-for rendering
6
11
 
package/README.md CHANGED
@@ -16,7 +16,7 @@
16
16
  ### Via NPM
17
17
 
18
18
  ```bash
19
- npm install rendux
19
+ npm install @ynck/rendux
20
20
  ```
21
21
 
22
22
  Then import in your project:
@@ -26,7 +26,7 @@ Then import in your project:
26
26
  import { rendux } from '@ynck/rendux';
27
27
 
28
28
  // CommonJS
29
- const { rendux } = require('rendux');
29
+ const { rendux } = require('@ynck/rendux');
30
30
  ```
31
31
 
32
32
  ### Via CDN (No Build Step)
@@ -41,7 +41,7 @@ const { rendux } = require('rendux');
41
41
 
42
42
  <!-- Or use specific version -->
43
43
  <script type="module">
44
- import { rendux } from 'https://unpkg.com/@ynck/rendux@0.93.0/dist/rendux.min.js';
44
+ import { rendux } from 'https://unpkg.com/@ynck/rendux@0.93.3/dist/rendux.min.js';
45
45
  </script>
46
46
  ```
47
47
 
@@ -55,7 +55,7 @@ const { rendux } = require('rendux');
55
55
 
56
56
  <!-- Or with specific version -->
57
57
  <script type="module">
58
- import { rendux } from 'https://esm.sh/@ynck/rendux@0.93.0'
58
+ import { rendux } from 'https://esm.sh/@ynck/rendux@0.93.3'
59
59
  </script>
60
60
  ```
61
61
 
@@ -68,7 +68,7 @@ const { rendux } = require('rendux');
68
68
 
69
69
  <!-- Or with specific version -->
70
70
  <script type="module">
71
- import { rendux } from 'https://cdn.skypack.dev/@ynck/rendux@0.93.0';
71
+ import { rendux } from 'https://cdn.skypack.dev/@ynck/rendux@0.93.3';
72
72
  </script>
73
73
  ```
74
74
 
@@ -348,7 +348,7 @@ You can also use rendux with plain HTML:
348
348
 
349
349
  ```js
350
350
  // Import using CommonJS
351
- const { rendux } = require('rendux');
351
+ const { rendux } = require('@ynck/rendux');
352
352
 
353
353
  // Create a state object with your data
354
354
  const state = {
@@ -465,6 +465,7 @@ The element is completely removed from the DOM (not just hidden) when the condit
465
465
  ### 2. List Rendering (`x-for`)
466
466
 
467
467
  Supports two syntaxes:
468
+
468
469
  ```html
469
470
  <!-- Simple iteration -->
470
471
  <template x-for="item of items">
@@ -477,11 +478,53 @@ Supports two syntaxes:
477
478
  </template>
478
479
  ```
479
480
 
481
+ #### Performance Optimization with `x-key`
482
+
483
+ For improved performance when updating lists, use the `x-key` attribute to enable **per-item diffing**:
484
+
485
+ ```html
486
+ <!-- Without x-key: full re-render on every change -->
487
+ <template x-for="(user, index) of users">
488
+ <div render="user.name"></div>
489
+ </template>
490
+
491
+ <!-- With x-key: only changed items are re-rendered -->
492
+ <template x-for="(user, index) of users">
493
+ <div render="user.name" x-key="user.id"></div>
494
+ </template>
495
+ ```
496
+
497
+ **How `x-key` works:**
498
+ - Each item is tracked by its unique `x-key` value (e.g., `user.id`, `index`, or any unique expression)
499
+ - When the array changes, `rendux` compares the new items with the old ones by their `x-key`
500
+ - **Reuses existing DOM nodes** for items that haven't changed (only updates their data)
501
+ - **Creates new nodes** only for new items
502
+ - **Removes nodes** only for deleted items
503
+ - **Reorders nodes** efficiently when items are moved
504
+
505
+ **Performance benefits:**
506
+ - ✅ **Faster updates**: Only changed items are processed, not the entire list
507
+ - ✅ **Preserves DOM state**: Input focus, scroll position, and animations are maintained
508
+ - ✅ **Reduces layout thrashing**: Minimal DOM manipulation
509
+ - ✅ **Better for large lists**: Scales well with thousands of items
510
+
511
+ **When to use `x-key`:**
512
+ - Lists that frequently update (add/remove/reorder items)
513
+ - Large lists (100+ items)
514
+ - Lists with user input (forms, checkboxes, text fields)
515
+ - Lists with animations or transitions
516
+
517
+ **Best practices:**
518
+ - Use a **unique, stable identifier** like `user.id` or `item.uuid`
519
+ - Avoid using `index` if items can be reordered or filtered
520
+ - The `x-key` expression is evaluated for each item and should return a primitive value (string, number)
521
+
480
522
  Features:
481
523
  - Uses `<template>` tag to define the repeatable content
482
524
  - Maintains its own render cache per iteration
483
525
  - Supports deep change detection for arrays
484
526
  - Provides iteration context to nested elements
527
+ - **Per-item diffing with `x-key` for optimal performance**
485
528
 
486
529
  ### 3. Class Binding (`x-class`)
487
530
 
@@ -810,7 +853,7 @@ This security model makes rendux suitable for applications handling user-generat
810
853
 
811
854
  rendux has been designed and audited to ensure maximum security for web applications:
812
855
 
813
- #### Core Engine Security
856
+ #### Core Engine Security
814
857
 
815
858
  **No Dangerous Functions**
816
859
  - ✓ No `eval()` or `Function()` constructor used
@@ -1,2 +1,2 @@
1
- /*! rendux v0.93.2 | Copyright (c) JULY 2025 Yannick J.A. CHARLERY | This software is licensed for non-commercial use only. Commercial licensing available upon request. For license details: https://github.com/ynck-chrl/rendux */
2
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0});const e={Math:Math,Date:Date,Number:Number,String:String,Boolean:Boolean,JSON:JSON,parseInt:parseInt,parseFloat:parseFloat,isFinite:isFinite,Array:Array};function t(t,r={}){const n=function(e){const t=[];let r=0;for(;r<e.length;){let n=e[r];if(/\s/.test(n)){r++;continue}if('"'===n||"'"===n){const o=n;let s="";for(r++;r<e.length&&e[r]!==o;)"\\"===e[r]?(r++,r<e.length&&(s+=e[r++])):s+=e[r++];r++,t.push({type:"string",value:s});continue}if(/[0-9]/.test(n)||"."===n&&/[0-9]/.test(e[r+1])){let n=r;for(;r<e.length&&/[0-9]/.test(e[r]);)r++;if("."===e[r])for(r++;r<e.length&&/[0-9]/.test(e[r]);)r++;const o=parseFloat(e.slice(n,r));t.push({type:"number",value:o});continue}if(/[a-zA-Z_$]/.test(n)){let n=r;for(r++;r<e.length&&/[a-zA-Z0-9_$]/.test(e[r]);)r++;const o=e.slice(n,r);"true"===o||"false"===o?t.push({type:"boolean",value:"true"===o}):"null"===o?t.push({type:"null",value:null}):t.push({type:"identifier",value:o});continue}const o=e.substr(r,2),s=e.substr(r,3);if(["===","!=="].includes(s))t.push({type:"operator",value:s}),r+=3;else if(["==","!=",">=","<=","&&","||"].includes(o))t.push({type:"operator",value:o}),r+=2;else{if(!["+","-","*","/","%",">","<","!","?",":","(",")","[","]",".",","].includes(n))throw new Error(`Invalid character '${n}' in expression`);t.push({type:"operator",value:n}),r++}}return t.push({type:"EOF"}),t}(t);let o=0;function s(){return n[o]||{type:"EOF"}}function l(){return n[o++]||{type:"EOF"}}function a(e,t){const r=s();if(r.type!==e||void 0!==t&&r.value!==t)throw new Error(`Expected ${t||e} but got ${r.value}`);return l(),r}function i(e){const t=s();return"operator"===t.type&&t.value===e}const c=u();if("EOF"!==s().type)throw new Error(`Unexpected token: ${s().value}`);return c;function u(){let e=function(){let e=f();for(;i("||");){l();const t=f();e=e||t}return e}();if(i("?")){l();const t=u();a("operator",":");const r=u();e=e?t:r}return e}function f(){let e=d();for(;i("&&");){l();const t=d();e=e&&t}return e}function d(){let e=h();for(;["==","!=","===","!=="].includes(s().value);){const t=l().value,r=h();switch(t){case"==":e=e==r;break;case"!=":e=e!=r;break;case"===":e=e===r;break;case"!==":e=e!==r}}return e}function h(){let e=p();for(;[">","<",">=","<="].includes(s().value);){const t=l().value,r=p();switch(t){case">":e=e>r;break;case"<":e=e<r;break;case">=":e=e>=r;break;case"<=":e=e<=r}}return e}function p(){let e=x();for(;["+","-"].includes(s().value);){const t=l().value,r=x();e="+"===t?e+r:e-r}return e}function x(){let e=g();for(;["*","/","%"].includes(s().value);){const t=l().value,r=g();switch(t){case"*":e*=r;break;case"/":e/=r;break;case"%":e%=r}}return e}function g(){if(["!","+","-"].includes(s().value)){const e=l().value,t=g();switch(e){case"!":return!t;case"+":return+t;case"-":return-t}}return function(){const t=s();if("number"===t.type||"string"===t.type||"boolean"===t.type||"null"===t.type)return l(),t.value;if("identifier"===t.type){l();let n=function(t){if(t in r)return r[t];if(t in e)return e[t];if(null!=r.this&&t in r.this)return r.this[t];throw new Error(`Unknown identifier: ${t}`)}(t.value);for(;;)if(i(".")){l();const e=a("identifier").value;n=null==n?void 0:n[e]}else if(i("[")){l();const e=u();a("operator","]"),n=null==n?void 0:n[e]}else{if(!i("("))break;{l();const e=[];if(!i(")"))do{e.push(u())}while(i(",")&&l());if(a("operator",")"),"function"!=typeof n)throw new Error(`'${t.value}' is not a function`);n=n.apply(r.this,e)}}return n}if(i("(")){l();const e=u();return a("operator",")"),e}throw new Error(`Unexpected token: ${t.value}`)}()}}const r=new Map;function n(e){if(!e||!e.name)throw new Error("Invalid plugin");r.set(e.name,e)}function o(e,t={}){const n=e.match(/^([a-zA-Z_$][\w$]*)\s*\(\s*(.*)\s*\)$/);if(!n)return null;const o=n[1],s=r.get(o);if(!s)return null;const l=n[2].trim();if(""===l)return{plugin:s,args:[]};return{plugin:s,args:l.split(/\s*,\s*/).map(e=>{if(e.startsWith('"')&&e.endsWith('"')||e.startsWith("'")&&e.endsWith("'"))return e.slice(1,-1);if(/^-?\d+(?:\.\d+)?$/.test(e))return parseFloat(e);if(e in t)return t[e];throw new Error(`Unknown identifier: ${e}`)})}}async function s(e=document,t={}){const n=Array.from(e.querySelectorAll("*")).filter(e=>Array.from(e.attributes).some(e=>e.name.startsWith("render.")));for(const e of n)for(const n of Array.from(e.attributes)){if(!n.name.startsWith("render."))continue;const s=n.name,l=n.value.trim();let a,i,c;if("render.plugin"===s){let e;try{e=o(l,t)}catch(e){console.error(e);continue}if(!e)continue;a=e.plugin,i=e.args}else{const e=s.slice(7);if(a=r.get(e),!a)continue;if(l.startsWith(e+"(")){let e;try{e=o(l,t)}catch(e){console.error(e);continue}if(!e)continue;a=e.plugin,i=e.args}else i=[l]}"function"==typeof a.onBeforeExecute&&a.onBeforeExecute(e,l,t);try{c=a.execute(...i)}catch(e){continue}e.textContent=null==c?"":String(c),"function"==typeof a.onAfterExecute&&a.onAfterExecute(e,l,t)}for(const n of r.values())"function"==typeof n.onAfterRender&&await n.onAfterRender(e,t)}function rendux(e,n){const o=e,l=n;let a=n.shadowRoot||n;if(!a)throw new Error("rendux requires a DOM element as the second argument");for(const e of r.values())"function"==typeof e.onBeforeRender&&e.onBeforeRender(a,o);const i=document,c=l.getAttribute&&l.getAttribute("logs"),u=Boolean(c),f=c?new Set(c.split(",").map(e=>e.trim().toLowerCase())):new Set,d=e=>f.has("all")||f.has(e.toLowerCase())||f.has(("x-"+e).toLowerCase()),h=u&&f.has("plugins"),p=(...e)=>console.warn(...e);l._xRenderCache||(l._xRenderCache=new Map),l._xRenderOriginalText||(l._xRenderOriginalText=new WeakMap),l._xIfData||(l._xIfData=new WeakMap),l._cloneContext||(l._cloneContext=new WeakMap),l._xClassCache||(l._xClassCache=new Map),l._xForCache||(l._xForCache=new WeakMap),l.__mirrorContainer||(l.__mirrorContainer=i.createDocumentFragment());const x={};function g(e){let t=e;for(;t;){const e=l._cloneContext.get(t);if(e)return l.getAttribute&&l.getAttribute("logs")&&l.getAttribute("logs").includes("plugins")&&console.log("Found context for element:",t,e),e;t=t.parentNode}return l.getAttribute&&l.getAttribute("logs")&&l.getAttribute("logs").includes("plugins")&&console.log("Using default component context:",o),o}function y(e,r){if(!e)return!0;const n=g(r);try{return t(e,{this:n,...x})}catch(t){return void p(`[evaluate] "${e}" failed in`,n,t)}}function m(e,t){const r=g(t),n=e.match(/([^[.\]]+)|\[(\d+)\]/g);if(n)return n.reduce((e,t)=>{if(null!=e){if(t.startsWith("[")){const r=parseInt(t.slice(1,-1),10);return Array.isArray(e)?e[r]:void 0}return e[t]}},r)}function A(e){const t=e.match(/^\s*(?:\(\s*([^,\s]+)\s*,\s*([^,\s]+)\s*\)|([^,\s()]+))\s+(?:in|of)\s+(.+)$/);if(!t)throw new Error("Invalid x-for: "+e);return{loopVar:t[1]||t[3],indexVar:t[2]||null,arrayPath:t[4].trim()}}function C(e,t,r,n,o){return new Proxy(e,{has:(e,r)=>r===t||n&&r===n||r in e,get:(e,s)=>s===t?r:n&&s===n?o:e[s]})}function _(e,t){l._cloneContext.set(e,t),e.querySelectorAll("*").forEach(e=>l._cloneContext.set(e,t))}l&&Object.getPrototypeOf(l)&&Object.getOwnPropertyNames(Object.getPrototypeOf(l)).forEach(e=>{"function"==typeof l[e]&&"constructor"!==e&&(x[e]=l[e].bind(l))}),x.plugins=r;const E=u&&d("for");function b(e){try{const t=new WeakSet;return JSON.stringify(e,(e,r)=>{if("function"!=typeof r&&!e.startsWith("$")){if("object"==typeof r&&null!==r){if(t.has(r))return"[Circular]";t.add(r)}return r}})}catch(t){return`${e.length}-${e[0]?.name||""}-${e[e.length-1]?.name||""}`}}E&&console.groupCollapsed("x-for");let v=new Set,w=!0;for(;w;){const e=Array.from(a.querySelectorAll("template[x-for]")).concat(Array.from(l.__mirrorContainer.querySelectorAll("template[x-for]"))).filter(e=>!v.has(e));if(0===e.length){w=!1;break}e.forEach(e=>{let r,n,s;v.add(e),e._xForMeta||(e._xForMeta={parent:e.parentNode,next:e.nextSibling},l.__mirrorContainer.appendChild(e));try{({loopVar:r,indexVar:n,arrayPath:s}=A(e.getAttribute("x-for")))}catch(e){return void p("[x-for]",e.message)}const a=m(s,e);if(!Array.isArray(a))return void p(`[x-for] expected array at ${s}`,a);E&&console.log(`iterating ${s} → length ${a.length}`);const c=e.content.cloneNode(!0).querySelector("*"),u=c&&c.hasAttribute("x-key");if(!u){const t=b(a),r=l._xForCache.get(e);if(r&&r.arrayRef===a&&r.length===a.length&&r.signature===t)return void(E&&console.log(" → skipped (no change)"));l._xForCache.set(e,{arrayRef:a,length:a.length,signature:t})}if(u&&e._forClones&&e._forClones.length>0){E&&console.log(" → using x-key diffing");const s=c.getAttribute("x-key"),u=new Map;e._forClones.forEach(e=>{if(1===e.nodeType){const t=e.getAttribute("x-key");t&&u.set(t,e)}});const f=[],d=new Set;a.forEach((a,c)=>{E&&console.groupCollapsed(` index ${c}`);const h=C(o,r,a,n,c);let g;try{g=t(s,{this:h,...x})}catch(e){p("[x-for] Error evaluating x-key:",e),g=c}const y=String(g);if(u.has(y)){const e=u.get(y);d.add(e),E&&console.log(` → reusing node for key: ${y}`),l._cloneContext.set(e,h),f.push(e)}else{E&&console.log(` → creating new node for key: ${y}`);const t=i.createDocumentFragment();t.appendChild(e.content.cloneNode(!0));const r=t.firstElementChild;if(r){t.removeChild(r),r.setAttribute("x-key",y),_(r,h);(r.hasAttribute("render")?[r,...r.querySelectorAll("[render]")]:r.querySelectorAll("[render]")).forEach(e=>{const t=e.getAttribute("render")?.trim();t&&(l._xRenderCache.set(e,t),l._xRenderOriginalText.set(e,e.textContent))});(r.hasAttribute("x-class")?[r,...r.querySelectorAll("[x-class]")]:r.querySelectorAll("[x-class]")).forEach(e=>{const t=e.getAttribute("x-class");null!=t&&l._xClassCache.set(e,t)}),f.push(r)}}E&&console.groupEnd()}),e._forClones.forEach(e=>{if(!d.has(e)){if(E&&console.log(" → removing unused node"),1===e.nodeType){(e.hasAttribute("render")?[e,...e.querySelectorAll("[render]")]:e.querySelectorAll("[render]")).forEach(e=>{l._xRenderCache.delete(e),l._xRenderOriginalText.delete(e)});(e.hasAttribute("x-class")?[e,...e.querySelectorAll("[x-class]")]:e.querySelectorAll("[x-class]")).forEach(e=>{l._xClassCache.delete(e),l._xClassDynamicValues.delete(e)})}e.remove()}}),f.forEach((t,r)=>{const n=e._xForMeta.parent,o=r<f.length-1?f[r+1]:e._xForMeta.next;t.parentNode&&t.nextSibling===o||n.insertBefore(t,o)}),e._forClones=f}else E&&u&&console.log(" → first render with x-key"),E&&!u&&console.log(" → no x-key, full re-render"),e._forClones&&e._forClones.forEach(e=>{if(1===e.nodeType){(e.hasAttribute("render")?[e,...e.querySelectorAll("[render]")]:e.querySelectorAll("[render]")).forEach(e=>{l._xRenderCache.delete(e),l._xRenderOriginalText.delete(e)});(e.hasAttribute("x-class")?[e,...e.querySelectorAll("[x-class]")]:e.querySelectorAll("[x-class]")).forEach(e=>{l._xClassCache.delete(e),l._xClassDynamicValues.delete(e)})}e.remove()}),e._forClones=[],a.forEach((s,a)=>{E&&console.groupCollapsed(` index ${a}`);const c=i.createDocumentFragment();c.appendChild(e.content.cloneNode(!0));const f=C(o,r,s,n,a);if(_(c,f),u){const e=c.firstElementChild;if(e){const r=e.getAttribute("x-key");if(r)try{const n=t(r,{this:f,...x});e.setAttribute("x-key",String(n))}catch(t){e.setAttribute("x-key",String(a))}}}for(;c.firstChild;){const t=c.firstChild;if(e._xForMeta.parent.insertBefore(t,e._xForMeta.next),e._forClones.push(t),1===t.nodeType){(t.hasAttribute("render")?[t,...t.querySelectorAll("[render]")]:t.querySelectorAll("[render]")).forEach(e=>{const t=e.getAttribute("render")?.trim();t&&(l._xRenderCache.set(e,t),l._xRenderOriginalText.set(e,e.textContent))});(t.hasAttribute("x-class")?[t,...t.querySelectorAll("[x-class]")]:t.querySelectorAll("[x-class]")).forEach(e=>{const t=e.getAttribute("x-class");null!=t&&l._xClassCache.set(e,t)})}}E&&console.groupEnd()})})}E&&console.groupEnd();const S=u&&d("if");Array.from(a.querySelectorAll("[x-if]")).concat(Array.from(l.__mirrorContainer.querySelectorAll("[x-if]"))).forEach(e=>{const t=e.getAttribute("x-if")?.trim(),r=!t||y(t,e);S&&console.groupCollapsed("x-if",e,t,"→",r);let n=l._xIfData.get(e);n||(n={placeholder:i.createComment("x-if placeholder"),isHidden:!1},l._xIfData.set(e,n)),r||n.isHidden?r&&n.isHidden&&(n.placeholder.parentNode.replaceChild(e,n.placeholder),n.isHidden=!1):(e.parentNode.replaceChild(n.placeholder,e),l.__mirrorContainer.appendChild(e),n.isHidden=!0),S&&console.groupEnd()}),Array.from(a.querySelectorAll("[render]")).forEach(e=>{if(!l._xRenderCache.has(e)){const t=e.getAttribute("render")?.trim();t&&(l._xRenderCache.set(e,t),l._xRenderOriginalText.set(e,e.textContent))}}),Array.from(a.querySelectorAll("[x-class]")).forEach(e=>{const t=e.getAttribute("x-class");null==t||l._xClassCache.has(e)||l._xClassCache.set(e,t)});const $=u&&d("attr");Array.from(a.querySelectorAll("*")).forEach(e=>{Array.from(e.attributes).forEach(t=>{if(t.name.startsWith("x-")&&!["x-for","x-key","x-class","x-if","x-hidden"].includes(t.name)){const n=t.name,s=r.get(n);if(s&&"attribute"===s.target){const r=y(t.value.trim(),e);try{h&&console.log("[plugin]",`Executing ${n} with:`,{element:e,value:r,component:o}),s.execute(e,r,o)}catch(e){h&&console.error("[plugin]",`Error executing ${n}:`,e)}}else{const r=t.name.slice(2),n=y(t.value.trim(),e);$&&console.groupCollapsed("x-attr",e,r,"=",n),n?e.setAttribute(r,String(n)):e.removeAttribute(r),$&&console.groupEnd()}}})});const k=u&&d("class");k&&console.groupCollapsed("x-class");for(const[e,t]of l._xClassCache){k&&console.log("element →",e);const r=t.trim();if(r.includes("(")){const t=/\(\s*([^,]+?)\s*,\s*([^)]+?)\s*\)/g;l._xClassDynamicValues||(l._xClassDynamicValues=new Map);(l._xClassDynamicValues.get(e)||[]).forEach(t=>e.classList.remove(t));const n=new Set;let o;for(;o=t.exec(r);){const t=o[1].trim(),r=o[2].trim();let s;s=/^[\w\-\s]+$/.test(t)?t:y(t,e);const l=Boolean(y(r,e));let a=[];"string"==typeof s?a=s.split(/\s+/).filter(Boolean):Array.isArray(s)?a=s:s&&"object"==typeof s&&(a=Object.keys(s).filter(e=>s[e])),l&&a.forEach(t=>e.classList.add(t)),a.forEach(e=>n.add(e)),k&&console.log(` ${t} → [${a.join(" ")}] (= ${l})`)}l._xClassDynamicValues.set(e,Array.from(n))}else{l._xClassDynamicValues||(l._xClassDynamicValues=new Map);let t;(l._xClassDynamicValues.get(e)||[]).forEach(t=>e.classList.remove(t));let n=!0;if(!r.includes(",")&&/[?:]/.test(r))t=y(r,e);else{const o=r.indexOf(","),s=o>=0?r.slice(0,o).trim():r,l=o>=0?r.slice(o+1).trim():"true";n=Boolean(y(l,e)),t=s}const o=[];"string"==typeof t?o.push(...t.split(/\s+/).filter(Boolean)):Array.isArray(t)?o.push(...t):t&&"object"==typeof t&&o.push(...Object.keys(t).filter(e=>t[e])),o.forEach(t=>{n?e.classList.add(t):e.classList.remove(t)}),k&&console.log(` x-class ${r} →`,o,`ok=${n}`),l._xClassDynamicValues.set(e,o)}}k&&console.groupEnd();const q=u&&d("render");q&&console.groupCollapsed("render");for(const[e,t]of l._xRenderCache){const r=l._xIfData.get(e);if(r&&r.isHidden)continue;q&&console.log("element →",e,"expr=",t);const n=t.indexOf(","),o=n<0?t:t.slice(0,n).trim(),s=y(n<0?"true":t.slice(n+1).trim(),e);let a;if(s){let t=y(o,e);if(void 0===t)a=l._xRenderOriginalText.get(e)||"";else if(null!=t&&"object"==typeof t)try{a=JSON.stringify(t,null,2)}catch{a=String(t)}else a=null==t||"boolean"==typeof t?"":String(t)}else a=l._xRenderOriginalText.get(e)||"";e.textContent!==a&&(e.textContent=a),q&&console.log(` → "${a}" (cond=${s})`)}q&&console.groupEnd(),Array.from(a.querySelectorAll("*")).forEach(e=>{const t=l._xIfData.get(e);t&&t.isHidden||[["render","render"]].forEach(([t,r])=>{e.hasAttribute(t)&&!d(r)&&e.removeAttribute(t)})}),function e(r){Array.from(r.querySelectorAll("*")).forEach(e=>{Array.from(e.attributes).forEach(r=>{if(r.name.startsWith("@")){const n=r.name.slice(1),s=r.value.trim();e._xEventListeners&&e._xEventListeners[n]&&e.removeEventListener(n,e._xEventListeners[n]),e._xEventListeners||(e._xEventListeners={});const l=g(e),a=new Proxy(l,{get:(e,t)=>t in e?e[t]:o[t]}),i=function(e){try{return t(s,{this:a,event:e,...x})}catch(e){p(`[rendux @${n}] Error evaluating: ${s}`,e)}};e.addEventListener(n,i),e._xEventListeners[n]=i}})}),Array.from(r.querySelectorAll("slot")).forEach(t=>{(t.assignedElements?t.assignedElements({flatten:!0}):[]).forEach(t=>{e(t)})})}(a),s(a,o)}rendux.use=function(e){return n(e),rendux},"undefined"!=typeof module&&module.exports&&(module.exports={rendux:rendux,use:n,process:s,plugins:r,parsePluginCall:o}),exports.parsePluginCall=o,exports.plugins=r,exports.process=s,exports.rendux=rendux,exports.use=n;
1
+ /*! rendux v0.94.0 | Copyright (c) JULY 2025 Yannick J.A. CHARLERY | This software is licensed for non-commercial use only. Commercial licensing available upon request. For license details: https://github.com/ynck-chrl/rendux */
2
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});const e={Math:Math,Date:Date,Number:Number,String:String,Boolean:Boolean,JSON:JSON,parseInt:parseInt,parseFloat:parseFloat,isFinite:isFinite,Array:Array};function t(t,r={}){const n=function(e){const t=[];let r=0;for(;r<e.length;){let n=e[r];if(/\s/.test(n)){r++;continue}if('"'===n||"'"===n){const o=n;let s="";for(r++;r<e.length&&e[r]!==o;)"\\"===e[r]?(r++,r<e.length&&(s+=e[r++])):s+=e[r++];r++,t.push({type:"string",value:s});continue}if(/[0-9]/.test(n)||"."===n&&/[0-9]/.test(e[r+1])){let n=r;for(;r<e.length&&/[0-9]/.test(e[r]);)r++;if("."===e[r])for(r++;r<e.length&&/[0-9]/.test(e[r]);)r++;const o=parseFloat(e.slice(n,r));t.push({type:"number",value:o});continue}if(/[a-zA-Z_$]/.test(n)){let n=r;for(r++;r<e.length&&/[a-zA-Z0-9_$]/.test(e[r]);)r++;const o=e.slice(n,r);"true"===o||"false"===o?t.push({type:"boolean",value:"true"===o}):"null"===o?t.push({type:"null",value:null}):t.push({type:"identifier",value:o});continue}const o=e.substr(r,2),s=e.substr(r,3);if(["===","!=="].includes(s))t.push({type:"operator",value:s}),r+=3;else if(["==","!=",">=","<=","&&","||"].includes(o))t.push({type:"operator",value:o}),r+=2;else{if(!["+","-","*","/","%",">","<","!","?",":","(",")","[","]",".",","].includes(n))throw new Error(`Invalid character '${n}' in expression`);t.push({type:"operator",value:n}),r++}}return t.push({type:"EOF"}),t}(t);let o=0;function s(){return n[o]||{type:"EOF"}}function l(){return n[o++]||{type:"EOF"}}function a(e,t){const r=s();if(r.type!==e||void 0!==t&&r.value!==t)throw new Error(`Expected ${t||e} but got ${r.value}`);return l(),r}function i(e){const t=s();return"operator"===t.type&&t.value===e}const c=u();if("EOF"!==s().type)throw new Error(`Unexpected token: ${s().value}`);return c;function u(){let e=function(){let e=f();for(;i("||");){l();const t=f();e=e||t}return e}();if(i("?")){l();const t=u();a("operator",":");const r=u();e=e?t:r}return e}function f(){let e=d();for(;i("&&");){l();const t=d();e=e&&t}return e}function d(){let e=h();for(;["==","!=","===","!=="].includes(s().value);){const t=l().value,r=h();switch(t){case"==":e=e==r;break;case"!=":e=e!=r;break;case"===":e=e===r;break;case"!==":e=e!==r}}return e}function h(){let e=p();for(;[">","<",">=","<="].includes(s().value);){const t=l().value,r=p();switch(t){case">":e=e>r;break;case"<":e=e<r;break;case">=":e=e>=r;break;case"<=":e=e<=r}}return e}function p(){let e=x();for(;["+","-"].includes(s().value);){const t=l().value,r=x();e="+"===t?e+r:e-r}return e}function x(){let e=g();for(;["*","/","%"].includes(s().value);){const t=l().value,r=g();switch(t){case"*":e*=r;break;case"/":e/=r;break;case"%":e%=r}}return e}function g(){if(["!","+","-"].includes(s().value)){const e=l().value,t=g();switch(e){case"!":return!t;case"+":return+t;case"-":return-t}}return function(){const t=s();if("number"===t.type||"string"===t.type||"boolean"===t.type||"null"===t.type)return l(),t.value;if("identifier"===t.type){l();let n=function(t){if(t in r)return r[t];if(t in e)return e[t];if(null!=r.this&&t in r.this)return r.this[t];throw new Error(`Unknown identifier: ${t}`)}(t.value);for(;;)if(i(".")){l();const e=a("identifier").value;n=null==n?void 0:n[e]}else if(i("[")){l();const e=u();a("operator","]"),n=null==n?void 0:n[e]}else{if(!i("("))break;{l();const e=[];if(!i(")"))do{e.push(u())}while(i(",")&&l());if(a("operator",")"),"function"!=typeof n)throw new Error(`'${t.value}' is not a function`);n=n.apply(r.this,e)}}return n}if(i("(")){l();const e=u();return a("operator",")"),e}throw new Error(`Unexpected token: ${t.value}`)}()}}const r=new Map;function n(e){if(!e||!e.name)throw new Error("Invalid plugin");r.set(e.name,e)}function o(e,t={}){const n=e.match(/^([a-zA-Z_$][\w$]*)\s*\(\s*(.*)\s*\)$/);if(!n)return null;const o=n[1],s=r.get(o);if(!s)return null;const l=n[2].trim();if(""===l)return{plugin:s,args:[]};return{plugin:s,args:l.split(/\s*,\s*/).map(e=>{if(e.startsWith('"')&&e.endsWith('"')||e.startsWith("'")&&e.endsWith("'"))return e.slice(1,-1);if(/^-?\d+(?:\.\d+)?$/.test(e))return parseFloat(e);if(e in t)return t[e];throw new Error(`Unknown identifier: ${e}`)})}}async function s(e=document,t={}){const n=Array.from(e.querySelectorAll("*")).filter(e=>Array.from(e.attributes).some(e=>e.name.startsWith("render.")));for(const e of n)for(const n of Array.from(e.attributes)){if(!n.name.startsWith("render."))continue;const s=n.name,l=n.value.trim();let a,i,c;if("render.plugin"===s){let e;try{e=o(l,t)}catch(e){console.error(e);continue}if(!e)continue;a=e.plugin,i=e.args}else{const e=s.slice(7);if(a=r.get(e),!a)continue;if(l.startsWith(e+"(")){let e;try{e=o(l,t)}catch(e){console.error(e);continue}if(!e)continue;a=e.plugin,i=e.args}else i=[l]}"function"==typeof a.onBeforeExecute&&a.onBeforeExecute(e,l,t);try{c=a.execute(...i)}catch(e){continue}e.textContent=null==c?"":String(c),"function"==typeof a.onAfterExecute&&a.onAfterExecute(e,l,t)}for(const n of r.values())"function"==typeof n.onAfterRender&&await n.onAfterRender(e,t)}function rendux(e,n){const o=e,l=n;let a=n.shadowRoot||n;if(!a)throw new Error("rendux requires a DOM element as the second argument");for(const e of r.values())"function"==typeof e.onBeforeRender&&e.onBeforeRender(a,o);const i=document,c=l.getAttribute&&l.getAttribute("logs"),u=Boolean(c),f=c?new Set(c.split(",").map(e=>e.trim().toLowerCase())):new Set,d=e=>f.has("all")||f.has(e.toLowerCase())||f.has(("x-"+e).toLowerCase()),h=u&&f.has("plugins"),p=(...e)=>console.warn(...e);l._xRenderCache||(l._xRenderCache=new Map),l._xRenderOriginalText||(l._xRenderOriginalText=new WeakMap),l._xIfData||(l._xIfData=new WeakMap),l._cloneContext||(l._cloneContext=new WeakMap),l._xClassCache||(l._xClassCache=new Map),l._xForCache||(l._xForCache=new WeakMap),l.__mirrorContainer||(l.__mirrorContainer=i.createDocumentFragment());const x={};function g(e){let t=e;for(;t;){const e=l._cloneContext.get(t);if(e)return l.getAttribute&&l.getAttribute("logs")&&l.getAttribute("logs").includes("plugins")&&console.log("Found context for element:",t,e),e;t=t.parentNode}return l.getAttribute&&l.getAttribute("logs")&&l.getAttribute("logs").includes("plugins")&&console.log("Using default component context:",o),o}function y(e,r){if(!e)return!0;const n=g(r);try{return t(e,{this:n,...x})}catch(t){return void p(`[evaluate] "${e}" failed in`,n,t)}}function m(e,t){const r=g(t),n=e.match(/([^[.\]]+)|\[(\d+)\]/g);if(n)return n.reduce((e,t)=>{if(null!=e){if(t.startsWith("[")){const r=parseInt(t.slice(1,-1),10);return Array.isArray(e)?e[r]:void 0}return e[t]}},r)}function A(e){const t=e.match(/^\s*(?:\(\s*([^,\s]+)\s*,\s*([^,\s]+)\s*\)|([^,\s()]+))\s+(?:in|of)\s+(.+)$/);if(!t)throw new Error("Invalid x-for: "+e);return{loopVar:t[1]||t[3],indexVar:t[2]||null,arrayPath:t[4].trim()}}function C(e,t,r,n,o){return new Proxy(e,{has:(e,r)=>r===t||n&&r===n||r in e,get:(e,s)=>s===t?r:n&&s===n?o:e[s]})}function _(e,t){l._cloneContext.set(e,t),e.querySelectorAll("*").forEach(e=>l._cloneContext.set(e,t))}l&&Object.getPrototypeOf(l)&&Object.getOwnPropertyNames(Object.getPrototypeOf(l)).forEach(e=>{"function"==typeof l[e]&&"constructor"!==e&&(x[e]=l[e].bind(l))}),x.plugins=r;const E=u&&d("for");function b(e){try{const t=new WeakSet;return JSON.stringify(e,(e,r)=>{if("function"!=typeof r&&!e.startsWith("$")){if("object"==typeof r&&null!==r){if(t.has(r))return"[Circular]";t.add(r)}return r}})}catch(t){return`${e.length}-${e[0]?.name||""}-${e[e.length-1]?.name||""}`}}E&&console.groupCollapsed("x-for");let v=new Set,w=!0;for(;w;){const e=Array.from(a.querySelectorAll("template[x-for]")).concat(Array.from(l.__mirrorContainer.querySelectorAll("template[x-for]"))).filter(e=>!v.has(e));if(0===e.length){w=!1;break}e.forEach(e=>{let r,n,s;v.add(e),e._xForMeta||(e._xForMeta={parent:e.parentNode,next:e.nextSibling},l.__mirrorContainer.appendChild(e));try{({loopVar:r,indexVar:n,arrayPath:s}=A(e.getAttribute("x-for")))}catch(e){return void p("[x-for]",e.message)}const a=m(s,e);if(!Array.isArray(a))return void p(`[x-for] expected array at ${s}`,a);E&&console.log(`iterating ${s} → length ${a.length}`);const c=e.content.cloneNode(!0).querySelector("*"),u=c&&c.hasAttribute("x-key");if(!u){const t=b(a),r=l._xForCache.get(e);if(r&&r.arrayRef===a&&r.length===a.length&&r.signature===t)return void(E&&console.log(" → skipped (no change)"));l._xForCache.set(e,{arrayRef:a,length:a.length,signature:t})}if(u&&e._forClones&&e._forClones.length>0){E&&console.log(" → using x-key diffing");const s=c.getAttribute("x-key"),u=new Map;e._forClones.forEach(e=>{if(1===e.nodeType){const t=e.getAttribute("x-key");t&&u.set(t,e)}});const f=[],d=new Set;a.forEach((a,c)=>{E&&console.groupCollapsed(` index ${c}`);const h=C(o,r,a,n,c);let g;try{g=t(s,{this:h,...x})}catch(e){p("[x-for] Error evaluating x-key:",e),g=c}const y=String(g);if(u.has(y)){const e=u.get(y);d.add(e),E&&console.log(` → reusing node for key: ${y}`),_(e,h),f.push(e)}else{E&&console.log(` → creating new node for key: ${y}`);const t=i.createDocumentFragment();t.appendChild(e.content.cloneNode(!0));const r=t.firstElementChild;if(r){t.removeChild(r),r.setAttribute("x-key",y),_(r,h);(r.hasAttribute("render")?[r,...r.querySelectorAll("[render]")]:r.querySelectorAll("[render]")).forEach(e=>{const t=e.getAttribute("render")?.trim();t&&(l._xRenderCache.set(e,t),l._xRenderOriginalText.set(e,e.textContent))});(r.hasAttribute("x-class")?[r,...r.querySelectorAll("[x-class]")]:r.querySelectorAll("[x-class]")).forEach(e=>{const t=e.getAttribute("x-class");null!=t&&l._xClassCache.set(e,t)}),f.push(r)}}E&&console.groupEnd()}),e._forClones.forEach(e=>{if(!d.has(e)){if(E&&console.log(" → removing unused node"),1===e.nodeType){(e.hasAttribute("render")?[e,...e.querySelectorAll("[render]")]:e.querySelectorAll("[render]")).forEach(e=>{l._xRenderCache.delete(e),l._xRenderOriginalText.delete(e)});(e.hasAttribute("x-class")?[e,...e.querySelectorAll("[x-class]")]:e.querySelectorAll("[x-class]")).forEach(e=>{l._xClassCache.delete(e),l._xClassDynamicValues.delete(e)})}e.remove()}});for(let t=f.length-1;t>=0;t--){const r=f[t],n=e._xForMeta.parent,o=t<f.length-1?f[t+1]:e._xForMeta.next;r.parentNode&&r.nextSibling===o||n.insertBefore(r,o)}e._forClones=f}else E&&u&&console.log(" → first render with x-key"),E&&!u&&console.log(" → no x-key, full re-render"),e._forClones&&e._forClones.forEach(e=>{if(1===e.nodeType){(e.hasAttribute("render")?[e,...e.querySelectorAll("[render]")]:e.querySelectorAll("[render]")).forEach(e=>{l._xRenderCache.delete(e),l._xRenderOriginalText.delete(e)});(e.hasAttribute("x-class")?[e,...e.querySelectorAll("[x-class]")]:e.querySelectorAll("[x-class]")).forEach(e=>{l._xClassCache.delete(e),l._xClassDynamicValues.delete(e)})}e.remove()}),e._forClones=[],a.forEach((s,a)=>{E&&console.groupCollapsed(` index ${a}`);const c=i.createDocumentFragment();c.appendChild(e.content.cloneNode(!0));const f=C(o,r,s,n,a);if(_(c,f),u){const e=c.firstElementChild;if(e){const r=e.getAttribute("x-key");if(r)try{const n=t(r,{this:f,...x});e.setAttribute("x-key",String(n))}catch(t){e.setAttribute("x-key",String(a))}}}for(;c.firstChild;){const t=c.firstChild;if(e._xForMeta.parent.insertBefore(t,e._xForMeta.next),e._forClones.push(t),1===t.nodeType){(t.hasAttribute("render")?[t,...t.querySelectorAll("[render]")]:t.querySelectorAll("[render]")).forEach(e=>{const t=e.getAttribute("render")?.trim();t&&(l._xRenderCache.set(e,t),l._xRenderOriginalText.set(e,e.textContent))});(t.hasAttribute("x-class")?[t,...t.querySelectorAll("[x-class]")]:t.querySelectorAll("[x-class]")).forEach(e=>{const t=e.getAttribute("x-class");null!=t&&l._xClassCache.set(e,t)})}}E&&console.groupEnd()})})}E&&console.groupEnd();const S=u&&d("if");Array.from(a.querySelectorAll("[x-if]")).concat(Array.from(l.__mirrorContainer.querySelectorAll("[x-if]"))).forEach(e=>{const t=e.getAttribute("x-if")?.trim(),r=!t||y(t,e);S&&console.groupCollapsed("x-if",e,t,"→",r);let n=l._xIfData.get(e);n||(n={placeholder:i.createComment("x-if placeholder"),isHidden:!1},l._xIfData.set(e,n)),r||n.isHidden?r&&n.isHidden&&(n.placeholder.parentNode.replaceChild(e,n.placeholder),n.isHidden=!1):(e.parentNode.replaceChild(n.placeholder,e),l.__mirrorContainer.appendChild(e),n.isHidden=!0),S&&console.groupEnd()}),Array.from(a.querySelectorAll("[render]")).forEach(e=>{if(!l._xRenderCache.has(e)){const t=e.getAttribute("render")?.trim();t&&(l._xRenderCache.set(e,t),l._xRenderOriginalText.set(e,e.textContent))}}),Array.from(a.querySelectorAll("[x-class]")).forEach(e=>{const t=e.getAttribute("x-class");null==t||l._xClassCache.has(e)||l._xClassCache.set(e,t)});const $=u&&d("attr");Array.from(a.querySelectorAll("*")).forEach(e=>{Array.from(e.attributes).forEach(t=>{if(t.name.startsWith("x-")&&!["x-for","x-key","x-class","x-if","x-hidden"].includes(t.name)){const n=t.name,s=r.get(n);if(s&&"attribute"===s.target){const r=y(t.value.trim(),e);try{h&&console.log("[plugin]",`Executing ${n} with:`,{element:e,value:r,component:o}),s.execute(e,r,o)}catch(e){h&&console.error("[plugin]",`Error executing ${n}:`,e)}}else{const r=t.name.slice(2),n=y(t.value.trim(),e);$&&console.groupCollapsed("x-attr",e,r,"=",n),n?e.setAttribute(r,String(n)):e.removeAttribute(r),$&&console.groupEnd()}}})});const k=u&&d("class");k&&console.groupCollapsed("x-class");for(const[e,t]of l._xClassCache){k&&console.log("element →",e);const r=t.trim();if(r.includes("(")){const t=/\(\s*([^,]+?)\s*,\s*([^)]+?)\s*\)/g;l._xClassDynamicValues||(l._xClassDynamicValues=new Map);(l._xClassDynamicValues.get(e)||[]).forEach(t=>e.classList.remove(t));const n=new Set;let o;for(;o=t.exec(r);){const t=o[1].trim(),r=o[2].trim();let s;s=/^[\w\-\s]+$/.test(t)?t:y(t,e);const l=Boolean(y(r,e));let a=[];"string"==typeof s?a=s.split(/\s+/).filter(Boolean):Array.isArray(s)?a=s:s&&"object"==typeof s&&(a=Object.keys(s).filter(e=>s[e])),l&&a.forEach(t=>e.classList.add(t)),a.forEach(e=>n.add(e)),k&&console.log(` ${t} → [${a.join(" ")}] (= ${l})`)}l._xClassDynamicValues.set(e,Array.from(n))}else{l._xClassDynamicValues||(l._xClassDynamicValues=new Map);let t;(l._xClassDynamicValues.get(e)||[]).forEach(t=>e.classList.remove(t));let n=!0;if(!r.includes(",")&&/[?:]/.test(r))t=y(r,e);else{const o=r.indexOf(","),s=o>=0?r.slice(0,o).trim():r,l=o>=0?r.slice(o+1).trim():"true";n=Boolean(y(l,e)),t=s}const o=[];"string"==typeof t?o.push(...t.split(/\s+/).filter(Boolean)):Array.isArray(t)?o.push(...t):t&&"object"==typeof t&&o.push(...Object.keys(t).filter(e=>t[e])),o.forEach(t=>{n?e.classList.add(t):e.classList.remove(t)}),k&&console.log(` x-class ${r} →`,o,`ok=${n}`),l._xClassDynamicValues.set(e,o)}}k&&console.groupEnd();const q=u&&d("render");q&&console.groupCollapsed("render");for(const[e,t]of l._xRenderCache){const r=l._xIfData.get(e);if(r&&r.isHidden)continue;q&&console.log("element →",e,"expr=",t);const n=t.indexOf(","),o=n<0?t:t.slice(0,n).trim(),s=y(n<0?"true":t.slice(n+1).trim(),e);let a;if(s){let t=y(o,e);if(void 0===t)a=l._xRenderOriginalText.get(e)||"";else if(null!=t&&"object"==typeof t)try{a=JSON.stringify(t,null,2)}catch{a=String(t)}else a=null==t||"boolean"==typeof t?"":String(t)}else a=l._xRenderOriginalText.get(e)||"";e.textContent!==a&&(e.textContent=a),q&&console.log(` → "${a}" (cond=${s})`)}q&&console.groupEnd(),Array.from(a.querySelectorAll("*")).forEach(e=>{const t=l._xIfData.get(e);t&&t.isHidden||[["render","render"]].forEach(([t,r])=>{e.hasAttribute(t)&&!d(r)&&e.removeAttribute(t)})}),function e(r){Array.from(r.querySelectorAll("*")).forEach(e=>{Array.from(e.attributes).forEach(r=>{if(r.name.startsWith("@")){const n=r.name.slice(1),s=r.value.trim();e._xEventListeners&&e._xEventListeners[n]&&e.removeEventListener(n,e._xEventListeners[n]),e._xEventListeners||(e._xEventListeners={});const l=g(e),a=new Proxy(l,{get:(e,t)=>t in e?e[t]:o[t]}),i=function(e){try{return t(s,{this:a,event:e,...x})}catch(e){p(`[rendux @${n}] Error evaluating: ${s}`,e)}};e.addEventListener(n,i),e._xEventListeners[n]=i}})}),Array.from(r.querySelectorAll("slot")).forEach(t=>{(t.assignedElements?t.assignedElements({flatten:!0}):[]).forEach(t=>{e(t)})})}(a),s(a,o)}rendux.use=function(e){return n(e),rendux},"undefined"!=typeof module&&module.exports&&(module.exports={rendux:rendux,use:n,process:s,plugins:r,parsePluginCall:o}),exports.html=(e,...t)=>String.raw({raw:e},...t),exports.parsePluginCall=o,exports.plugins=r,exports.process=s,exports.rendux=rendux,exports.use=n;
@@ -1,2 +1,2 @@
1
- /*! rendux v0.93.2 | Copyright (c) JULY 2025 Yannick J.A. CHARLERY | This software is licensed for non-commercial use only. Commercial licensing available upon request. For license details: https://github.com/ynck-chrl/rendux */
2
- const e={Math:Math,Date:Date,Number:Number,String:String,Boolean:Boolean,JSON:JSON,parseInt:parseInt,parseFloat:parseFloat,isFinite:isFinite,Array:Array};function t(t,r={}){const n=function(e){const t=[];let r=0;for(;r<e.length;){let n=e[r];if(/\s/.test(n)){r++;continue}if('"'===n||"'"===n){const o=n;let s="";for(r++;r<e.length&&e[r]!==o;)"\\"===e[r]?(r++,r<e.length&&(s+=e[r++])):s+=e[r++];r++,t.push({type:"string",value:s});continue}if(/[0-9]/.test(n)||"."===n&&/[0-9]/.test(e[r+1])){let n=r;for(;r<e.length&&/[0-9]/.test(e[r]);)r++;if("."===e[r])for(r++;r<e.length&&/[0-9]/.test(e[r]);)r++;const o=parseFloat(e.slice(n,r));t.push({type:"number",value:o});continue}if(/[a-zA-Z_$]/.test(n)){let n=r;for(r++;r<e.length&&/[a-zA-Z0-9_$]/.test(e[r]);)r++;const o=e.slice(n,r);"true"===o||"false"===o?t.push({type:"boolean",value:"true"===o}):"null"===o?t.push({type:"null",value:null}):t.push({type:"identifier",value:o});continue}const o=e.substr(r,2),s=e.substr(r,3);if(["===","!=="].includes(s))t.push({type:"operator",value:s}),r+=3;else if(["==","!=",">=","<=","&&","||"].includes(o))t.push({type:"operator",value:o}),r+=2;else{if(!["+","-","*","/","%",">","<","!","?",":","(",")","[","]",".",","].includes(n))throw new Error(`Invalid character '${n}' in expression`);t.push({type:"operator",value:n}),r++}}return t.push({type:"EOF"}),t}(t);let o=0;function s(){return n[o]||{type:"EOF"}}function l(){return n[o++]||{type:"EOF"}}function a(e,t){const r=s();if(r.type!==e||void 0!==t&&r.value!==t)throw new Error(`Expected ${t||e} but got ${r.value}`);return l(),r}function i(e){const t=s();return"operator"===t.type&&t.value===e}const c=u();if("EOF"!==s().type)throw new Error(`Unexpected token: ${s().value}`);return c;function u(){let e=function(){let e=f();for(;i("||");){l();const t=f();e=e||t}return e}();if(i("?")){l();const t=u();a("operator",":");const r=u();e=e?t:r}return e}function f(){let e=d();for(;i("&&");){l();const t=d();e=e&&t}return e}function d(){let e=h();for(;["==","!=","===","!=="].includes(s().value);){const t=l().value,r=h();switch(t){case"==":e=e==r;break;case"!=":e=e!=r;break;case"===":e=e===r;break;case"!==":e=e!==r}}return e}function h(){let e=p();for(;[">","<",">=","<="].includes(s().value);){const t=l().value,r=p();switch(t){case">":e=e>r;break;case"<":e=e<r;break;case">=":e=e>=r;break;case"<=":e=e<=r}}return e}function p(){let e=x();for(;["+","-"].includes(s().value);){const t=l().value,r=x();e="+"===t?e+r:e-r}return e}function x(){let e=g();for(;["*","/","%"].includes(s().value);){const t=l().value,r=g();switch(t){case"*":e*=r;break;case"/":e/=r;break;case"%":e%=r}}return e}function g(){if(["!","+","-"].includes(s().value)){const e=l().value,t=g();switch(e){case"!":return!t;case"+":return+t;case"-":return-t}}return function(){const t=s();if("number"===t.type||"string"===t.type||"boolean"===t.type||"null"===t.type)return l(),t.value;if("identifier"===t.type){l();let n=function(t){if(t in r)return r[t];if(t in e)return e[t];if(null!=r.this&&t in r.this)return r.this[t];throw new Error(`Unknown identifier: ${t}`)}(t.value);for(;;)if(i(".")){l();const e=a("identifier").value;n=null==n?void 0:n[e]}else if(i("[")){l();const e=u();a("operator","]"),n=null==n?void 0:n[e]}else{if(!i("("))break;{l();const e=[];if(!i(")"))do{e.push(u())}while(i(",")&&l());if(a("operator",")"),"function"!=typeof n)throw new Error(`'${t.value}' is not a function`);n=n.apply(r.this,e)}}return n}if(i("(")){l();const e=u();return a("operator",")"),e}throw new Error(`Unexpected token: ${t.value}`)}()}}const r=new Map;function n(e){if(!e||!e.name)throw new Error("Invalid plugin");r.set(e.name,e)}function o(e,t={}){const n=e.match(/^([a-zA-Z_$][\w$]*)\s*\(\s*(.*)\s*\)$/);if(!n)return null;const o=n[1],s=r.get(o);if(!s)return null;const l=n[2].trim();if(""===l)return{plugin:s,args:[]};return{plugin:s,args:l.split(/\s*,\s*/).map(e=>{if(e.startsWith('"')&&e.endsWith('"')||e.startsWith("'")&&e.endsWith("'"))return e.slice(1,-1);if(/^-?\d+(?:\.\d+)?$/.test(e))return parseFloat(e);if(e in t)return t[e];throw new Error(`Unknown identifier: ${e}`)})}}async function s(e=document,t={}){const n=Array.from(e.querySelectorAll("*")).filter(e=>Array.from(e.attributes).some(e=>e.name.startsWith("render.")));for(const e of n)for(const n of Array.from(e.attributes)){if(!n.name.startsWith("render."))continue;const s=n.name,l=n.value.trim();let a,i,c;if("render.plugin"===s){let e;try{e=o(l,t)}catch(e){console.error(e);continue}if(!e)continue;a=e.plugin,i=e.args}else{const e=s.slice(7);if(a=r.get(e),!a)continue;if(l.startsWith(e+"(")){let e;try{e=o(l,t)}catch(e){console.error(e);continue}if(!e)continue;a=e.plugin,i=e.args}else i=[l]}"function"==typeof a.onBeforeExecute&&a.onBeforeExecute(e,l,t);try{c=a.execute(...i)}catch(e){continue}e.textContent=null==c?"":String(c),"function"==typeof a.onAfterExecute&&a.onAfterExecute(e,l,t)}for(const n of r.values())"function"==typeof n.onAfterRender&&await n.onAfterRender(e,t)}function rendux(e,n){const o=e,l=n;let a=n.shadowRoot||n;if(!a)throw new Error("rendux requires a DOM element as the second argument");for(const e of r.values())"function"==typeof e.onBeforeRender&&e.onBeforeRender(a,o);const i=document,c=l.getAttribute&&l.getAttribute("logs"),u=Boolean(c),f=c?new Set(c.split(",").map(e=>e.trim().toLowerCase())):new Set,d=e=>f.has("all")||f.has(e.toLowerCase())||f.has(("x-"+e).toLowerCase()),h=u&&f.has("plugins"),p=(...e)=>console.warn(...e);l._xRenderCache||(l._xRenderCache=new Map),l._xRenderOriginalText||(l._xRenderOriginalText=new WeakMap),l._xIfData||(l._xIfData=new WeakMap),l._cloneContext||(l._cloneContext=new WeakMap),l._xClassCache||(l._xClassCache=new Map),l._xForCache||(l._xForCache=new WeakMap),l.__mirrorContainer||(l.__mirrorContainer=i.createDocumentFragment());const x={};function g(e){let t=e;for(;t;){const e=l._cloneContext.get(t);if(e)return l.getAttribute&&l.getAttribute("logs")&&l.getAttribute("logs").includes("plugins")&&console.log("Found context for element:",t,e),e;t=t.parentNode}return l.getAttribute&&l.getAttribute("logs")&&l.getAttribute("logs").includes("plugins")&&console.log("Using default component context:",o),o}function y(e,r){if(!e)return!0;const n=g(r);try{return t(e,{this:n,...x})}catch(t){return void p(`[evaluate] "${e}" failed in`,n,t)}}function m(e,t){const r=g(t),n=e.match(/([^[.\]]+)|\[(\d+)\]/g);if(n)return n.reduce((e,t)=>{if(null!=e){if(t.startsWith("[")){const r=parseInt(t.slice(1,-1),10);return Array.isArray(e)?e[r]:void 0}return e[t]}},r)}function A(e){const t=e.match(/^\s*(?:\(\s*([^,\s]+)\s*,\s*([^,\s]+)\s*\)|([^,\s()]+))\s+(?:in|of)\s+(.+)$/);if(!t)throw new Error("Invalid x-for: "+e);return{loopVar:t[1]||t[3],indexVar:t[2]||null,arrayPath:t[4].trim()}}function C(e,t,r,n,o){return new Proxy(e,{has:(e,r)=>r===t||n&&r===n||r in e,get:(e,s)=>s===t?r:n&&s===n?o:e[s]})}function _(e,t){l._cloneContext.set(e,t),e.querySelectorAll("*").forEach(e=>l._cloneContext.set(e,t))}l&&Object.getPrototypeOf(l)&&Object.getOwnPropertyNames(Object.getPrototypeOf(l)).forEach(e=>{"function"==typeof l[e]&&"constructor"!==e&&(x[e]=l[e].bind(l))}),x.plugins=r;const E=u&&d("for");function b(e){try{const t=new WeakSet;return JSON.stringify(e,(e,r)=>{if("function"!=typeof r&&!e.startsWith("$")){if("object"==typeof r&&null!==r){if(t.has(r))return"[Circular]";t.add(r)}return r}})}catch(t){return`${e.length}-${e[0]?.name||""}-${e[e.length-1]?.name||""}`}}E&&console.groupCollapsed("x-for");let v=new Set,w=!0;for(;w;){const e=Array.from(a.querySelectorAll("template[x-for]")).concat(Array.from(l.__mirrorContainer.querySelectorAll("template[x-for]"))).filter(e=>!v.has(e));if(0===e.length){w=!1;break}e.forEach(e=>{let r,n,s;v.add(e),e._xForMeta||(e._xForMeta={parent:e.parentNode,next:e.nextSibling},l.__mirrorContainer.appendChild(e));try{({loopVar:r,indexVar:n,arrayPath:s}=A(e.getAttribute("x-for")))}catch(e){return void p("[x-for]",e.message)}const a=m(s,e);if(!Array.isArray(a))return void p(`[x-for] expected array at ${s}`,a);E&&console.log(`iterating ${s} → length ${a.length}`);const c=e.content.cloneNode(!0).querySelector("*"),u=c&&c.hasAttribute("x-key");if(!u){const t=b(a),r=l._xForCache.get(e);if(r&&r.arrayRef===a&&r.length===a.length&&r.signature===t)return void(E&&console.log(" → skipped (no change)"));l._xForCache.set(e,{arrayRef:a,length:a.length,signature:t})}if(u&&e._forClones&&e._forClones.length>0){E&&console.log(" → using x-key diffing");const s=c.getAttribute("x-key"),u=new Map;e._forClones.forEach(e=>{if(1===e.nodeType){const t=e.getAttribute("x-key");t&&u.set(t,e)}});const f=[],d=new Set;a.forEach((a,c)=>{E&&console.groupCollapsed(` index ${c}`);const h=C(o,r,a,n,c);let g;try{g=t(s,{this:h,...x})}catch(e){p("[x-for] Error evaluating x-key:",e),g=c}const y=String(g);if(u.has(y)){const e=u.get(y);d.add(e),E&&console.log(` → reusing node for key: ${y}`),l._cloneContext.set(e,h),f.push(e)}else{E&&console.log(` → creating new node for key: ${y}`);const t=i.createDocumentFragment();t.appendChild(e.content.cloneNode(!0));const r=t.firstElementChild;if(r){t.removeChild(r),r.setAttribute("x-key",y),_(r,h);(r.hasAttribute("render")?[r,...r.querySelectorAll("[render]")]:r.querySelectorAll("[render]")).forEach(e=>{const t=e.getAttribute("render")?.trim();t&&(l._xRenderCache.set(e,t),l._xRenderOriginalText.set(e,e.textContent))});(r.hasAttribute("x-class")?[r,...r.querySelectorAll("[x-class]")]:r.querySelectorAll("[x-class]")).forEach(e=>{const t=e.getAttribute("x-class");null!=t&&l._xClassCache.set(e,t)}),f.push(r)}}E&&console.groupEnd()}),e._forClones.forEach(e=>{if(!d.has(e)){if(E&&console.log(" → removing unused node"),1===e.nodeType){(e.hasAttribute("render")?[e,...e.querySelectorAll("[render]")]:e.querySelectorAll("[render]")).forEach(e=>{l._xRenderCache.delete(e),l._xRenderOriginalText.delete(e)});(e.hasAttribute("x-class")?[e,...e.querySelectorAll("[x-class]")]:e.querySelectorAll("[x-class]")).forEach(e=>{l._xClassCache.delete(e),l._xClassDynamicValues.delete(e)})}e.remove()}}),f.forEach((t,r)=>{const n=e._xForMeta.parent,o=r<f.length-1?f[r+1]:e._xForMeta.next;t.parentNode&&t.nextSibling===o||n.insertBefore(t,o)}),e._forClones=f}else E&&u&&console.log(" → first render with x-key"),E&&!u&&console.log(" → no x-key, full re-render"),e._forClones&&e._forClones.forEach(e=>{if(1===e.nodeType){(e.hasAttribute("render")?[e,...e.querySelectorAll("[render]")]:e.querySelectorAll("[render]")).forEach(e=>{l._xRenderCache.delete(e),l._xRenderOriginalText.delete(e)});(e.hasAttribute("x-class")?[e,...e.querySelectorAll("[x-class]")]:e.querySelectorAll("[x-class]")).forEach(e=>{l._xClassCache.delete(e),l._xClassDynamicValues.delete(e)})}e.remove()}),e._forClones=[],a.forEach((s,a)=>{E&&console.groupCollapsed(` index ${a}`);const c=i.createDocumentFragment();c.appendChild(e.content.cloneNode(!0));const f=C(o,r,s,n,a);if(_(c,f),u){const e=c.firstElementChild;if(e){const r=e.getAttribute("x-key");if(r)try{const n=t(r,{this:f,...x});e.setAttribute("x-key",String(n))}catch(t){e.setAttribute("x-key",String(a))}}}for(;c.firstChild;){const t=c.firstChild;if(e._xForMeta.parent.insertBefore(t,e._xForMeta.next),e._forClones.push(t),1===t.nodeType){(t.hasAttribute("render")?[t,...t.querySelectorAll("[render]")]:t.querySelectorAll("[render]")).forEach(e=>{const t=e.getAttribute("render")?.trim();t&&(l._xRenderCache.set(e,t),l._xRenderOriginalText.set(e,e.textContent))});(t.hasAttribute("x-class")?[t,...t.querySelectorAll("[x-class]")]:t.querySelectorAll("[x-class]")).forEach(e=>{const t=e.getAttribute("x-class");null!=t&&l._xClassCache.set(e,t)})}}E&&console.groupEnd()})})}E&&console.groupEnd();const S=u&&d("if");Array.from(a.querySelectorAll("[x-if]")).concat(Array.from(l.__mirrorContainer.querySelectorAll("[x-if]"))).forEach(e=>{const t=e.getAttribute("x-if")?.trim(),r=!t||y(t,e);S&&console.groupCollapsed("x-if",e,t,"→",r);let n=l._xIfData.get(e);n||(n={placeholder:i.createComment("x-if placeholder"),isHidden:!1},l._xIfData.set(e,n)),r||n.isHidden?r&&n.isHidden&&(n.placeholder.parentNode.replaceChild(e,n.placeholder),n.isHidden=!1):(e.parentNode.replaceChild(n.placeholder,e),l.__mirrorContainer.appendChild(e),n.isHidden=!0),S&&console.groupEnd()}),Array.from(a.querySelectorAll("[render]")).forEach(e=>{if(!l._xRenderCache.has(e)){const t=e.getAttribute("render")?.trim();t&&(l._xRenderCache.set(e,t),l._xRenderOriginalText.set(e,e.textContent))}}),Array.from(a.querySelectorAll("[x-class]")).forEach(e=>{const t=e.getAttribute("x-class");null==t||l._xClassCache.has(e)||l._xClassCache.set(e,t)});const $=u&&d("attr");Array.from(a.querySelectorAll("*")).forEach(e=>{Array.from(e.attributes).forEach(t=>{if(t.name.startsWith("x-")&&!["x-for","x-key","x-class","x-if","x-hidden"].includes(t.name)){const n=t.name,s=r.get(n);if(s&&"attribute"===s.target){const r=y(t.value.trim(),e);try{h&&console.log("[plugin]",`Executing ${n} with:`,{element:e,value:r,component:o}),s.execute(e,r,o)}catch(e){h&&console.error("[plugin]",`Error executing ${n}:`,e)}}else{const r=t.name.slice(2),n=y(t.value.trim(),e);$&&console.groupCollapsed("x-attr",e,r,"=",n),n?e.setAttribute(r,String(n)):e.removeAttribute(r),$&&console.groupEnd()}}})});const k=u&&d("class");k&&console.groupCollapsed("x-class");for(const[e,t]of l._xClassCache){k&&console.log("element →",e);const r=t.trim();if(r.includes("(")){const t=/\(\s*([^,]+?)\s*,\s*([^)]+?)\s*\)/g;l._xClassDynamicValues||(l._xClassDynamicValues=new Map);(l._xClassDynamicValues.get(e)||[]).forEach(t=>e.classList.remove(t));const n=new Set;let o;for(;o=t.exec(r);){const t=o[1].trim(),r=o[2].trim();let s;s=/^[\w\-\s]+$/.test(t)?t:y(t,e);const l=Boolean(y(r,e));let a=[];"string"==typeof s?a=s.split(/\s+/).filter(Boolean):Array.isArray(s)?a=s:s&&"object"==typeof s&&(a=Object.keys(s).filter(e=>s[e])),l&&a.forEach(t=>e.classList.add(t)),a.forEach(e=>n.add(e)),k&&console.log(` ${t} → [${a.join(" ")}] (= ${l})`)}l._xClassDynamicValues.set(e,Array.from(n))}else{l._xClassDynamicValues||(l._xClassDynamicValues=new Map);let t;(l._xClassDynamicValues.get(e)||[]).forEach(t=>e.classList.remove(t));let n=!0;if(!r.includes(",")&&/[?:]/.test(r))t=y(r,e);else{const o=r.indexOf(","),s=o>=0?r.slice(0,o).trim():r,l=o>=0?r.slice(o+1).trim():"true";n=Boolean(y(l,e)),t=s}const o=[];"string"==typeof t?o.push(...t.split(/\s+/).filter(Boolean)):Array.isArray(t)?o.push(...t):t&&"object"==typeof t&&o.push(...Object.keys(t).filter(e=>t[e])),o.forEach(t=>{n?e.classList.add(t):e.classList.remove(t)}),k&&console.log(` x-class ${r} →`,o,`ok=${n}`),l._xClassDynamicValues.set(e,o)}}k&&console.groupEnd();const q=u&&d("render");q&&console.groupCollapsed("render");for(const[e,t]of l._xRenderCache){const r=l._xIfData.get(e);if(r&&r.isHidden)continue;q&&console.log("element →",e,"expr=",t);const n=t.indexOf(","),o=n<0?t:t.slice(0,n).trim(),s=y(n<0?"true":t.slice(n+1).trim(),e);let a;if(s){let t=y(o,e);if(void 0===t)a=l._xRenderOriginalText.get(e)||"";else if(null!=t&&"object"==typeof t)try{a=JSON.stringify(t,null,2)}catch{a=String(t)}else a=null==t||"boolean"==typeof t?"":String(t)}else a=l._xRenderOriginalText.get(e)||"";e.textContent!==a&&(e.textContent=a),q&&console.log(` → "${a}" (cond=${s})`)}q&&console.groupEnd(),Array.from(a.querySelectorAll("*")).forEach(e=>{const t=l._xIfData.get(e);t&&t.isHidden||[["render","render"]].forEach(([t,r])=>{e.hasAttribute(t)&&!d(r)&&e.removeAttribute(t)})}),function e(r){Array.from(r.querySelectorAll("*")).forEach(e=>{Array.from(e.attributes).forEach(r=>{if(r.name.startsWith("@")){const n=r.name.slice(1),s=r.value.trim();e._xEventListeners&&e._xEventListeners[n]&&e.removeEventListener(n,e._xEventListeners[n]),e._xEventListeners||(e._xEventListeners={});const l=g(e),a=new Proxy(l,{get:(e,t)=>t in e?e[t]:o[t]}),i=function(e){try{return t(s,{this:a,event:e,...x})}catch(e){p(`[rendux @${n}] Error evaluating: ${s}`,e)}};e.addEventListener(n,i),e._xEventListeners[n]=i}})}),Array.from(r.querySelectorAll("slot")).forEach(t=>{(t.assignedElements?t.assignedElements({flatten:!0}):[]).forEach(t=>{e(t)})})}(a),s(a,o)}rendux.use=function(e){return n(e),rendux},"undefined"!=typeof module&&module.exports&&(module.exports={rendux:rendux,use:n,process:s,plugins:r,parsePluginCall:o});export{o as parsePluginCall,r as plugins,s as process,rendux,n as use};
1
+ /*! rendux v0.94.0 | Copyright (c) JULY 2025 Yannick J.A. CHARLERY | This software is licensed for non-commercial use only. Commercial licensing available upon request. For license details: https://github.com/ynck-chrl/rendux */
2
+ const e={Math:Math,Date:Date,Number:Number,String:String,Boolean:Boolean,JSON:JSON,parseInt:parseInt,parseFloat:parseFloat,isFinite:isFinite,Array:Array},t=(e,...t)=>String.raw({raw:e},...t);function r(t,r={}){const n=function(e){const t=[];let r=0;for(;r<e.length;){let n=e[r];if(/\s/.test(n)){r++;continue}if('"'===n||"'"===n){const o=n;let s="";for(r++;r<e.length&&e[r]!==o;)"\\"===e[r]?(r++,r<e.length&&(s+=e[r++])):s+=e[r++];r++,t.push({type:"string",value:s});continue}if(/[0-9]/.test(n)||"."===n&&/[0-9]/.test(e[r+1])){let n=r;for(;r<e.length&&/[0-9]/.test(e[r]);)r++;if("."===e[r])for(r++;r<e.length&&/[0-9]/.test(e[r]);)r++;const o=parseFloat(e.slice(n,r));t.push({type:"number",value:o});continue}if(/[a-zA-Z_$]/.test(n)){let n=r;for(r++;r<e.length&&/[a-zA-Z0-9_$]/.test(e[r]);)r++;const o=e.slice(n,r);"true"===o||"false"===o?t.push({type:"boolean",value:"true"===o}):"null"===o?t.push({type:"null",value:null}):t.push({type:"identifier",value:o});continue}const o=e.substr(r,2),s=e.substr(r,3);if(["===","!=="].includes(s))t.push({type:"operator",value:s}),r+=3;else if(["==","!=",">=","<=","&&","||"].includes(o))t.push({type:"operator",value:o}),r+=2;else{if(!["+","-","*","/","%",">","<","!","?",":","(",")","[","]",".",","].includes(n))throw new Error(`Invalid character '${n}' in expression`);t.push({type:"operator",value:n}),r++}}return t.push({type:"EOF"}),t}(t);let o=0;function s(){return n[o]||{type:"EOF"}}function l(){return n[o++]||{type:"EOF"}}function a(e,t){const r=s();if(r.type!==e||void 0!==t&&r.value!==t)throw new Error(`Expected ${t||e} but got ${r.value}`);return l(),r}function i(e){const t=s();return"operator"===t.type&&t.value===e}const c=u();if("EOF"!==s().type)throw new Error(`Unexpected token: ${s().value}`);return c;function u(){let e=function(){let e=f();for(;i("||");){l();const t=f();e=e||t}return e}();if(i("?")){l();const t=u();a("operator",":");const r=u();e=e?t:r}return e}function f(){let e=d();for(;i("&&");){l();const t=d();e=e&&t}return e}function d(){let e=h();for(;["==","!=","===","!=="].includes(s().value);){const t=l().value,r=h();switch(t){case"==":e=e==r;break;case"!=":e=e!=r;break;case"===":e=e===r;break;case"!==":e=e!==r}}return e}function h(){let e=p();for(;[">","<",">=","<="].includes(s().value);){const t=l().value,r=p();switch(t){case">":e=e>r;break;case"<":e=e<r;break;case">=":e=e>=r;break;case"<=":e=e<=r}}return e}function p(){let e=x();for(;["+","-"].includes(s().value);){const t=l().value,r=x();e="+"===t?e+r:e-r}return e}function x(){let e=g();for(;["*","/","%"].includes(s().value);){const t=l().value,r=g();switch(t){case"*":e*=r;break;case"/":e/=r;break;case"%":e%=r}}return e}function g(){if(["!","+","-"].includes(s().value)){const e=l().value,t=g();switch(e){case"!":return!t;case"+":return+t;case"-":return-t}}return function(){const t=s();if("number"===t.type||"string"===t.type||"boolean"===t.type||"null"===t.type)return l(),t.value;if("identifier"===t.type){l();let n=function(t){if(t in r)return r[t];if(t in e)return e[t];if(null!=r.this&&t in r.this)return r.this[t];throw new Error(`Unknown identifier: ${t}`)}(t.value);for(;;)if(i(".")){l();const e=a("identifier").value;n=null==n?void 0:n[e]}else if(i("[")){l();const e=u();a("operator","]"),n=null==n?void 0:n[e]}else{if(!i("("))break;{l();const e=[];if(!i(")"))do{e.push(u())}while(i(",")&&l());if(a("operator",")"),"function"!=typeof n)throw new Error(`'${t.value}' is not a function`);n=n.apply(r.this,e)}}return n}if(i("(")){l();const e=u();return a("operator",")"),e}throw new Error(`Unexpected token: ${t.value}`)}()}}const n=new Map;function o(e){if(!e||!e.name)throw new Error("Invalid plugin");n.set(e.name,e)}function s(e,t={}){const r=e.match(/^([a-zA-Z_$][\w$]*)\s*\(\s*(.*)\s*\)$/);if(!r)return null;const o=r[1],s=n.get(o);if(!s)return null;const l=r[2].trim();if(""===l)return{plugin:s,args:[]};return{plugin:s,args:l.split(/\s*,\s*/).map(e=>{if(e.startsWith('"')&&e.endsWith('"')||e.startsWith("'")&&e.endsWith("'"))return e.slice(1,-1);if(/^-?\d+(?:\.\d+)?$/.test(e))return parseFloat(e);if(e in t)return t[e];throw new Error(`Unknown identifier: ${e}`)})}}async function l(e=document,t={}){const r=Array.from(e.querySelectorAll("*")).filter(e=>Array.from(e.attributes).some(e=>e.name.startsWith("render.")));for(const e of r)for(const r of Array.from(e.attributes)){if(!r.name.startsWith("render."))continue;const o=r.name,l=r.value.trim();let a,i,c;if("render.plugin"===o){let e;try{e=s(l,t)}catch(e){console.error(e);continue}if(!e)continue;a=e.plugin,i=e.args}else{const e=o.slice(7);if(a=n.get(e),!a)continue;if(l.startsWith(e+"(")){let e;try{e=s(l,t)}catch(e){console.error(e);continue}if(!e)continue;a=e.plugin,i=e.args}else i=[l]}"function"==typeof a.onBeforeExecute&&a.onBeforeExecute(e,l,t);try{c=a.execute(...i)}catch(e){continue}e.textContent=null==c?"":String(c),"function"==typeof a.onAfterExecute&&a.onAfterExecute(e,l,t)}for(const r of n.values())"function"==typeof r.onAfterRender&&await r.onAfterRender(e,t)}function rendux(e,t){const o=e,s=t;let a=t.shadowRoot||t;if(!a)throw new Error("rendux requires a DOM element as the second argument");for(const e of n.values())"function"==typeof e.onBeforeRender&&e.onBeforeRender(a,o);const i=document,c=s.getAttribute&&s.getAttribute("logs"),u=Boolean(c),f=c?new Set(c.split(",").map(e=>e.trim().toLowerCase())):new Set,d=e=>f.has("all")||f.has(e.toLowerCase())||f.has(("x-"+e).toLowerCase()),h=u&&f.has("plugins"),p=(...e)=>console.warn(...e);s._xRenderCache||(s._xRenderCache=new Map),s._xRenderOriginalText||(s._xRenderOriginalText=new WeakMap),s._xIfData||(s._xIfData=new WeakMap),s._cloneContext||(s._cloneContext=new WeakMap),s._xClassCache||(s._xClassCache=new Map),s._xForCache||(s._xForCache=new WeakMap),s.__mirrorContainer||(s.__mirrorContainer=i.createDocumentFragment());const x={};function g(e){let t=e;for(;t;){const e=s._cloneContext.get(t);if(e)return s.getAttribute&&s.getAttribute("logs")&&s.getAttribute("logs").includes("plugins")&&console.log("Found context for element:",t,e),e;t=t.parentNode}return s.getAttribute&&s.getAttribute("logs")&&s.getAttribute("logs").includes("plugins")&&console.log("Using default component context:",o),o}function y(e,t){if(!e)return!0;const n=g(t);try{return r(e,{this:n,...x})}catch(t){return void p(`[evaluate] "${e}" failed in`,n,t)}}function m(e,t){const r=g(t),n=e.match(/([^[.\]]+)|\[(\d+)\]/g);if(n)return n.reduce((e,t)=>{if(null!=e){if(t.startsWith("[")){const r=parseInt(t.slice(1,-1),10);return Array.isArray(e)?e[r]:void 0}return e[t]}},r)}function A(e){const t=e.match(/^\s*(?:\(\s*([^,\s]+)\s*,\s*([^,\s]+)\s*\)|([^,\s()]+))\s+(?:in|of)\s+(.+)$/);if(!t)throw new Error("Invalid x-for: "+e);return{loopVar:t[1]||t[3],indexVar:t[2]||null,arrayPath:t[4].trim()}}function C(e,t,r,n,o){return new Proxy(e,{has:(e,r)=>r===t||n&&r===n||r in e,get:(e,s)=>s===t?r:n&&s===n?o:e[s]})}function _(e,t){s._cloneContext.set(e,t),e.querySelectorAll("*").forEach(e=>s._cloneContext.set(e,t))}s&&Object.getPrototypeOf(s)&&Object.getOwnPropertyNames(Object.getPrototypeOf(s)).forEach(e=>{"function"==typeof s[e]&&"constructor"!==e&&(x[e]=s[e].bind(s))}),x.plugins=n;const E=u&&d("for");function b(e){try{const t=new WeakSet;return JSON.stringify(e,(e,r)=>{if("function"!=typeof r&&!e.startsWith("$")){if("object"==typeof r&&null!==r){if(t.has(r))return"[Circular]";t.add(r)}return r}})}catch(t){return`${e.length}-${e[0]?.name||""}-${e[e.length-1]?.name||""}`}}E&&console.groupCollapsed("x-for");let v=new Set,w=!0;for(;w;){const e=Array.from(a.querySelectorAll("template[x-for]")).concat(Array.from(s.__mirrorContainer.querySelectorAll("template[x-for]"))).filter(e=>!v.has(e));if(0===e.length){w=!1;break}e.forEach(e=>{let t,n,l;v.add(e),e._xForMeta||(e._xForMeta={parent:e.parentNode,next:e.nextSibling},s.__mirrorContainer.appendChild(e));try{({loopVar:t,indexVar:n,arrayPath:l}=A(e.getAttribute("x-for")))}catch(e){return void p("[x-for]",e.message)}const a=m(l,e);if(!Array.isArray(a))return void p(`[x-for] expected array at ${l}`,a);E&&console.log(`iterating ${l} → length ${a.length}`);const c=e.content.cloneNode(!0).querySelector("*"),u=c&&c.hasAttribute("x-key");if(!u){const t=b(a),r=s._xForCache.get(e);if(r&&r.arrayRef===a&&r.length===a.length&&r.signature===t)return void(E&&console.log(" → skipped (no change)"));s._xForCache.set(e,{arrayRef:a,length:a.length,signature:t})}if(u&&e._forClones&&e._forClones.length>0){E&&console.log(" → using x-key diffing");const l=c.getAttribute("x-key"),u=new Map;e._forClones.forEach(e=>{if(1===e.nodeType){const t=e.getAttribute("x-key");t&&u.set(t,e)}});const f=[],d=new Set;a.forEach((a,c)=>{E&&console.groupCollapsed(` index ${c}`);const h=C(o,t,a,n,c);let g;try{g=r(l,{this:h,...x})}catch(e){p("[x-for] Error evaluating x-key:",e),g=c}const y=String(g);if(u.has(y)){const e=u.get(y);d.add(e),E&&console.log(` → reusing node for key: ${y}`),_(e,h),f.push(e)}else{E&&console.log(` → creating new node for key: ${y}`);const t=i.createDocumentFragment();t.appendChild(e.content.cloneNode(!0));const r=t.firstElementChild;if(r){t.removeChild(r),r.setAttribute("x-key",y),_(r,h);(r.hasAttribute("render")?[r,...r.querySelectorAll("[render]")]:r.querySelectorAll("[render]")).forEach(e=>{const t=e.getAttribute("render")?.trim();t&&(s._xRenderCache.set(e,t),s._xRenderOriginalText.set(e,e.textContent))});(r.hasAttribute("x-class")?[r,...r.querySelectorAll("[x-class]")]:r.querySelectorAll("[x-class]")).forEach(e=>{const t=e.getAttribute("x-class");null!=t&&s._xClassCache.set(e,t)}),f.push(r)}}E&&console.groupEnd()}),e._forClones.forEach(e=>{if(!d.has(e)){if(E&&console.log(" → removing unused node"),1===e.nodeType){(e.hasAttribute("render")?[e,...e.querySelectorAll("[render]")]:e.querySelectorAll("[render]")).forEach(e=>{s._xRenderCache.delete(e),s._xRenderOriginalText.delete(e)});(e.hasAttribute("x-class")?[e,...e.querySelectorAll("[x-class]")]:e.querySelectorAll("[x-class]")).forEach(e=>{s._xClassCache.delete(e),s._xClassDynamicValues.delete(e)})}e.remove()}});for(let t=f.length-1;t>=0;t--){const r=f[t],n=e._xForMeta.parent,o=t<f.length-1?f[t+1]:e._xForMeta.next;r.parentNode&&r.nextSibling===o||n.insertBefore(r,o)}e._forClones=f}else E&&u&&console.log(" → first render with x-key"),E&&!u&&console.log(" → no x-key, full re-render"),e._forClones&&e._forClones.forEach(e=>{if(1===e.nodeType){(e.hasAttribute("render")?[e,...e.querySelectorAll("[render]")]:e.querySelectorAll("[render]")).forEach(e=>{s._xRenderCache.delete(e),s._xRenderOriginalText.delete(e)});(e.hasAttribute("x-class")?[e,...e.querySelectorAll("[x-class]")]:e.querySelectorAll("[x-class]")).forEach(e=>{s._xClassCache.delete(e),s._xClassDynamicValues.delete(e)})}e.remove()}),e._forClones=[],a.forEach((l,a)=>{E&&console.groupCollapsed(` index ${a}`);const c=i.createDocumentFragment();c.appendChild(e.content.cloneNode(!0));const f=C(o,t,l,n,a);if(_(c,f),u){const e=c.firstElementChild;if(e){const t=e.getAttribute("x-key");if(t)try{const n=r(t,{this:f,...x});e.setAttribute("x-key",String(n))}catch(t){e.setAttribute("x-key",String(a))}}}for(;c.firstChild;){const t=c.firstChild;if(e._xForMeta.parent.insertBefore(t,e._xForMeta.next),e._forClones.push(t),1===t.nodeType){(t.hasAttribute("render")?[t,...t.querySelectorAll("[render]")]:t.querySelectorAll("[render]")).forEach(e=>{const t=e.getAttribute("render")?.trim();t&&(s._xRenderCache.set(e,t),s._xRenderOriginalText.set(e,e.textContent))});(t.hasAttribute("x-class")?[t,...t.querySelectorAll("[x-class]")]:t.querySelectorAll("[x-class]")).forEach(e=>{const t=e.getAttribute("x-class");null!=t&&s._xClassCache.set(e,t)})}}E&&console.groupEnd()})})}E&&console.groupEnd();const S=u&&d("if");Array.from(a.querySelectorAll("[x-if]")).concat(Array.from(s.__mirrorContainer.querySelectorAll("[x-if]"))).forEach(e=>{const t=e.getAttribute("x-if")?.trim(),r=!t||y(t,e);S&&console.groupCollapsed("x-if",e,t,"→",r);let n=s._xIfData.get(e);n||(n={placeholder:i.createComment("x-if placeholder"),isHidden:!1},s._xIfData.set(e,n)),r||n.isHidden?r&&n.isHidden&&(n.placeholder.parentNode.replaceChild(e,n.placeholder),n.isHidden=!1):(e.parentNode.replaceChild(n.placeholder,e),s.__mirrorContainer.appendChild(e),n.isHidden=!0),S&&console.groupEnd()}),Array.from(a.querySelectorAll("[render]")).forEach(e=>{if(!s._xRenderCache.has(e)){const t=e.getAttribute("render")?.trim();t&&(s._xRenderCache.set(e,t),s._xRenderOriginalText.set(e,e.textContent))}}),Array.from(a.querySelectorAll("[x-class]")).forEach(e=>{const t=e.getAttribute("x-class");null==t||s._xClassCache.has(e)||s._xClassCache.set(e,t)});const $=u&&d("attr");Array.from(a.querySelectorAll("*")).forEach(e=>{Array.from(e.attributes).forEach(t=>{if(t.name.startsWith("x-")&&!["x-for","x-key","x-class","x-if","x-hidden"].includes(t.name)){const r=t.name,s=n.get(r);if(s&&"attribute"===s.target){const n=y(t.value.trim(),e);try{h&&console.log("[plugin]",`Executing ${r} with:`,{element:e,value:n,component:o}),s.execute(e,n,o)}catch(e){h&&console.error("[plugin]",`Error executing ${r}:`,e)}}else{const r=t.name.slice(2),n=y(t.value.trim(),e);$&&console.groupCollapsed("x-attr",e,r,"=",n),n?e.setAttribute(r,String(n)):e.removeAttribute(r),$&&console.groupEnd()}}})});const k=u&&d("class");k&&console.groupCollapsed("x-class");for(const[e,t]of s._xClassCache){k&&console.log("element →",e);const r=t.trim();if(r.includes("(")){const t=/\(\s*([^,]+?)\s*,\s*([^)]+?)\s*\)/g;s._xClassDynamicValues||(s._xClassDynamicValues=new Map);(s._xClassDynamicValues.get(e)||[]).forEach(t=>e.classList.remove(t));const n=new Set;let o;for(;o=t.exec(r);){const t=o[1].trim(),r=o[2].trim();let s;s=/^[\w\-\s]+$/.test(t)?t:y(t,e);const l=Boolean(y(r,e));let a=[];"string"==typeof s?a=s.split(/\s+/).filter(Boolean):Array.isArray(s)?a=s:s&&"object"==typeof s&&(a=Object.keys(s).filter(e=>s[e])),l&&a.forEach(t=>e.classList.add(t)),a.forEach(e=>n.add(e)),k&&console.log(` ${t} → [${a.join(" ")}] (= ${l})`)}s._xClassDynamicValues.set(e,Array.from(n))}else{s._xClassDynamicValues||(s._xClassDynamicValues=new Map);let t;(s._xClassDynamicValues.get(e)||[]).forEach(t=>e.classList.remove(t));let n=!0;if(!r.includes(",")&&/[?:]/.test(r))t=y(r,e);else{const o=r.indexOf(","),s=o>=0?r.slice(0,o).trim():r,l=o>=0?r.slice(o+1).trim():"true";n=Boolean(y(l,e)),t=s}const o=[];"string"==typeof t?o.push(...t.split(/\s+/).filter(Boolean)):Array.isArray(t)?o.push(...t):t&&"object"==typeof t&&o.push(...Object.keys(t).filter(e=>t[e])),o.forEach(t=>{n?e.classList.add(t):e.classList.remove(t)}),k&&console.log(` x-class ${r} →`,o,`ok=${n}`),s._xClassDynamicValues.set(e,o)}}k&&console.groupEnd();const q=u&&d("render");q&&console.groupCollapsed("render");for(const[e,t]of s._xRenderCache){const r=s._xIfData.get(e);if(r&&r.isHidden)continue;q&&console.log("element →",e,"expr=",t);const n=t.indexOf(","),o=n<0?t:t.slice(0,n).trim(),l=y(n<0?"true":t.slice(n+1).trim(),e);let a;if(l){let t=y(o,e);if(void 0===t)a=s._xRenderOriginalText.get(e)||"";else if(null!=t&&"object"==typeof t)try{a=JSON.stringify(t,null,2)}catch{a=String(t)}else a=null==t||"boolean"==typeof t?"":String(t)}else a=s._xRenderOriginalText.get(e)||"";e.textContent!==a&&(e.textContent=a),q&&console.log(` → "${a}" (cond=${l})`)}q&&console.groupEnd(),Array.from(a.querySelectorAll("*")).forEach(e=>{const t=s._xIfData.get(e);t&&t.isHidden||[["render","render"]].forEach(([t,r])=>{e.hasAttribute(t)&&!d(r)&&e.removeAttribute(t)})}),function e(t){Array.from(t.querySelectorAll("*")).forEach(e=>{Array.from(e.attributes).forEach(t=>{if(t.name.startsWith("@")){const n=t.name.slice(1),s=t.value.trim();e._xEventListeners&&e._xEventListeners[n]&&e.removeEventListener(n,e._xEventListeners[n]),e._xEventListeners||(e._xEventListeners={});const l=g(e),a=new Proxy(l,{get:(e,t)=>t in e?e[t]:o[t]}),i=function(e){try{return r(s,{this:a,event:e,...x})}catch(e){p(`[rendux @${n}] Error evaluating: ${s}`,e)}};e.addEventListener(n,i),e._xEventListeners[n]=i}})}),Array.from(t.querySelectorAll("slot")).forEach(t=>{(t.assignedElements?t.assignedElements({flatten:!0}):[]).forEach(t=>{e(t)})})}(a),l(a,o)}rendux.use=function(e){return o(e),rendux},"undefined"!=typeof module&&module.exports&&(module.exports={rendux:rendux,use:o,process:l,plugins:n,parsePluginCall:s});export{t as html,s as parsePluginCall,n as plugins,l as process,rendux,o as use};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ynck/rendux",
3
- "version": "0.93.2",
3
+ "version": "0.94.0",
4
4
  "type": "module",
5
5
  "main": "./dist/rendux.cjs.min.js",
6
6
  "module": "./dist/rendux.min.js",