lightview 2.0.8 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. package/README.md +47 -1283
  2. package/build-bundles.mjs +109 -0
  3. package/cdom/helpers/array.js +70 -0
  4. package/cdom/helpers/compare.js +26 -0
  5. package/cdom/helpers/conditional.js +34 -0
  6. package/cdom/helpers/datetime.js +54 -0
  7. package/cdom/helpers/format.js +20 -0
  8. package/cdom/helpers/logic.js +24 -0
  9. package/cdom/helpers/lookup.js +25 -0
  10. package/cdom/helpers/math.js +34 -0
  11. package/cdom/helpers/network.js +41 -0
  12. package/cdom/helpers/state.js +77 -0
  13. package/cdom/helpers/stats.js +39 -0
  14. package/cdom/helpers/string.js +49 -0
  15. package/cdom/parser.js +602 -0
  16. package/components/actions/button.js +19 -6
  17. package/components/actions/dropdown.js +6 -6
  18. package/components/actions/modal.js +9 -9
  19. package/components/actions/swap.js +31 -8
  20. package/components/daisyui.js +1 -1
  21. package/components/data-display/accordion.js +6 -6
  22. package/components/data-display/alert.js +17 -7
  23. package/components/data-display/avatar.js +7 -7
  24. package/components/data-display/badge.js +14 -6
  25. package/components/data-display/card.js +7 -7
  26. package/components/data-display/carousel.js +4 -4
  27. package/components/data-display/chart.js +8 -8
  28. package/components/data-display/chat.js +7 -7
  29. package/components/data-display/collapse.js +5 -5
  30. package/components/data-display/countdown.js +3 -3
  31. package/components/data-display/diff.js +6 -6
  32. package/components/data-display/kbd.js +12 -6
  33. package/components/data-display/loading.js +14 -6
  34. package/components/data-display/progress.js +14 -6
  35. package/components/data-display/radial-progress.js +15 -6
  36. package/components/data-display/stats.js +9 -9
  37. package/components/data-display/table.js +9 -9
  38. package/components/data-display/timeline.js +8 -8
  39. package/components/data-display/toast.js +3 -3
  40. package/components/data-display/tooltip.js +20 -3
  41. package/components/data-input/checkbox.js +5 -5
  42. package/components/data-input/file-input.js +3 -3
  43. package/components/data-input/input.js +5 -5
  44. package/components/data-input/radio.js +9 -9
  45. package/components/data-input/range.js +3 -3
  46. package/components/data-input/rating.js +3 -3
  47. package/components/data-input/select.js +5 -5
  48. package/components/data-input/textarea.js +3 -3
  49. package/components/data-input/toggle.js +5 -5
  50. package/components/layout/divider.js +24 -4
  51. package/components/layout/drawer.js +7 -7
  52. package/components/layout/footer.js +5 -5
  53. package/components/layout/hero.js +5 -5
  54. package/components/layout/indicator.js +18 -4
  55. package/components/layout/join.js +4 -4
  56. package/components/layout/navbar.js +6 -6
  57. package/components/navigation/breadcrumbs.js +4 -4
  58. package/components/navigation/dock.js +5 -5
  59. package/components/navigation/menu.js +6 -6
  60. package/components/navigation/pagination.js +3 -3
  61. package/components/navigation/steps.js +4 -4
  62. package/components/navigation/tabs.js +296 -21
  63. package/components/theme/theme-switch.js +30 -30
  64. package/docs/about.html +141 -7
  65. package/docs/api/computed.html +1 -6
  66. package/docs/api/effects.html +1 -7
  67. package/docs/api/elements.html +130 -58
  68. package/docs/api/enhance.html +1 -6
  69. package/docs/api/hypermedia.html +182 -23
  70. package/docs/api/index.html +11 -12
  71. package/docs/api/nav.html +35 -4
  72. package/docs/api/signals.html +1 -6
  73. package/docs/api/state.html +1 -6
  74. package/docs/assets/js/examplify-sandbox.html +2 -2
  75. package/docs/assets/js/examplify.js +15 -15
  76. package/docs/cdom-nav.html +29 -0
  77. package/docs/cdom.html +362 -0
  78. package/docs/components/accordion.html +4 -4
  79. package/docs/components/alert.html +12 -12
  80. package/docs/components/avatar.html +4 -4
  81. package/docs/components/badge.html +59 -4
  82. package/docs/components/breadcrumbs.html +3 -3
  83. package/docs/components/button.html +83 -97
  84. package/docs/components/card.html +4 -4
  85. package/docs/components/carousel.html +3 -3
  86. package/docs/components/chart-area.html +6 -6
  87. package/docs/components/chart-bar.html +6 -6
  88. package/docs/components/chart-column.html +6 -6
  89. package/docs/components/chart-line.html +6 -6
  90. package/docs/components/chart-pie.html +6 -6
  91. package/docs/components/chart.html +2 -2
  92. package/docs/components/chat.html +4 -4
  93. package/docs/components/checkbox.html +4 -4
  94. package/docs/components/collapse.html +4 -4
  95. package/docs/components/component-nav.html +1 -1
  96. package/docs/components/countdown.html +4 -4
  97. package/docs/components/diff.html +3 -3
  98. package/docs/components/divider.html +68 -24
  99. package/docs/components/dock.html +3 -3
  100. package/docs/components/drawer.html +4 -4
  101. package/docs/components/dropdown.html +4 -4
  102. package/docs/components/file-input.html +4 -4
  103. package/docs/components/footer.html +3 -3
  104. package/docs/components/gallery.html +2 -2
  105. package/docs/components/hero.html +3 -3
  106. package/docs/components/index.css +5 -3
  107. package/docs/components/index.html +4 -4
  108. package/docs/components/indicator.html +88 -34
  109. package/docs/components/input.html +4 -4
  110. package/docs/components/join.html +3 -3
  111. package/docs/components/kbd.html +67 -28
  112. package/docs/components/loading.html +59 -43
  113. package/docs/components/menu.html +4 -4
  114. package/docs/components/modal.html +4 -4
  115. package/docs/components/navbar.html +3 -3
  116. package/docs/components/pagination.html +3 -3
  117. package/docs/components/progress.html +48 -7
  118. package/docs/components/radial-progress.html +35 -15
  119. package/docs/components/radio.html +4 -4
  120. package/docs/components/range.html +4 -4
  121. package/docs/components/rating.html +4 -4
  122. package/docs/components/select.html +4 -4
  123. package/docs/components/sidebar-setup.js +1 -1
  124. package/docs/components/spinner.html +4 -4
  125. package/docs/components/stats.html +4 -4
  126. package/docs/components/steps.html +3 -3
  127. package/docs/components/swap.html +187 -104
  128. package/docs/components/switch.html +4 -4
  129. package/docs/components/table.html +4 -4
  130. package/docs/components/tabs.html +147 -279
  131. package/docs/components/text-input.html +4 -4
  132. package/docs/components/textarea.html +4 -4
  133. package/docs/components/timeline.html +4 -4
  134. package/docs/components/toast.html +4 -4
  135. package/docs/components/toggle.html +4 -4
  136. package/docs/components/tooltip.html +75 -35
  137. package/docs/examples/getting-started-example.html +1 -1
  138. package/docs/examples/index.html +1 -2
  139. package/docs/getting-started/index.html +112 -19
  140. package/docs/index.html +1 -6
  141. package/docs/router-nav.html +13 -0
  142. package/docs/router.html +60 -17
  143. package/docs/styles/index.html +2 -7
  144. package/docs/syntax-nav.html +10 -0
  145. package/docs/syntax.html +146 -0
  146. package/functions/_middleware.js +17 -10
  147. package/functions/processServerScripts.js +127 -0
  148. package/index.html +8 -8
  149. package/lightview-all.js +1 -0
  150. package/lightview-cdom.js +1 -0
  151. package/lightview-router.js +71 -22
  152. package/lightview-x.js +1 -1247
  153. package/lightview.js +1 -760
  154. package/lightview.js.bak +1 -0
  155. package/package.json +37 -26
  156. package/scripts/analysis/README.md +2 -0
  157. package/scripts/analysis/analyze.js +266 -0
  158. package/scripts/analysis/latest_metrics.md +185 -0
  159. package/src/lightview-all.js +10 -0
  160. package/src/lightview-cdom.js +305 -0
  161. package/src/lightview-x.js +1581 -0
  162. package/src/lightview.js +694 -0
  163. package/src/reactivity/signal.js +133 -0
  164. package/src/reactivity/state.js +217 -0
  165. package/test-text-tag.js +6 -0
  166. package/tests/cdom/fixtures/helpers.cdomc +62 -0
  167. package/tests/cdom/fixtures/user.cdom +14 -0
  168. package/tests/cdom/fixtures/user.cdomc +12 -0
  169. package/tests/cdom/fixtures/user.odom +18 -0
  170. package/tests/cdom/fixtures/user.vdom +11 -0
  171. package/tests/cdom/helpers.test.js +121 -0
  172. package/tests/cdom/loader.test.js +125 -0
  173. package/tests/cdom/parser.test.js +108 -0
  174. package/tests/cdom/reactivity.test.js +186 -0
  175. package/tests/text-tag.test.js +77 -0
  176. package/vite.config.mjs +52 -0
  177. package/wrangler.toml +6 -0
  178. package/components/data-display/skeleton.js +0 -66
  179. package/docs/components/skeleton.html +0 -447
  180. package/docs/playground.html +0 -416
