@jbpark/live-editor 4.0.1 → 4.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/ast-jab6hQrT.js +1544 -0
  2. package/dist/ast-jab6hQrT.js.map +1 -0
  3. package/dist/{boundary-C90q3YsT.js → boundary-D2IFja-3.js} +2 -2
  4. package/dist/boundary-D2IFja-3.js.map +1 -0
  5. package/dist/cache-B0-XcIxJ.js +51 -0
  6. package/dist/cache-B0-XcIxJ.js.map +1 -0
  7. package/dist/{utils-BQ7WL7Jh.js → cn-CAnad2vL.js} +3 -128
  8. package/dist/cn-CAnad2vL.js.map +1 -0
  9. package/dist/compile-CBOAtQMz.js +103 -0
  10. package/dist/compile-CBOAtQMz.js.map +1 -0
  11. package/dist/constants-BD3FZKWE.js +1450 -0
  12. package/dist/constants-BD3FZKWE.js.map +1 -0
  13. package/dist/{context-Y-M9fViD.js → context-DQ-UtoyS.js} +3 -3
  14. package/dist/context-DQ-UtoyS.js.map +1 -0
  15. package/dist/{core-Co8Nlq16.js → core-DaXVtvH5.js} +3 -2
  16. package/dist/core-DaXVtvH5.js.map +1 -0
  17. package/dist/detect-typescript-b0A751v2.js +10 -0
  18. package/dist/detect-typescript-b0A751v2.js.map +1 -0
  19. package/dist/dnd/index.d.ts +1 -1
  20. package/dist/dnd/index.js +1 -1
  21. package/dist/{dnd-_0jfnnVB.js → dnd-CMrXfD8O.js} +31 -9
  22. package/dist/dnd-CMrXfD8O.js.map +1 -0
  23. package/dist/{extract-abHYmNsu.js → document-C5Dypv4_.js} +7373 -9553
  24. package/dist/document-C5Dypv4_.js.map +1 -0
  25. package/dist/editor/index.js +2 -2
  26. package/dist/{editor-aNRx7hRY.js → editor-C3tQaMlU.js} +2 -2
  27. package/dist/{editor-aNRx7hRY.js.map → editor-C3tQaMlU.js.map} +1 -1
  28. package/dist/editor-caches-ClMvdcQF.js +23 -0
  29. package/dist/editor-caches-ClMvdcQF.js.map +1 -0
  30. package/dist/error/index.js +2 -2
  31. package/dist/{error-o0SBwtTv.js → error-KW8XCcjH.js} +2 -2
  32. package/dist/{error-o0SBwtTv.js.map → error-KW8XCcjH.js.map} +1 -1
  33. package/dist/{index-Doh4VTXr.d.ts → index-C4t0OawE.d.ts} +2 -2
  34. package/dist/index.d.ts +2 -2
  35. package/dist/index.js +5 -5
  36. package/dist/preview/index.js +2 -2
  37. package/dist/{preview-DKyKKcu7.js → preview-BwEfltgi.js} +10 -6
  38. package/dist/preview-BwEfltgi.js.map +1 -0
  39. package/dist/provider/index.js +1 -1
  40. package/dist/sections-B7dWoNVJ.js +19 -0
  41. package/dist/sections-B7dWoNVJ.js.map +1 -0
  42. package/dist/style.css +550 -688
  43. package/dist/{use-dynamic-tailwind-BF8TQ3Yy.js → use-dynamic-tailwind-DuGLeDR3.js} +20 -6
  44. package/dist/use-dynamic-tailwind-DuGLeDR3.js.map +1 -0
  45. package/dist/utils/ast/index.js +2 -2
  46. package/dist/utils/index.d.ts +16 -6
  47. package/dist/utils/index.js +6 -2
  48. package/dist/utils/tailwind/index.js +27 -1
  49. package/dist/{tailwind-CbjdLyVX.js.map → utils/tailwind/index.js.map} +1 -1
  50. package/package.json +2 -2
  51. package/dist/ast-CZwpVrJv.js +0 -851
  52. package/dist/ast-CZwpVrJv.js.map +0 -1
  53. package/dist/boundary-C90q3YsT.js.map +0 -1
  54. package/dist/context-Y-M9fViD.js.map +0 -1
  55. package/dist/core-Co8Nlq16.js.map +0 -1
  56. package/dist/dnd-_0jfnnVB.js.map +0 -1
  57. package/dist/extract-abHYmNsu.js.map +0 -1
  58. package/dist/preview-DKyKKcu7.js.map +0 -1
  59. package/dist/tailwind-CbjdLyVX.js +0 -28
  60. package/dist/use-dynamic-tailwind-BF8TQ3Yy.js.map +0 -1
  61. package/dist/utils-BQ7WL7Jh.js.map +0 -1
