streetui 1.6.0 → 1.7.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.
@@ -1,6 +1,6 @@
1
- import { S as StreetApp } from './compile-CEom4hkp.cjs';
2
- export { V as VERSION } from './compile-CEom4hkp.cjs';
3
- import { R as RenderHandle, H as HydrationDiagnostic } from './hydration-diagnostics-D286jpZZ.cjs';
1
+ import { S as StreetApp } from './compile-Cte0nf06.cjs';
2
+ export { V as VERSION } from './compile-Cte0nf06.cjs';
3
+ import { R as RenderHandle, H as HydrationDiagnostic } from './hydration-diagnostics-zK-fihvd.cjs';
4
4
  export * from 'streetui/diagnostics';
5
5
 
6
6
  /**
package/dist/testing.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { S as StreetApp } from './compile-CEom4hkp.js';
2
- export { V as VERSION } from './compile-CEom4hkp.js';
3
- import { R as RenderHandle, H as HydrationDiagnostic } from './hydration-diagnostics-YrG-OJJe.js';
1
+ import { S as StreetApp } from './compile-Cte0nf06.js';
2
+ export { V as VERSION } from './compile-Cte0nf06.js';
3
+ import { R as RenderHandle, H as HydrationDiagnostic } from './hydration-diagnostics-Ddgs3Puu.js';
4
4
  export * from 'streetui/diagnostics';
5
5
 
6
6
  /**
package/dist/testing.js CHANGED
@@ -253,6 +253,18 @@ var BrowserDOMAdapter = class {
253
253
  focus(element) {
254
254
  element.focus?.();
255
255
  }
256
+ body() {
257
+ return document.body ?? null;
258
+ }
259
+ activeElement() {
260
+ return document.activeElement ?? null;
261
+ }
262
+ contains(ancestor, node) {
263
+ return ancestor.contains(node);
264
+ }
265
+ matches(element, selector) {
266
+ return typeof element.matches === "function" && element.matches(selector);
267
+ }
256
268
  isElement(node) {
257
269
  return node.nodeType === Node.ELEMENT_NODE;
258
270
  }
@@ -310,6 +322,14 @@ var ServerFragment = class {
310
322
  parent = null;
311
323
  children = [];
312
324
  };
325
+ var ServerRawHTML = class {
326
+ kind = "raw";
327
+ parent = null;
328
+ html;
329
+ constructor(html) {
330
+ this.html = html;
331
+ }
332
+ };
313
333
  var ServerElement = class {
314
334
  kind = "element";
315
335
  parent = null;
@@ -460,6 +480,8 @@ function serializeServerNode(node) {
460
480
  return `<!--${node.data}-->`;
461
481
  case "fragment":
462
482
  return serializeChildren(node);
483
+ case "raw":
484
+ return node.html;
463
485
  case "element": {
464
486
  const el = node;
465
487
  const tag = el.tagName;
@@ -499,6 +521,15 @@ var ServerDOMAdapter = class {
499
521
  createFragment() {
500
522
  return new ServerFragment();
501
523
  }
524
+ /**
525
+ * Create a verbatim pre-serialized HTML node (v1.7 static SSR plan, §6).
526
+ * Server-only: the browser adapter does not implement this, and the renderer
527
+ * fast path only invokes it when a static SSR plan is present (SSR). The
528
+ * stored HTML was produced by this same serializer, so it is emitted as-is.
529
+ */
530
+ createRawHTML(html) {
531
+ return new ServerRawHTML(html);
532
+ }
502
533
  appendChild(parent, child) {
503
534
  const p = asParent(parent);
504
535
  const c = asServer(child);
@@ -600,6 +631,18 @@ var ServerDOMAdapter = class {
600
631
  }
601
632
  focus() {
602
633
  }
634
+ body() {
635
+ return null;
636
+ }
637
+ activeElement() {
638
+ return null;
639
+ }
640
+ contains(_ancestor, _node) {
641
+ return false;
642
+ }
643
+ matches(_element, _selector) {
644
+ return false;
645
+ }
603
646
  isElement(node) {
604
647
  return asServer(node).kind === "element";
605
648
  }
@@ -650,14 +693,90 @@ var ServerDOMAdapter = class {
650
693
  }
651
694
  };