@@ -0,0 +1 @@
1
+ !function(){"use strict";const e=globalThis.__LIGHTVIEW_INTERNALS__||(globalThis.__LIGHTVIEW_INTERNALS__={currentEffect:null,registry:new Map,dependencyMap:new WeakMap}),t=(t,n)=>{let o="string"==typeof n?n:null==n?void 0:n.name;const r=null==n?void 0:n.storage;if(o&&r)try{const e=r.getItem(o);null!==e&&(t=JSON.parse(e))}catch(l){}let s=t;const i=new Set,a=(...e)=>{if(0===e.length)return a.value;a.value=e[0]};if(Object.defineProperty(a,"value",{get:()=>(e.currentEffect&&(i.add(e.currentEffect),e.currentEffect.dependencies.add(i)),s),set(e){if(s!==e){if(s=e,o&&r)try{r.setItem(o,JSON.stringify(s))}catch(l){}[...i].forEach(e=>e())}}}),o)if(e.registry.has(o)){if(e.registry.get(o)!==a)throw new Error(`Lightview: A signal or state with the name "${o}" is already registered.`)}else e.registry.set(o,a);return a};t.get=(n,o)=>e.registry.has(n)||void 0===o?e.registry.get(n):t(o,n);const n=t=>{const n=()=>{if(n.active&&!n.running){n.dependencies.forEach(e=>e.delete(n)),n.dependencies.clear(),n.running=!0,e.currentEffect=n;try{t()}finally{e.currentEffect=null,n.running=!1}}};return n.active=!0,n.running=!1,n.dependencies=new Set,n.stop=()=>{n.dependencies.forEach(e=>e.delete(n)),n.dependencies.clear(),n.active=!1},n(),n},o={get currentEffect(){return(globalThis.__LIGHTVIEW_INTERNALS__||(globalThis.__LIGHTVIEW_INTERNALS__={})).currentEffect}},r=(e,t,n)=>{let o=e.get(t);return o||(o=n(),e.set(t,o)),o},s=new WeakMap,i=()=>({effects:[],onmount:null,onunmount:null}),a=e.registry,l=(e,t)=>{const n=r(s,e,i);n.effects||(n.effects=[]),n.effects.push(t)},c=Symbol("lightview.shadowDOM"),d=e=>e&&"object"==typeof e&&!0===e[c],u=(e,t)=>{if(t.shadowRoot)return void console.warn("Lightview: Element already has a shadowRoot, skipping shadowDOM directive");const n=t.attachShadow({mode:e.mode}),o=[],r=[...e.styles||[]];if(e.adoptedStyleSheets&&e.adoptedStyleSheets.length>0&&e.adoptedStyleSheets.forEach(e=>{e instanceof CSSStyleSheet?o.push(e):"string"==typeof e&&r.push(e)}),o.length>0)try{n.adoptedStyleSheets=o}catch(s){console.warn("Lightview: adoptedStyleSheets not supported")}for(const i of r){const e=document.createElement("link");e.rel="stylesheet",e.href=i,n.appendChild(e)}e.children&&e.children.length>0&&S(e.children,n)};let f=!1;const h=new WeakMap,p=(e,t,n={},o=[])=>{const r=y({tag:t,attributes:n,children:o,get domEl(){return e}});return h.set(e,r),r},g=(e,t={},n=[])=>{if(C[e]&&(e=C[e]),"function"==typeof e){const o=e({...t},n);return m(o)}if("shadowDOM"===e)return((e,t)=>({[c]:!0,mode:e.mode||"open",styles:e.styles||[],adoptedStyleSheets:e.adoptedStyleSheets||[],children:t}))(t,n);const o="svg"===e.toLowerCase(),r=f;o&&(f=!0);const s=f?document.createElementNS("http://www.w3.org/2000/svg",e):document.createElement(e),i=p(s,e,t,n);return i.attributes=t,i.children=n,o&&(f=r),i},m=e=>{if(!e)return null;if(T.hooks.processChild&&(e=T.hooks.processChild(e)??e),e.domEl)return e;const t=typeof e;if("object"===t&&e instanceof HTMLElement)return p(e,e.tagName.toLowerCase(),{},[]);if("object"===t&&e instanceof String){const t=document.createElement("span");return t.textContent=e.toString(),p(t,"span",{},[])}if("string"===t){const t=document.createElement("template");t.innerHTML=e.trim();const n=t.content;if(1===n.childNodes.length&&n.firstChild instanceof HTMLElement){const e=n.firstChild;return p(e,e.tagName.toLowerCase(),{},[])}{const e=document.createElement("span");return e.style.display="contents",e.appendChild(n),p(e,"span",{},[])}}return"object"==typeof e&&e.tag?g(e.tag,e.attributes||{},e.children||[]):null},y=e=>{const t=e.domEl;return new Proxy(e,{set:(e,n,o)=>(e[n]="attributes"===n?v(o,t):"children"===n?L(o,t):o,!0)})},w=new Set(["value","checked","selected","selectedIndex","className","innerHTML","innerText"]),b=(e,t,n)=>{const o="boolean"==typeof e[t];"href"!==t&&"src"!==t||"string"!=typeof n||!/^(javascript|vbscript|data:text\/html|data:application\/javascript)/i.test(n)||(console.warn(`[Lightview] Blocked dangerous protocol in ${t}: ${n}`),n="javascript:void(0)"),w.has(t)||o?e[t]=o?null!=n&&!1!==n&&"false"!==n:n:null==n?e.removeAttribute(t):e.setAttribute(t,n)},v=(e,t)=>{const o={};for(let[a,c]of Object.entries(e))if("onmount"===a||"onunmount"===a){r(s,t,i)[a]=c,"onmount"===a&&t.isConnected&&c(t)}else if(a.startsWith("on")){if("function"==typeof c){const e=a.slice(2).toLowerCase();t.addEventListener(e,c)}else"string"==typeof c&&t.setAttribute(a,c);o[a]=c}else if("function"==typeof c){const e=n(()=>{const e=c();"style"===a&&"object"==typeof e?Object.assign(t.style,e):b(t,a,e)});l(t,e),o[a]=c}else"style"===a&&"object"==typeof c?(Object.entries(c).forEach(([e,o])=>{if("function"==typeof o){const r=n(()=>{t.style[e]=o()});l(t,r)}else t.style[e]=o}),o[a]=c):(b(t,a,c),o[a]=c);return o},E=(e,t,o=!0)=>{o&&void 0!==t.innerHTML&&(t.innerHTML="");const r=[],s=t.tagName&&("script"===t.tagName.toLowerCase()||"style"===t.tagName.toLowerCase()),i=e.flat(1/0);for(let a of i){if(T.hooks.processChild&&!s&&(a=T.hooks.processChild(a)??a),d(a)){if(t instanceof ShadowRoot){console.warn("Lightview: Cannot nest shadowDOM inside another shadowDOM");continue}u(a,t);continue}const e=typeof a;if("function"===e){const e=document.createComment("lv:s"),o=document.createComment("lv:e");let s;t.appendChild(e),t.appendChild(o);const i=()=>{for(;e.nextSibling&&e.nextSibling!==o;)e.nextSibling.remove();const t=a();if(null!=t)if(!s||e.isConnected)if("object"==typeof t&&t instanceof String){const e=document.createTextNode(t);o.parentNode.insertBefore(e,o)}else{const e=document.createDocumentFragment(),n=Array.isArray(t)?t:[t];E(n,e,!1),o.parentNode.insertBefore(e,o)}else s.stop()};s=n(i),l(e,s),r.push(a)}else if(["string","number","boolean","symbol"].includes(e)||a&&"object"===e&&a instanceof String)t.appendChild(document.createTextNode(a)),r.push(a);else if(a&&"object"===e&&a.tag){const e=a.domEl?a:g(a.tag,a.attributes||{},a.children||[]);t.appendChild(e.domEl),r.push(e)}}return r},S=(e,t)=>E(e,t,!1),L=(e,t)=>E(e,t,!0),C={},N=new Proxy({},{get(e,t){if("_customTags"===t)return{...C};const n=(...e)=>{let n={},o=e;const r=e[0];return e.length>0&&r&&"object"==typeof r&&!r.tag&&!r.domEl&&!Array.isArray(r)&&(n=r,o=e.slice(1)),g(C[t]||t,n,o)};return C[t]&&Object.assign(n,C[t]),n},set:(e,t,n)=>(C[t]=n,!0)}),T={signal:t,computed:e=>{const o=t(void 0);return n(()=>{o.value=e()}),o},effect:n,get:get,registry:a,element:g,enhance:(e,t={})=>{const o="string"==typeof e?document.querySelector(e):e,r=o.domEl||o;if(!(r instanceof HTMLElement))return null;const s=r.tagName.toLowerCase();let i=h.get(r);i||(i=p(r,s));const{innerText:a,innerHTML:l,...c}=t;return void 0!==a&&("function"==typeof a?n(()=>{r.innerText=a()}):r.innerText=a),void 0!==l&&("function"==typeof l?n(()=>{r.innerHTML=l()}):r.innerHTML=l),Object.keys(c).length>0&&(i.attributes=c),i},tags:N,$:(e,t=document.body)=>{const n="string"==typeof e?t.querySelector(e):e;return n?(Object.defineProperty(n,"content",{value(e,t="inner"){t=t.toLowerCase(),T.tags;const o=n.tagName&&("script"===n.tagName.toLowerCase()||"style"===n.tagName.toLowerCase()),r=(Array.isArray(e)?e:[e]).map(e=>(T.hooks.processChild&&!o&&(e=T.hooks.processChild(e)??e),e.tag&&!e.domEl?g(e.tag,e.attributes||{},e.children||[]).domEl:e.domEl||e)),s="shadow"===t?n.shadowRoot||n.attachShadow({mode:"open"}):n;return"inner"===t||"shadow"===t?s.replaceChildren(...r):"outer"===t?s.replaceWith(...r):"afterbegin"===t?s.prepend(...r):"beforeend"===t?s.append(...r):r.forEach(e=>n.insertAdjacentElement(t,e)),n},configurable:!0,writable:!0}),n):null},hooks:{onNonStandardHref:null,processChild:null,validateUrl:null},internals:{core:o,domToElement:h,wrapDomElement:p,setupChildren:L}};if("undefined"!=typeof module&&module.exports&&(module.exports=T),"undefined"!=typeof window&&(globalThis.Lightview=T,globalThis.addEventListener("click",e=>{const t=e.composedPath().find(e=>{var t,n;return"A"===e.tagName&&(null==(n=null==(t=e.getAttribute)?void 0:t.call(e,"href"))?void 0:n.startsWith("#"))});if(t&&!e.defaultPrevented){const n=t.getAttribute("href");if(n.length>1){const o=n.slice(1),r=t.getRootNode(),s=(r.getElementById?r.getElementById(o):null)||(r.querySelector?r.querySelector(`#${o}`):null);s&&(e.preventDefault(),requestAnimationFrame(()=>{requestAnimationFrame(()=>{s.style.scrollMarginTop="calc(var(--site-nav-height, 0px) + 2rem)",s.scrollIntoView({behavior:"smooth",block:"start",inline:"start"})})}))}}T.hooks.onNonStandardHref&&T.hooks.onNonStandardHref(e)}),"undefined"!=typeof MutationObserver)){const e=(t,n)=>{var o;n(t),null==(o=t.childNodes)||o.forEach(t=>e(t,n)),t.shadowRoot&&e(t.shadowRoot,n)},t=t=>e(t,e=>{var t,n;const o=s.get(e);o&&(null==(t=o.effects)||t.forEach(e=>e.stop()),null==(n=o.onunmount)||n.call(o,e),s.delete(e))}),n=t=>e(t,e=>{var t,n;null==(n=null==(t=s.get(e))?void 0:t.onmount)||n.call(t,e)}),o=new MutationObserver(e=>{e.forEach(e=>{e.removedNodes.forEach(t),e.addedNodes.forEach(n)})}),r=()=>{document.body&&o.observe(document.body,{childList:!0,subtree:!0})};"loading"===document.readyState?document.addEventListener("DOMContentLoaded",r):r()}}();
package/package.json CHANGED
@@ -1,27 +1,38 @@
1
- {
2
- "name": "lightview",
3
- "version": "2.0.8",
4
- "description": "A lightweight reactive UI library with features of Bau, Juris, and HTMX",
5
- "main": "lightview.js",
6
- "directories": {
7
- "doc": "docs"
8
- },
9
- "scripts": {
10
- "dev": "wrangler pages dev . --port 3000",
11
- "preview": "npm run build && wrangler pages dev ./dist --port 8788",
12
- "build": "node build.js",
13
- "watch": "node watch.js",
14
- "deploy": "npm run build && wrangler pages deploy dist",
15
- "test": "echo \"Error: no test specified\" && exit 1"
16
- },
17
- "keywords": [],
18
- "author": "Simon Y. Blackwell, AnyWhichWay LLC",
19
- "license": "MIT",
20
- "type": "commonjs",
21
- "devDependencies": {
22
- "wrangler": "^4.54.0"
23
- },
24
- "dependencies": {
25
- "marked": "^17.0.1"
26
- }
1
+ {
2
+ "name": "lightview",
3
+ "version": "2.1.0",
4
+ "description": "A lightweight reactive UI library with features of Bau, Juris, and HTMX",
5
+ "main": "lightview.js",
6
+ "directories": {
7
+ "doc": "docs"
8
+ },
9
+ "scripts": {
10
+ "dev": "wrangler pages dev . --port 3000",
11
+ "preview": "npm run build && wrangler pages dev ./dist --port 8788",
12
+ "build": "node build-bundles.mjs && node build.js",
13
+ "watch": "node watch.js",
14
+ "deploy": "npm run build && wrangler pages deploy dist",
15
+ "test": "echo \"Error: no test specified\" && exit 1"
16
+ },
17
+ "keywords": [],
18
+ "author": "Simon Y. Blackwell, AnyWhichWay LLC",
19
+ "license": "MIT",
20
+ "type": "commonjs",
21
+ "devDependencies": {
22
+ "@grucloud/bau": "^0.106.0",
23
+ "acorn": "^8.15.0",
24
+ "acorn-walk": "^8.3.4",
25
+ "htmx.org": "^2.0.8",
26
+ "jsdom": "^27.4.0",
27
+ "juris": "^0.9.0",
28
+ "react": "^19.2.3",
29
+ "terser": "^5.24.0",
30
+ "vite": "^5.0.0",
31
+ "vitest": "^4.0.16",
32
+ "wrangler": "^4.54.0"
33
+ },
34
+ "dependencies": {
35
+ "linkedom": "^0.18.12",
36
+ "marked": "^17.0.1"
37
+ }
27
38
  }
@@ -0,0 +1,2 @@
1
+ # Analysis Scripts
2
+ This directory contains scripts used for codebase analysis (complexity, maintainability, etc.).
@@ -0,0 +1,266 @@
1
+ const fs = require('fs');
2
+ const acorn = require('acorn');
3
+ const walk = require('acorn-walk');
4
+ const path = require('path');
5
+
6
+ /**
7
+ * 2025 LIGHTVIEW CODEBASE METRICS ENGINE
8
+ * Calculates:
9
+ * - Cyclomatic Complexity (Control Flow Paths)
10
+ * - Cognitive Complexity (2025 SonarSource Spec)
11
+ * - Halstead Volume (Operator/Operand Density)
12
+ * - SLOC (Source Lines of Code)
13
+ * - Maintainability Index (MI)
14
+ */
15
+
16
+ const TARGET_FILES = [
17
+ '../../lightview.js',
18
+ '../../lightview-x.js',
19
+ '../../lightview-router.js',
20
+ '../../node_modules/react/cjs/react.development.js',
21
+ '../../node_modules/@grucloud/bau/bau.js',
22
+ '../../node_modules/htmx.org/dist/htmx.js',
23
+ '../../node_modules/juris/juris.js'
24
+ ];
25
+
26
+ // --- METRIC CALCULATORS ---
27
+
28
+ const getCyclomaticComplexity = (fnNode) => {
29
+ let complexity = 1;
30
+ walk.recursive(fnNode.body, null, {
31
+ IfStatement(node, state, c) { complexity++; c(node.test, state); c(node.consequent, state); if (node.alternate) c(node.alternate, state); },
32
+ ConditionalExpression(node, state, c) { complexity++; c(node.test, state); c(node.consequent, state); c(node.alternate, state); },
33
+ WhileStatement(node, state, c) { complexity++; c(node.test, state); c(node.body, state); },
34
+ DoWhileStatement(node, state, c) { complexity++; c(node.test, state); c(node.body, state); },
35
+ ForStatement(node, state, c) { complexity++; if (node.init) c(node.init, state); if (node.test) c(node.test, state); if (node.update) c(node.update, state); c(node.body, state); },
36
+ ForInStatement(node, state, c) { complexity++; c(node.left, state); c(node.right, state); c(node.body, state); },
37
+ ForOfStatement(node, state, c) { complexity++; c(node.left, state); c(node.right, state); c(node.body, state); },
38
+ LogicalExpression(node, state, c) {
39
+ if (node.operator === '&&' || node.operator === '||' || node.operator === '??') complexity++;
40
+ c(node.left, state); c(node.right, state);
41
+ },
42
+ SwitchCase(node, state, c) { if (node.test) complexity++; if (node.test) c(node.test, state); node.consequent.forEach(n => c(n, state)); },
43
+ CatchClause(node, state, c) { complexity++; if (node.param) c(node.param, state); c(node.body, state); },
44
+ FunctionDeclaration() { }, FunctionExpression() { }, ArrowFunctionExpression() { }
45
+ });
46
+ return complexity;
47
+ };
48
+
49
+ const getCognitiveComplexity = (fnNode, fnName) => {
50
+ let score = 0;
51
+ const findRecursion = (node) => {
52
+ let count = 0;
53
+ walk.simple(node, {
54
+ CallExpression(call) { if (call.callee.type === 'Identifier' && call.callee.name === fnName) count++; }
55
+ });
56
+ return count;
57
+ };
58
+ const traverse = (n, d) => {
59
+ if (!n) return 0;
60
+ let s = 0;
61
+ switch (n.type) {
62
+ case 'IfStatement':
63
+ s += (1 + d);
64
+ s += traverse(n.test, d);
65
+ s += traverse(n.consequent, d + 1);
66
+ if (n.alternate) {
67
+ s += 1;
68
+ if (n.alternate.type === 'IfStatement') s += traverseBranch(n.alternate, d);
69
+ else s += traverse(n.alternate, d + 1);
70
+ }
71
+ break;
72
+ case 'SwitchStatement': s += (1 + d); n.cases.forEach(c => s += traverse(c, d + 1)); break;
73
+ case 'ForStatement': case 'ForInStatement': case 'ForOfStatement': case 'WhileStatement': case 'DoWhileStatement':
74
+ s += (1 + d); s += traverse(n.body, d + 1); break;
75
+ case 'CatchClause': s += (1 + d); s += traverse(n.body, d + 1); break;
76
+ case 'LogicalExpression': s += 1; break; // Naive: count each operator sequence as 1
77
+ case 'ConditionalExpression': s += 1; s += traverse(n.consequent, d + 1); s += traverse(n.alternate, d + 1); break;
78
+ case 'BreakStatement': case 'ContinueStatement': s += 1; break;
79
+ case 'FunctionDeclaration': case 'FunctionExpression': case 'ArrowFunctionExpression': break;
80
+ default:
81
+ for (const key in n) {
82
+ const c = n[key];
83
+ if (Array.isArray(c)) c.forEach(i => { if (i?.type) s += traverse(i, d); });
84
+ else if (c?.type) s += traverse(c, d);
85
+ }
86
+ }
87
+ return s;
88
+ };
89
+ const traverseBranch = (n, d) => {
90
+ let s = 1; s += traverse(n.test, d); s += traverse(n.consequent, d + 1);
91
+ if (n.alternate) {
92
+ if (n.alternate.type === 'IfStatement') s += traverseBranch(n.alternate, d);
93
+ else { s += 1; s += traverse(n.alternate, d + 1); }
94
+ }
95
+ return s;
96
+ };
97
+ if (fnNode.body) {
98
+ score += traverse(fnNode.body, 0);
99
+ score += findRecursion(fnNode.body);
100
+ }
101
+ return score;
102
+ };
103
+
104
+ const calculateHalstead = (code) => {
105
+ let tokens = [];
106
+ try {
107
+ tokens = [...acorn.tokenizer(code, { ecmaVersion: 'latest' })];
108
+ } catch (e) {
109
+ // Fallback or skip if tokenization fails for complex files
110
+ }
111
+ const operators = new Set(['(', ')', '[', ']', '{', '}', '.', ',', ';', ':', '?', '...', '=', '+=', '-=', '*=', '/=', '==', '===', '!=', '!==', '<', '>', '<=', '>=', '+', '-', '*', '/', '%', '++', '--', '&&', '||', '??', '!', 'typeof', 'let', 'const', 'var', 'if', 'else', 'for', 'while', 'return']);
112
+ let N1 = 0, N2 = 0; const n1set = new Set(), n2set = new Set();
113
+ tokens.forEach(t => {
114
+ const txt = t.value !== undefined ? String(t.value) : t.type.label;
115
+ if (operators.has(txt)) { N1++; n1set.add(txt); } else { N2++; n2set.add(txt); }
116
+ });
117
+ const N = N1 + N2;
118
+ const n = n1set.size + n2set.size;
119
+ const volume = N * Math.log2(n || 1);
120
+ return { volume, n1: n1set.size, n2: n2set.size, N1, N2 };
121
+ };
122
+
123
+ const getSLOC = (code) => code.split('\n').filter(l => l.trim().length > 0 && !l.trim().startsWith('//')).length;
124
+
125
+ const calculateMI = (volume, cc, sloc) => {
126
+ // Standard MI formula: 171 - 5.2 * ln(V) - 0.23 * CC - 16.2 * ln(SLOC)
127
+ const mi = 171 - 5.2 * Math.log(volume || 1) - 0.23 * cc - 16.2 * Math.log(sloc || 1);
128
+ return Math.max(0, Math.min(100, (mi * 100) / 171));
129
+ };
130
+
131
+ // --- ANALYSIS CORE ---
132
+
133
+ const analyzeFile = (filePath) => {
134
+ const fullPath = path.resolve(__dirname, filePath);
135
+ if (!fs.existsSync(fullPath)) return { error: `File not found: ${filePath}` };
136
+
137
+ const code = fs.readFileSync(fullPath, 'utf8');
138
+ let ast;
139
+ try {
140
+ ast = acorn.parse(code, { ecmaVersion: 'latest', sourceType: 'script' });
141
+ } catch (e) {
142
+ try {
143
+ ast = acorn.parse(code, { ecmaVersion: 'latest', sourceType: 'module' });
144
+ } catch (e2) {
145
+ return { error: `AST Parse Error: ${e2.message}` };
146
+ }
147
+ }
148
+ const sloc = getSLOC(code);
149
+ const fileHalstead = calculateHalstead(code);
150
+
151
+ const fns = [];
152
+ walk.ancestor(ast, {
153
+ FunctionDeclaration(node) { fns.push({ node, name: node.id?.name || '<anonymous>' }); },
154
+ FunctionExpression(node, state, anc) {
155
+ let name = '<anonymous>';
156
+ const p = anc[anc.length - 2];
157
+ if (p?.type === 'VariableDeclarator' && p.id?.name) name = p.id.name;
158
+ else if (p?.type === 'Property' && p.key?.name) name = p.key.name;
159
+ else if (p?.type === 'AssignmentExpression' && p.left?.name) name = p.left.name;
160
+ fns.push({ node, name });
161
+ },
162
+ ArrowFunctionExpression(node, state, anc) {
163
+ let name = '<anonymous>';
164
+ const p = anc[anc.length - 2];
165
+ if (p?.type === 'VariableDeclarator' && p.id?.name) name = p.id.name;
166
+ fns.push({ node, name });
167
+ }
168
+ });
169
+
170
+ const results = fns.map(f => {
171
+ const cc = getCyclomaticComplexity(f.node);
172
+ const cog = getCognitiveComplexity(f.node, f.name);
173
+ const fCode = code.slice(f.node.start, f.node.end);
174
+ const h = calculateHalstead(fCode);
175
+ const fSloc = getSLOC(fCode);
176
+ const mi = calculateMI(h.volume, cc, fSloc);
177
+ return { name: f.name, cc, cog, mi, volume: h.volume, sloc: fSloc };
178
+ });
179
+
180
+ return {
181
+ fileName: path.basename(filePath),
182
+ sloc,
183
+ fnCount: fns.length,
184
+ totalVolume: fileHalstead.volume,
185
+ results
186
+ };
187
+ };
188
+
189
+ // --- REPORT GENERATION ---
190
+
191
+ const generateReport = () => {
192
+ let md = "# Codebase Ethics & Complexity Report\n\n";
193
+ md += `Generated on: ${new Date().toLocaleString()}\n\n`;
194
+
195
+ const summaries = [];
196
+
197
+ TARGET_FILES.forEach(file => {
198
+ console.log(`Analyzing ${file}...`);
199
+ const data = analyzeFile(file);
200
+ if (data.error) {
201
+ md += `### !! Error analyzing ${file}: ${data.error}\n\n`;
202
+ summaries.push({
203
+ name: path.basename(file),
204
+ sloc: '-',
205
+ fns: '-',
206
+ mi: 'Error',
207
+ cog: 'Error',
208
+ status: "❌ Error"
209
+ });
210
+ return;
211
+ }
212
+
213
+ const miScores = data.results.map(r => r.mi);
214
+ const cogScores = data.results.map(r => r.cog);
215
+
216
+ const minMI = miScores.length ? Math.min(...miScores) : 0;
217
+ const avgMI = miScores.length ? miScores.reduce((a, b) => a + b, 0) / miScores.length : 0;
218
+ const maxMI = miScores.length ? Math.max(...miScores) : 0;
219
+
220
+ const minCog = cogScores.length ? Math.min(...cogScores) : 0;
221
+ const avgCog = cogScores.length ? cogScores.reduce((a, b) => a + b, 0) / cogScores.length : 0;
222
+ const maxCog = cogScores.length ? Math.max(...cogScores) : 0;
223
+
224
+ summaries.push({
225
+ name: data.fileName,
226
+ sloc: data.sloc,
227
+ fns: data.fnCount,
228
+ mi: `${minMI.toFixed(1)} / ${avgMI.toFixed(1)} / ${maxMI.toFixed(1)}`,
229
+ cog: `${minCog.toFixed(0)} / ${avgCog.toFixed(1)} / ${maxCog.toFixed(0)}`,
230
+ avgMI,
231
+ status: avgMI > 80 ? "✅ Excellent" : (avgMI > 65 ? "⚖️ Good" : "⚠️ Attention")
232
+ });
233
+
234
+ md += `## Detail: ${data.fileName}\n\n`;
235
+ md += `| Metric | Overall Value |\n| :--- | :--- |\n`;
236
+ md += `| **SLOC** | ${data.sloc} |\n`;
237
+ md += `| **Function Count** | ${data.fnCount} |\n`;
238
+ md += `| **Avg Maintainability** | **${avgMI.toFixed(2)}/100** |\n\n`;
239
+
240
+ md += `### Top 10 High Friction Functions\n`;
241
+ md += `| Function | Cognitive | Cyclomatic | MI | Status |\n| :--- | :--- | :--- | :--- | :--- |\n`;
242
+
243
+ data.results
244
+ .sort((a, b) => b.cog - a.cog)
245
+ .slice(0, 10)
246
+ .forEach(r => {
247
+ const status = r.cog > 25 ? "🛑 Critical" : (r.cog > 15 ? "⚠️ High" : "✅ Clean");
248
+ md += `| \`${r.name}\` | ${r.cog} | ${r.cc} | ${r.mi.toFixed(1)} | ${status} |\n`;
249
+ });
250
+
251
+ md += "\n---\n\n";
252
+ });
253
+
254
+ let summaryHeader = "## Executive Summary\n\n| File | Functions | Maintainability (min/avg/max) | Cognitive (min/avg/max) | Status |\n| :--- | :--- | :--- | :--- | :--- |\n";
255
+ summaries.sort((a, b) => {
256
+ // Optional: sort by status or name. Let's keep TARGET_FILES order if possible, or sort by name.
257
+ return 0; // Keep order of TARGET_FILES
258
+ }).forEach(s => {
259
+ summaryHeader += `| \`${s.name}\` | ${s.fns} | ${s.mi} | ${s.cog} | ${s.status} |\n`;
260
+ });
261
+
262
+ fs.writeFileSync(path.resolve(__dirname, 'latest_metrics.md'), md.replace("# Codebase Ethics & Complexity Report", "# Metrics Report\n\n" + summaryHeader));
263
+ console.log("Analysis complete. Report saved to latest_metrics.md");
264
+ };
265
+
266
+ generateReport();
@@ -0,0 +1,185 @@
1
+ # Metrics Report
2
+
3
+ ## Executive Summary
4
+
5
+ | File | Functions | Maintainability (min/avg/max) | Cognitive (min/avg/max) | Status |
6
+ | :--- | :--- | :--- | :--- | :--- |
7
+ | `lightview.js` | 58 | 7.2 / 65.5 / 92.9 | 0 / 3.4 / 25 | ⚖️ Good |
8
+ | `lightview-x.js` | 104 | 0.0 / 66.7 / 93.5 | 0 / 3.2 / 23 | ⚖️ Good |
9
+ | `lightview-router.js` | 27 | 24.8 / 68.6 / 93.5 | 0 / 2.1 / 19 | ⚖️ Good |
10
+ | `react.development.js` | 109 | 0.0 / 65.2 / 91.5 | 0 / 2.2 / 33 | ⚖️ Good |
11
+ | `bau.js` | 79 | 11.2 / 71.3 / 92.9 | 0 / 1.5 / 20 | ⚖️ Good |
12
+ | `htmx.js` | 335 | 0.0 / 65.3 / 92.9 | 0 / 3.4 / 116 | ⚖️ Good |
13
+ | `juris.js` | 360 | 21.2 / 70.1 / 96.5 | 0 / 2.6 / 51 | ⚖️ Good |
14
+
15
+
16
+ Generated on: 12/30/2025, 7:44:35 AM
17
+
18
+ ## Detail: lightview.js
19
+
20
+ | Metric | Overall Value |
21
+ | :--- | :--- |
22
+ | **SLOC** | 599 |
23
+ | **Function Count** | 58 |
24
+ | **Avg Maintainability** | **65.52/100** |
25
+
26
+ ### Top 10 High Friction Functions
27
+ | Function | Cognitive | Cyclomatic | MI | Status |
28
+ | :--- | :--- | :--- | :--- | :--- |
29
+ | `processChildren` | 25 | 19 | 35.1 | ⚠️ High |
30
+ | `makeReactiveAttributes` | 19 | 12 | 38.8 | ⚠️ High |
31
+ | `processComponentResult` | 17 | 15 | 42.0 | ⚠️ High |
32
+ | `enhance` | 13 | 10 | 44.6 | ✅ Clean |
33
+ | `value` | 11 | 11 | 44.2 | ✅ Clean |
34
+ | `processShadowDOM` | 10 | 10 | 43.5 | ✅ Clean |
35
+ | `signal` | 9 | 7 | 40.9 | ✅ Clean |
36
+ | `<anonymous>` | 9 | 9 | 59.0 | ✅ Clean |
37
+ | `<anonymous>` | 9 | 9 | 49.2 | ✅ Clean |
38
+ | `<anonymous>` | 9 | 6 | 7.2 | ✅ Clean |
39
+
40
+ ---
41
+
42
+ ## Detail: lightview-x.js
43
+
44
+ | Metric | Overall Value |
45
+ | :--- | :--- |
46
+ | **SLOC** | 1012 |
47
+ | **Function Count** | 104 |
48
+ | **Avg Maintainability** | **66.70/100** |
49
+
50
+ ### Top 10 High Friction Functions
51
+ | Function | Cognitive | Cyclomatic | MI | Status |
52
+ | :--- | :--- | :--- | :--- | :--- |
53
+ | `<anonymous>` | 23 | 20 | 0.0 | ⚠️ High |
54
+ | `state` | 22 | 25 | 39.6 | ⚠️ High |
55
+ | `handleNonStandardHref` | 16 | 12 | 36.5 | ⚠️ High |
56
+ | `registerStyleSheet` | 14 | 8 | 46.5 | ✅ Clean |
57
+ | `removeInsertedContent` | 14 | 7 | 45.5 | ✅ Clean |
58
+ | `convertObjectDOM` | 13 | 12 | 54.0 | ✅ Clean |
59
+ | `<anonymous>` | 13 | 9 | 55.7 | ✅ Clean |
60
+ | `activateReactiveSyntax` | 13 | 10 | 36.9 | ✅ Clean |
61
+ | `<anonymous>` | 12 | 12 | 44.9 | ✅ Clean |
62
+ | `handleSrcAttribute` | 10 | 9 | 47.1 | ✅ Clean |
63
+
64
+ ---
65
+
66
+ ## Detail: lightview-router.js
67
+
68
+ | Metric | Overall Value |
69
+ | :--- | :--- |
70
+ | **SLOC** | 141 |
71
+ | **Function Count** | 27 |
72
+ | **Avg Maintainability** | **68.59/100** |
73
+
74
+ ### Top 10 High Friction Functions
75
+ | Function | Cognitive | Cyclomatic | MI | Status |
76
+ | :--- | :--- | :--- | :--- | :--- |
77
+ | `route` | 19 | 10 | 51.6 | ⚠️ High |
78
+ | `normalizePath` | 7 | 8 | 63.1 | ✅ Clean |
79
+ | `<anonymous>` | 5 | 6 | 50.2 | ✅ Clean |
80
+ | `handleRequest` | 5 | 6 | 52.6 | ✅ Clean |
81
+ | `fetchHandler` | 4 | 4 | 64.3 | ✅ Clean |
82
+ | `<anonymous>` | 4 | 4 | 24.8 | ✅ Clean |
83
+ | `base` | 2 | 3 | 65.1 | ✅ Clean |
84
+ | `<anonymous>` | 2 | 4 | 84.4 | ✅ Clean |
85
+ | `use` | 2 | 3 | 63.6 | ✅ Clean |
86
+ | `<anonymous>` | 2 | 4 | 64.7 | ✅ Clean |
87
+
88
+ ---
89
+
90
+ ## Detail: react.development.js
91
+
92
+ | Metric | Overall Value |
93
+ | :--- | :--- |
94
+ | **SLOC** | 1282 |
95
+ | **Function Count** | 109 |
96
+ | **Avg Maintainability** | **65.19/100** |
97
+
98
+ ### Top 10 High Friction Functions
99
+ | Function | Cognitive | Cyclomatic | MI | Status |
100
+ | :--- | :--- | :--- | :--- | :--- |
101
+ | `mapIntoArray` | 33 | 39 | 23.6 | 🛑 Critical |
102
+ | `getComponentNameFromType` | 20 | 31 | 34.7 | ⚠️ High |
103
+ | `flushActQueue` | 19 | 7 | 47.0 | ⚠️ High |
104
+ | `<anonymous>` | 19 | 26 | 35.6 | ⚠️ High |
105
+ | `<anonymous>` | 18 | 20 | 35.9 | ⚠️ High |
106
+ | `lazyInitializer` | 9 | 9 | 37.7 | ✅ Clean |
107
+ | `recursivelyFlushAsyncActWork` | 9 | 5 | 51.3 | ✅ Clean |
108
+ | `<anonymous>` | 8 | 9 | 48.2 | ✅ Clean |
109
+ | `<anonymous>` | 7 | 10 | 29.6 | ✅ Clean |
110
+ | `<anonymous>` | 7 | 10 | 0.0 | ✅ Clean |
111
+
112
+ ---
113
+
114
+ ## Detail: bau.js
115
+
116
+ | Metric | Overall Value |
117
+ | :--- | :--- |
118
+ | **SLOC** | 458 |
119
+ | **Function Count** | 79 |
120
+ | **Avg Maintainability** | **71.26/100** |
121
+
122
+ ### Top 10 High Friction Functions
123
+ | Function | Cognitive | Cyclomatic | MI | Status |
124
+ | :--- | :--- | :--- | :--- | :--- |
125
+ | `<anonymous>` | 20 | 17 | 34.0 | ⚠️ High |
126
+ | `updateBinding` | 17 | 11 | 40.0 | ⚠️ High |
127
+ | `replaceChildren` | 14 | 8 | 48.2 | ✅ Clean |
128
+ | `add` | 9 | 7 | 53.3 | ✅ Clean |
129
+ | `toDom` | 6 | 5 | 56.8 | ✅ Clean |
130
+ | `get` | 5 | 6 | 49.4 | ✅ Clean |
131
+ | `processDom` | 4 | 4 | 51.1 | ✅ Clean |
132
+ | `val` | 4 | 5 | 58.1 | ✅ Clean |
133
+ | `val` | 4 | 4 | 55.4 | ✅ Clean |
134
+ | `<anonymous>` | 3 | 6 | 49.7 | ✅ Clean |
135
+
136
+ ---
137
+
138
+ ## Detail: htmx.js
139
+
140
+ | Metric | Overall Value |
141
+ | :--- | :--- |
142
+ | **SLOC** | 4803 |
143
+ | **Function Count** | 335 |
144
+ | **Avg Maintainability** | **65.27/100** |
145
+
146
+ ### Top 10 High Friction Functions
147
+ | Function | Cognitive | Cyclomatic | MI | Status |
148
+ | :--- | :--- | :--- | :--- | :--- |
149
+ | `issueAjaxRequest` | 116 | 73 | 5.5 | 🛑 Critical |
150
+ | `parseAndCacheTrigger` | 68 | 34 | 28.0 | 🛑 Critical |
151
+ | `doSwap` | 41 | 24 | 26.8 | 🛑 Critical |
152
+ | `eventListener` | 39 | 22 | 33.2 | 🛑 Critical |
153
+ | `handleAjaxResponse` | 34 | 29 | 22.9 | 🛑 Critical |
154
+ | `querySelectorAllExt` | 33 | 26 | 31.9 | 🛑 Critical |
155
+ | `swapWithStyle` | 29 | 16 | 38.3 | 🛑 Critical |
156
+ | `updateScrollState` | 27 | 23 | 39.3 | 🛑 Critical |
157
+ | `maybeGenerateConditional` | 26 | 9 | 42.0 | 🛑 Critical |
158
+ | `getSwapSpecification` | 25 | 19 | 36.1 | ⚠️ High |
159
+
160
+ ---
161
+
162
+ ## Detail: juris.js
163
+
164
+ | Metric | Overall Value |
165
+ | :--- | :--- |
166
+ | **SLOC** | 2736 |
167
+ | **Function Count** | 360 |
168
+ | **Avg Maintainability** | **70.11/100** |
169
+
170
+ ### Top 10 High Friction Functions
171
+ | Function | Cognitive | Cyclomatic | MI | Status |
172
+ | :--- | :--- | :--- | :--- | :--- |
173
+ | `<anonymous>` | 51 | 28 | 21.2 | 🛑 Critical |
174
+ | `<anonymous>` | 37 | 33 | 26.4 | 🛑 Critical |
175
+ | `<anonymous>` | 36 | 28 | 31.2 | 🛑 Critical |
176
+ | `<anonymous>` | 24 | 14 | 43.2 | ⚠️ High |
177
+ | `<anonymous>` | 24 | 26 | 28.7 | ⚠️ High |
178
+ | `<anonymous>` | 22 | 12 | 44.1 | ⚠️ High |
179
+ | `<anonymous>` | 20 | 8 | 49.5 | ⚠️ High |
180
+ | `updateChildren` | 19 | 11 | 37.4 | ⚠️ High |
181
+ | `<anonymous>` | 18 | 23 | 38.6 | ⚠️ High |
182
+ | `<anonymous>` | 17 | 9 | 46.3 | ⚠️ High |
183
+
184
+ ---
185
+
@@ -0,0 +1,10 @@
1
+ /**
2
+ * LIGHTVIEW-ALL
3
+ * Full bundle including Core, X, and CDOM.
4
+ */
5
+
6
+ import './lightview.js';
7
+ import './lightview-x.js';
8
+ import './lightview-cdom.js';
9
+
10
+ console.log('Lightview Full Bundle Loaded');