@@ -1,851 +0,0 @@
1
- import { C as unwrap, I as nanoid, K as __toESM, L as require_lib, P as traverse, R as require_lib$1, S as generateCode, V as DATA_ATTR, _ as parseArrayExpression, b as unwrapExpression, c as parseBinding, f as createNodeFromValue, g as isLosslesslyEvaluable, i as STRING_VALUED_TYPES, m as extractObjectProperties, n as extract, p as extractNodeValue, r as nodeToJSX, v as parseValue, w as wrap, x as valueToExpression, z as BINDING_PROP } from "./extract-abHYmNsu.js";
2
- //#region src/utils/selection.ts
3
- var import_lib = require_lib$1();
4
- var import_lib$1 = /* @__PURE__ */ __toESM(require_lib(), 1);
5
- const removeIndices = (items, indices) => {
6
- return items.filter((_, index) => !indices.has(index));
7
- };
8
- /**
9
- * Shifts every selected index up/down by one step as a block, preserving
10
- * relative order — scattered selections stop moving individually once they
11
- * hit an unselected neighbor, so the whole group slides together instead of
12
- * items passing through each other.
13
- */
14
- const moveSelectedIndices = (items, indices, direction) => {
15
- const next = [...items];
16
- const nextIndices = new Set(indices);
17
- const ordered = [...indices].sort((a, b) => direction === "up" ? a - b : b - a);
18
- for (const index of ordered) {
19
- const target = direction === "up" ? index - 1 : index + 1;
20
- if (target < 0 || target >= next.length || nextIndices.has(target)) continue;
21
- [next[index], next[target]] = [next[target], next[index]];
22
- nextIndices.delete(index);
23
- nextIndices.add(target);
24
- }
25
- return {
26
- items: next,
27
- indices: nextIndices
28
- };
29
- };
30
- //#endregion
31
- //#region src/utils/ast/patch.ts
32
- const hasOnlyLayoutNewlines = (content) => {
33
- let index = 0;
34
- while (index < content.length) {
35
- const char = content[index];
36
- if (char === "`" || char === "<") return false;
37
- if (char === "\"" || char === "'") {
38
- const next = skipStringLiteral(content, index);
39
- if (next === -1) return false;
40
- index = next;
41
- continue;
42
- }
43
- if (char === "/") {
44
- const following = content[index + 1];
45
- if (following === "/") {
46
- const lineEnd = content.indexOf("\n", index + 2);
47
- index = lineEnd === -1 ? content.length : lineEnd;
48
- continue;
49
- }
50
- if (following === "*") {
51
- const commentEnd = content.indexOf("*/", index + 2);
52
- if (commentEnd === -1) return false;
53
- index = commentEnd + 2;
54
- continue;
55
- }
56
- return false;
57
- }
58
- index++;
59
- }
60
- return true;
61
- };
62
- const skipStringLiteral = (content, start) => {
63
- const quote = content[start];
64
- for (let index = start + 1; index < content.length; index++) {
65
- const char = content[index];
66
- if (char === "\\") {
67
- const escaped = content[index + 1];
68
- if (escaped === "\n" || escaped === "\r") return -1;
69
- index++;
70
- continue;
71
- }
72
- if (char === quote) return index + 1;
73
- if (char === "\n") return -1;
74
- }
75
- return -1;
76
- };
77
- const lineIndentAt = (source, offset) => {
78
- const lineStart = source.lastIndexOf("\n", offset - 1) + 1;
79
- return /^[ \t]*/.exec(source.slice(lineStart, offset))?.[0] ?? "";
80
- };
81
- const lineTerminatorOf = (source) => {
82
- return source.includes("\r\n") ? "\r\n" : "\n";
83
- };
84
- const applyEdits = (source, edits) => {
85
- if (edits.length === 0) return source;
86
- const ordered = [...edits].sort((a, b) => a.start - b.start || a.end - b.end);
87
- 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}`);
88
- for (let i = 1; i < ordered.length; i++) {
89
- const previous = ordered[i - 1];
90
- const current = ordered[i];
91
- if (current.start < previous.end) throw new Error(`Overlapping source edits: [${previous.start}, ${previous.end}) and [${current.start}, ${current.end})`);
92
- }
93
- let result = "";
94
- let cursor = 0;
95
- const terminator = lineTerminatorOf(source);
96
- for (const edit of ordered) {
97
- const content = edit.indent && edit.content.includes("\n") && hasOnlyLayoutNewlines(edit.content) ? edit.content.replace(/\n/g, `${terminator}${lineIndentAt(source, edit.start)}`) : edit.content;
98
- result += source.slice(cursor, edit.start) + content;
99
- cursor = edit.end;
100
- }
101
- return result + source.slice(cursor);
102
- };
103
- //#endregion
104
- //#region src/utils/ast/array-source.ts
105
- const commaIn = (source, start, end) => {
106
- let comma = null;
107
- for (let at = start; at < end; at++) {
108
- if (/\s/.test(source[at])) continue;
109
- if (source.startsWith("/*", at)) {
110
- const close = source.indexOf("*/", at + 2);
111
- if (close < 0 || close + 2 > end) return null;
112
- at = close + 1;
113
- } else if (source.startsWith("//", at)) {
114
- const newline = source.indexOf("\n", at + 2);
115
- at = newline < 0 ? end : newline;
116
- } else if (source[at] === "," && comma === null) comma = at;
117
- else return null;
118
- }
119
- return comma;
120
- };
121
- const denseArraySource = (source) => {
122
- const array = parseArrayExpression(source);
123
- if (!array || array.elements.some((node) => !import_lib$1.isExpression(node) || node.extra?.parenthesized)) return null;
124
- const elements = array.elements;
125
- const commas = elements.slice(0, -1).map((node, index) => commaIn(source, node.end, elements[index + 1].start));
126
- if (commas.some((comma) => comma === null)) return null;
127
- const last = elements.at(-1);
128
- return {
129
- array,
130
- elements,
131
- commas,
132
- trailing: last ? commaIn(source, last.end, array.end - 1) : null
133
- };
134
- };
135
- const canStructurallyEditArray = (source) => denseArraySource(source) !== null;
136
- const validArrayIndices = (indices, length) => [...indices].every((index) => Number.isInteger(index) && index >= 0 && index < length);
137
- const reorderArraySource = (source, elements, next) => applyEdits(source, elements.map((node, index) => ({
138
- start: node.start,
139
- end: node.end,
140
- content: source.slice(next[index].start, next[index].end)
141
- })));
142
- const removeArraySource = (source, data, indices) => {
143
- const edits = [];
144
- const removedCommas = /* @__PURE__ */ new Set();
145
- const { elements, commas, trailing } = data;
146
- for (let index = 0; index < elements.length; index++) {
147
- if (!indices.has(index)) continue;
148
- const start = index;
149
- while (index + 1 < elements.length && indices.has(index + 1)) index++;
150
- for (let current = start; current <= index; current++) {
151
- const node = elements[current];
152
- edits.push({
153
- start: node.start,
154
- end: node.end,
155
- content: ""
156
- });
157
- if (current < elements.length - 1) removedCommas.add(commas[current]);
158
- }
159
- if (index === elements.length - 1) {
160
- if (trailing !== null) removedCommas.add(trailing);
161
- else if (start > 0) removedCommas.add(commas[start - 1]);
162
- }
163
- }
164
- for (const comma of removedCommas) edits.push({
165
- start: comma,
166
- end: comma + 1,
167
- content: ""
168
- });
169
- return applyEdits(source, edits);
170
- };
171
- const appendArraySource = (source, data, copies) => {
172
- const last = data.elements.at(-1);
173
- if (!last || copies.length === 0) return source;
174
- return applyEdits(source, [{
175
- start: last.end,
176
- end: last.end,
177
- content: `, ${copies.join(", ")}`
178
- }]);
179
- };
180
- //#endregion
181
- //#region src/utils/ast/items.ts
182
- const elementsOf = (code) => parseArrayExpression(code)?.elements ?? null;
183
- const kindOf = (element) => import_lib$1.isObjectExpression(element) ? "object" : "primitive";
184
- const parseItems = (code) => {
185
- return elementsOf(code)?.flatMap((node, index) => import_lib$1.isExpression(node) ? [{
186
- index,
187
- kind: kindOf(node),
188
- node
189
- }] : []) ?? null;
190
- };
191
- const resolveRenderLeaf = (render, key) => {
192
- const leaf = render?.[key];
193
- return leaf && "type" in leaf ? leaf : null;
194
- };
195
- const toTemplateRaw = (value) => {
196
- return value.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${").replace(/\r/g, "\\r");
197
- };
198
- const buildPropertyValue = (value, declaredType, renderLeaf, current) => {
199
- if (renderLeaf?.property === BINDING_PROP.INNER_HTML) {
200
- const raw = String(value);
201
- return import_lib$1.templateLiteral([import_lib$1.templateElement({
202
- raw: toTemplateRaw(raw),
203
- cooked: raw
204
- }, true)], []);
205
- }
206
- if (renderLeaf?.type === "jsx" || import_lib$1.isJSXElement(current) || import_lib$1.isJSXFragment(current)) {
207
- const trimmed = String(value).trim();
208
- if (!trimmed.startsWith("<")) return import_lib$1.stringLiteral(trimmed);
209
- try {
210
- return (0, import_lib.parseExpression)(trimmed, { plugins: ["jsx", "typescript"] });
211
- } catch {
212
- return;
213
- }
214
- }
215
- if (declaredType === "array" || declaredType === "object") {
216
- if (typeof value === "string") try {
217
- return (0, import_lib.parseExpression)(value, { plugins: ["jsx", "typescript"] });
218
- } catch {
219
- return;
220
- }
221
- if (!isLosslesslyEvaluable(current)) return;
222
- return valueToExpression(value) ?? void 0;
223
- }
224
- return createNodeFromValue(declaredType, parseValue(value)) ?? void 0;
225
- };
226
- const updateArrayItemProperty = (code, index, key, value, render) => {
227
- const elements = elementsOf(code);
228
- const element = elements?.[index];
229
- if (!elements || !import_lib$1.isObjectExpression(element)) return null;
230
- const target = element.properties.find((property) => import_lib$1.isObjectProperty(property) && import_lib$1.isIdentifier(property.key) && property.key.name === key);
231
- if (!target) return null;
232
- const declaredType = extractObjectProperties(element)[key]?.type ?? "string";
233
- const nextValue = buildPropertyValue(value, declaredType, resolveRenderLeaf(render, key), target.value);
234
- if (!nextValue) return null;
235
- let serialized = typeof value === "string" && (declaredType === "array" || declaredType === "object" || import_lib$1.isJSXElement(nextValue) || import_lib$1.isJSXFragment(nextValue)) ? value.trim() : generateCode(nextValue);
236
- if (nextValue.trailingComments?.some((comment) => comment.type === "CommentLine")) serialized += "\n";
237
- const content = target.shorthand ? `${key}: ${serialized}` : serialized;
238
- return applyEdits(code, [{
239
- start: target.value.start,
240
- end: target.value.end,
241
- content
242
- }]);
243
- };
244
- const updateArrayItemValue = (code, index, value) => {
245
- const elements = elementsOf(code);
246
- const element = elements?.[index];
247
- if (!elements || !import_lib$1.isExpression(element)) return null;
248
- const nextValue = createNodeFromValue(extractNodeValue(element).type, parseValue(value));
249
- if (!nextValue) return null;
250
- return applyEdits(code, [{
251
- start: element.start,
252
- end: element.end,
253
- content: generateCode(nextValue)
254
- }]);
255
- };
256
- const moveArrayItem = (code, from, to) => {
257
- const data = denseArraySource(code);
258
- if (!data || !validArrayIndices([from], data.elements.length) || !Number.isInteger(to)) return null;
259
- const next = [...data.elements];
260
- const [moved] = next.splice(from, 1);
261
- next.splice(to, 0, moved);
262
- return reorderArraySource(code, data.elements, next);
263
- };
264
- const moveArrayItems = (code, indices, direction) => {
265
- const data = denseArraySource(code);
266
- if (!data || !validArrayIndices(indices, data.elements.length) || !["up", "down"].includes(direction)) return null;
267
- const next = moveSelectedIndices(data.elements, indices, direction);
268
- return {
269
- code: reorderArraySource(code, data.elements, next.items),
270
- indices: next.indices
271
- };
272
- };
273
- const removeArrayItems = (code, indices, kind) => {
274
- const data = denseArraySource(code);
275
- if (!data || !validArrayIndices(indices, data.elements.length)) return null;
276
- const remaining = removeIndices(data.elements, indices);
277
- if (!(kind === void 0 ? remaining : remaining.filter((node) => kindOf(node) === kind)).length) return null;
278
- return removeArraySource(code, data, indices);
279
- };
280
- const copyItem = (code, element, generateId, usedIds) => {
281
- const edits = [];
282
- let unsupported = false;
283
- const reserveId = (id) => {
284
- if (usedIds.has(id)) unsupported = true;
285
- usedIds.add(id);
286
- return JSON.stringify(id);
287
- };
288
- import_lib$1.traverseFast(element, (node) => {
289
- if (!import_lib$1.isJSXOpeningElement(node)) return;
290
- const ids = node.attributes.filter((attr) => import_lib$1.isJSXAttribute(attr) && import_lib$1.isJSXIdentifier(attr.name, { name: "data-id" }));
291
- const attr = ids[0];
292
- 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)))) {
293
- unsupported = true;
294
- return;
295
- }
296
- if (attr && import_lib$1.isStringLiteral(attr.value)) edits.push({
297
- start: attr.value.start,
298
- end: attr.value.end,
299
- content: reserveId(generateId())
300
- });
301
- });
302
- if (import_lib$1.isObjectExpression(element)) {
303
- 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" })));
304
- const key = keys[0];
305
- 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;
306
- if (key && import_lib$1.isStringLiteral(key.value)) edits.push({
307
- start: key.value.start,
308
- end: key.value.end,
309
- content: reserveId(`${key.value.value}-${generateId()}`)
310
- });
311
- }
312
- if (unsupported) return null;
313
- return applyEdits(code.slice(element.start, element.end), edits.map((edit) => ({
314
- ...edit,
315
- start: edit.start - element.start,
316
- end: edit.end - element.start
317
- })));
318
- };
319
- const duplicateArrayItems = (code, indices, generateId = () => nanoid(6)) => {
320
- const data = denseArraySource(code);
321
- if (!data || !indices.size || !validArrayIndices(indices, data.elements.length)) return null;
322
- const usedIds = /* @__PURE__ */ new Set();
323
- import_lib$1.traverseFast(data.array, (node) => {
324
- 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);
325
- 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);
326
- });
327
- const copies = [...indices].sort((a, b) => a - b).map((index) => copyItem(code, data.elements[index], generateId, usedIds));
328
- return copies.every((copy) => copy !== null) ? appendArraySource(code, data, copies) : null;
329
- };
330
- const appendArrayItem = (code, kind, generateId = () => nanoid(6)) => {
331
- const index = denseArraySource(code)?.elements.findIndex((node) => kindOf(node) === kind) ?? -1;
332
- return index < 0 ? null : duplicateArrayItems(code, /* @__PURE__ */ new Set([index]), generateId);
333
- };
334
- //#endregion
335
- //#region src/utils/ast/children.ts
336
- const modeledSignatures = (nodes) => nodes.map((node) => {
337
- const jsx = nodeToJSX(node);
338
- if (!jsx) throw new Error("Invalid child node");
339
- return generateCode(jsx);
340
- });
341
- const getChildrenSignatures = (nodes) => {
342
- const modeled = modeledSignatures(nodes);
343
- return nodes.map((node, index) => JSON.stringify([node.source ?? null, modeled[index]]));
344
- };
345
- const indicesAreValid = (indices, count) => Array.isArray(indices) && new Set(indices).size === indices.length && indices.every((index) => Number.isInteger(index) && index >= 0 && index < count);
346
- const copySource = (source, usedIds) => {
347
- const node = (0, import_lib.parseExpression)(source, { plugins: ["jsx", "typescript"] });
348
- const edits = [];
349
- const newId = () => {
350
- let id = nanoid(12);
351
- while (usedIds.has(id)) id = nanoid(12);
352
- usedIds.add(id);
353
- return id;
354
- };
355
- import_lib$1.traverseFast(node, (child) => {
356
- if (!import_lib$1.isJSXOpeningElement(child)) return;
357
- const ids = child.attributes.filter((attr) => import_lib$1.isJSXAttribute(attr) && import_lib$1.isJSXIdentifier(attr.name, { name: "data-id" }));
358
- if (ids.length > 1) throw new Error("Duplicate data-id attributes");
359
- const attr = ids[0];
360
- if (attr && child.attributes.slice(child.attributes.indexOf(attr) + 1).some((attribute) => import_lib$1.isJSXSpreadAttribute(attribute))) throw new Error("A spread may override the copied data-id");
361
- if (attr) {
362
- const value = attr.value;
363
- if (!import_lib$1.isStringLiteral(value) || value.start == null || value.end == null) throw new Error("Cannot copy a dynamic data-id");
364
- edits.push({
365
- start: value.start,
366
- end: value.end,
367
- content: JSON.stringify(newId())
368
- });
369
- } else {
370
- const at = child.attributes.at(-1)?.end ?? child.name.end;
371
- if (at == null) throw new Error("Missing opening element span");
372
- edits.push({
373
- start: at,
374
- end: at,
375
- content: ` data-id="${newId()}"`
376
- });
377
- }
378
- });
379
- return applyEdits(source, edits);
380
- };
381
- const editChildrenSource = (source, parent, input) => {
382
- try {
383
- if (!parent.closingElement || parent.start == null || parent.end == null) return null;
384
- const value = typeof input === "string" ? JSON.parse(input) : input;
385
- const children = parent.children.filter((child) => import_lib$1.isJSXElement(child) || import_lib$1.isJSXFragment(child));
386
- const raw = children.map((child) => source.slice(child.start, child.end));
387
- const models = extract(source.slice(parent.start, parent.end))[0]?.children ?? [];
388
- if (models.length !== children.length) return null;
389
- const signatures = modeledSignatures(models);
390
- const replaceSlots = (next) => {
391
- const edits = children.map((child, index) => ({
392
- start: child.start,
393
- end: child.end,
394
- content: next[index] ?? ""
395
- }));
396
- if (next.length > children.length) {
397
- const at = parent.closingElement.start;
398
- edits.push({
399
- start: at,
400
- end: at,
401
- content: next.slice(children.length).join("")
402
- });
403
- }
404
- return edits;
405
- };
406
- if (Array.isArray(value)) {
407
- const desired = modeledSignatures(value);
408
- const lossless = raw.every((text, index) => generateCode((0, import_lib.parseExpression)(text, { plugins: ["jsx", "typescript"] })) === signatures[index]);
409
- const consumed = /* @__PURE__ */ new Set();
410
- const next = desired.map((signature, position) => {
411
- const index = signatures.findIndex((candidate, i) => candidate === signature && !consumed.has(i) && (value[position].source === void 0 || value[position].source === raw[i]));
412
- if (index >= 0) {
413
- consumed.add(index);
414
- return raw[index];
415
- }
416
- if (!lossless || value[position].source !== void 0 && !raw.includes(value[position].source)) throw new Error("Use children commands to copy or edit unsupported source");
417
- return signature;
418
- });
419
- const countIds = (text) => {
420
- const counts = /* @__PURE__ */ new Map();
421
- import_lib$1.traverseFast((0, import_lib.parseExpression)(text, { plugins: ["jsx", "typescript"] }), (node) => {
422
- if (import_lib$1.isJSXAttribute(node) && import_lib$1.isJSXIdentifier(node.name, { name: "data-id" }) && import_lib$1.isStringLiteral(node.value)) counts.set(node.value.value, (counts.get(node.value.value) ?? 0) + 1);
423
- });
424
- return counts;
425
- };
426
- const previousIds = countIds(source);
427
- if ([...countIds(applyEdits(source, replaceSlots(next)))].some(([id, count]) => count > 1 && count > (previousIds.get(id) ?? 0))) return null;
428
- return replaceSlots(next);
429
- }
430
- const request = value;
431
- if (request?.kind !== "children-edit" || !Array.isArray(request.expected) || request.expected.length !== signatures.length || !getChildrenSignatures(models).every((signature, index) => signature === request.expected[index]) || !request.action) return null;
432
- const { action } = request;
433
- const usedIds = /* @__PURE__ */ new Set();
434
- import_lib$1.traverseFast(parent, (node) => {
435
- 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);
436
- });
437
- switch (action.type) {
438
- case "move": {
439
- if (!indicesAreValid([action.from], raw.length) || !indicesAreValid([action.to], raw.length)) return null;
440
- const next = [...raw];
441
- const [moved] = next.splice(action.from, 1);
442
- next.splice(action.to, 0, moved);
443
- return replaceSlots(next);
444
- }
445
- case "move-selected":
446
- if (!indicesAreValid(action.indices, raw.length) || !["up", "down"].includes(action.direction)) return null;
447
- return replaceSlots(moveSelectedIndices(raw, new Set(action.indices), action.direction).items);
448
- case "remove":
449
- if (!indicesAreValid(action.indices, raw.length)) return null;
450
- return action.indices.map((index) => ({
451
- start: children[index].start,
452
- end: children[index].end,
453
- content: ""
454
- }));
455
- case "duplicate": {
456
- if (!indicesAreValid(action.indices, raw.length)) return null;
457
- const content = [...action.indices].sort((a, b) => a - b).map((index) => copySource(raw[index], usedIds)).join("");
458
- const at = parent.closingElement.start;
459
- return [{
460
- start: at,
461
- end: at,
462
- content
463
- }];
464
- }
465
- case "append": {
466
- const content = copySource(raw[0] ?? "<div></div>", usedIds);
467
- const at = parent.closingElement.start;
468
- return [{
469
- start: at,
470
- end: at,
471
- content
472
- }];
473
- }
474
- default: return null;
475
- }
476
- } catch {
477
- return null;
478
- }
479
- };
480
- //#endregion
481
- //#region src/utils/ast/update.ts
482
- const childrenRange = (element) => {
483
- const { openingElement, closingElement } = element;
484
- if (!closingElement || openingElement.end == null || closingElement.start == null) return null;
485
- return {
486
- start: openingElement.end,
487
- end: closingElement.start
488
- };
489
- };
490
- const findAttribute = (opening, propertyName) => {
491
- return opening.attributes.find((attr) => import_lib$1.isJSXAttribute(attr) && import_lib$1.isJSXIdentifier(attr.name) && attr.name.name === propertyName);
492
- };
493
- const attributeInsertPoint = (opening) => {
494
- return opening.attributes[opening.attributes.length - 1]?.end ?? opening.name.end ?? null;
495
- };
496
- const editInnerText = (source, element, value) => {
497
- const range = childrenRange(element);
498
- if (!range) return [];
499
- const [only] = element.children;
500
- if (element.children.length === 1 && import_lib$1.isJSXText(only) && only.start != null && only.end != null) {
501
- const raw = source.slice(only.start, only.end);
502
- if (raw.trim() !== "") {
503
- const leading = raw.length - raw.trimStart().length;
504
- const trailing = raw.length - raw.trimEnd().length;
505
- return [{
506
- start: only.start + leading,
507
- end: only.end - trailing,
508
- content: value
509
- }];
510
- }
511
- }
512
- const edits = [];
513
- for (const child of element.children) if (import_lib$1.isJSXText(child) && child.start != null && child.end != null) edits.push({
514
- start: child.start,
515
- end: child.end,
516
- content: ""
517
- });
518
- edits.push({
519
- start: range.end,
520
- end: range.end,
521
- content: value
522
- });
523
- return edits;
524
- };
525
- const editInnerHTML = (element, value) => {
526
- const range = childrenRange(element);
527
- if (!range) return [];
528
- return [{
529
- start: range.start,
530
- end: range.end,
531
- content: value
532
- }];
533
- };
534
- const editRichtext = (element, value) => {
535
- const edits = [];
536
- const range = childrenRange(element);
537
- if (range && range.start !== range.end) edits.push({
538
- start: range.start,
539
- end: range.end,
540
- content: ""
541
- });
542
- const opening = element.openingElement;
543
- const attribute = import_lib$1.jsxAttribute(import_lib$1.jsxIdentifier("dangerouslySetInnerHTML"), import_lib$1.jsxExpressionContainer(import_lib$1.objectExpression([import_lib$1.objectProperty(import_lib$1.identifier("__html"), import_lib$1.stringLiteral(value))])));
544
- const content = generateCode(attribute);
545
- const existing = findAttribute(opening, "dangerouslySetInnerHTML");
546
- if (existing && existing.start != null && existing.end != null) {
547
- edits.push({
548
- start: existing.start,
549
- end: existing.end,
550
- content,
551
- indent: true
552
- });
553
- return edits;
554
- }
555
- const insertAt = attributeInsertPoint(opening);
556
- if (insertAt == null) return null;
557
- edits.push({
558
- start: insertAt,
559
- end: insertAt,
560
- content: ` ${content}`,
561
- indent: true
562
- });
563
- return edits;
564
- };
565
- const editJsxAttribute = (opening, propertyName, value) => {
566
- const attribute = findAttribute(opening, propertyName);
567
- if (!attribute) return null;
568
- const content = `{${String(value).trim()}}`;
569
- if (attribute.value?.start != null && attribute.value.end != null) return [{
570
- start: attribute.value.start,
571
- end: attribute.value.end,
572
- content
573
- }];
574
- const insertAt = attribute.name.end;
575
- if (insertAt == null) return null;
576
- return [{
577
- start: insertAt,
578
- end: insertAt,
579
- content: `=${content}`
580
- }];
581
- };
582
- const holdsStructuralExpression = (value) => import_lib$1.isJSXExpressionContainer(value) && (import_lib$1.isArrayExpression(unwrapExpression(value.expression)) || import_lib$1.isObjectExpression(unwrapExpression(value.expression)));
583
- const structuralSource = (value) => {
584
- try {
585
- const expression = (0, import_lib.parseExpression)(value.trim(), { plugins: ["jsx", "typescript"] });
586
- return import_lib$1.isArrayExpression(unwrapExpression(expression)) || import_lib$1.isObjectExpression(unwrapExpression(expression)) ? expression : null;
587
- } catch {
588
- return null;
589
- }
590
- };
591
- const buildAttributeValue = (value, type, current) => {
592
- if (type === "array" || type === "object") {
593
- if (typeof value === "string") try {
594
- return import_lib$1.jsxExpressionContainer((0, import_lib.parseExpression)(value.trim(), { plugins: ["jsx", "typescript"] }));
595
- } catch {
596
- return import_lib$1.stringLiteral(value);
597
- }
598
- const expr = valueToExpression(value);
599
- return expr ? import_lib$1.jsxExpressionContainer(expr) : import_lib$1.stringLiteral("");
600
- }
601
- if (typeof value === "string") {
602
- if (holdsStructuralExpression(current) && !(type && STRING_VALUED_TYPES.has(type))) {
603
- const structural = structuralSource(value);
604
- if (structural) return import_lib$1.jsxExpressionContainer(structural);
605
- }
606
- return import_lib$1.stringLiteral(value);
607
- }
608
- const expr = valueToExpression(value);
609
- return expr ? import_lib$1.jsxExpressionContainer(expr) : import_lib$1.stringLiteral(String(value));
610
- };
611
- const editAttribute = (opening, propertyName, value, type) => {
612
- const attribute = findAttribute(opening, propertyName);
613
- if (!attribute || attribute.start == null || attribute.end == null) return null;
614
- if (typeof value === "string" && (type === "array" || type === "object" || holdsStructuralExpression(attribute.value) && !(type && STRING_VALUED_TYPES.has(type)))) {
615
- const expression = structuralSource(value);
616
- if (expression && import_lib$1.isJSXExpressionContainer(attribute.value)) {
617
- const text = value.trim();
618
- const lineComment = expression.trailingComments?.some((comment) => comment.type === "CommentLine");
619
- return [{
620
- start: attribute.value.expression.start,
621
- end: attribute.value.expression.end,
622
- content: `${text}${lineComment ? "\n" : ""}`
623
- }];
624
- }
625
- }
626
- const content = generateCode(import_lib$1.jsxAttribute(attribute.name, buildAttributeValue(value, type, attribute.value)));
627
- return [{
628
- start: attribute.start,
629
- end: attribute.end,
630
- content,
631
- indent: true
632
- }];
633
- };
634
- const canEditAttributeValue = (attribute, value, type) => {
635
- if (!import_lib$1.isJSXExpressionContainer(attribute.value) || import_lib$1.isJSXEmptyExpression(attribute.value.expression) || isLosslesslyEvaluable(attribute.value.expression)) return true;
636
- return typeof value === "string" && holdsStructuralExpression(attribute.value) && !(type && STRING_VALUED_TYPES.has(type)) && structuralSource(value) !== null;
637
- };
638
- const update = (code, dataId, label, value, property) => {
639
- try {
640
- const wrapped = wrap(code);
641
- const ast = (0, import_lib.parse)(wrapped, {
642
- sourceType: "module",
643
- plugins: ["jsx", "typescript"]
644
- });
645
- let changed = false;
646
- let failure;
647
- const edits = [];
648
- const collect = (result, onNull) => {
649
- if (!result) {
650
- failure = onNull();
651
- return;
652
- }
653
- edits.push(...result);
654
- changed = true;
655
- };
656
- traverse(ast, { JSXElement(path) {
657
- const opening = path.node.openingElement;
658
- if (!opening.attributes.find((attr) => {
659
- return import_lib$1.isJSXAttribute(attr) && import_lib$1.isJSXIdentifier(attr.name) && attr.name.name === DATA_ATTR.ID && attr.value && import_lib$1.isStringLiteral(attr.value) && attr.value.value === dataId;
660
- })) return;
661
- const bindingAttr = opening.attributes.find((attr) => import_lib$1.isJSXAttribute(attr) && import_lib$1.isJSXIdentifier(attr.name) && attr.name.name === DATA_ATTR.BINDING);
662
- if (!bindingAttr?.value) {
663
- failure = {
664
- reason: "no-binding",
665
- dataId
666
- };
667
- return;
668
- }
669
- let bindingValue = "";
670
- if (import_lib$1.isStringLiteral(bindingAttr.value)) bindingValue = bindingAttr.value.value;
671
- else if (import_lib$1.isJSXExpressionContainer(bindingAttr.value)) try {
672
- bindingValue = generateCode(bindingAttr.value.expression);
673
- } catch (error) {
674
- failure = {
675
- reason: "parse-error",
676
- error
677
- };
678
- return;
679
- }
680
- const matches = parseBinding(bindingValue).filter((binding) => property !== void 0 ? binding.property === property : binding.label === label);
681
- if (matches.length === 0) {
682
- failure = {
683
- reason: "binding-not-declared",
684
- dataId,
685
- label,
686
- property
687
- };
688
- return;
689
- }
690
- if (matches.length > 1) {
691
- failure = {
692
- reason: "duplicate-binding",
693
- dataId,
694
- label,
695
- property,
696
- count: matches.length
697
- };
698
- return;
699
- }
700
- const propertyBinding = matches[0];
701
- const prop = propertyBinding.property;
702
- switch (prop) {
703
- case BINDING_PROP.INNER_TEXT:
704
- collect(editInnerText(wrapped, path.node, String(value)), () => ({
705
- reason: "parse-error",
706
- error: /* @__PURE__ */ new Error(`could not update "${prop}"`)
707
- }));
708
- break;
709
- case BINDING_PROP.INNER_HTML:
710
- collect(propertyBinding.type === "richtext" ? editRichtext(path.node, String(value)) : editInnerHTML(path.node, String(value)), () => ({
711
- reason: "parse-error",
712
- error: /* @__PURE__ */ new Error(`could not update "${prop}"`)
713
- }));
714
- break;
715
- case BINDING_PROP.CHILDREN:
716
- collect(editChildrenSource(wrapped, path.node, value), () => ({
717
- reason: "parse-error",
718
- error: /* @__PURE__ */ new Error("Unsupported or stale children edit; source was preserved")
719
- }));
720
- break;
721
- default: {
722
- const attribute = findAttribute(opening, prop);
723
- if (attribute && propertyBinding.type !== "jsx" && !canEditAttributeValue(attribute, value, propertyBinding.type)) {
724
- failure = {
725
- reason: "unsupported-syntax",
726
- dataId,
727
- property: prop
728
- };
729
- break;
730
- }
731
- collect(propertyBinding.type === "jsx" ? editJsxAttribute(opening, prop, value) : editAttribute(opening, prop, value, propertyBinding.type), () => ({
732
- reason: "attribute-not-found",
733
- dataId,
734
- property: prop
735
- }));
736
- break;
737
- }
738
- }
739
- } });
740
- if (!changed) return {
741
- code,
742
- success: false,
743
- failure: failure ?? {
744
- reason: "element-not-found",
745
- dataId
746
- }
747
- };
748
- return {
749
- code: unwrap(applyEdits(wrapped, edits)),
750
- success: true
751
- };
752
- } catch (error) {
753
- console.error("❌ Code update error:", error);
754
- return {
755
- code,
756
- success: false,
757
- failure: {
758
- reason: "parse-error",
759
- error
760
- }
761
- };
762
- }
763
- };
764
- const bulkUpdate = (raw, entries) => {
765
- let current = raw;
766
- const failures = [];
767
- for (const entry of entries) {
768
- const result = update(current, entry.dataId, entry.label, entry.value, entry.property);
769
- current = result.code;
770
- if (!result.success && result.failure) failures.push(result.failure);
771
- }
772
- return failures.length > 0 ? {
773
- code: current,
774
- success: false,
775
- failures
776
- } : {
777
- code: current,
778
- success: true
779
- };
780
- };
781
- //#endregion
782
- //#region src/utils/ast/tree.ts
783
- const replaceIds = (code, generateId = () => nanoid(6)) => {
784
- return code.replace(new RegExp(`${DATA_ATTR.ID}="[^"]*"`, "g"), () => {
785
- return `${DATA_ATTR.ID}="${generateId()}"`;
786
- });
787
- };
788
- const fillIds = (code, generateId = () => nanoid(6)) => {
789
- return code.replace(new RegExp(`${DATA_ATTR.ID}=""`, "g"), () => {
790
- return `${DATA_ATTR.ID}="${generateId()}"`;
791
- });
792
- };
793
- const clone = (element, generateId = () => nanoid(6)) => {
794
- const cloned = import_lib$1.cloneNode(element, true);
795
- const generatedCode = generateCode(cloned);
796
- const code = replaceIds(generatedCode, generateId);
797
- return (0, import_lib.parseExpression)(code, { plugins: ["jsx", "typescript"] });
798
- };
799
- //#endregion
800
- //#region src/utils/ast/validate.ts
801
- const VALID = { valid: true };
802
- const validateBindingValue = (binding, value) => {
803
- if (value === "" || value === null || value === void 0) {
804
- if (binding.required) return {
805
- valid: false,
806
- message: "This field is required."
807
- };
808
- return VALID;
809
- }
810
- if (typeof value === "number") {
811
- if (binding.min !== void 0 && value < binding.min) return {
812
- valid: false,
813
- message: `Must be at least ${binding.min}.`
814
- };
815
- if (binding.max !== void 0 && value > binding.max) return {
816
- valid: false,
817
- message: `Must be at most ${binding.max}.`
818
- };
819
- }
820
- if (typeof value === "string" && binding.pattern) {
821
- let regex;
822
- try {
823
- regex = new RegExp(binding.pattern);
824
- } catch {
825
- return VALID;
826
- }
827
- if (!regex.test(value)) return {
828
- valid: false,
829
- message: "Value does not match the required format."
830
- };
831
- }
832
- if (typeof value === "string" && binding.type === "url") try {
833
- new URL(value);
834
- } catch {
835
- return {
836
- valid: false,
837
- message: "Must be a valid URL."
838
- };
839
- }
840
- if (typeof value === "string" && binding.type === "date") {
841
- if (Number.isNaN(Date.parse(value))) return {
842
- valid: false,
843
- message: "Must be a valid date."
844
- };
845
- }
846
- return VALID;
847
- };
848
- //#endregion
849
- export { moveSelectedIndices as _, bulkUpdate as a, appendArrayItem as c, moveArrayItems as d, parseItems as f, canStructurallyEditArray as g, updateArrayItemValue as h, replaceIds as i, duplicateArrayItems as l, updateArrayItemProperty as m, clone as n, update as o, removeArrayItems as p, fillIds as r, getChildrenSignatures as s, validateBindingValue as t, moveArrayItem as u };
850
-
851
- //# sourceMappingURL=ast-CZwpVrJv.js.map