652
695
 
696
+ // ../dom/src/focus.ts
697
+ var FOCUSABLE_SELECTOR = 'a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"])';
698
+ function focusById(dom, root, id) {
699
+ const el = dom.querySelector(root, `[id="${id}"]`);
700
+ if (el === null) return false;
701
+ dom.focus(el);
702
+ return true;
703
+ }
704
+ function focusFirst(dom, container, selector = FOCUSABLE_SELECTOR) {
705
+ const el = dom.querySelector(container, selector);
706
+ if (el === null) return false;
707
+ dom.focus(el);
708
+ return true;
709
+ }
710
+ function getFocusable(dom, container, selector = FOCUSABLE_SELECTOR) {
711
+ return Array.from(dom.querySelectorAll(container, selector)).filter(
712
+ (el) => dom.matches(el, selector)
713
+ );
714
+ }
715
+ function saveFocus(dom) {
716
+ return dom.activeElement();
717
+ }
718
+ function restoreFocus(dom, saved) {
719
+ if (saved !== null) dom.focus(saved);
720
+ }
721
+ function focusInitial(dom, container, initialFocusId) {
722
+ if (initialFocusId !== void 0 && focusById(dom, container, initialFocusId)) return;
723
+ focusFirst(dom, container);
724
+ }
725
+ function trapFocus(dom, container) {
726
+ const onKeydown = (event) => {
727
+ if (event.key !== "Tab") return;
728
+ const items = getFocusable(dom, container);
729
+ if (items.length === 0) {
730
+ event.preventDefault();
731
+ return;
732
+ }
733
+ const first = items[0];
734
+ const last = items[items.length - 1];
735
+ const active = dom.activeElement();
736
+ if (active === null || !dom.contains(container, active)) {
737
+ event.preventDefault();
738
+ dom.focus(first);
739
+ } else if (event.shiftKey && active === first) {
740
+ event.preventDefault();
741
+ dom.focus(last);
742
+ } else if (!event.shiftKey && active === last) {
743
+ event.preventDefault();
744
+ dom.focus(first);
745
+ }
746
+ };
747
+ dom.addEventListener(container, "keydown", onKeydown);
748
+ return () => dom.removeEventListener(container, "keydown", onKeydown);
749
+ }
750
+ function containFocus(dom, container) {
751
+ const body = dom.body();
752
+ if (body === null) return () => {
753
+ };
754
+ const onFocusIn = (event) => {
755
+ const target = event.target;
756
+ if (target !== null && !dom.contains(container, target)) {
757
+ focusFirst(dom, container);
758
+ }
759
+ };
760
+ dom.addEventListener(body, "focusin", onFocusIn);
761
+ return () => dom.removeEventListener(body, "focusin", onFocusIn);
762
+ }
763
+ function onEscape(dom, target, handler) {
764
+ const onKeydown = (event) => {
765
+ if (event.key === "Escape") handler();
766
+ };
767
+ dom.addEventListener(target, "keydown", onKeydown);
768
+ return () => dom.removeEventListener(target, "keydown", onKeydown);
769
+ }
770
+
653
771
  // ../renderer/src/render-context.ts
