{"version":3,"sources":["../src/traverse.ts","../src/serialize.ts"],"sourcesContent":["import type { Spec, UIElement } from \"@json-render/core\";\n\n/**\n * Visitor function for spec traversal\n */\nexport interface TreeVisitor {\n  (\n    element: UIElement,\n    key: string,\n    depth: number,\n    parent: UIElement | null,\n  ): void;\n}\n\n/**\n * Traverse a UI spec depth-first\n */\nexport function traverseSpec(\n  spec: Spec,\n  visitor: TreeVisitor,\n  startKey?: string,\n): void {\n  if (!spec || !spec.root) return;\n\n  const rootKey = startKey ?? spec.root;\n  const rootElement = spec.elements[rootKey];\n  if (!rootElement) return;\n\n  function visit(key: string, depth: number, parent: UIElement | null): void {\n    const element = spec.elements[key];\n    if (!element) return;\n\n    visitor(element, key, depth, parent);\n\n    if (element.children) {\n      for (const childKey of element.children) {\n        visit(childKey, depth + 1, element);\n      }\n    }\n  }\n\n  visit(rootKey, 0, null);\n}\n\n/**\n * Collect all unique component types used in a spec\n */\nexport function collectUsedComponents(spec: Spec): Set<string> {\n  const components = new Set<string>();\n\n  traverseSpec(spec, (element, _key) => {\n    components.add(element.type);\n  });\n\n  return components;\n}\n\n/**\n * Collect all state paths referenced in a spec\n */\nexport function collectStatePaths(spec: Spec): Set<string> {\n  const paths = new Set<string>();\n\n  traverseSpec(spec, (element, _key) => {\n    // Check props for data paths\n    for (const [propName, propValue] of Object.entries(element.props)) {\n      // Check for path props (e.g., statePath, dataPath, bindPath)\n      if (typeof propValue === \"string\") {\n        if (\n          propName.endsWith(\"Path\") ||\n          propName === \"bindPath\" ||\n          propName === \"statePath\"\n        ) {\n          paths.add(propValue);\n        }\n      }\n\n      // Check for dynamic value objects with $state\n      if (\n        propValue &&\n        typeof propValue === \"object\" &&\n        \"$state\" in propValue &&\n        typeof (propValue as { $state: unknown }).$state === \"string\"\n      ) {\n        paths.add((propValue as { $state: string }).$state);\n      }\n    }\n\n    // Check visibility conditions for $state paths\n    if (element.visible != null && typeof element.visible !== \"boolean\") {\n      collectPathsFromCondition(element.visible, paths);\n    }\n  });\n\n  return paths;\n}\n\nfunction collectPathFromItem(\n  item: Record<string, unknown>,\n  paths: Set<string>,\n): void {\n  if (typeof item.$state === \"string\") {\n    paths.add(item.$state);\n  }\n  // Also collect $state references in comparison values (eq, neq, etc.)\n  for (const op of [\"eq\", \"neq\", \"gt\", \"gte\", \"lt\", \"lte\"]) {\n    const val = item[op];\n    if (\n      val &&\n      typeof val === \"object\" &&\n      \"$state\" in (val as Record<string, unknown>) &&\n      typeof (val as Record<string, unknown>).$state === \"string\"\n    ) {\n      paths.add((val as { $state: string }).$state);\n    }\n  }\n}\n\nfunction collectPathsFromCondition(\n  condition: unknown,\n  paths: Set<string>,\n): void {\n  if (!condition || typeof condition !== \"object\") return;\n\n  // Array = implicit AND\n  if (Array.isArray(condition)) {\n    for (const item of condition) {\n      if (item && typeof item === \"object\") {\n        collectPathFromItem(item as Record<string, unknown>, paths);\n      }\n    }\n    return;\n  }\n\n  const cond = condition as Record<string, unknown>;\n\n  // $or: recurse into each child condition\n  if (\"$or\" in cond && Array.isArray(cond.$or)) {\n    for (const child of cond.$or) {\n      collectPathsFromCondition(child, paths);\n    }\n    return;\n  }\n\n  // Single StateCondition\n  collectPathFromItem(cond, paths);\n}\n\n/**\n * Collect all action names used in a spec\n */\nexport function collectActions(spec: Spec): Set<string> {\n  const actions = new Set<string>();\n\n  traverseSpec(spec, (element, _key) => {\n    for (const propValue of Object.values(element.props)) {\n      // Check for action prop (string action name)\n      if (typeof propValue === \"string\" && propValue.startsWith(\"action:\")) {\n        actions.add(propValue.slice(7));\n      }\n\n      // Check for action objects\n      if (\n        propValue &&\n        typeof propValue === \"object\" &&\n        \"name\" in propValue &&\n        typeof (propValue as { name: unknown }).name === \"string\"\n      ) {\n        actions.add((propValue as { name: string }).name);\n      }\n    }\n\n    // Also check direct action prop\n    const actionProp = element.props.action;\n    if (typeof actionProp === \"string\") {\n      actions.add(actionProp);\n    }\n\n    // Collect actions from on event bindings\n    const onBindings = element.on;\n    if (onBindings) {\n      for (const binding of Object.values(onBindings)) {\n        const bindings = Array.isArray(binding) ? binding : [binding];\n        for (const b of bindings) {\n          if (\n            b &&\n            typeof b === \"object\" &&\n            \"action\" in b &&\n            typeof (b as { action: unknown }).action === \"string\"\n          ) {\n            actions.add((b as { action: string }).action);\n          }\n        }\n      }\n    }\n  });\n\n  return actions;\n}\n","/**\n * Options for serialization\n */\nexport interface SerializeOptions {\n  /** Quote style for strings */\n  quotes?: \"single\" | \"double\";\n  /** Indent for objects/arrays */\n  indent?: number;\n}\n\nconst DEFAULT_OPTIONS: Required<SerializeOptions> = {\n  quotes: \"double\",\n  indent: 2,\n};\n\n/**\n * Escape a string for use in code\n */\nexport function escapeString(\n  str: string,\n  quotes: \"single\" | \"double\" = \"double\",\n): string {\n  const quoteChar = quotes === \"single\" ? \"'\" : '\"';\n  const escaped = str\n    .replace(/\\\\/g, \"\\\\\\\\\")\n    .replace(/\\n/g, \"\\\\n\")\n    .replace(/\\r/g, \"\\\\r\")\n    .replace(/\\t/g, \"\\\\t\");\n\n  if (quotes === \"single\") {\n    return escaped.replace(/'/g, \"\\\\'\");\n  }\n  return escaped.replace(/\"/g, '\\\\\"');\n}\n\n/**\n * Serialize a single prop value to a code string\n *\n * @returns Object with `value` (the serialized string) and `needsBraces` (whether JSX needs {})\n */\nexport function serializePropValue(\n  value: unknown,\n  options: SerializeOptions = {},\n): { value: string; needsBraces: boolean } {\n  const opts = { ...DEFAULT_OPTIONS, ...options };\n  const q = opts.quotes === \"single\" ? \"'\" : '\"';\n\n  if (value === null) {\n    return { value: \"null\", needsBraces: true };\n  }\n\n  if (value === undefined) {\n    return { value: \"undefined\", needsBraces: true };\n  }\n\n  if (typeof value === \"string\") {\n    return {\n      value: `${q}${escapeString(value, opts.quotes)}${q}`,\n      needsBraces: false,\n    };\n  }\n\n  if (typeof value === \"number\") {\n    return { value: String(value), needsBraces: true };\n  }\n\n  if (typeof value === \"boolean\") {\n    if (value === true) {\n      return { value: \"true\", needsBraces: false }; // Can use shorthand\n    }\n    return { value: \"false\", needsBraces: true };\n  }\n\n  if (Array.isArray(value)) {\n    const items = value.map((v) => serializePropValue(v, opts).value);\n    return { value: `[${items.join(\", \")}]`, needsBraces: true };\n  }\n\n  if (typeof value === \"object\") {\n    // Check for $state reference\n    if (\n      \"$state\" in value &&\n      typeof (value as { $state: unknown }).$state === \"string\"\n    ) {\n      return {\n        value: `{ $state: ${q}${escapeString((value as { $state: string }).$state, opts.quotes)}${q} }`,\n        needsBraces: true,\n      };\n    }\n\n    const entries = Object.entries(value)\n      .filter(([, v]) => v !== undefined)\n      .map(([k, v]) => {\n        const serialized = serializePropValue(v, opts).value;\n        // Use shorthand if key matches value for simple identifiers\n        return `${k}: ${serialized}`;\n      });\n\n    return { value: `{ ${entries.join(\", \")} }`, needsBraces: true };\n  }\n\n  return { value: String(value), needsBraces: true };\n}\n\n/**\n * Serialize props object to JSX attributes string\n */\nexport function serializeProps(\n  props: Record<string, unknown>,\n  options: SerializeOptions = {},\n): string {\n  const parts: string[] = [];\n\n  for (const [key, value] of Object.entries(props)) {\n    if (value === undefined || value === null) continue;\n\n    const serialized = serializePropValue(value, options);\n\n    // Boolean true can be shorthand\n    if (typeof value === \"boolean\" && value === true) {\n      parts.push(key);\n    } else if (serialized.needsBraces) {\n      parts.push(`${key}={${serialized.value}}`);\n    } else {\n      parts.push(`${key}=${serialized.value}`);\n    }\n  }\n\n  return parts.join(\" \");\n}\n"],"mappings":"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","names":[]}