@jbpark/live-editor 4.4.0 → 4.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/{boundary-B17cepvm.js → boundary-DnjGgsK6.js} +8 -5
- package/dist/boundary-DnjGgsK6.js.map +1 -0
- package/dist/{context-DB5kW3qe.js → context-DsEXJl4h.js} +10 -4
- package/dist/context-DsEXJl4h.js.map +1 -0
- package/dist/dnd/index.d.ts +1 -1
- package/dist/dnd/index.js +1 -1
- package/dist/{dnd-BE5ZizqX.js → dnd-DGNp20Nk.js} +370 -305
- package/dist/dnd-DGNp20Nk.js.map +1 -0
- package/dist/{document-BDn_f08_.js → document-DGHTfrda.js} +7738 -7735
- package/dist/document-DGHTfrda.js.map +1 -0
- package/dist/error/index.d.ts +1 -1
- package/dist/error/index.js +2 -2
- package/dist/{error-DWhxQEom.js → error-DwqwjJ2C.js} +13 -6
- package/dist/error-DwqwjJ2C.js.map +1 -0
- package/dist/{index-f7ijuNTa.d.ts → index-CHknA6k1.d.ts} +108 -154
- package/dist/{index-Dgh1JTX3.d.ts → index-P9Aifaui.d.ts} +3 -2
- package/dist/index-WQgFy510.d.ts +11 -0
- package/dist/{index-C4SGQ3Ci.d.ts → index-vtyggWy0.d.ts} +4 -1
- package/dist/index.d.ts +18 -18
- package/dist/index.js +11 -7
- package/dist/index.js.map +1 -1
- package/dist/{ast-dYb78955.js → items-DdrwGxJw.js} +324 -373
- package/dist/items-DdrwGxJw.js.map +1 -0
- package/dist/messages-Cp3g1mn1.d.ts +129 -0
- package/dist/messages-CzUPVnzH.js +132 -0
- package/dist/messages-CzUPVnzH.js.map +1 -0
- package/dist/preview/index.js +2 -2
- package/dist/{preview-BnaCEFsF.js → preview-BKFgq_5u.js} +6 -4
- package/dist/preview-BKFgq_5u.js.map +1 -0
- package/dist/provider/index.d.ts +3 -8
- package/dist/provider/index.js +3 -2
- package/dist/{sections-CBVMO8Jl.js → sections-BRH3KHYc.js} +8 -3
- package/dist/sections-BRH3KHYc.js.map +1 -0
- package/dist/style.css +505 -21
- package/dist/{use-dynamic-tailwind-Bcipc1hL.js → use-dynamic-tailwind-T9r9ExbR.js} +9 -3
- package/dist/{use-dynamic-tailwind-Bcipc1hL.js.map → use-dynamic-tailwind-T9r9ExbR.js.map} +1 -1
- package/dist/utils/ast/index.d.ts +3 -2
- package/dist/utils/ast/index.js +66 -3
- package/dist/utils/ast/index.js.map +1 -0
- package/dist/utils/index.d.ts +30 -13
- package/dist/utils/index.js +26 -6
- package/dist/utils/index.js.map +1 -0
- package/dist/validate-DB59Z_Nx.d.ts +100 -0
- package/dist/validate-iLo8IONY.js +64 -0
- package/dist/validate-iLo8IONY.js.map +1 -0
- package/package.json +3 -3
- package/dist/ast-dYb78955.js.map +0 -1
- package/dist/boundary-B17cepvm.js.map +0 -1
- package/dist/context-DB5kW3qe.js.map +0 -1
- package/dist/dnd-BE5ZizqX.js.map +0 -1
- package/dist/document-BDn_f08_.js.map +0 -1
- package/dist/error-DWhxQEom.js.map +0 -1
- package/dist/preview-BnaCEFsF.js.map +0 -1
- package/dist/sections-CBVMO8Jl.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as require_lib, S as require_lib$1, _ as
|
|
1
|
+
import { C as require_lib, S as require_lib$1, _ as generateCode, b as getJSXTagName, d as traverse, g as attrValue, v as unwrap, w as __toESM, x as nanoid, y as wrap } from "./document-DGHTfrda.js";
|
|
2
2
|
import { c as RESERVED_BINDING_PROPERTIES, n as CONFIG, r as DATA_ATTR, s as REGEX, t as BINDING_PROP } from "./constants-h1JAgOeW.js";
|
|
3
3
|
import { n as registerEditorCache } from "./editor-caches-ClMvdcQF.js";
|
|
4
4
|
import { t as createBoundedCache } from "./cache-B0-XcIxJ.js";
|
|
@@ -496,337 +496,6 @@ const findEditableChildren = (node) => {
|
|
|
496
496
|
return editableChildren;
|
|
497
497
|
};
|
|
498
498
|
//#endregion
|
|
499
|
-
//#region src/utils/selection.ts
|
|
500
|
-
const removeIndices = (items, indices) => {
|
|
501
|
-
return items.filter((_, index) => !indices.has(index));
|
|
502
|
-
};
|
|
503
|
-
/**
|
|
504
|
-
* Shifts every selected index up/down by one step as a block, preserving
|
|
505
|
-
* relative order — scattered selections stop moving individually once they
|
|
506
|
-
* hit an unselected neighbor, so the whole group slides together instead of
|
|
507
|
-
* items passing through each other.
|
|
508
|
-
*/
|
|
509
|
-
const moveSelectedIndices = (items, indices, direction) => {
|
|
510
|
-
const next = [...items];
|
|
511
|
-
const nextIndices = new Set(indices);
|
|
512
|
-
const ordered = [...indices].sort((a, b) => direction === "up" ? a - b : b - a);
|
|
513
|
-
for (const index of ordered) {
|
|
514
|
-
const target = direction === "up" ? index - 1 : index + 1;
|
|
515
|
-
if (target < 0 || target >= next.length || nextIndices.has(target)) continue;
|
|
516
|
-
[next[index], next[target]] = [next[target], next[index]];
|
|
517
|
-
nextIndices.delete(index);
|
|
518
|
-
nextIndices.add(target);
|
|
519
|
-
}
|
|
520
|
-
return {
|
|
521
|
-
items: next,
|
|
522
|
-
indices: nextIndices
|
|
523
|
-
};
|
|
524
|
-
};
|
|
525
|
-
//#endregion
|
|
526
|
-
//#region src/utils/ast/patch.ts
|
|
527
|
-
const hasOnlyLayoutNewlines = (content) => {
|
|
528
|
-
let index = 0;
|
|
529
|
-
while (index < content.length) {
|
|
530
|
-
const char = content[index];
|
|
531
|
-
if (char === "`" || char === "<") return false;
|
|
532
|
-
if (char === "\"" || char === "'") {
|
|
533
|
-
const next = skipStringLiteral(content, index);
|
|
534
|
-
if (next === -1) return false;
|
|
535
|
-
index = next;
|
|
536
|
-
continue;
|
|
537
|
-
}
|
|
538
|
-
if (char === "/") {
|
|
539
|
-
const following = content[index + 1];
|
|
540
|
-
if (following === "/") {
|
|
541
|
-
const lineEnd = content.indexOf("\n", index + 2);
|
|
542
|
-
index = lineEnd === -1 ? content.length : lineEnd;
|
|
543
|
-
continue;
|
|
544
|
-
}
|
|
545
|
-
if (following === "*") {
|
|
546
|
-
const commentEnd = content.indexOf("*/", index + 2);
|
|
547
|
-
if (commentEnd === -1) return false;
|
|
548
|
-
index = commentEnd + 2;
|
|
549
|
-
continue;
|
|
550
|
-
}
|
|
551
|
-
return false;
|
|
552
|
-
}
|
|
553
|
-
index++;
|
|
554
|
-
}
|
|
555
|
-
return true;
|
|
556
|
-
};
|
|
557
|
-
const skipStringLiteral = (content, start) => {
|
|
558
|
-
const quote = content[start];
|
|
559
|
-
for (let index = start + 1; index < content.length; index++) {
|
|
560
|
-
const char = content[index];
|
|
561
|
-
if (char === "\\") {
|
|
562
|
-
const escaped = content[index + 1];
|
|
563
|
-
if (escaped === "\n" || escaped === "\r") return -1;
|
|
564
|
-
index++;
|
|
565
|
-
continue;
|
|
566
|
-
}
|
|
567
|
-
if (char === quote) return index + 1;
|
|
568
|
-
if (char === "\n") return -1;
|
|
569
|
-
}
|
|
570
|
-
return -1;
|
|
571
|
-
};
|
|
572
|
-
const lineIndentAt = (source, offset) => {
|
|
573
|
-
const lineStart = source.lastIndexOf("\n", offset - 1) + 1;
|
|
574
|
-
return /^[ \t]*/.exec(source.slice(lineStart, offset))?.[0] ?? "";
|
|
575
|
-
};
|
|
576
|
-
const lineTerminatorOf = (source) => {
|
|
577
|
-
return source.includes("\r\n") ? "\r\n" : "\n";
|
|
578
|
-
};
|
|
579
|
-
const applyEdits = (source, edits) => {
|
|
580
|
-
if (edits.length === 0) return source;
|
|
581
|
-
const ordered = [...edits].sort((a, b) => a.start - b.start || a.end - b.end);
|
|
582
|
-
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}`);
|
|
583
|
-
for (let i = 1; i < ordered.length; i++) {
|
|
584
|
-
const previous = ordered[i - 1];
|
|
585
|
-
const current = ordered[i];
|
|
586
|
-
if (current.start < previous.end) throw new Error(`Overlapping source edits: [${previous.start}, ${previous.end}) and [${current.start}, ${current.end})`);
|
|
587
|
-
}
|
|
588
|
-
let result = "";
|
|
589
|
-
let cursor = 0;
|
|
590
|
-
const terminator = lineTerminatorOf(source);
|
|
591
|
-
for (const edit of ordered) {
|
|
592
|
-
const content = edit.indent && edit.content.includes("\n") && hasOnlyLayoutNewlines(edit.content) ? edit.content.replace(/\n/g, `${terminator}${lineIndentAt(source, edit.start)}`) : edit.content;
|
|
593
|
-
result += source.slice(cursor, edit.start) + content;
|
|
594
|
-
cursor = edit.end;
|
|
595
|
-
}
|
|
596
|
-
return result + source.slice(cursor);
|
|
597
|
-
};
|
|
598
|
-
//#endregion
|
|
599
|
-
//#region src/utils/ast/array-source.ts
|
|
600
|
-
const commaIn = (source, start, end) => {
|
|
601
|
-
let comma = null;
|
|
602
|
-
for (let at = start; at < end; at++) {
|
|
603
|
-
if (/\s/.test(source[at])) continue;
|
|
604
|
-
if (source.startsWith("/*", at)) {
|
|
605
|
-
const close = source.indexOf("*/", at + 2);
|
|
606
|
-
if (close < 0 || close + 2 > end) return null;
|
|
607
|
-
at = close + 1;
|
|
608
|
-
} else if (source.startsWith("//", at)) {
|
|
609
|
-
const newline = source.indexOf("\n", at + 2);
|
|
610
|
-
at = newline < 0 ? end : newline;
|
|
611
|
-
} else if (source[at] === "," && comma === null) comma = at;
|
|
612
|
-
else return null;
|
|
613
|
-
}
|
|
614
|
-
return comma;
|
|
615
|
-
};
|
|
616
|
-
const denseArraySource = (source) => {
|
|
617
|
-
const array = parseArrayExpression(source);
|
|
618
|
-
if (!array || array.elements.some((node) => !import_lib$1.isExpression(node) || node.extra?.parenthesized)) return null;
|
|
619
|
-
const elements = array.elements;
|
|
620
|
-
const commas = elements.slice(0, -1).map((node, index) => commaIn(source, node.end, elements[index + 1].start));
|
|
621
|
-
if (commas.some((comma) => comma === null)) return null;
|
|
622
|
-
const last = elements.at(-1);
|
|
623
|
-
return {
|
|
624
|
-
array,
|
|
625
|
-
elements,
|
|
626
|
-
commas,
|
|
627
|
-
trailing: last ? commaIn(source, last.end, array.end - 1) : null
|
|
628
|
-
};
|
|
629
|
-
};
|
|
630
|
-
const canStructurallyEditArray = (source) => denseArraySource(source) !== null;
|
|
631
|
-
const validArrayIndices = (indices, length) => [...indices].every((index) => Number.isInteger(index) && index >= 0 && index < length);
|
|
632
|
-
const reorderArraySource = (source, elements, next) => applyEdits(source, elements.map((node, index) => ({
|
|
633
|
-
start: node.start,
|
|
634
|
-
end: node.end,
|
|
635
|
-
content: source.slice(next[index].start, next[index].end)
|
|
636
|
-
})));
|
|
637
|
-
const removeArraySource = (source, data, indices) => {
|
|
638
|
-
const edits = [];
|
|
639
|
-
const removedCommas = /* @__PURE__ */ new Set();
|
|
640
|
-
const { elements, commas, trailing } = data;
|
|
641
|
-
for (let index = 0; index < elements.length; index++) {
|
|
642
|
-
if (!indices.has(index)) continue;
|
|
643
|
-
const start = index;
|
|
644
|
-
while (index + 1 < elements.length && indices.has(index + 1)) index++;
|
|
645
|
-
for (let current = start; current <= index; current++) {
|
|
646
|
-
const node = elements[current];
|
|
647
|
-
edits.push({
|
|
648
|
-
start: node.start,
|
|
649
|
-
end: node.end,
|
|
650
|
-
content: ""
|
|
651
|
-
});
|
|
652
|
-
if (current < elements.length - 1) removedCommas.add(commas[current]);
|
|
653
|
-
}
|
|
654
|
-
if (index === elements.length - 1) {
|
|
655
|
-
if (trailing !== null) removedCommas.add(trailing);
|
|
656
|
-
else if (start > 0) removedCommas.add(commas[start - 1]);
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
for (const comma of removedCommas) edits.push({
|
|
660
|
-
start: comma,
|
|
661
|
-
end: comma + 1,
|
|
662
|
-
content: ""
|
|
663
|
-
});
|
|
664
|
-
return applyEdits(source, edits);
|
|
665
|
-
};
|
|
666
|
-
const appendArraySource = (source, data, copies) => {
|
|
667
|
-
const last = data.elements.at(-1);
|
|
668
|
-
if (!last || copies.length === 0) return source;
|
|
669
|
-
return applyEdits(source, [{
|
|
670
|
-
start: last.end,
|
|
671
|
-
end: last.end,
|
|
672
|
-
content: `, ${copies.join(", ")}`
|
|
673
|
-
}]);
|
|
674
|
-
};
|
|
675
|
-
//#endregion
|
|
676
|
-
//#region src/utils/ast/items.ts
|
|
677
|
-
const elementsOf = (code) => parseArrayExpression(code)?.elements ?? null;
|
|
678
|
-
const kindOf = (element) => import_lib$1.isObjectExpression(element) ? "object" : "primitive";
|
|
679
|
-
const parseItems = (code) => {
|
|
680
|
-
return elementsOf(code)?.flatMap((node, index) => import_lib$1.isExpression(node) ? [{
|
|
681
|
-
index,
|
|
682
|
-
kind: kindOf(node),
|
|
683
|
-
node
|
|
684
|
-
}] : []) ?? null;
|
|
685
|
-
};
|
|
686
|
-
const resolveRenderLeaf = (render, key) => {
|
|
687
|
-
const leaf = render?.[key];
|
|
688
|
-
return leaf && "type" in leaf ? leaf : null;
|
|
689
|
-
};
|
|
690
|
-
const toTemplateRaw = (value) => {
|
|
691
|
-
return value.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${").replace(/\r/g, "\\r");
|
|
692
|
-
};
|
|
693
|
-
const buildPropertyValue = (value, declaredType, renderLeaf, current) => {
|
|
694
|
-
if (renderLeaf?.property === BINDING_PROP.INNER_HTML) {
|
|
695
|
-
const raw = String(value);
|
|
696
|
-
return import_lib$1.templateLiteral([import_lib$1.templateElement({
|
|
697
|
-
raw: toTemplateRaw(raw),
|
|
698
|
-
cooked: raw
|
|
699
|
-
}, true)], []);
|
|
700
|
-
}
|
|
701
|
-
if (renderLeaf?.type === "jsx" || import_lib$1.isJSXElement(current) || import_lib$1.isJSXFragment(current)) {
|
|
702
|
-
const trimmed = String(value).trim();
|
|
703
|
-
if (!trimmed.startsWith("<")) return import_lib$1.stringLiteral(trimmed);
|
|
704
|
-
try {
|
|
705
|
-
return (0, import_lib.parseExpression)(trimmed, { plugins: ["jsx", "typescript"] });
|
|
706
|
-
} catch {
|
|
707
|
-
return;
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
if (declaredType === "array" || declaredType === "object") {
|
|
711
|
-
if (typeof value === "string") try {
|
|
712
|
-
return (0, import_lib.parseExpression)(value, { plugins: ["jsx", "typescript"] });
|
|
713
|
-
} catch {
|
|
714
|
-
return;
|
|
715
|
-
}
|
|
716
|
-
if (!isLosslesslyEvaluable(current)) return;
|
|
717
|
-
return valueToExpression(value) ?? void 0;
|
|
718
|
-
}
|
|
719
|
-
return createNodeFromValue(declaredType, parseValue(value)) ?? void 0;
|
|
720
|
-
};
|
|
721
|
-
const updateArrayItemProperty = (code, index, key, value, render) => {
|
|
722
|
-
const elements = elementsOf(code);
|
|
723
|
-
const element = elements?.[index];
|
|
724
|
-
if (!elements || !import_lib$1.isObjectExpression(element)) return null;
|
|
725
|
-
const target = element.properties.find((property) => import_lib$1.isObjectProperty(property) && import_lib$1.isIdentifier(property.key) && property.key.name === key);
|
|
726
|
-
if (!target) return null;
|
|
727
|
-
const declaredType = extractObjectProperties(element)[key]?.type ?? "string";
|
|
728
|
-
const nextValue = buildPropertyValue(value, declaredType, resolveRenderLeaf(render, key), target.value);
|
|
729
|
-
if (!nextValue) return null;
|
|
730
|
-
let serialized = typeof value === "string" && (declaredType === "array" || declaredType === "object" || import_lib$1.isJSXElement(nextValue) || import_lib$1.isJSXFragment(nextValue)) ? value.trim() : generateCode(nextValue);
|
|
731
|
-
if (nextValue.trailingComments?.some((comment) => comment.type === "CommentLine")) serialized += "\n";
|
|
732
|
-
const content = target.shorthand ? `${key}: ${serialized}` : serialized;
|
|
733
|
-
return applyEdits(code, [{
|
|
734
|
-
start: target.value.start,
|
|
735
|
-
end: target.value.end,
|
|
736
|
-
content
|
|
737
|
-
}]);
|
|
738
|
-
};
|
|
739
|
-
const updateArrayItemValue = (code, index, value) => {
|
|
740
|
-
const elements = elementsOf(code);
|
|
741
|
-
const element = elements?.[index];
|
|
742
|
-
if (!elements || !import_lib$1.isExpression(element)) return null;
|
|
743
|
-
const nextValue = createNodeFromValue(extractNodeValue(element).type, parseValue(value));
|
|
744
|
-
if (!nextValue) return null;
|
|
745
|
-
return applyEdits(code, [{
|
|
746
|
-
start: element.start,
|
|
747
|
-
end: element.end,
|
|
748
|
-
content: generateCode(nextValue)
|
|
749
|
-
}]);
|
|
750
|
-
};
|
|
751
|
-
const moveArrayItem = (code, from, to) => {
|
|
752
|
-
const data = denseArraySource(code);
|
|
753
|
-
if (!data || !validArrayIndices([from], data.elements.length) || !Number.isInteger(to)) return null;
|
|
754
|
-
const next = [...data.elements];
|
|
755
|
-
const [moved] = next.splice(from, 1);
|
|
756
|
-
next.splice(to, 0, moved);
|
|
757
|
-
return reorderArraySource(code, data.elements, next);
|
|
758
|
-
};
|
|
759
|
-
const moveArrayItems = (code, indices, direction) => {
|
|
760
|
-
const data = denseArraySource(code);
|
|
761
|
-
if (!data || !validArrayIndices(indices, data.elements.length) || !["up", "down"].includes(direction)) return null;
|
|
762
|
-
const next = moveSelectedIndices(data.elements, indices, direction);
|
|
763
|
-
return {
|
|
764
|
-
code: reorderArraySource(code, data.elements, next.items),
|
|
765
|
-
indices: next.indices
|
|
766
|
-
};
|
|
767
|
-
};
|
|
768
|
-
const removeArrayItems = (code, indices, kind) => {
|
|
769
|
-
const data = denseArraySource(code);
|
|
770
|
-
if (!data || !validArrayIndices(indices, data.elements.length)) return null;
|
|
771
|
-
const remaining = removeIndices(data.elements, indices);
|
|
772
|
-
if (!(kind === void 0 ? remaining : remaining.filter((node) => kindOf(node) === kind)).length) return null;
|
|
773
|
-
return removeArraySource(code, data, indices);
|
|
774
|
-
};
|
|
775
|
-
const copyItem = (code, element, generateId, usedIds) => {
|
|
776
|
-
const edits = [];
|
|
777
|
-
let unsupported = false;
|
|
778
|
-
const reserveId = (id) => {
|
|
779
|
-
if (usedIds.has(id)) unsupported = true;
|
|
780
|
-
usedIds.add(id);
|
|
781
|
-
return JSON.stringify(id);
|
|
782
|
-
};
|
|
783
|
-
import_lib$1.traverseFast(element, (node) => {
|
|
784
|
-
if (!import_lib$1.isJSXOpeningElement(node)) return;
|
|
785
|
-
const ids = node.attributes.filter((attr) => import_lib$1.isJSXAttribute(attr) && import_lib$1.isJSXIdentifier(attr.name, { name: "data-id" }));
|
|
786
|
-
const attr = ids[0];
|
|
787
|
-
if (ids.length > 1 || attr && (!import_lib$1.isStringLiteral(attr.value) || node.attributes.slice(node.attributes.indexOf(attr) + 1).some((a) => import_lib$1.isJSXSpreadAttribute(a)))) {
|
|
788
|
-
unsupported = true;
|
|
789
|
-
return;
|
|
790
|
-
}
|
|
791
|
-
if (attr && import_lib$1.isStringLiteral(attr.value)) edits.push({
|
|
792
|
-
start: attr.value.start,
|
|
793
|
-
end: attr.value.end,
|
|
794
|
-
content: reserveId(generateId())
|
|
795
|
-
});
|
|
796
|
-
});
|
|
797
|
-
if (import_lib$1.isObjectExpression(element)) {
|
|
798
|
-
const keys = element.properties.filter((prop) => import_lib$1.isObjectProperty(prop) && !prop.computed && (import_lib$1.isIdentifier(prop.key, { name: "key" }) || import_lib$1.isStringLiteral(prop.key, { value: "key" })));
|
|
799
|
-
const key = keys[0];
|
|
800
|
-
if (keys.length > 1 || key && (!import_lib$1.isStringLiteral(key.value) || element.properties.slice(element.properties.indexOf(key) + 1).some((prop) => import_lib$1.isSpreadElement(prop) || "computed" in prop && prop.computed))) return null;
|
|
801
|
-
if (key && import_lib$1.isStringLiteral(key.value)) edits.push({
|
|
802
|
-
start: key.value.start,
|
|
803
|
-
end: key.value.end,
|
|
804
|
-
content: reserveId(`${key.value.value}-${generateId()}`)
|
|
805
|
-
});
|
|
806
|
-
}
|
|
807
|
-
if (unsupported) return null;
|
|
808
|
-
return applyEdits(code.slice(element.start, element.end), edits.map((edit) => ({
|
|
809
|
-
...edit,
|
|
810
|
-
start: edit.start - element.start,
|
|
811
|
-
end: edit.end - element.start
|
|
812
|
-
})));
|
|
813
|
-
};
|
|
814
|
-
const duplicateArrayItems = (code, indices, generateId = () => nanoid(6)) => {
|
|
815
|
-
const data = denseArraySource(code);
|
|
816
|
-
if (!data || !indices.size || !validArrayIndices(indices, data.elements.length)) return null;
|
|
817
|
-
const usedIds = /* @__PURE__ */ new Set();
|
|
818
|
-
import_lib$1.traverseFast(data.array, (node) => {
|
|
819
|
-
if (import_lib$1.isJSXAttribute(node) && import_lib$1.isJSXIdentifier(node.name, { name: "data-id" }) && import_lib$1.isStringLiteral(node.value)) usedIds.add(node.value.value);
|
|
820
|
-
else if (import_lib$1.isObjectProperty(node) && !node.computed && (import_lib$1.isIdentifier(node.key, { name: "key" }) || import_lib$1.isStringLiteral(node.key, { value: "key" })) && import_lib$1.isStringLiteral(node.value)) usedIds.add(node.value.value);
|
|
821
|
-
});
|
|
822
|
-
const copies = [...indices].sort((a, b) => a - b).map((index) => copyItem(code, data.elements[index], generateId, usedIds));
|
|
823
|
-
return copies.every((copy) => copy !== null) ? appendArraySource(code, data, copies) : null;
|
|
824
|
-
};
|
|
825
|
-
const appendArrayItem = (code, kind, generateId = () => nanoid(6)) => {
|
|
826
|
-
const index = denseArraySource(code)?.elements.findIndex((node) => kindOf(node) === kind) ?? -1;
|
|
827
|
-
return index < 0 ? null : duplicateArrayItems(code, /* @__PURE__ */ new Set([index]), generateId);
|
|
828
|
-
};
|
|
829
|
-
//#endregion
|
|
830
499
|
//#region src/utils/ast/extract.ts
|
|
831
500
|
const collectText = (children) => {
|
|
832
501
|
return children.filter((c) => import_lib$1.isJSXText(c)).map((c) => c.value.trim()).filter((v) => v.length).join(" ");
|
|
@@ -1092,6 +761,106 @@ function clearExtractCache() {
|
|
|
1092
761
|
}
|
|
1093
762
|
registerEditorCache(clearExtractCache);
|
|
1094
763
|
//#endregion
|
|
764
|
+
//#region src/utils/selection.ts
|
|
765
|
+
const removeIndices = (items, indices) => {
|
|
766
|
+
return items.filter((_, index) => !indices.has(index));
|
|
767
|
+
};
|
|
768
|
+
/**
|
|
769
|
+
* Shifts every selected index up/down by one step as a block, preserving
|
|
770
|
+
* relative order — scattered selections stop moving individually once they
|
|
771
|
+
* hit an unselected neighbor, so the whole group slides together instead of
|
|
772
|
+
* items passing through each other.
|
|
773
|
+
*/
|
|
774
|
+
const moveSelectedIndices = (items, indices, direction) => {
|
|
775
|
+
const next = [...items];
|
|
776
|
+
const nextIndices = new Set(indices);
|
|
777
|
+
const ordered = [...indices].sort((a, b) => direction === "up" ? a - b : b - a);
|
|
778
|
+
for (const index of ordered) {
|
|
779
|
+
const target = direction === "up" ? index - 1 : index + 1;
|
|
780
|
+
if (target < 0 || target >= next.length || nextIndices.has(target)) continue;
|
|
781
|
+
[next[index], next[target]] = [next[target], next[index]];
|
|
782
|
+
nextIndices.delete(index);
|
|
783
|
+
nextIndices.add(target);
|
|
784
|
+
}
|
|
785
|
+
return {
|
|
786
|
+
items: next,
|
|
787
|
+
indices: nextIndices
|
|
788
|
+
};
|
|
789
|
+
};
|
|
790
|
+
//#endregion
|
|
791
|
+
//#region src/utils/ast/patch.ts
|
|
792
|
+
const hasOnlyLayoutNewlines = (content) => {
|
|
793
|
+
let index = 0;
|
|
794
|
+
while (index < content.length) {
|
|
795
|
+
const char = content[index];
|
|
796
|
+
if (char === "`" || char === "<") return false;
|
|
797
|
+
if (char === "\"" || char === "'") {
|
|
798
|
+
const next = skipStringLiteral(content, index);
|
|
799
|
+
if (next === -1) return false;
|
|
800
|
+
index = next;
|
|
801
|
+
continue;
|
|
802
|
+
}
|
|
803
|
+
if (char === "/") {
|
|
804
|
+
const following = content[index + 1];
|
|
805
|
+
if (following === "/") {
|
|
806
|
+
const lineEnd = content.indexOf("\n", index + 2);
|
|
807
|
+
index = lineEnd === -1 ? content.length : lineEnd;
|
|
808
|
+
continue;
|
|
809
|
+
}
|
|
810
|
+
if (following === "*") {
|
|
811
|
+
const commentEnd = content.indexOf("*/", index + 2);
|
|
812
|
+
if (commentEnd === -1) return false;
|
|
813
|
+
index = commentEnd + 2;
|
|
814
|
+
continue;
|
|
815
|
+
}
|
|
816
|
+
return false;
|
|
817
|
+
}
|
|
818
|
+
index++;
|
|
819
|
+
}
|
|
820
|
+
return true;
|
|
821
|
+
};
|
|
822
|
+
const skipStringLiteral = (content, start) => {
|
|
823
|
+
const quote = content[start];
|
|
824
|
+
for (let index = start + 1; index < content.length; index++) {
|
|
825
|
+
const char = content[index];
|
|
826
|
+
if (char === "\\") {
|
|
827
|
+
const escaped = content[index + 1];
|
|
828
|
+
if (escaped === "\n" || escaped === "\r") return -1;
|
|
829
|
+
index++;
|
|
830
|
+
continue;
|
|
831
|
+
}
|
|
832
|
+
if (char === quote) return index + 1;
|
|
833
|
+
if (char === "\n") return -1;
|
|
834
|
+
}
|
|
835
|
+
return -1;
|
|
836
|
+
};
|
|
837
|
+
const lineIndentAt = (source, offset) => {
|
|
838
|
+
const lineStart = source.lastIndexOf("\n", offset - 1) + 1;
|
|
839
|
+
return /^[ \t]*/.exec(source.slice(lineStart, offset))?.[0] ?? "";
|
|
840
|
+
};
|
|
841
|
+
const lineTerminatorOf = (source) => {
|
|
842
|
+
return source.includes("\r\n") ? "\r\n" : "\n";
|
|
843
|
+
};
|
|
844
|
+
const applyEdits = (source, edits) => {
|
|
845
|
+
if (edits.length === 0) return source;
|
|
846
|
+
const ordered = [...edits].sort((a, b) => a.start - b.start || a.end - b.end);
|
|
847
|
+
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}`);
|
|
848
|
+
for (let i = 1; i < ordered.length; i++) {
|
|
849
|
+
const previous = ordered[i - 1];
|
|
850
|
+
const current = ordered[i];
|
|
851
|
+
if (current.start < previous.end) throw new Error(`Overlapping source edits: [${previous.start}, ${previous.end}) and [${current.start}, ${current.end})`);
|
|
852
|
+
}
|
|
853
|
+
let result = "";
|
|
854
|
+
let cursor = 0;
|
|
855
|
+
const terminator = lineTerminatorOf(source);
|
|
856
|
+
for (const edit of ordered) {
|
|
857
|
+
const content = edit.indent && edit.content.includes("\n") && hasOnlyLayoutNewlines(edit.content) ? edit.content.replace(/\n/g, `${terminator}${lineIndentAt(source, edit.start)}`) : edit.content;
|
|
858
|
+
result += source.slice(cursor, edit.start) + content;
|
|
859
|
+
cursor = edit.end;
|
|
860
|
+
}
|
|
861
|
+
return result + source.slice(cursor);
|
|
862
|
+
};
|
|
863
|
+
//#endregion
|
|
1095
864
|
//#region src/utils/ast/children.ts
|
|
1096
865
|
const modeledSignatures = (nodes) => nodes.map((node) => {
|
|
1097
866
|
const jsx = nodeToJSX(node);
|
|
@@ -1639,55 +1408,237 @@ const bulkUpdate = (raw, entries) => {
|
|
|
1639
1408
|
};
|
|
1640
1409
|
};
|
|
1641
1410
|
//#endregion
|
|
1642
|
-
//#region src/utils/ast/
|
|
1643
|
-
const
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
if (
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1411
|
+
//#region src/utils/ast/array-source.ts
|
|
1412
|
+
const commaIn = (source, start, end) => {
|
|
1413
|
+
let comma = null;
|
|
1414
|
+
for (let at = start; at < end; at++) {
|
|
1415
|
+
if (/\s/.test(source[at])) continue;
|
|
1416
|
+
if (source.startsWith("/*", at)) {
|
|
1417
|
+
const close = source.indexOf("*/", at + 2);
|
|
1418
|
+
if (close < 0 || close + 2 > end) return null;
|
|
1419
|
+
at = close + 1;
|
|
1420
|
+
} else if (source.startsWith("//", at)) {
|
|
1421
|
+
const newline = source.indexOf("\n", at + 2);
|
|
1422
|
+
at = newline < 0 ? end : newline;
|
|
1423
|
+
} else if (source[at] === "," && comma === null) comma = at;
|
|
1424
|
+
else return null;
|
|
1651
1425
|
}
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1426
|
+
return comma;
|
|
1427
|
+
};
|
|
1428
|
+
const denseArraySource = (source) => {
|
|
1429
|
+
const array = parseArrayExpression(source);
|
|
1430
|
+
if (!array || array.elements.some((node) => !import_lib$1.isExpression(node) || node.extra?.parenthesized)) return null;
|
|
1431
|
+
const elements = array.elements;
|
|
1432
|
+
const commas = elements.slice(0, -1).map((node, index) => commaIn(source, node.end, elements[index + 1].start));
|
|
1433
|
+
if (commas.some((comma) => comma === null)) return null;
|
|
1434
|
+
const last = elements.at(-1);
|
|
1435
|
+
return {
|
|
1436
|
+
array,
|
|
1437
|
+
elements,
|
|
1438
|
+
commas,
|
|
1439
|
+
trailing: last ? commaIn(source, last.end, array.end - 1) : null
|
|
1440
|
+
};
|
|
1441
|
+
};
|
|
1442
|
+
const canStructurallyEditArray = (source) => denseArraySource(source) !== null;
|
|
1443
|
+
const validArrayIndices = (indices, length) => [...indices].every((index) => Number.isInteger(index) && index >= 0 && index < length);
|
|
1444
|
+
const reorderArraySource = (source, elements, next) => applyEdits(source, elements.map((node, index) => ({
|
|
1445
|
+
start: node.start,
|
|
1446
|
+
end: node.end,
|
|
1447
|
+
content: source.slice(next[index].start, next[index].end)
|
|
1448
|
+
})));
|
|
1449
|
+
const removeArraySource = (source, data, indices) => {
|
|
1450
|
+
const edits = [];
|
|
1451
|
+
const removedCommas = /* @__PURE__ */ new Set();
|
|
1452
|
+
const { elements, commas, trailing } = data;
|
|
1453
|
+
for (let index = 0; index < elements.length; index++) {
|
|
1454
|
+
if (!indices.has(index)) continue;
|
|
1455
|
+
const start = index;
|
|
1456
|
+
while (index + 1 < elements.length && indices.has(index + 1)) index++;
|
|
1457
|
+
for (let current = start; current <= index; current++) {
|
|
1458
|
+
const node = elements[current];
|
|
1459
|
+
edits.push({
|
|
1460
|
+
start: node.start,
|
|
1461
|
+
end: node.end,
|
|
1462
|
+
content: ""
|
|
1463
|
+
});
|
|
1464
|
+
if (current < elements.length - 1) removedCommas.add(commas[current]);
|
|
1465
|
+
}
|
|
1466
|
+
if (index === elements.length - 1) {
|
|
1467
|
+
if (trailing !== null) removedCommas.add(trailing);
|
|
1468
|
+
else if (start > 0) removedCommas.add(commas[start - 1]);
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
for (const comma of removedCommas) edits.push({
|
|
1472
|
+
start: comma,
|
|
1473
|
+
end: comma + 1,
|
|
1474
|
+
content: ""
|
|
1475
|
+
});
|
|
1476
|
+
return applyEdits(source, edits);
|
|
1477
|
+
};
|
|
1478
|
+
const appendArraySource = (source, data, copies) => {
|
|
1479
|
+
const last = data.elements.at(-1);
|
|
1480
|
+
if (!last || copies.length === 0) return source;
|
|
1481
|
+
return applyEdits(source, [{
|
|
1482
|
+
start: last.end,
|
|
1483
|
+
end: last.end,
|
|
1484
|
+
content: `, ${copies.join(", ")}`
|
|
1485
|
+
}]);
|
|
1486
|
+
};
|
|
1487
|
+
//#endregion
|
|
1488
|
+
//#region src/utils/ast/items.ts
|
|
1489
|
+
const elementsOf = (code) => parseArrayExpression(code)?.elements ?? null;
|
|
1490
|
+
const kindOf = (element) => import_lib$1.isObjectExpression(element) ? "object" : "primitive";
|
|
1491
|
+
const parseItems = (code) => {
|
|
1492
|
+
return elementsOf(code)?.flatMap((node, index) => import_lib$1.isExpression(node) ? [{
|
|
1493
|
+
index,
|
|
1494
|
+
kind: kindOf(node),
|
|
1495
|
+
node
|
|
1496
|
+
}] : []) ?? null;
|
|
1497
|
+
};
|
|
1498
|
+
const resolveRenderLeaf = (render, key) => {
|
|
1499
|
+
const leaf = render?.[key];
|
|
1500
|
+
return leaf && "type" in leaf ? leaf : null;
|
|
1501
|
+
};
|
|
1502
|
+
const toTemplateRaw = (value) => {
|
|
1503
|
+
return value.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${").replace(/\r/g, "\\r");
|
|
1504
|
+
};
|
|
1505
|
+
const buildPropertyValue = (value, declaredType, renderLeaf, current) => {
|
|
1506
|
+
if (renderLeaf?.property === BINDING_PROP.INNER_HTML) {
|
|
1507
|
+
const raw = String(value);
|
|
1508
|
+
return import_lib$1.templateLiteral([import_lib$1.templateElement({
|
|
1509
|
+
raw: toTemplateRaw(raw),
|
|
1510
|
+
cooked: raw
|
|
1511
|
+
}, true)], []);
|
|
1661
1512
|
}
|
|
1662
|
-
if (
|
|
1663
|
-
|
|
1513
|
+
if (renderLeaf?.type === "jsx" || import_lib$1.isJSXElement(current) || import_lib$1.isJSXFragment(current)) {
|
|
1514
|
+
const trimmed = String(value).trim();
|
|
1515
|
+
if (!trimmed.startsWith("<")) return import_lib$1.stringLiteral(trimmed);
|
|
1664
1516
|
try {
|
|
1665
|
-
|
|
1517
|
+
return (0, import_lib.parseExpression)(trimmed, { plugins: ["jsx", "typescript"] });
|
|
1666
1518
|
} catch {
|
|
1667
|
-
return
|
|
1519
|
+
return;
|
|
1668
1520
|
}
|
|
1669
|
-
if (!regex.test(value)) return {
|
|
1670
|
-
valid: false,
|
|
1671
|
-
message: "Value does not match the required format."
|
|
1672
|
-
};
|
|
1673
1521
|
}
|
|
1674
|
-
if (
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1522
|
+
if (declaredType === "array" || declaredType === "object") {
|
|
1523
|
+
if (typeof value === "string") try {
|
|
1524
|
+
return (0, import_lib.parseExpression)(value, { plugins: ["jsx", "typescript"] });
|
|
1525
|
+
} catch {
|
|
1526
|
+
return;
|
|
1527
|
+
}
|
|
1528
|
+
if (!isLosslesslyEvaluable(current)) return;
|
|
1529
|
+
return valueToExpression(value) ?? void 0;
|
|
1681
1530
|
}
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1531
|
+
return createNodeFromValue(declaredType, parseValue(value)) ?? void 0;
|
|
1532
|
+
};
|
|
1533
|
+
const updateArrayItemProperty = (code, index, key, value, render) => {
|
|
1534
|
+
const elements = elementsOf(code);
|
|
1535
|
+
const element = elements?.[index];
|
|
1536
|
+
if (!elements || !import_lib$1.isObjectExpression(element)) return null;
|
|
1537
|
+
const target = element.properties.find((property) => import_lib$1.isObjectProperty(property) && import_lib$1.isIdentifier(property.key) && property.key.name === key);
|
|
1538
|
+
if (!target) return null;
|
|
1539
|
+
const declaredType = extractObjectProperties(element)[key]?.type ?? "string";
|
|
1540
|
+
const nextValue = buildPropertyValue(value, declaredType, resolveRenderLeaf(render, key), target.value);
|
|
1541
|
+
if (!nextValue) return null;
|
|
1542
|
+
let serialized = typeof value === "string" && (declaredType === "array" || declaredType === "object" || import_lib$1.isJSXElement(nextValue) || import_lib$1.isJSXFragment(nextValue)) ? value.trim() : generateCode(nextValue);
|
|
1543
|
+
if (nextValue.trailingComments?.some((comment) => comment.type === "CommentLine")) serialized += "\n";
|
|
1544
|
+
const content = target.shorthand ? `${key}: ${serialized}` : serialized;
|
|
1545
|
+
return applyEdits(code, [{
|
|
1546
|
+
start: target.value.start,
|
|
1547
|
+
end: target.value.end,
|
|
1548
|
+
content
|
|
1549
|
+
}]);
|
|
1550
|
+
};
|
|
1551
|
+
const updateArrayItemValue = (code, index, value) => {
|
|
1552
|
+
const elements = elementsOf(code);
|
|
1553
|
+
const element = elements?.[index];
|
|
1554
|
+
if (!elements || !import_lib$1.isExpression(element)) return null;
|
|
1555
|
+
const nextValue = createNodeFromValue(extractNodeValue(element).type, parseValue(value));
|
|
1556
|
+
if (!nextValue) return null;
|
|
1557
|
+
return applyEdits(code, [{
|
|
1558
|
+
start: element.start,
|
|
1559
|
+
end: element.end,
|
|
1560
|
+
content: generateCode(nextValue)
|
|
1561
|
+
}]);
|
|
1562
|
+
};
|
|
1563
|
+
const moveArrayItem = (code, from, to) => {
|
|
1564
|
+
const data = denseArraySource(code);
|
|
1565
|
+
if (!data || !validArrayIndices([from], data.elements.length) || !Number.isInteger(to)) return null;
|
|
1566
|
+
const next = [...data.elements];
|
|
1567
|
+
const [moved] = next.splice(from, 1);
|
|
1568
|
+
next.splice(to, 0, moved);
|
|
1569
|
+
return reorderArraySource(code, data.elements, next);
|
|
1570
|
+
};
|
|
1571
|
+
const moveArrayItems = (code, indices, direction) => {
|
|
1572
|
+
const data = denseArraySource(code);
|
|
1573
|
+
if (!data || !validArrayIndices(indices, data.elements.length) || !["up", "down"].includes(direction)) return null;
|
|
1574
|
+
const next = moveSelectedIndices(data.elements, indices, direction);
|
|
1575
|
+
return {
|
|
1576
|
+
code: reorderArraySource(code, data.elements, next.items),
|
|
1577
|
+
indices: next.indices
|
|
1578
|
+
};
|
|
1579
|
+
};
|
|
1580
|
+
const removeArrayItems = (code, indices, kind) => {
|
|
1581
|
+
const data = denseArraySource(code);
|
|
1582
|
+
if (!data || !validArrayIndices(indices, data.elements.length)) return null;
|
|
1583
|
+
const remaining = removeIndices(data.elements, indices);
|
|
1584
|
+
if (!(kind === void 0 ? remaining : remaining.filter((node) => kindOf(node) === kind)).length) return null;
|
|
1585
|
+
return removeArraySource(code, data, indices);
|
|
1586
|
+
};
|
|
1587
|
+
const copyItem = (code, element, generateId, usedIds) => {
|
|
1588
|
+
const edits = [];
|
|
1589
|
+
let unsupported = false;
|
|
1590
|
+
const reserveId = (id) => {
|
|
1591
|
+
if (usedIds.has(id)) unsupported = true;
|
|
1592
|
+
usedIds.add(id);
|
|
1593
|
+
return JSON.stringify(id);
|
|
1594
|
+
};
|
|
1595
|
+
import_lib$1.traverseFast(element, (node) => {
|
|
1596
|
+
if (!import_lib$1.isJSXOpeningElement(node)) return;
|
|
1597
|
+
const ids = node.attributes.filter((attr) => import_lib$1.isJSXAttribute(attr) && import_lib$1.isJSXIdentifier(attr.name, { name: "data-id" }));
|
|
1598
|
+
const attr = ids[0];
|
|
1599
|
+
if (ids.length > 1 || attr && (!import_lib$1.isStringLiteral(attr.value) || node.attributes.slice(node.attributes.indexOf(attr) + 1).some((a) => import_lib$1.isJSXSpreadAttribute(a)))) {
|
|
1600
|
+
unsupported = true;
|
|
1601
|
+
return;
|
|
1602
|
+
}
|
|
1603
|
+
if (attr && import_lib$1.isStringLiteral(attr.value)) edits.push({
|
|
1604
|
+
start: attr.value.start,
|
|
1605
|
+
end: attr.value.end,
|
|
1606
|
+
content: reserveId(generateId())
|
|
1607
|
+
});
|
|
1608
|
+
});
|
|
1609
|
+
if (import_lib$1.isObjectExpression(element)) {
|
|
1610
|
+
const keys = element.properties.filter((prop) => import_lib$1.isObjectProperty(prop) && !prop.computed && (import_lib$1.isIdentifier(prop.key, { name: "key" }) || import_lib$1.isStringLiteral(prop.key, { value: "key" })));
|
|
1611
|
+
const key = keys[0];
|
|
1612
|
+
if (keys.length > 1 || key && (!import_lib$1.isStringLiteral(key.value) || element.properties.slice(element.properties.indexOf(key) + 1).some((prop) => import_lib$1.isSpreadElement(prop) || "computed" in prop && prop.computed))) return null;
|
|
1613
|
+
if (key && import_lib$1.isStringLiteral(key.value)) edits.push({
|
|
1614
|
+
start: key.value.start,
|
|
1615
|
+
end: key.value.end,
|
|
1616
|
+
content: reserveId(`${key.value.value}-${generateId()}`)
|
|
1617
|
+
});
|
|
1687
1618
|
}
|
|
1688
|
-
return
|
|
1619
|
+
if (unsupported) return null;
|
|
1620
|
+
return applyEdits(code.slice(element.start, element.end), edits.map((edit) => ({
|
|
1621
|
+
...edit,
|
|
1622
|
+
start: edit.start - element.start,
|
|
1623
|
+
end: edit.end - element.start
|
|
1624
|
+
})));
|
|
1625
|
+
};
|
|
1626
|
+
const duplicateArrayItems = (code, indices, generateId = () => nanoid(6)) => {
|
|
1627
|
+
const data = denseArraySource(code);
|
|
1628
|
+
if (!data || !indices.size || !validArrayIndices(indices, data.elements.length)) return null;
|
|
1629
|
+
const usedIds = /* @__PURE__ */ new Set();
|
|
1630
|
+
import_lib$1.traverseFast(data.array, (node) => {
|
|
1631
|
+
if (import_lib$1.isJSXAttribute(node) && import_lib$1.isJSXIdentifier(node.name, { name: "data-id" }) && import_lib$1.isStringLiteral(node.value)) usedIds.add(node.value.value);
|
|
1632
|
+
else if (import_lib$1.isObjectProperty(node) && !node.computed && (import_lib$1.isIdentifier(node.key, { name: "key" }) || import_lib$1.isStringLiteral(node.key, { value: "key" })) && import_lib$1.isStringLiteral(node.value)) usedIds.add(node.value.value);
|
|
1633
|
+
});
|
|
1634
|
+
const copies = [...indices].sort((a, b) => a - b).map((index) => copyItem(code, data.elements[index], generateId, usedIds));
|
|
1635
|
+
return copies.every((copy) => copy !== null) ? appendArraySource(code, data, copies) : null;
|
|
1636
|
+
};
|
|
1637
|
+
const appendArrayItem = (code, kind, generateId = () => nanoid(6)) => {
|
|
1638
|
+
const index = denseArraySource(code)?.elements.findIndex((node) => kindOf(node) === kind) ?? -1;
|
|
1639
|
+
return index < 0 ? null : duplicateArrayItems(code, /* @__PURE__ */ new Set([index]), generateId);
|
|
1689
1640
|
};
|
|
1690
1641
|
//#endregion
|
|
1691
|
-
export {
|
|
1642
|
+
export { extractNodeValue as A, parseBinding as C, arrayExpressionToCode as D, resolveBindings as E, setEditableValue as F, flattenEditableValue as M, parseArrayExpression as N, canLosslesslyEvaluateSource as O, parseValue as P, isComponentTagName as S, readNodeBindings as T, findEditableChildren as _, parseItems as a, getRegistryBindings as b, updateArrayItemValue as c, update as d, updateAll as f, extract as g, clearExtractCache as h, moveArrayItems as i, extractObjectProperties as j, createNodeFromValue as k, canStructurallyEditArray as l, moveSelectedIndices as m, duplicateArrayItems as n, removeArrayItems as o, getChildrenSignatures as p, moveArrayItem as r, updateArrayItemProperty as s, appendArrayItem as t, bulkUpdate as u, getCurrentValue as v, parseBindingExpression as w, getStructuredValue as x, getKeyBindings as y };
|
|
1692
1643
|
|
|
1693
|
-
//# sourceMappingURL=
|
|
1644
|
+
//# sourceMappingURL=items-DdrwGxJw.js.map
|