654
- function createRenderContext(dom, graph, container, hydrationDiagnostics) {
772
+ function createRenderContext(dom, graph, container, hydrationDiagnostics, staticHTML) {
655
773
  return {
656
774
  dom,
657
775
  graph,
658
776
  instances: /* @__PURE__ */ new Map(),
659
777
  container,
660
- ...hydrationDiagnostics !== void 0 ? { hydrationDiagnostics } : {}
778
+ ...hydrationDiagnostics !== void 0 ? { hydrationDiagnostics } : {},
779
+ ...staticHTML !== void 0 ? { staticHTML } : {}
661
780
  };
662
781
  }
663
782
 
@@ -806,7 +925,12 @@ var TAG_MAP = {
806
925
  component: "div",
807
926
  slot: "div",
808
927
  fragment: "div",
809
- "reactive-list": "ul"
928
+ "reactive-list": "ul",
929
+ // A portal renders as a neutral inline anchor <div> at its declaration site;
930
+ // its children are relocated to a document.body container on the browser
931
+ // (see the portal branch in mount.ts). On the server (no body) it renders
932
+ // inline, so the anchor tag is what SSR/hydration positionally match on.
933
+ portal: "div"
810
934
  };
811
935
  function resolveTag(type) {
812
936
  return TAG_MAP[type] ?? "div";
@@ -1085,6 +1209,17 @@ function mountGraph(ctx) {
1085
1209
  }
1086
1210
  function mountNode(ctx, graphNode, parentDom) {
1087
1211
  const { dom, graph } = ctx;
1212
+ const staticHTML = ctx.staticHTML;
1213
+ if (staticHTML !== void 0 && dom.createRawHTML !== void 0) {
1214
+ const precomputed = staticHTML.get(graphNode.id);
1215
+ if (precomputed !== void 0) {
1216
+ const raw = dom.createRawHTML(precomputed);
1217
+ dom.appendChild(parentDom, raw);
1218
+ const instance2 = new NodeInstance(graphNode, raw);
1219
+ ctx.instances.set(graphNode.id, instance2);
1220
+ return instance2;
1221
+ }
1222
+ }
1088
1223
  if (graphNode.type === "application") {
1089
1224
  const instance2 = new NodeInstance(graphNode, parentDom);
1090
1225
  ctx.instances.set(graphNode.id, instance2);
@@ -1207,6 +1342,28 @@ function mountNode(ctx, graphNode, parentDom) {
1207
1342
  wireReactiveList(ctx, graphNode, instance2, el2);
1208
1343
  return instance2;
1209
1344
  }
1345
+ if (graphNode.type === "portal") {
1346
+ const anchor = dom.createElement(resolveTag("portal"));
1347
+ dom.setAttribute(anchor, "data-streetui-portal", "");
1348
+ applyNodeProps(ctx, graphNode, anchor);
1349
+ const instance2 = new NodeInstance(graphNode, anchor);
1350
+ ctx.instances.set(graphNode.id, instance2);
1351
+ const body = dom.body();
1352
+ let target = anchor;
1353
+ if (body !== null) {
1354
+ const portalContainer = dom.createElement("div");
1355
+ dom.setAttribute(portalContainer, "data-streetui-portal-container", "");
1356
+ dom.appendChild(body, portalContainer);
1357
+ instance2.trackCleanup(() => dom.removeChild(body, portalContainer));
1358
+ target = portalContainer;
1359
+ }
1360
+ for (const child of graphNode.children) {
1361
+ instance2.addChild(mountNode(ctx, child, target));
1362
+ }
1363
+ dom.appendChild(parentDom, anchor);
1364
+ wireOverlayBehavior(ctx, graphNode, instance2, target);
1365
+ return instance2;
1366
+ }
1210
1367
  const tag = resolveTag(graphNode.type);
1211
1368
  const el = dom.createElement(tag);
1212
1369
  applyNodeProps(ctx, graphNode, el);
@@ -1356,6 +1513,44 @@ function forgetInstance(ctx, instance) {
1356
1513
  ctx.instances.delete(instance.graphNode.id);
1357
1514
  for (const child of instance.children) forgetInstance(ctx, child);
1358
1515
  }
1516
+ function wireOverlayBehavior(ctx, graphNode, instance, target) {
1517
+ const { dom, graph } = ctx;
1518
+ if (dom.body() === null) return;
1519
+ const descFn = graph.getHandler(`__overlay__${graphNode.id}`);
1520
+ if (descFn === void 0) return;
1521
+ const desc = descFn();
1522
+ const openSig = desc.open;
1523
+ if (openSig === void 0 || typeof openSig.subscribe !== "function") return;
1524
+ let active = [];
1525
+ let saved = null;
1526
+ const teardown = () => {
1527
+ for (const fn of active) fn();
1528
+ active = [];
1529
+ };
1530
+ const onOpenChange = (isOpen) => {
1531
+ if (isOpen) {
1532
+ if (desc.restoreFocus) saved = saveFocus(dom);
1533
+ if (desc.takesFocus) focusInitial(dom, target, desc.initialFocusId);
1534
+ if (desc.modal) {
1535
+ active.push(trapFocus(dom, target));
1536
+ active.push(containFocus(dom, target));
1537
+ }
1538
+ if (desc.closeOnEscape && desc.onClose !== void 0) {
1539
+ active.push(onEscape(dom, target, desc.onClose));
1540
+ }
1541
+ } else {
1542
+ teardown();
1543
+ if (desc.restoreFocus && saved !== null) {
1544
+ restoreFocus(dom, saved);
1545
+ saved = null;
1546
+ }
1547
+ }
1548
+ };
1549
+ const unsub = openSig.subscribe(onOpenChange);
1550
+ instance.trackCleanup(unsub);
1551
+ instance.trackCleanup(teardown);
1552
+ if (openSig.peek() === true) onOpenChange(true);
1553
+ }
1359
1554
 
1360
1555
  // ../renderer/src/hydration-diagnostics.ts
1361
1556
  function formatHydrationDiagnostic(d) {
@@ -1451,6 +1646,25 @@ function hydrateNode(ctx, graphNode, domNode, path) {
1451
1646
  wireReactiveList(ctx, graphNode, instance, domNode);
1452
1647
  return instance;
1453
1648
  }
1649
+ case "portal": {
1650
+ const instance = new NodeInstance(graphNode, domNode);
1651
+ ctx.instances.set(graphNode.id, instance);
1652
+ const body = dom.body();
1653
+ let target = domNode;
1654
+ if (body !== null) {
1655
+ const portalContainer = dom.createElement("div");
1656
+ dom.setAttribute(portalContainer, "data-streetui-portal-container", "");
1657
+ for (const child of dom.childNodes(domNode)) {
1658
+ dom.appendChild(portalContainer, child);
1659
+ }
1660
+ dom.appendChild(body, portalContainer);
1661
+ instance.trackCleanup(() => dom.removeChild(body, portalContainer));
1662
+ target = portalContainer;
1663
+ }
1664
+ hydrateChildren(ctx, graphNode, instance, target, path);
1665
+ wireOverlayBehavior(ctx, graphNode, instance, target);
1666
+ return instance;
1667
+ }
1454
1668
  default: {
1455
1669
  const instance = new NodeInstance(graphNode, domNode);
1456
1670
  ctx.instances.set(graphNode.id, instance);
@@ -1629,11 +1843,167 @@ function createRenderer(options) {
1629
1843
  return new StreetRendererImpl(options);
1630
1844
  }
1631
1845
 
1846
+ // ../compiler/dist/diagnostics.js
1847
+ var TEXT_PROP_KEYS = /* @__PURE__ */ new Set(["text", "label", "value"]);
1848
+ function analyzeGraph(graph) {
1849
+ const nodes = /* @__PURE__ */ new Map();
1850
+ const summary = {
1851
+ totalNodes: 0,
1852
+ staticNodes: 0,
1853
+ staticSubtrees: 0,
1854
+ dynamicTextNodes: 0,
1855
+ dynamicAttrNodes: 0,
1856
+ eventNodes: 0,
1857
+ lists: 0,
1858
+ conditionals: 0
1859
+ };
1860
+ const visit = (node) => {
1861
+ let allChildrenStatic = true;
1862
+ for (const child of node.children) {
1863
+ const childSubtreeStatic = visit(child);
1864
+ if (!childSubtreeStatic) allChildrenStatic = false;
1865
+ }
1866
+ let hasDynamicText = false;
1867
+ let hasDynamicAttr = false;
1868
+ for (const ref of node.stateRefs) {
1869
+ if (TEXT_PROP_KEYS.has(ref.propKey)) hasDynamicText = true;
1870
+ else hasDynamicAttr = true;
1871
+ }
1872
+ const hasEvents = node.events.length > 0;
1873
+ const isList = node.type === "reactive-list";
1874
+ const isConditional = node.type === "conditional";
1875
+ const isPortal = node.type === "portal";
1876
+ const isStatic = node.stateRefs.length === 0 && !hasEvents && !isList && !isConditional && !isPortal;
1877
+ const isStaticSubtree = isStatic && allChildrenStatic;
1878
+ nodes.set(node.id, {
1879
+ isStatic,
1880
+ isStaticSubtree,
1881
+ hasDynamicText,
1882
+ hasDynamicAttr,
1883
+ hasEvents,
1884
+ isList,
1885
+ isConditional
1886
+ });
1887
+ summary.totalNodes += 1;
1888
+ if (isStatic) summary.staticNodes += 1;
1889
+ if (isStaticSubtree) summary.staticSubtrees += 1;
1890
+ if (hasDynamicText) summary.dynamicTextNodes += 1;
1891
+ if (hasDynamicAttr) summary.dynamicAttrNodes += 1;
1892
+ if (hasEvents) summary.eventNodes += 1;
1893
+ if (isList) summary.lists += 1;
1894
+ if (isConditional) summary.conditionals += 1;
1895
+ return isStaticSubtree;
1896
+ };
1897
+ visit(graph.root);
1898
+ return { nodes, summary };
1899
+ }
1900
+ function inspectCompilation(graph) {
1901
+ const analysis = analyzeGraph(graph);
1902
+ const nodes = [];
1903
+ const walk = (node, depth) => {
1904
+ const a = analysis.nodes.get(node.id);
1905
+ if (a !== void 0) {
1906
+ const classification = a.isStaticSubtree ? "static-subtree-root" : a.isStatic ? "static" : "dynamic";
1907
+ nodes.push({
1908
+ id: node.id,
1909
+ type: node.type,
1910
+ depth,
1911
+ classification,
1912
+ dynamicText: a.hasDynamicText,
1913
+ dynamicAttrs: a.hasDynamicAttr,
1914
+ events: node.events.map((e) => e.type),
1915
+ boundProps: node.stateRefs.map((r) => r.propKey),
1916
+ isList: a.isList,
1917
+ isConditional: a.isConditional,
1918
+ hydration: a.isStaticSubtree ? "adopt-static" : "verify-dynamic"
1919
+ });
1920
+ }
1921
+ for (const child of node.children) walk(child, depth + 1);
1922
+ };
1923
+ walk(graph.root, 0);
1924
+ const staticRatio = analysis.summary.totalNodes === 0 ? 0 : analysis.summary.staticNodes / analysis.summary.totalNodes;
1925
+ return {
1926
+ name: graph.name,
1927
+ version: graph.version,
1928
+ summary: { ...analysis.summary, staticRatio: +staticRatio.toFixed(4) },
1929
+ nodes
1930
+ };
1931
+ }
1932
+ function formatInspection(inspection) {
1933
+ const s = inspection.summary;
1934
+ const lines = [];
1935
+ lines.push(`StreetUI compiler inspection \u2014 ${inspection.name} v${inspection.version}`);
1936
+ lines.push(
1937
+ ` nodes=${s.totalNodes} static=${s.staticNodes} staticSubtrees=${s.staticSubtrees} dynamicText=${s.dynamicTextNodes} dynamicAttrs=${s.dynamicAttrNodes} events=${s.eventNodes} lists=${s.lists} conditionals=${s.conditionals} staticRatio=${(s.staticRatio * 100).toFixed(1)}%`
1938
+ );
1939
+ for (const n of inspection.nodes) {
1940
+ const flags = [];
1941
+ if (n.dynamicText) flags.push("text");
1942
+ if (n.dynamicAttrs) flags.push("attr:" + n.boundProps.join(","));
1943
+ if (n.events.length > 0) flags.push("on:" + n.events.join(","));
1944
+ if (n.isList) flags.push("list");
1945
+ if (n.isConditional) flags.push("cond");
1946
+ lines.push(
1947
+ ` ${" ".repeat(n.depth)}${n.type}#${n.id} [${n.classification}]` + (flags.length > 0 ? ` {${flags.join(" ")}}` : "")
1948
+ );
1949
+ }
1950
+ return lines.join("\n");
1951
+ }
1952
+
1953
+ // ../renderer/src/static-ssr-plan.ts
1954
+ function collectMaximalStaticRoots(graph) {
1955
+ const analysis = analyzeGraph(graph);
1956
+ const roots = [];
1957
+ const walk = (node) => {
1958
+ if (node.type !== "application") {
1959
+ const a = analysis.nodes.get(node.id);
1960
+ if (a !== void 0 && a.isStaticSubtree) {
1961
+ roots.push(node);
1962
+ return;
1963
+ }
1964
+ }
1965
+ for (const child of node.children) walk(child);
1966
+ };
1967
+ walk(graph.root);
1968
+ return roots;
1969
+ }
1970
+ function serializeStaticSubtree(dom, graph, root) {
1971
+ const container = dom.createElement("div");
1972
+ const ctx = createRenderContext(dom, graph, container);
1973
+ const instance = mountNode(ctx, root, container);
1974
+ const html = dom.serializeInner(container);
1975
+ instance.dispose();
1976
+ ctx.instances.clear();
1977
+ return html;
1978
+ }
1979
+ function buildStaticSSRPlan(compiled) {
1980
+ const graph = compiled.graph;
1981
+ const roots = collectMaximalStaticRoots(graph);
1982
+ const plan = /* @__PURE__ */ new Map();
1983
+ if (roots.length === 0) return plan;
1984
+ const dom = new ServerDOMAdapter();
1985
+ for (const root of roots) {
1986
+ plan.set(root.id, serializeStaticSubtree(dom, graph, root));
1987
+ }
1988
+ return plan;
1989
+ }
1990
+ var PLAN_CACHE = /* @__PURE__ */ new WeakMap();
1991
+ function getStaticSSRPlan(compiled) {
1992
+ let plan = PLAN_CACHE.get(compiled);
1993
+ if (plan === void 0) {
1994
+ plan = buildStaticSSRPlan(compiled);
1995
+ PLAN_CACHE.set(compiled, plan);
1996
+ }
1997
+ return plan;
1998
+ }
1999
+
1632
2000
  // ../renderer/src/ssr.ts
1633
2001
  function renderToString(compiled, options = {}) {
1634
2002
  const dom = options.domAdapter ?? new ServerDOMAdapter();
2003
+ const plan = options.staticPlan === null ? void 0 : options.staticPlan ?? getStaticSSRPlan(compiled);
2004
+ const staticHTML = plan !== void 0 && plan.size > 0 ? plan : void 0;
1635
2005
  const container = dom.createElement("div");
1636
- const ctx = createRenderContext(dom, compiled.graph, container);
2006
+ const ctx = createRenderContext(dom, compiled.graph, container, void 0, staticHTML);
1637
2007
  const rootInstance = mountGraph(ctx);
1638
2008
  const html = dom.serializeInner(container);
1639
2009
  rootInstance.dispose();
@@ -1928,114 +2298,8 @@ function renderServerThenHydrate(build, options = {}) {
1928
2298
  };
1929
2299
  }
1930
2300
 
1931
- // ../compiler/dist/diagnostics.js
1932
- var TEXT_PROP_KEYS = /* @__PURE__ */ new Set(["text", "label", "value"]);
1933
- function analyzeGraph(graph) {
1934
- const nodes = /* @__PURE__ */ new Map();
1935
- const summary = {
1936
- totalNodes: 0,
1937
- staticNodes: 0,
1938
- staticSubtrees: 0,
1939
- dynamicTextNodes: 0,
1940
- dynamicAttrNodes: 0,
1941
- eventNodes: 0,
1942
- lists: 0,
1943
- conditionals: 0
1944
- };
1945
- const visit = (node) => {
1946
- let allChildrenStatic = true;
1947
- for (const child of node.children) {
1948
- const childSubtreeStatic = visit(child);
1949
- if (!childSubtreeStatic) allChildrenStatic = false;
1950
- }
1951
- let hasDynamicText = false;
1952
- let hasDynamicAttr = false;
1953
- for (const ref of node.stateRefs) {
1954
- if (TEXT_PROP_KEYS.has(ref.propKey)) hasDynamicText = true;
1955
- else hasDynamicAttr = true;
1956
- }
1957
- const hasEvents = node.events.length > 0;
1958
- const isList = node.type === "reactive-list";
1959
- const isConditional = node.type === "conditional";
1960
- const isStatic = node.stateRefs.length === 0 && !hasEvents && !isList && !isConditional;
1961
- const isStaticSubtree = isStatic && allChildrenStatic;
1962
- nodes.set(node.id, {
1963
- isStatic,
1964
- isStaticSubtree,
1965
- hasDynamicText,
1966
- hasDynamicAttr,
1967
- hasEvents,
1968
- isList,
1969
- isConditional
1970
- });
1971
- summary.totalNodes += 1;
1972
- if (isStatic) summary.staticNodes += 1;
1973
- if (isStaticSubtree) summary.staticSubtrees += 1;
1974
- if (hasDynamicText) summary.dynamicTextNodes += 1;
1975
- if (hasDynamicAttr) summary.dynamicAttrNodes += 1;
1976
- if (hasEvents) summary.eventNodes += 1;
1977
- if (isList) summary.lists += 1;
1978
- if (isConditional) summary.conditionals += 1;
1979
- return isStaticSubtree;
1980
- };
1981
- visit(graph.root);
1982
- return { nodes, summary };
1983
- }
1984
- function inspectCompilation(graph) {
1985
- const analysis = analyzeGraph(graph);
1986
- const nodes = [];
1987
- const walk = (node, depth) => {
1988
- const a = analysis.nodes.get(node.id);
1989
- if (a !== void 0) {
1990
- const classification = a.isStaticSubtree ? "static-subtree-root" : a.isStatic ? "static" : "dynamic";
1991
- nodes.push({
1992
- id: node.id,
1993
- type: node.type,
1994
- depth,
1995
- classification,
1996
- dynamicText: a.hasDynamicText,
1997
- dynamicAttrs: a.hasDynamicAttr,
1998
- events: node.events.map((e) => e.type),
1999
- boundProps: node.stateRefs.map((r) => r.propKey),
2000
- isList: a.isList,
2001
- isConditional: a.isConditional,
2002
- hydration: a.isStaticSubtree ? "adopt-static" : "verify-dynamic"
2003
- });
2004
- }
2005
- for (const child of node.children) walk(child, depth + 1);
2006
- };
2007
- walk(graph.root, 0);
2008
- const staticRatio = analysis.summary.totalNodes === 0 ? 0 : analysis.summary.staticNodes / analysis.summary.totalNodes;
2009
- return {
2010
- name: graph.name,
2011
- version: graph.version,
2012
- summary: { ...analysis.summary, staticRatio: +staticRatio.toFixed(4) },
2013
- nodes
2014
- };
2015
- }
2016
- function formatInspection(inspection) {
2017
- const s = inspection.summary;
2018
- const lines = [];
2019
- lines.push(`StreetUI compiler inspection \u2014 ${inspection.name} v${inspection.version}`);
2020
- lines.push(
2021
- ` nodes=${s.totalNodes} static=${s.staticNodes} staticSubtrees=${s.staticSubtrees} dynamicText=${s.dynamicTextNodes} dynamicAttrs=${s.dynamicAttrNodes} events=${s.eventNodes} lists=${s.lists} conditionals=${s.conditionals} staticRatio=${(s.staticRatio * 100).toFixed(1)}%`
2022
- );
2023
- for (const n of inspection.nodes) {
2024
- const flags = [];
2025
- if (n.dynamicText) flags.push("text");
2026
- if (n.dynamicAttrs) flags.push("attr:" + n.boundProps.join(","));
2027
- if (n.events.length > 0) flags.push("on:" + n.events.join(","));
2028
- if (n.isList) flags.push("list");
2029
- if (n.isConditional) flags.push("cond");
2030
- lines.push(
2031
- ` ${" ".repeat(n.depth)}${n.type}#${n.id} [${n.classification}]` + (flags.length > 0 ? ` {${flags.join(" ")}}` : "")
2032
- );
2033
- }
2034
- return lines.join("\n");
2035
- }
2036
-
2037
2301
  // src/version.ts
2038
- var VERSION = "1.6.0";
2302
+ var VERSION = "1.7.0";
2039
2303
  export {
2040
2304
  VERSION,
2041
2305
  analyzeGraph,