@jbpark/live-editor 1.4.0 → 1.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.
@@ -0,0 +1,759 @@
1
+ import { S as __toESM, _ as DATA_ATTR, b as REGEX, c as generateCode, d as createBoundedCache, f as require_lib$2, g as CONFIG, h as BINDING_PROP, l as unwrap, m as require_lib, p as require_lib$1, s as attrValue, u as wrap } from "./document-BGG0pNR2.mjs";
2
+ import { z } from "zod";
3
+ import crypto from "crypto";
4
+
5
+ //#region src/utils/ast/types.ts
6
+ const BINDING_TYPES = [
7
+ "array",
8
+ "object",
9
+ "string",
10
+ "number",
11
+ "boolean",
12
+ "color",
13
+ "jsx",
14
+ "richtext",
15
+ "date",
16
+ "url",
17
+ "icon-picker",
18
+ "asset-picker"
19
+ ];
20
+
21
+ //#endregion
22
+ //#region src/utils/ast/value.ts
23
+ var import_lib$1 = require_lib();
24
+ var import_lib$2 = /* @__PURE__ */ __toESM(require_lib$1(), 1);
25
+ const dedent = (str) => {
26
+ const lines = str.replace(/^\n/, "").replace(/\n\s*$/, "").split("\n");
27
+ const indent = lines.reduce((min, line) => {
28
+ if (!line.trim()) return min;
29
+ const match = line.match(/^(\s*)/);
30
+ return Math.min(min, match?.[1]?.length ?? 0);
31
+ }, Infinity);
32
+ return indent === Infinity ? str.trim() : lines.map((line) => line.slice(indent)).join("\n");
33
+ };
34
+ const evaluateLiteral = (node) => {
35
+ if (import_lib$2.isStringLiteral(node)) return node.value;
36
+ if (import_lib$2.isNumericLiteral(node)) return node.value;
37
+ if (import_lib$2.isBooleanLiteral(node)) return node.value;
38
+ if (import_lib$2.isNullLiteral(node)) return null;
39
+ if (import_lib$2.isIdentifier(node) && node.name === "undefined") return;
40
+ if (import_lib$2.isUnaryExpression(node) && node.operator === "-" && import_lib$2.isNumericLiteral(node.argument)) return -node.argument.value;
41
+ if (import_lib$2.isTemplateLiteral(node) && node.expressions.length === 0) return node.quasis[0]?.value.cooked ?? node.quasis[0]?.value.raw ?? "";
42
+ if (import_lib$2.isJSXElement(node) || import_lib$2.isJSXFragment(node)) return generateCode(node);
43
+ if (import_lib$2.isArrayExpression(node)) return node.elements.map((element) => element ? evaluateLiteral(element) : null);
44
+ if (import_lib$2.isObjectExpression(node)) {
45
+ const result = {};
46
+ for (const prop of node.properties) {
47
+ if (!import_lib$2.isObjectProperty(prop)) continue;
48
+ let key = null;
49
+ if (import_lib$2.isIdentifier(prop.key)) key = prop.key.name;
50
+ else if (import_lib$2.isStringLiteral(prop.key)) key = prop.key.value;
51
+ else if (import_lib$2.isNumericLiteral(prop.key)) key = String(prop.key.value);
52
+ if (key === null) continue;
53
+ result[key] = evaluateLiteral(prop.value);
54
+ }
55
+ return result;
56
+ }
57
+ };
58
+ const parseValue = (value) => {
59
+ if (typeof value !== "string") return value;
60
+ const trimmed = value.trim();
61
+ if (!trimmed) return value;
62
+ if (REGEX.NUMBER.test(trimmed)) return parseFloat(trimmed);
63
+ if (REGEX.BOOLEAN_OR_NULL.test(trimmed)) {
64
+ if (trimmed === "true") return true;
65
+ if (trimmed === "false") return false;
66
+ if (trimmed === "null") return null;
67
+ if (trimmed === "undefined") return;
68
+ }
69
+ if (trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]")) {
70
+ try {
71
+ const ast = (0, import_lib$1.parseExpression)(trimmed, { plugins: ["jsx", "typescript"] });
72
+ if (import_lib$2.isObjectExpression(ast) || import_lib$2.isArrayExpression(ast)) return evaluateLiteral(ast);
73
+ } catch {}
74
+ return value;
75
+ }
76
+ return value;
77
+ };
78
+ const extractNodeValue = (node) => {
79
+ if (import_lib$2.isBooleanLiteral(node)) return {
80
+ type: "boolean",
81
+ value: node.value
82
+ };
83
+ if (import_lib$2.isNumericLiteral(node)) return {
84
+ type: "number",
85
+ value: node.value
86
+ };
87
+ if (import_lib$2.isStringLiteral(node)) return {
88
+ type: "string",
89
+ value: node.value
90
+ };
91
+ if (import_lib$2.isTemplateLiteral(node)) {
92
+ if (node.expressions.length === 0 && node.quasis.length === 1) return {
93
+ type: "string",
94
+ value: dedent(node.quasis[0].value.cooked ?? node.quasis[0].value.raw)
95
+ };
96
+ return {
97
+ type: "string",
98
+ value: generateCode(node)
99
+ };
100
+ }
101
+ if (import_lib$2.isNullLiteral(node)) return {
102
+ type: "null",
103
+ value: null
104
+ };
105
+ if (import_lib$2.isArrayExpression(node)) return {
106
+ type: "array",
107
+ value: generateCode(node)
108
+ };
109
+ if (import_lib$2.isObjectExpression(node)) return {
110
+ type: "object",
111
+ value: generateCode(node)
112
+ };
113
+ if (import_lib$2.isJSXElement(node) || import_lib$2.isJSXFragment(node)) return {
114
+ type: "string",
115
+ value: generateCode(node)
116
+ };
117
+ return {
118
+ type: "unknown",
119
+ value: null
120
+ };
121
+ };
122
+ const createNodeFromValue = (type, value) => {
123
+ switch (type) {
124
+ case "boolean": return import_lib$2.booleanLiteral(value === true);
125
+ case "number": return import_lib$2.numericLiteral(Number(value));
126
+ case "string": return import_lib$2.stringLiteral(String(value));
127
+ case "null": return import_lib$2.nullLiteral();
128
+ case "array":
129
+ case "object":
130
+ case "unknown": return null;
131
+ default: return null;
132
+ }
133
+ };
134
+ const parseArrayExpression = (value) => {
135
+ try {
136
+ const ast = (0, import_lib$1.parseExpression)(value, { plugins: ["jsx", "typescript"] });
137
+ if (!import_lib$2.isArrayExpression(ast)) return null;
138
+ return ast;
139
+ } catch (error) {
140
+ console.error("❌ Array parsing error:", error);
141
+ return null;
142
+ }
143
+ };
144
+ const extractObjectProperties = (element) => {
145
+ const properties = {};
146
+ element.properties.forEach((prop) => {
147
+ if (import_lib$2.isObjectProperty(prop) && import_lib$2.isIdentifier(prop.key)) {
148
+ const key = prop.key.name;
149
+ if (key === "children") return;
150
+ properties[key] = {
151
+ ...extractNodeValue(prop.value),
152
+ astNode: prop.value
153
+ };
154
+ }
155
+ });
156
+ return properties;
157
+ };
158
+ const arrayExpressionToCode = (elements) => {
159
+ return generateCode(import_lib$2.arrayExpression(elements));
160
+ };
161
+
162
+ //#endregion
163
+ //#region src/utils/ast/binding.ts
164
+ const bindingTypeSchema = z.enum(BINDING_TYPES);
165
+ const bindingOptionSchema = z.object({
166
+ label: z.string(),
167
+ value: z.string()
168
+ });
169
+ const bindingRenderLeafSchema = z.object({
170
+ type: bindingTypeSchema,
171
+ property: z.string().optional()
172
+ });
173
+ const rawBindingItemSchema = z.object({
174
+ label: z.string(),
175
+ property: z.string().optional(),
176
+ type: bindingTypeSchema.optional(),
177
+ options: z.array(bindingOptionSchema).optional(),
178
+ min: z.number().optional(),
179
+ max: z.number().optional(),
180
+ pattern: z.string().optional(),
181
+ required: z.boolean().optional()
182
+ });
183
+ const isPlainObject = (value) => {
184
+ return typeof value === "object" && value !== null && !Array.isArray(value);
185
+ };
186
+ const sanitizeRenderMap = (value) => {
187
+ if (!isPlainObject(value)) return;
188
+ const map = {};
189
+ for (const [key, raw] of Object.entries(value)) {
190
+ if (!isPlainObject(raw)) continue;
191
+ if ("type" in raw) {
192
+ const leaf = bindingRenderLeafSchema.safeParse(raw);
193
+ if (leaf.success) {
194
+ const render = sanitizeRenderMap(raw.render);
195
+ map[key] = render ? {
196
+ ...leaf.data,
197
+ render
198
+ } : leaf.data;
199
+ }
200
+ continue;
201
+ }
202
+ const nested = sanitizeRenderMap(raw);
203
+ if (nested) map[key] = nested;
204
+ }
205
+ return Object.keys(map).length > 0 ? map : void 0;
206
+ };
207
+ const parseBinding = (bindingValue) => {
208
+ if (!bindingValue) return [];
209
+ const ast = parseArrayExpression(bindingValue);
210
+ if (!ast) return [];
211
+ const raw = evaluateLiteral(ast);
212
+ if (!Array.isArray(raw)) return [];
213
+ const items = [];
214
+ for (const rawItem of raw) {
215
+ if (!isPlainObject(rawItem)) continue;
216
+ const sanitizedType = bindingTypeSchema.safeParse(rawItem.type);
217
+ const sanitizedOptions = Array.isArray(rawItem.options) ? rawItem.options.map((option) => {
218
+ const parsed = bindingOptionSchema.safeParse(option);
219
+ return parsed.success ? parsed.data : null;
220
+ }).filter((option) => option !== null) : void 0;
221
+ const parsed = rawBindingItemSchema.safeParse({
222
+ ...rawItem,
223
+ type: sanitizedType.success ? sanitizedType.data : void 0,
224
+ options: sanitizedOptions?.length ? sanitizedOptions : void 0
225
+ });
226
+ if (!parsed.success) continue;
227
+ const { label, property, type, options, min, max, pattern, required } = parsed.data;
228
+ if (property === void 0 && type !== "richtext") continue;
229
+ const render = sanitizeRenderMap(rawItem.render);
230
+ items.push({
231
+ label,
232
+ property: property ?? BINDING_PROP.INNER_HTML,
233
+ ...type !== void 0 && { type },
234
+ ...options?.length && { options },
235
+ ...render && { render },
236
+ ...min !== void 0 && { min },
237
+ ...max !== void 0 && { max },
238
+ ...pattern !== void 0 && { pattern },
239
+ ...required !== void 0 && { required }
240
+ });
241
+ }
242
+ return items;
243
+ };
244
+ const getCurrentValue = (node, property) => {
245
+ switch (property) {
246
+ case BINDING_PROP.INNER_TEXT: return node.textContent || "";
247
+ case BINDING_PROP.INNER_HTML: {
248
+ const dsiAttr = node.attributes.find((a) => a.name === "dangerouslySetInnerHTML");
249
+ if (dsiAttr?.value) try {
250
+ const expr = (0, import_lib$1.parseExpression)(dsiAttr.value, { plugins: ["jsx", "typescript"] });
251
+ if (import_lib$2.isObjectExpression(expr)) {
252
+ const htmlProp = expr.properties.find((p) => import_lib$2.isObjectProperty(p) && import_lib$2.isIdentifier(p.key) && p.key.name === "__html");
253
+ if (htmlProp) {
254
+ if (import_lib$2.isStringLiteral(htmlProp.value)) return htmlProp.value.value;
255
+ if (import_lib$2.isTemplateLiteral(htmlProp.value) && htmlProp.value.expressions.length === 0) return htmlProp.value.quasis[0]?.value.cooked ?? htmlProp.value.quasis[0]?.value.raw ?? "";
256
+ }
257
+ }
258
+ } catch {}
259
+ return node.rawChildren || node.textContent || "";
260
+ }
261
+ case BINDING_PROP.CHILDREN: return JSON.stringify(node?.children || []);
262
+ default: return node.attributes.find((attr) => attr.name === property)?.value || "";
263
+ }
264
+ };
265
+ const hasEditableBindings = (node) => {
266
+ const bindingAttr = node.dataAttributes.find((attr) => attr.name === DATA_ATTR.BINDING);
267
+ if (!bindingAttr?.value) return false;
268
+ return (node.bindings || parseBinding(bindingAttr.value)).length > 0;
269
+ };
270
+ const findEditableChildren = (node) => {
271
+ const editableChildren = [];
272
+ const traverse = (children) => {
273
+ if (!children) return;
274
+ for (const child of children) {
275
+ if (hasEditableBindings(child)) editableChildren.push(child);
276
+ traverse(child.children);
277
+ }
278
+ };
279
+ traverse(node.children);
280
+ return editableChildren;
281
+ };
282
+
283
+ //#endregion
284
+ //#region node_modules/.pnpm/nanoid@3.3.11/node_modules/nanoid/url-alphabet/index.js
285
+ let urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
286
+
287
+ //#endregion
288
+ //#region node_modules/.pnpm/nanoid@3.3.11/node_modules/nanoid/index.js
289
+ const POOL_SIZE_MULTIPLIER = 128;
290
+ let pool, poolOffset;
291
+ let fillPool = (bytes) => {
292
+ if (!pool || pool.length < bytes) {
293
+ pool = Buffer.allocUnsafe(bytes * POOL_SIZE_MULTIPLIER);
294
+ crypto.randomFillSync(pool);
295
+ poolOffset = 0;
296
+ } else if (poolOffset + bytes > pool.length) {
297
+ crypto.randomFillSync(pool);
298
+ poolOffset = 0;
299
+ }
300
+ poolOffset += bytes;
301
+ };
302
+ let nanoid = (size = 21) => {
303
+ fillPool(size |= 0);
304
+ let id = "";
305
+ for (let i = poolOffset - size; i < poolOffset; i++) id += urlAlphabet[pool[i] & 63];
306
+ return id;
307
+ };
308
+
309
+ //#endregion
310
+ //#region src/utils/ast/extract.ts
311
+ var import_lib = /* @__PURE__ */ __toESM(require_lib$2(), 1);
312
+ const collectText = (children) => {
313
+ return children.filter((c) => import_lib$2.isJSXText(c)).map((c) => c.value.trim()).filter((v) => v.length).join(" ");
314
+ };
315
+ const getTagName = (opening) => {
316
+ if (import_lib$2.isJSXIdentifier(opening.name)) return opening.name.name;
317
+ if (import_lib$2.isJSXMemberExpression(opening.name)) return resolveMemberName(opening.name);
318
+ return "";
319
+ };
320
+ const resolveMemberName = (expr) => {
321
+ const parts = [];
322
+ const collectMemberParts = (node) => {
323
+ if (import_lib$2.isJSXIdentifier(node)) parts.push(node.name);
324
+ else if (import_lib$2.isJSXMemberExpression(node)) {
325
+ collectMemberParts(node.object);
326
+ if (import_lib$2.isJSXIdentifier(node.property)) parts.push(node.property.name);
327
+ }
328
+ };
329
+ collectMemberParts(expr.object);
330
+ if (import_lib$2.isJSXIdentifier(expr.property)) parts.push(expr.property.name);
331
+ return parts.join(".");
332
+ };
333
+ const parseJSXName = (tagName) => {
334
+ const parts = tagName.split(".");
335
+ if (parts.length === 1) return import_lib$2.jsxIdentifier(parts[0]);
336
+ let expr = import_lib$2.jsxIdentifier(parts[0]);
337
+ for (let i = 1; i < parts.length; i++) expr = import_lib$2.jsxMemberExpression(expr, import_lib$2.jsxIdentifier(parts[i]));
338
+ return expr;
339
+ };
340
+ const extractCache = createBoundedCache(CONFIG.CACHE_LIMIT);
341
+ const extractAttributes = (attributes) => {
342
+ const allAttrs = [];
343
+ const dataAttrs = [];
344
+ for (const attr of attributes) {
345
+ if (!import_lib$2.isJSXAttribute(attr) || !import_lib$2.isJSXIdentifier(attr.name)) continue;
346
+ const name = attr.name.name;
347
+ let value = null;
348
+ let isStringLiteral = false;
349
+ if (attr.value) {
350
+ if (import_lib$2.isStringLiteral(attr.value)) {
351
+ value = attr.value.value;
352
+ isStringLiteral = true;
353
+ } else if (import_lib$2.isJSXExpressionContainer(attr.value)) try {
354
+ value = generateCode(attr.value.expression);
355
+ isStringLiteral = false;
356
+ } catch {
357
+ value = null;
358
+ }
359
+ }
360
+ const entry = {
361
+ name,
362
+ value,
363
+ isStringLiteral
364
+ };
365
+ allAttrs.push(entry);
366
+ if (name.startsWith("data-")) dataAttrs.push(entry);
367
+ }
368
+ return {
369
+ allAttrs,
370
+ dataAttrs
371
+ };
372
+ };
373
+ const buildChildElements = (node) => {
374
+ const childElements = [];
375
+ if (node.textContent) childElements.push(import_lib$2.jsxText(node.textContent));
376
+ node.children?.forEach((child) => {
377
+ const childJSX = nodeToJSX(child);
378
+ if (childJSX) childElements.push(childJSX);
379
+ });
380
+ return childElements;
381
+ };
382
+ const nodeToJSX = (node) => {
383
+ try {
384
+ if (node.isFragment) {
385
+ const childElements = buildChildElements(node);
386
+ return import_lib$2.jsxFragment(import_lib$2.jsxOpeningFragment(), import_lib$2.jsxClosingFragment(), childElements);
387
+ }
388
+ if (node.tagName === "div" && node.dataAttributes.some((attr) => attr.name === "data-item")) {
389
+ if (node.children) return nodeToJSX(node.children[0]);
390
+ return null;
391
+ }
392
+ const attributes = node.attributes.map((attr) => {
393
+ const attrName = import_lib$2.jsxIdentifier(attr.name);
394
+ if (!attr.value) return import_lib$2.jsxAttribute(attrName, null);
395
+ return import_lib$2.jsxAttribute(attrName, attrValue(attr));
396
+ });
397
+ const elementName = parseJSXName(node.tagName);
398
+ const openingElement = import_lib$2.jsxOpeningElement(elementName, attributes);
399
+ const closingElement = import_lib$2.jsxClosingElement(elementName);
400
+ const children = buildChildElements(node);
401
+ return import_lib$2.jsxElement(openingElement, closingElement, children, false);
402
+ } catch (error) {
403
+ console.error("❌ DataAttrNode to JSX conversion error:", error);
404
+ return null;
405
+ }
406
+ };
407
+ const createWrapperNode = (textContent, children) => ({
408
+ tagName: "div",
409
+ id: nanoid(6),
410
+ attributes: [{
411
+ name: DATA_ATTR.ITEM,
412
+ value: "true"
413
+ }],
414
+ dataAttributes: [{
415
+ name: DATA_ATTR.ITEM,
416
+ value: "true"
417
+ }],
418
+ textContent,
419
+ children
420
+ });
421
+ const createFragmentNode = (textContent, children) => ({
422
+ tagName: "",
423
+ id: nanoid(6),
424
+ attributes: [],
425
+ dataAttributes: [],
426
+ textContent,
427
+ children,
428
+ isFragment: true
429
+ });
430
+ const processChildrenBinding = (jsxElement, processedNodes, shouldWrap = true) => {
431
+ const jsxChildren = jsxElement.children.filter((child) => import_lib$2.isJSXElement(child) || import_lib$2.isJSXFragment(child));
432
+ if (!jsxChildren.length) return;
433
+ const childrenNodes = [];
434
+ jsxChildren.forEach((child) => {
435
+ if (import_lib$2.isJSXElement(child)) {
436
+ const childResults = extractFromNode(child, processedNodes);
437
+ if (shouldWrap) {
438
+ const wrapperNode = createWrapperNode(collectText(child.children), childResults);
439
+ childrenNodes.push(wrapperNode);
440
+ } else childrenNodes.push(...childResults);
441
+ } else if (import_lib$2.isJSXFragment(child)) {
442
+ processedNodes?.add(child);
443
+ const fragmentChildren = [];
444
+ child.children.forEach((fragmentChild) => {
445
+ if (import_lib$2.isJSXElement(fragmentChild)) {
446
+ const childResults = extractFromNode(fragmentChild, processedNodes);
447
+ fragmentChildren.push(...childResults);
448
+ }
449
+ });
450
+ if (fragmentChildren.length) {
451
+ const fragmentNode = createFragmentNode(collectText(child.children), fragmentChildren);
452
+ childrenNodes.push(fragmentNode);
453
+ }
454
+ }
455
+ });
456
+ return childrenNodes.length ? childrenNodes : void 0;
457
+ };
458
+ const skipItemsChildren = (jsxElement, processedNodes, propertyName = "items") => {
459
+ const itemsAttr = jsxElement.openingElement.attributes.find((attr) => import_lib$2.isJSXAttribute(attr) && import_lib$2.isJSXIdentifier(attr.name) && attr.name.name === propertyName);
460
+ if (!itemsAttr || !import_lib$2.isJSXAttribute(itemsAttr)) return;
461
+ if (itemsAttr.value && import_lib$2.isJSXExpressionContainer(itemsAttr.value) && import_lib$2.isArrayExpression(itemsAttr.value.expression)) itemsAttr.value.expression.elements.forEach((element) => {
462
+ if (import_lib$2.isObjectExpression(element)) element.properties.forEach((prop) => {
463
+ if (import_lib$2.isObjectProperty(prop) && import_lib$2.isIdentifier(prop.key) && import_lib$2.isJSXElement(prop.value)) markProcessedJSX(prop.value, processedNodes);
464
+ });
465
+ });
466
+ };
467
+ const markProcessedJSX = (node, processedNodes) => {
468
+ if (import_lib$2.isJSXElement(node) || import_lib$2.isJSXFragment(node)) {
469
+ processedNodes.add(node);
470
+ node.children.forEach((child) => markProcessedJSX(child, processedNodes));
471
+ return;
472
+ }
473
+ if (import_lib$2.isJSXExpressionContainer(node) || import_lib$2.isParenthesizedExpression(node)) markProcessedJSX(node.expression, processedNodes);
474
+ };
475
+ const readNodeBindingInfo = (node) => {
476
+ const opening = node.openingElement;
477
+ const tagName = getTagName(opening);
478
+ const { allAttrs, dataAttrs } = extractAttributes(opening.attributes);
479
+ const bindingAttr = dataAttrs.find((attr) => attr.name === DATA_ATTR.BINDING);
480
+ const bindings = bindingAttr?.value ? parseBinding(bindingAttr.value) : [];
481
+ const childrenBinding = bindings.find((b) => b.property === BINDING_PROP.CHILDREN);
482
+ const arrayBindings = bindings.filter((b) => b.property === BINDING_PROP.ITEMS || b.type === "array");
483
+ const innerHtmlBinding = bindings.find((b) => b.property === BINDING_PROP.INNER_HTML);
484
+ let rawChildren;
485
+ if (innerHtmlBinding && node.children.length > 0) rawChildren = node.children.map((child) => generateCode(child)).join("").trim();
486
+ return {
487
+ tagName,
488
+ allAttrs,
489
+ dataAttrs,
490
+ bindings,
491
+ childrenBinding,
492
+ arrayBindings,
493
+ rawChildren
494
+ };
495
+ };
496
+ const parseToNodes = (raw) => {
497
+ const ast = (0, import_lib$1.parse)(wrap(raw), {
498
+ sourceType: "module",
499
+ plugins: ["jsx", "typescript"],
500
+ errorRecovery: true
501
+ });
502
+ const results = [];
503
+ const processedNodes = /* @__PURE__ */ new WeakSet();
504
+ (0, import_lib.default)(ast, { JSXElement(path) {
505
+ if (processedNodes.has(path.node)) return;
506
+ const { tagName, allAttrs, dataAttrs, bindings, childrenBinding, arrayBindings, rawChildren } = readNodeBindingInfo(path.node);
507
+ if (!tagName || !dataAttrs.length) return;
508
+ let childrenNodes;
509
+ if (childrenBinding) childrenNodes = processChildrenBinding(path.node, processedNodes);
510
+ for (const arrayBinding of arrayBindings) skipItemsChildren(path.node, processedNodes, arrayBinding.property);
511
+ results.push({
512
+ tagName,
513
+ attributes: allAttrs,
514
+ dataAttributes: dataAttrs,
515
+ textContent: collectText(path.node.children),
516
+ rawChildren,
517
+ children: childrenNodes,
518
+ bindings,
519
+ loc: path.node.loc ? {
520
+ start: {
521
+ line: path.node.loc.start.line,
522
+ column: path.node.loc.start.column
523
+ },
524
+ end: {
525
+ line: path.node.loc.end.line,
526
+ column: path.node.loc.end.column
527
+ }
528
+ } : void 0
529
+ });
530
+ } });
531
+ return results;
532
+ };
533
+ function extract(raw) {
534
+ if (extractCache.has(raw)) return extractCache.get(raw);
535
+ const results = parseToNodes(raw);
536
+ extractCache.set(raw, results);
537
+ return results;
538
+ }
539
+ function extractFromNode(node, processedNodes) {
540
+ processedNodes?.add(node);
541
+ const { tagName, allAttrs, dataAttrs, bindings, childrenBinding, rawChildren } = readNodeBindingInfo(node);
542
+ let childrenNodes;
543
+ if (childrenBinding) childrenNodes = processChildrenBinding(node, processedNodes);
544
+ if (!childrenNodes) childrenNodes = processChildrenBinding(node, processedNodes, false);
545
+ return [{
546
+ tagName,
547
+ attributes: allAttrs,
548
+ dataAttributes: dataAttrs,
549
+ textContent: collectText(node.children),
550
+ rawChildren,
551
+ children: childrenNodes,
552
+ bindings
553
+ }];
554
+ }
555
+ function clearExtractCache() {
556
+ extractCache.clear();
557
+ }
558
+
559
+ //#endregion
560
+ //#region src/utils/ast/update.ts
561
+ const updateInnerText = (path, value) => {
562
+ const jsxChildren = path.node.children;
563
+ for (let i = jsxChildren.length - 1; i >= 0; i--) if (import_lib$2.isJSXText(jsxChildren[i])) jsxChildren.splice(i, 1);
564
+ jsxChildren.push(import_lib$2.jsxText(value));
565
+ return true;
566
+ };
567
+ const injectPlaceholder = (prefix, value, placeholders, { asRawContent = false } = {}) => {
568
+ const name = `__${prefix}_${nanoid(6)}__`;
569
+ const container = import_lib$2.jsxExpressionContainer(import_lib$2.identifier(name));
570
+ placeholders.set(asRawContent ? `{${name}}` : name, value);
571
+ return container;
572
+ };
573
+ const updateInnerHTML = (path, value, placeholders) => {
574
+ path.node.children = [injectPlaceholder("HTML", value, placeholders, { asRawContent: true })];
575
+ return true;
576
+ };
577
+ const updateChildren = (path, value) => {
578
+ try {
579
+ const childrenData = JSON.parse(value);
580
+ path.node.children.length = 0;
581
+ childrenData.forEach((childData) => {
582
+ const jsxElement = nodeToJSX(childData);
583
+ if (jsxElement) path.node.children.push(jsxElement);
584
+ });
585
+ return true;
586
+ } catch (error) {
587
+ console.error("❌ Children update error:", error);
588
+ return false;
589
+ }
590
+ };
591
+ const updateRichtext = (path, value) => {
592
+ path.node.children = [];
593
+ const opening = path.node.openingElement;
594
+ const htmlObject = import_lib$2.objectExpression([import_lib$2.objectProperty(import_lib$2.identifier("__html"), import_lib$2.stringLiteral(value))]);
595
+ const existingAttr = opening.attributes.find((a) => import_lib$2.isJSXAttribute(a) && import_lib$2.isJSXIdentifier(a.name) && a.name.name === "dangerouslySetInnerHTML");
596
+ if (existingAttr && import_lib$2.isJSXAttribute(existingAttr)) existingAttr.value = import_lib$2.jsxExpressionContainer(htmlObject);
597
+ else opening.attributes.push(import_lib$2.jsxAttribute(import_lib$2.jsxIdentifier("dangerouslySetInnerHTML"), import_lib$2.jsxExpressionContainer(htmlObject)));
598
+ return true;
599
+ };
600
+ const updateAttribute = (opening, propertyName, value) => {
601
+ const customAttr = opening.attributes.find((attr) => import_lib$2.isJSXAttribute(attr) && import_lib$2.isJSXIdentifier(attr.name) && attr.name.name === propertyName);
602
+ if (customAttr && import_lib$2.isJSXAttribute(customAttr)) {
603
+ const trimmed = value.trim();
604
+ customAttr.value = attrValue({
605
+ name: propertyName,
606
+ value,
607
+ isStringLiteral: !(trimmed.startsWith("[") || trimmed.startsWith("{") || REGEX.NUMBER.test(trimmed) || REGEX.BOOLEAN_OR_NULL.test(trimmed))
608
+ });
609
+ return true;
610
+ }
611
+ return false;
612
+ };
613
+ const update = (code, dataId, label, value) => {
614
+ try {
615
+ const ast = (0, import_lib$1.parse)(wrap(code), {
616
+ sourceType: "module",
617
+ plugins: ["jsx", "typescript"]
618
+ });
619
+ let changed = false;
620
+ const jsxPlaceholders = /* @__PURE__ */ new Map();
621
+ (0, import_lib.default)(ast, { JSXElement(path) {
622
+ const opening = path.node.openingElement;
623
+ if (!opening.attributes.find((attr) => {
624
+ return import_lib$2.isJSXAttribute(attr) && import_lib$2.isJSXIdentifier(attr.name) && attr.name.name === DATA_ATTR.ID && attr.value && import_lib$2.isStringLiteral(attr.value) && attr.value.value === dataId;
625
+ })) return;
626
+ const bindingAttr = opening.attributes.find((attr) => import_lib$2.isJSXAttribute(attr) && import_lib$2.isJSXIdentifier(attr.name) && attr.name.name === DATA_ATTR.BINDING);
627
+ if (!bindingAttr?.value) return;
628
+ let bindingValue = "";
629
+ if (import_lib$2.isStringLiteral(bindingAttr.value)) bindingValue = bindingAttr.value.value;
630
+ else if (import_lib$2.isJSXExpressionContainer(bindingAttr.value)) try {
631
+ bindingValue = generateCode(bindingAttr.value.expression);
632
+ } catch {
633
+ return;
634
+ }
635
+ const propertyBinding = parseBinding(bindingValue).find((binding) => binding.label === label);
636
+ if (!propertyBinding) return;
637
+ switch (propertyBinding.property) {
638
+ case BINDING_PROP.INNER_TEXT:
639
+ changed = updateInnerText(path, value);
640
+ break;
641
+ case BINDING_PROP.INNER_HTML:
642
+ if (propertyBinding.type === "richtext") changed = updateRichtext(path, value);
643
+ else changed = updateInnerHTML(path, value, jsxPlaceholders);
644
+ break;
645
+ case BINDING_PROP.CHILDREN:
646
+ changed = updateChildren(path, value);
647
+ break;
648
+ default:
649
+ if (propertyBinding.type === "jsx") {
650
+ const attr = opening.attributes.find((a) => import_lib$2.isJSXAttribute(a) && import_lib$2.isJSXIdentifier(a.name) && a.name.name === propertyBinding.property);
651
+ if (attr && import_lib$2.isJSXAttribute(attr)) {
652
+ attr.value = injectPlaceholder("JSX", value.trim(), jsxPlaceholders);
653
+ changed = true;
654
+ }
655
+ } else changed = updateAttribute(opening, propertyBinding.property, value);
656
+ break;
657
+ }
658
+ } });
659
+ if (!changed) return {
660
+ code,
661
+ success: false
662
+ };
663
+ let result = unwrap(generateCode(ast));
664
+ for (const [placeholder, original] of jsxPlaceholders) result = result.replace(placeholder, () => original);
665
+ return {
666
+ code: result,
667
+ success: true
668
+ };
669
+ } catch (error) {
670
+ console.error("❌ Code update error:", error);
671
+ return {
672
+ code,
673
+ success: false
674
+ };
675
+ }
676
+ };
677
+ const bulkUpdate = (raw, entries) => {
678
+ let current = raw;
679
+ let allSucceeded = true;
680
+ for (const entry of entries) {
681
+ const result = update(current, entry.dataId, entry.label, entry.value);
682
+ current = result.code;
683
+ allSucceeded = allSucceeded && result.success;
684
+ }
685
+ return {
686
+ code: current,
687
+ success: allSucceeded
688
+ };
689
+ };
690
+
691
+ //#endregion
692
+ //#region src/utils/ast/tree.ts
693
+ const replaceIds = (code, generateId = () => nanoid(6)) => {
694
+ return code.replace(new RegExp(`${DATA_ATTR.ID}="[^"]*"`, "g"), () => {
695
+ return `${DATA_ATTR.ID}="${generateId()}"`;
696
+ });
697
+ };
698
+ const fillIds = (code, generateId = () => nanoid(6)) => {
699
+ return code.replace(new RegExp(`${DATA_ATTR.ID}=""`, "g"), () => {
700
+ return `${DATA_ATTR.ID}="${generateId()}"`;
701
+ });
702
+ };
703
+ const clone = (element, generateId = () => nanoid(6)) => {
704
+ return (0, import_lib$1.parseExpression)(replaceIds(generateCode(import_lib$2.cloneNode(element, true)), generateId), { plugins: ["jsx", "typescript"] });
705
+ };
706
+
707
+ //#endregion
708
+ //#region src/utils/ast/validate.ts
709
+ const VALID = { valid: true };
710
+ const validateBindingValue = (binding, value) => {
711
+ if (value === "" || value === null || value === void 0) {
712
+ if (binding.required) return {
713
+ valid: false,
714
+ message: "This field is required."
715
+ };
716
+ return VALID;
717
+ }
718
+ if (typeof value === "number") {
719
+ if (binding.min !== void 0 && value < binding.min) return {
720
+ valid: false,
721
+ message: `Must be at least ${binding.min}.`
722
+ };
723
+ if (binding.max !== void 0 && value > binding.max) return {
724
+ valid: false,
725
+ message: `Must be at most ${binding.max}.`
726
+ };
727
+ }
728
+ if (typeof value === "string" && binding.pattern) {
729
+ let regex;
730
+ try {
731
+ regex = new RegExp(binding.pattern);
732
+ } catch {
733
+ return VALID;
734
+ }
735
+ if (!regex.test(value)) return {
736
+ valid: false,
737
+ message: "Value does not match the required format."
738
+ };
739
+ }
740
+ if (typeof value === "string" && binding.type === "url") try {
741
+ new URL(value);
742
+ } catch {
743
+ return {
744
+ valid: false,
745
+ message: "Must be a valid URL."
746
+ };
747
+ }
748
+ if (typeof value === "string" && binding.type === "date") {
749
+ if (Number.isNaN(Date.parse(value))) return {
750
+ valid: false,
751
+ message: "Must be a valid date."
752
+ };
753
+ }
754
+ return VALID;
755
+ };
756
+
757
+ //#endregion
758
+ export { parseArrayExpression as _, bulkUpdate as a, extract as c, getCurrentValue as d, parseBinding as f, extractObjectProperties as g, extractNodeValue as h, replaceIds as i, nanoid as l, createNodeFromValue as m, clone as n, update as o, arrayExpressionToCode as p, fillIds as r, clearExtractCache as s, validateBindingValue as t, findEditableChildren as u, parseValue as v };
759
+ //# sourceMappingURL=ast-DyDLUN5F.mjs.map