@jbpark/live-editor 2.0.0 → 2.0.2

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.
@@ -426,6 +426,197 @@ const findEditableChildren = (node) => {
426
426
  return editableChildren;
427
427
  };
428
428
  //#endregion
429
+ //#region src/utils/selection.ts
430
+ const removeIndices = (items, indices) => {
431
+ return items.filter((_, index) => !indices.has(index));
432
+ };
433
+ /**
434
+ * Shifts every selected index up/down by one step as a block, preserving
435
+ * relative order — scattered selections stop moving individually once they
436
+ * hit an unselected neighbor, so the whole group slides together instead of
437
+ * items passing through each other.
438
+ */
439
+ const moveSelectedIndices = (items, indices, direction) => {
440
+ const next = [...items];
441
+ const nextIndices = new Set(indices);
442
+ const ordered = [...indices].sort((a, b) => direction === "up" ? a - b : b - a);
443
+ for (const index of ordered) {
444
+ const target = direction === "up" ? index - 1 : index + 1;
445
+ if (target < 0 || target >= next.length || nextIndices.has(target)) continue;
446
+ [next[index], next[target]] = [next[target], next[index]];
447
+ nextIndices.delete(index);
448
+ nextIndices.add(target);
449
+ }
450
+ return {
451
+ items: next,
452
+ indices: nextIndices
453
+ };
454
+ };
455
+ //#endregion
456
+ //#region src/utils/ast/tree.ts
457
+ const replaceIds = (code, generateId = () => nanoid(6)) => {
458
+ return code.replace(new RegExp(`${DATA_ATTR.ID}="[^"]*"`, "g"), () => {
459
+ return `${DATA_ATTR.ID}="${generateId()}"`;
460
+ });
461
+ };
462
+ const fillIds = (code, generateId = () => nanoid(6)) => {
463
+ return code.replace(new RegExp(`${DATA_ATTR.ID}=""`, "g"), () => {
464
+ return `${DATA_ATTR.ID}="${generateId()}"`;
465
+ });
466
+ };
467
+ const clone = (element, generateId = () => nanoid(6)) => {
468
+ const cloned = import_lib$2.cloneNode(element, true);
469
+ const generatedCode = generateCode(cloned);
470
+ const code = replaceIds(generatedCode, generateId);
471
+ return (0, import_lib$1.parseExpression)(code, { plugins: ["jsx", "typescript"] });
472
+ };
473
+ //#endregion
474
+ //#region src/utils/ast/items.ts
475
+ const elementsOf = (code) => {
476
+ const ast = parseArrayExpression(code);
477
+ if (!ast) return null;
478
+ return ast.elements.filter((element) => Boolean(element));
479
+ };
480
+ const kindOf = (element) => import_lib$2.isObjectExpression(element) ? "object" : "primitive";
481
+ const toCode = (elements) => {
482
+ return generateCode(import_lib$2.arrayExpression(elements));
483
+ };
484
+ const parseItems = (code) => {
485
+ const elements = elementsOf(code);
486
+ if (!elements) return null;
487
+ return elements.map((node, index) => ({
488
+ index,
489
+ kind: kindOf(node),
490
+ node
491
+ }));
492
+ };
493
+ const resolveRenderLeaf = (render, key) => {
494
+ const leaf = render?.[key];
495
+ return leaf && "type" in leaf ? leaf : null;
496
+ };
497
+ const isLosslesslyEvaluable = (node) => {
498
+ if (import_lib$2.isStringLiteral(node) || import_lib$2.isNumericLiteral(node) || import_lib$2.isBooleanLiteral(node) || import_lib$2.isNullLiteral(node)) return true;
499
+ if (import_lib$2.isUnaryExpression(node) && node.operator === "-" && import_lib$2.isNumericLiteral(node.argument)) return true;
500
+ if (import_lib$2.isTemplateLiteral(node)) return node.expressions.length === 0;
501
+ if (import_lib$2.isArrayExpression(node)) return node.elements.every((element) => element !== null && isLosslesslyEvaluable(element));
502
+ if (import_lib$2.isObjectExpression(node)) return node.properties.every((property) => import_lib$2.isObjectProperty(property) && !property.computed && (import_lib$2.isIdentifier(property.key) || import_lib$2.isStringLiteral(property.key) || import_lib$2.isNumericLiteral(property.key)) && isLosslesslyEvaluable(property.value));
503
+ return false;
504
+ };
505
+ const toTemplateRaw = (value) => {
506
+ return value.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${").replace(/\r/g, "\\r");
507
+ };
508
+ const buildPropertyValue = (value, declaredType, renderLeaf, current) => {
509
+ if (renderLeaf?.property === BINDING_PROP.INNER_HTML) {
510
+ const raw = String(value);
511
+ return import_lib$2.templateLiteral([import_lib$2.templateElement({
512
+ raw: toTemplateRaw(raw),
513
+ cooked: raw
514
+ }, true)], []);
515
+ }
516
+ if (renderLeaf?.type === "jsx") {
517
+ const trimmed = String(value).trim();
518
+ if (!trimmed.startsWith("<")) return import_lib$2.stringLiteral(trimmed);
519
+ try {
520
+ return (0, import_lib$1.parseExpression)(trimmed, { plugins: ["jsx", "typescript"] });
521
+ } catch {
522
+ return;
523
+ }
524
+ }
525
+ if (declaredType === "array" || declaredType === "object") {
526
+ if (typeof value === "string") try {
527
+ return (0, import_lib$1.parseExpression)(value, { plugins: ["jsx", "typescript"] });
528
+ } catch {
529
+ return;
530
+ }
531
+ if (!isLosslesslyEvaluable(current)) return;
532
+ return valueToExpression(value) ?? void 0;
533
+ }
534
+ return createNodeFromValue(declaredType, parseValue(value)) ?? void 0;
535
+ };
536
+ const updateArrayItemProperty = (code, index, key, value, render) => {
537
+ const elements = elementsOf(code);
538
+ const element = elements?.[index];
539
+ if (!elements || !import_lib$2.isObjectExpression(element)) return null;
540
+ const target = element.properties.find((property) => import_lib$2.isObjectProperty(property) && import_lib$2.isIdentifier(property.key) && property.key.name === key);
541
+ if (!target) return null;
542
+ const declaredType = extractObjectProperties(element)[key]?.type ?? "string";
543
+ const nextValue = buildPropertyValue(value, declaredType, resolveRenderLeaf(render, key), target.value);
544
+ if (!nextValue) return null;
545
+ target.value = nextValue;
546
+ return toCode(elements);
547
+ };
548
+ const updateArrayItemValue = (code, index, value) => {
549
+ const elements = elementsOf(code);
550
+ const element = elements?.[index];
551
+ if (!elements || !element) return null;
552
+ const nextValue = createNodeFromValue(extractNodeValue(element).type, parseValue(value));
553
+ if (!nextValue) return null;
554
+ elements[index] = nextValue;
555
+ return toCode(elements);
556
+ };
557
+ const moveArrayItem = (code, from, to) => {
558
+ const elements = elementsOf(code);
559
+ if (!elements?.[from]) return null;
560
+ const next = [...elements];
561
+ const [moved] = next.splice(from, 1);
562
+ next.splice(to, 0, moved);
563
+ return toCode(next);
564
+ };
565
+ const moveArrayItems = (code, indices, direction) => {
566
+ const elements = elementsOf(code);
567
+ if (!elements) return null;
568
+ const { items, indices: nextIndices } = moveSelectedIndices(elements, indices, direction);
569
+ return {
570
+ code: toCode(items),
571
+ indices: nextIndices
572
+ };
573
+ };
574
+ const removeArrayItems = (code, indices, kind) => {
575
+ const elements = elementsOf(code);
576
+ if (!elements) return null;
577
+ const remaining = removeIndices(elements, indices);
578
+ if ((kind === void 0 ? remaining : remaining.filter((element) => kindOf(element) === kind)).length < 1) return null;
579
+ return toCode(remaining);
580
+ };
581
+ const cloneItem = (element, generateId) => {
582
+ const cloned = clone(element);
583
+ if (!import_lib$2.isObjectExpression(cloned)) return cloned;
584
+ const editable = extractObjectProperties(cloned);
585
+ cloned.properties.forEach((property) => {
586
+ if (!import_lib$2.isObjectProperty(property) || !import_lib$2.isIdentifier(property.key)) return;
587
+ const key = property.key.name;
588
+ if (key === "key" && import_lib$2.isStringLiteral(property.value)) {
589
+ property.value = import_lib$2.stringLiteral(`${property.value.value}-${generateId()}`);
590
+ return;
591
+ }
592
+ const source = editable[key];
593
+ if (source) {
594
+ const next = createNodeFromValue(source.type, source.value);
595
+ if (next) property.value = next;
596
+ }
597
+ });
598
+ return cloned;
599
+ };
600
+ const duplicateArrayItems = (code, indices, generateId = () => nanoid(6)) => {
601
+ const elements = elementsOf(code);
602
+ if (!elements) return null;
603
+ const clones = [...indices].sort((a, b) => a - b).map((index) => elements[index]).filter((element) => Boolean(element)).map((element) => cloneItem(element, generateId));
604
+ if (clones.length === 0) return null;
605
+ return toCode([...elements, ...clones]);
606
+ };
607
+ const appendArrayItem = (code, kind, generateId = () => nanoid(6)) => {
608
+ const elements = elementsOf(code);
609
+ if (!elements) return null;
610
+ const template = elements.find((element) => kindOf(element) === kind);
611
+ if (!template) return null;
612
+ if (kind === "primitive") {
613
+ const { type, value } = extractNodeValue(template);
614
+ const next = createNodeFromValue(type, value);
615
+ return next ? toCode([...elements, next]) : null;
616
+ }
617
+ return toCode([...elements, cloneItem(template, generateId)]);
618
+ };
619
+ //#endregion
429
620
  //#region src/utils/ast/extract.ts
430
621
  const collectText = (children) => {
431
622
  return children.filter((c) => import_lib$2.isJSXText(c)).map((c) => c.value.trim()).filter((v) => v.length).join(" ");
@@ -683,45 +874,196 @@ function clearExtractCache() {
683
874
  extractCache.clear();
684
875
  }
685
876
  //#endregion
686
- //#region src/utils/ast/update.ts
687
- const updateInnerText = (path, value) => {
688
- const jsxChildren = path.node.children;
689
- for (let i = jsxChildren.length - 1; i >= 0; i--) if (import_lib$2.isJSXText(jsxChildren[i])) jsxChildren.splice(i, 1);
690
- jsxChildren.push(import_lib$2.jsxText(value));
877
+ //#region src/utils/ast/patch.ts
878
+ const hasOnlyLayoutNewlines = (content) => {
879
+ let index = 0;
880
+ while (index < content.length) {
881
+ const char = content[index];
882
+ if (char === "`" || char === "<") return false;
883
+ if (char === "\"" || char === "'") {
884
+ const next = skipStringLiteral(content, index);
885
+ if (next === -1) return false;
886
+ index = next;
887
+ continue;
888
+ }
889
+ if (char === "/") {
890
+ const following = content[index + 1];
891
+ if (following === "/") {
892
+ const lineEnd = content.indexOf("\n", index + 2);
893
+ index = lineEnd === -1 ? content.length : lineEnd;
894
+ continue;
895
+ }
896
+ if (following === "*") {
897
+ const commentEnd = content.indexOf("*/", index + 2);
898
+ if (commentEnd === -1) return false;
899
+ index = commentEnd + 2;
900
+ continue;
901
+ }
902
+ return false;
903
+ }
904
+ index++;
905
+ }
691
906
  return true;
692
907
  };
693
- const injectPlaceholder = (prefix, value, placeholders, { asRawContent = false } = {}) => {
694
- const name = `__${prefix}_${nanoid(6)}__`;
695
- const container = import_lib$2.jsxExpressionContainer(import_lib$2.identifier(name));
696
- placeholders.set(asRawContent ? `{${name}}` : name, value);
697
- return container;
908
+ const skipStringLiteral = (content, start) => {
909
+ const quote = content[start];
910
+ for (let index = start + 1; index < content.length; index++) {
911
+ const char = content[index];
912
+ if (char === "\\") {
913
+ const escaped = content[index + 1];
914
+ if (escaped === "\n" || escaped === "\r") return -1;
915
+ index++;
916
+ continue;
917
+ }
918
+ if (char === quote) return index + 1;
919
+ if (char === "\n") return -1;
920
+ }
921
+ return -1;
698
922
  };
699
- const updateInnerHTML = (path, value, placeholders) => {
700
- path.node.children = [injectPlaceholder("HTML", value, placeholders, { asRawContent: true })];
701
- return true;
923
+ const lineIndentAt = (source, offset) => {
924
+ const lineStart = source.lastIndexOf("\n", offset - 1) + 1;
925
+ return /^[ \t]*/.exec(source.slice(lineStart, offset))?.[0] ?? "";
702
926
  };
703
- const updateChildren = (path, value) => {
927
+ const lineTerminatorOf = (source) => {
928
+ return source.includes("\r\n") ? "\r\n" : "\n";
929
+ };
930
+ const applyEdits = (source, edits) => {
931
+ if (edits.length === 0) return source;
932
+ const ordered = [...edits].sort((a, b) => a.start - b.start || a.end - b.end);
933
+ for (const edit of ordered) if (!Number.isInteger(edit.start) || !Number.isInteger(edit.end) || edit.start < 0 || edit.end > source.length || edit.start > edit.end) throw new Error(`Invalid source edit [${edit.start}, ${edit.end}) for a source of length ${source.length}`);
934
+ for (let i = 1; i < ordered.length; i++) {
935
+ const previous = ordered[i - 1];
936
+ const current = ordered[i];
937
+ if (current.start < previous.end) throw new Error(`Overlapping source edits: [${previous.start}, ${previous.end}) and [${current.start}, ${current.end})`);
938
+ }
939
+ let result = "";
940
+ let cursor = 0;
941
+ const terminator = lineTerminatorOf(source);
942
+ for (const edit of ordered) {
943
+ const content = edit.indent && edit.content.includes("\n") && hasOnlyLayoutNewlines(edit.content) ? edit.content.replace(/\n/g, `${terminator}${lineIndentAt(source, edit.start)}`) : edit.content;
944
+ result += source.slice(cursor, edit.start) + content;
945
+ cursor = edit.end;
946
+ }
947
+ return result + source.slice(cursor);
948
+ };
949
+ //#endregion
950
+ //#region src/utils/ast/update.ts
951
+ const childrenRange = (element) => {
952
+ const { openingElement, closingElement } = element;
953
+ if (!closingElement || openingElement.end == null || closingElement.start == null) return null;
954
+ return {
955
+ start: openingElement.end,
956
+ end: closingElement.start
957
+ };
958
+ };
959
+ const findAttribute = (opening, propertyName) => {
960
+ return opening.attributes.find((attr) => import_lib$2.isJSXAttribute(attr) && import_lib$2.isJSXIdentifier(attr.name) && attr.name.name === propertyName);
961
+ };
962
+ const attributeInsertPoint = (opening) => {
963
+ return opening.attributes[opening.attributes.length - 1]?.end ?? opening.name.end ?? null;
964
+ };
965
+ const editInnerText = (source, element, value) => {
966
+ const range = childrenRange(element);
967
+ if (!range) return [];
968
+ const [only] = element.children;
969
+ if (element.children.length === 1 && import_lib$2.isJSXText(only) && only.start != null && only.end != null) {
970
+ const raw = source.slice(only.start, only.end);
971
+ if (raw.trim() !== "") {
972
+ const leading = raw.length - raw.trimStart().length;
973
+ const trailing = raw.length - raw.trimEnd().length;
974
+ return [{
975
+ start: only.start + leading,
976
+ end: only.end - trailing,
977
+ content: value
978
+ }];
979
+ }
980
+ }
981
+ const edits = [];
982
+ for (const child of element.children) if (import_lib$2.isJSXText(child) && child.start != null && child.end != null) edits.push({
983
+ start: child.start,
984
+ end: child.end,
985
+ content: ""
986
+ });
987
+ edits.push({
988
+ start: range.end,
989
+ end: range.end,
990
+ content: value
991
+ });
992
+ return edits;
993
+ };
994
+ const editInnerHTML = (element, value) => {
995
+ const range = childrenRange(element);
996
+ if (!range) return [];
997
+ return [{
998
+ start: range.start,
999
+ end: range.end,
1000
+ content: value
1001
+ }];
1002
+ };
1003
+ const editChildren = (element, value) => {
704
1004
  try {
705
1005
  const childrenData = typeof value === "string" ? JSON.parse(value) : value;
706
- path.node.children.length = 0;
707
- childrenData.forEach((childData) => {
708
- const jsxElement = nodeToJSX(childData);
709
- if (jsxElement) path.node.children.push(jsxElement);
710
- });
711
- return true;
1006
+ const range = childrenRange(element);
1007
+ if (!range) return [];
1008
+ const content = childrenData.map((childData) => nodeToJSX(childData)).filter((node) => node !== null).map((node) => generateCode(node)).join("");
1009
+ return [{
1010
+ start: range.start,
1011
+ end: range.end,
1012
+ content,
1013
+ indent: true
1014
+ }];
712
1015
  } catch (error) {
713
1016
  console.error("❌ Children update error:", error);
714
- return false;
1017
+ return null;
715
1018
  }
716
1019
  };
717
- const updateRichtext = (path, value) => {
718
- path.node.children = [];
719
- const opening = path.node.openingElement;
720
- const htmlObject = import_lib$2.objectExpression([import_lib$2.objectProperty(import_lib$2.identifier("__html"), import_lib$2.stringLiteral(value))]);
721
- const existingAttr = opening.attributes.find((a) => import_lib$2.isJSXAttribute(a) && import_lib$2.isJSXIdentifier(a.name) && a.name.name === "dangerouslySetInnerHTML");
722
- if (existingAttr && import_lib$2.isJSXAttribute(existingAttr)) existingAttr.value = import_lib$2.jsxExpressionContainer(htmlObject);
723
- else opening.attributes.push(import_lib$2.jsxAttribute(import_lib$2.jsxIdentifier("dangerouslySetInnerHTML"), import_lib$2.jsxExpressionContainer(htmlObject)));
724
- return true;
1020
+ const editRichtext = (element, value) => {
1021
+ const edits = [];
1022
+ const range = childrenRange(element);
1023
+ if (range && range.start !== range.end) edits.push({
1024
+ start: range.start,
1025
+ end: range.end,
1026
+ content: ""
1027
+ });
1028
+ const opening = element.openingElement;
1029
+ const attribute = import_lib$2.jsxAttribute(import_lib$2.jsxIdentifier("dangerouslySetInnerHTML"), import_lib$2.jsxExpressionContainer(import_lib$2.objectExpression([import_lib$2.objectProperty(import_lib$2.identifier("__html"), import_lib$2.stringLiteral(value))])));
1030
+ const content = generateCode(attribute);
1031
+ const existing = findAttribute(opening, "dangerouslySetInnerHTML");
1032
+ if (existing && existing.start != null && existing.end != null) {
1033
+ edits.push({
1034
+ start: existing.start,
1035
+ end: existing.end,
1036
+ content,
1037
+ indent: true
1038
+ });
1039
+ return edits;
1040
+ }
1041
+ const insertAt = attributeInsertPoint(opening);
1042
+ if (insertAt == null) return null;
1043
+ edits.push({
1044
+ start: insertAt,
1045
+ end: insertAt,
1046
+ content: ` ${content}`,
1047
+ indent: true
1048
+ });
1049
+ return edits;
1050
+ };
1051
+ const editJsxAttribute = (opening, propertyName, value) => {
1052
+ const attribute = findAttribute(opening, propertyName);
1053
+ if (!attribute) return null;
1054
+ const content = `{${String(value).trim()}}`;
1055
+ if (attribute.value?.start != null && attribute.value.end != null) return [{
1056
+ start: attribute.value.start,
1057
+ end: attribute.value.end,
1058
+ content
1059
+ }];
1060
+ const insertAt = attribute.name.end;
1061
+ if (insertAt == null) return null;
1062
+ return [{
1063
+ start: insertAt,
1064
+ end: insertAt,
1065
+ content: `=${content}`
1066
+ }];
725
1067
  };
726
1068
  const buildAttributeValue = (value, type) => {
727
1069
  if (type === "array" || type === "object") {
@@ -737,13 +1079,16 @@ const buildAttributeValue = (value, type) => {
737
1079
  const expr = valueToExpression(value);
738
1080
  return expr ? import_lib$2.jsxExpressionContainer(expr) : import_lib$2.stringLiteral(String(value));
739
1081
  };
740
- const updateAttribute = (opening, propertyName, value, type) => {
741
- const customAttr = opening.attributes.find((attr) => import_lib$2.isJSXAttribute(attr) && import_lib$2.isJSXIdentifier(attr.name) && attr.name.name === propertyName);
742
- if (customAttr && import_lib$2.isJSXAttribute(customAttr)) {
743
- customAttr.value = buildAttributeValue(value, type);
744
- return true;
745
- }
746
- return false;
1082
+ const editAttribute = (opening, propertyName, value, type) => {
1083
+ const attribute = findAttribute(opening, propertyName);
1084
+ if (!attribute || attribute.start == null || attribute.end == null) return null;
1085
+ const content = generateCode(import_lib$2.jsxAttribute(attribute.name, buildAttributeValue(value, type)));
1086
+ return [{
1087
+ start: attribute.start,
1088
+ end: attribute.end,
1089
+ content,
1090
+ indent: true
1091
+ }];
747
1092
  };
748
1093
  const update = (code, dataId, label, value, property) => {
749
1094
  try {
@@ -753,7 +1098,12 @@ const update = (code, dataId, label, value, property) => {
753
1098
  plugins: ["jsx", "typescript"]
754
1099
  });
755
1100
  let changed = false;
756
- const jsxPlaceholders = /* @__PURE__ */ new Map();
1101
+ const edits = [];
1102
+ const collect = (result) => {
1103
+ if (!result) return;
1104
+ edits.push(...result);
1105
+ changed = true;
1106
+ };
757
1107
  traverse(ast, { JSXElement(path) {
758
1108
  const opening = path.node.openingElement;
759
1109
  if (!opening.attributes.find((attr) => {
@@ -773,32 +1123,23 @@ const update = (code, dataId, label, value, property) => {
773
1123
  const propertyBinding = matches[0];
774
1124
  switch (propertyBinding.property) {
775
1125
  case BINDING_PROP.INNER_TEXT:
776
- changed = updateInnerText(path, String(value));
1126
+ collect(editInnerText(wrapped, path.node, String(value)));
777
1127
  break;
778
1128
  case BINDING_PROP.INNER_HTML:
779
- if (propertyBinding.type === "richtext") changed = updateRichtext(path, String(value));
780
- else changed = updateInnerHTML(path, String(value), jsxPlaceholders);
1129
+ collect(propertyBinding.type === "richtext" ? editRichtext(path.node, String(value)) : editInnerHTML(path.node, String(value)));
781
1130
  break;
782
1131
  case BINDING_PROP.CHILDREN:
783
- changed = updateChildren(path, value);
1132
+ collect(editChildren(path.node, value));
784
1133
  break;
785
- default: if (propertyBinding.type === "jsx") {
786
- const attr = opening.attributes.find((a) => import_lib$2.isJSXAttribute(a) && import_lib$2.isJSXIdentifier(a.name) && a.name.name === propertyBinding.property);
787
- if (attr && import_lib$2.isJSXAttribute(attr)) {
788
- attr.value = injectPlaceholder("JSX", String(value).trim(), jsxPlaceholders);
789
- changed = true;
790
- }
791
- } else changed = updateAttribute(opening, propertyBinding.property, value, propertyBinding.type);
1134
+ default: collect(propertyBinding.type === "jsx" ? editJsxAttribute(opening, propertyBinding.property, value) : editAttribute(opening, propertyBinding.property, value, propertyBinding.type));
792
1135
  }
793
1136
  } });
794
1137
  if (!changed) return {
795
1138
  code,
796
1139
  success: false
797
1140
  };
798
- let result = unwrap(generateCode(ast));
799
- for (const [placeholder, original] of jsxPlaceholders) result = result.replace(placeholder, () => original);
800
1141
  return {
801
- code: result,
1142
+ code: unwrap(applyEdits(wrapped, edits)),
802
1143
  success: true
803
1144
  };
804
1145
  } catch (error) {
@@ -823,24 +1164,6 @@ const bulkUpdate = (raw, entries) => {
823
1164
  };
824
1165
  };
825
1166
  //#endregion
826
- //#region src/utils/ast/tree.ts
827
- const replaceIds = (code, generateId = () => nanoid(6)) => {
828
- return code.replace(new RegExp(`${DATA_ATTR.ID}="[^"]*"`, "g"), () => {
829
- return `${DATA_ATTR.ID}="${generateId()}"`;
830
- });
831
- };
832
- const fillIds = (code, generateId = () => nanoid(6)) => {
833
- return code.replace(new RegExp(`${DATA_ATTR.ID}=""`, "g"), () => {
834
- return `${DATA_ATTR.ID}="${generateId()}"`;
835
- });
836
- };
837
- const clone = (element, generateId = () => nanoid(6)) => {
838
- const cloned = import_lib$2.cloneNode(element, true);
839
- const generatedCode = generateCode(cloned);
840
- const code = replaceIds(generatedCode, generateId);
841
- return (0, import_lib$1.parseExpression)(code, { plugins: ["jsx", "typescript"] });
842
- };
843
- //#endregion
844
1167
  //#region src/utils/ast/validate.ts
845
1168
  const VALID = { valid: true };
846
1169
  const validateBindingValue = (binding, value) => {
@@ -890,6 +1213,6 @@ const validateBindingValue = (binding, value) => {
890
1213
  return VALID;
891
1214
  };
892
1215
  //#endregion
893
- export { generateCode as S, extractObjectProperties as _, bulkUpdate as a, parseValue as b, extract as c, getStructuredValue as d, parseBinding as f, extractNodeValue as g, createNodeFromValue as h, replaceIds as i, findEditableChildren as l, arrayExpressionToCode as m, clone as n, update as o, parseBindingExpression as p, fillIds as r, clearExtractCache as s, validateBindingValue as t, getCurrentValue as u, flattenEditableValue as v, setEditableValue as x, parseArrayExpression as y };
1216
+ export { parseValue as A, parseBindingExpression as C, extractObjectProperties as D, extractNodeValue as E, generateCode as M, flattenEditableValue as O, parseBinding as S, createNodeFromValue as T, moveSelectedIndices as _, extract as a, getCurrentValue as b, moveArrayItem as c, removeArrayItems as d, updateArrayItemProperty as f, replaceIds as g, fillIds as h, clearExtractCache as i, setEditableValue as j, parseArrayExpression as k, moveArrayItems as l, clone as m, bulkUpdate as n, appendArrayItem as o, updateArrayItemValue as p, update as r, duplicateArrayItems as s, validateBindingValue as t, parseItems as u, removeIndices as v, arrayExpressionToCode as w, getStructuredValue as x, findEditableChildren as y };
894
1217
 
895
- //# sourceMappingURL=ast-CrSMBsL4.js.map
1218
+ //# sourceMappingURL=ast-BKQmEBj9.js.map