@setzkasten-cms/astro-admin 1.4.0 → 1.4.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 (153) hide show
  1. package/dist/api-routes/_auth-guard.d.ts +47 -0
  2. package/dist/api-routes/_auth-guard.js +18 -0
  3. package/dist/api-routes/_commit-trailers.d.ts +8 -0
  4. package/dist/api-routes/_commit-trailers.js +8 -0
  5. package/dist/api-routes/_feature-gate.d.ts +23 -0
  6. package/dist/api-routes/_feature-gate.js +7 -0
  7. package/dist/api-routes/_github-cache.d.ts +4 -0
  8. package/dist/api-routes/_github-cache.js +8 -0
  9. package/dist/api-routes/_github-token.d.ts +27 -0
  10. package/dist/api-routes/_github-token.js +8 -0
  11. package/dist/api-routes/_license-tier.d.ts +22 -0
  12. package/dist/api-routes/_license-tier.js +6 -0
  13. package/dist/api-routes/_pages-meta-store.d.ts +32 -0
  14. package/dist/api-routes/_pages-meta-store.js +9 -0
  15. package/dist/api-routes/_role-resolver.d.ts +15 -0
  16. package/dist/api-routes/_role-resolver.js +13 -0
  17. package/dist/api-routes/_session-cookie.d.ts +18 -0
  18. package/dist/api-routes/_session-cookie.js +6 -0
  19. package/dist/api-routes/_storage-config.d.ts +60 -0
  20. package/dist/api-routes/_storage-config.js +10 -0
  21. package/dist/api-routes/_vercel-origin.d.ts +16 -0
  22. package/dist/api-routes/_vercel-origin.js +6 -0
  23. package/dist/api-routes/_webhook-dispatcher.d.ts +13 -0
  24. package/dist/api-routes/_webhook-dispatcher.js +97 -0
  25. package/dist/api-routes/_webhook-signing.d.ts +11 -0
  26. package/dist/api-routes/_webhook-signing.js +6 -0
  27. package/dist/api-routes/_webhook-status-store.d.ts +19 -0
  28. package/dist/api-routes/_webhook-status-store.js +10 -0
  29. package/dist/api-routes/_website-resolver.d.ts +49 -0
  30. package/dist/api-routes/_website-resolver.js +14 -0
  31. package/dist/api-routes/_websites-store.d.ts +30 -0
  32. package/dist/api-routes/_websites-store.js +11 -0
  33. package/dist/api-routes/asset-proxy.d.ts +12 -0
  34. package/dist/api-routes/asset-proxy.js +67 -0
  35. package/dist/api-routes/auth-callback.d.ts +9 -0
  36. package/dist/api-routes/auth-callback.js +68 -0
  37. package/dist/api-routes/auth-login.d.ts +11 -0
  38. package/dist/api-routes/auth-login.js +27 -0
  39. package/dist/api-routes/auth-logout.d.ts +10 -0
  40. package/dist/api-routes/auth-logout.js +13 -0
  41. package/dist/api-routes/auth-session.d.ts +9 -0
  42. package/dist/api-routes/auth-session.js +31 -0
  43. package/dist/api-routes/auth-setzkasten-login.d.ts +18 -0
  44. package/dist/api-routes/auth-setzkasten-login.js +74 -0
  45. package/dist/api-routes/catalog-add.d.ts +14 -0
  46. package/dist/api-routes/catalog-add.js +153 -0
  47. package/dist/api-routes/catalog-export.d.ts +13 -0
  48. package/dist/api-routes/catalog-export.js +71 -0
  49. package/dist/api-routes/catalog-helpers.d.ts +41 -0
  50. package/dist/api-routes/catalog-helpers.js +11 -0
  51. package/dist/api-routes/catalog-list.d.ts +11 -0
  52. package/dist/api-routes/catalog-list.js +12 -0
  53. package/dist/api-routes/config.d.ts +12 -0
  54. package/dist/api-routes/config.js +43 -0
  55. package/dist/api-routes/deploy-hook.d.ts +14 -0
  56. package/dist/api-routes/deploy-hook.js +52 -0
  57. package/dist/api-routes/editors.d.ts +29 -0
  58. package/dist/api-routes/editors.js +18 -0
  59. package/dist/api-routes/github-proxy.d.ts +12 -0
  60. package/dist/api-routes/github-proxy.js +82 -0
  61. package/dist/api-routes/global-config.d.ts +20 -0
  62. package/dist/api-routes/global-config.js +19 -0
  63. package/dist/api-routes/history-rollback.d.ts +22 -0
  64. package/dist/api-routes/history-rollback.js +111 -0
  65. package/dist/api-routes/history-version.d.ts +11 -0
  66. package/dist/api-routes/history-version.js +57 -0
  67. package/dist/api-routes/history.d.ts +13 -0
  68. package/dist/api-routes/history.js +85 -0
  69. package/dist/api-routes/icons-local.d.ts +28 -0
  70. package/dist/api-routes/icons-local.js +115 -0
  71. package/dist/api-routes/init-add-section.d.ts +23 -0
  72. package/dist/api-routes/init-add-section.js +396 -0
  73. package/dist/api-routes/init-apply.d.ts +11 -0
  74. package/dist/api-routes/init-apply.js +266 -0
  75. package/dist/api-routes/init-migrate.d.ts +16 -0
  76. package/dist/api-routes/init-migrate.js +205 -0
  77. package/dist/api-routes/init-scan-page.d.ts +39 -0
  78. package/dist/api-routes/init-scan-page.js +260 -0
  79. package/dist/api-routes/init-scan.d.ts +11 -0
  80. package/dist/api-routes/init-scan.js +128 -0
  81. package/dist/api-routes/migrate-to-multi.d.ts +26 -0
  82. package/dist/api-routes/migrate-to-multi.js +188 -0
  83. package/dist/api-routes/pages.d.ts +39 -0
  84. package/dist/api-routes/pages.js +88 -0
  85. package/dist/api-routes/section-add.d.ts +18 -0
  86. package/dist/api-routes/section-add.js +173 -0
  87. package/dist/api-routes/section-commit-pending.d.ts +18 -0
  88. package/dist/api-routes/section-commit-pending.js +207 -0
  89. package/dist/api-routes/section-delete.d.ts +15 -0
  90. package/dist/api-routes/section-delete.js +149 -0
  91. package/dist/api-routes/section-duplicate.d.ts +15 -0
  92. package/dist/api-routes/section-duplicate.js +143 -0
  93. package/dist/api-routes/section-management.d.ts +41 -0
  94. package/dist/api-routes/section-management.js +14 -0
  95. package/dist/api-routes/section-prepare-copy.d.ts +25 -0
  96. package/dist/api-routes/section-prepare-copy.js +69 -0
  97. package/dist/api-routes/section-prepare.d.ts +18 -0
  98. package/dist/api-routes/section-prepare.js +104 -0
  99. package/dist/api-routes/setup-github-app-bounce.d.ts +13 -0
  100. package/dist/api-routes/setup-github-app-bounce.js +45 -0
  101. package/dist/api-routes/setup-github-app-branches.d.ts +14 -0
  102. package/dist/api-routes/setup-github-app-branches.js +58 -0
  103. package/dist/api-routes/setup-github-app-callback.d.ts +15 -0
  104. package/dist/api-routes/setup-github-app-callback.js +45 -0
  105. package/dist/api-routes/setup-github-app-installed.d.ts +15 -0
  106. package/dist/api-routes/setup-github-app-installed.js +33 -0
  107. package/dist/api-routes/setup-github-app-repos.d.ts +17 -0
  108. package/dist/api-routes/setup-github-app-repos.js +41 -0
  109. package/dist/api-routes/setup-github-app.d.ts +15 -0
  110. package/dist/api-routes/setup-github-app.js +41 -0
  111. package/dist/api-routes/updater-check.d.ts +10 -0
  112. package/dist/api-routes/updater-check.js +37 -0
  113. package/dist/api-routes/updater-register.d.ts +14 -0
  114. package/dist/api-routes/updater-register.js +71 -0
  115. package/dist/api-routes/updater-transfer.d.ts +11 -0
  116. package/dist/api-routes/updater-transfer.js +37 -0
  117. package/dist/api-routes/updater-unbind.d.ts +17 -0
  118. package/dist/api-routes/updater-unbind.js +35 -0
  119. package/dist/api-routes/webhooks-status.d.ts +12 -0
  120. package/dist/api-routes/webhooks-status.js +22 -0
  121. package/dist/api-routes/webhooks-test.d.ts +13 -0
  122. package/dist/api-routes/webhooks-test.js +124 -0
  123. package/dist/api-routes/webhooks.d.ts +6 -0
  124. package/dist/api-routes/webhooks.js +148 -0
  125. package/dist/api-routes/websites-add.d.ts +15 -0
  126. package/dist/api-routes/websites-add.js +92 -0
  127. package/dist/api-routes/websites-list.d.ts +12 -0
  128. package/dist/api-routes/websites-list.js +35 -0
  129. package/dist/api-routes/websites-remove.d.ts +15 -0
  130. package/dist/api-routes/websites-remove.js +69 -0
  131. package/dist/chunk-35S35OIV.js +80 -0
  132. package/dist/chunk-45ARVNT3.js +25 -0
  133. package/dist/chunk-5PIMDP4N.js +25 -0
  134. package/dist/chunk-5ZFTG4BW.js +10 -0
  135. package/dist/chunk-6UIKVKED.js +51 -0
  136. package/dist/chunk-737TIZRU.js +9 -0
  137. package/dist/chunk-AM4DZXXM.js +120 -0
  138. package/dist/chunk-FXNOTESI.js +87 -0
  139. package/dist/chunk-GHNK2GFE.js +48 -0
  140. package/dist/chunk-GRG3LNKH.js +37 -0
  141. package/dist/chunk-INIWFKQ3.js +236 -0
  142. package/dist/chunk-JHY6XTLL.js +24 -0
  143. package/dist/chunk-K22A4ZBS.js +1574 -0
  144. package/dist/chunk-KH22FJO5.js +17 -0
  145. package/dist/chunk-NKDATSPA.js +43 -0
  146. package/dist/chunk-RHJONMLK.js +1267 -0
  147. package/dist/chunk-TJNJKPUL.js +11 -0
  148. package/dist/chunk-V6IMPVF3.js +120 -0
  149. package/dist/chunk-W3QHY5GW.js +19 -0
  150. package/dist/chunk-ZQDGGWJP.js +43 -0
  151. package/package.json +249 -53
  152. package/src/api-routes/__tests__/route-registry.test.ts +7 -1
  153. package/tsconfig.json +0 -9
@@ -0,0 +1,1267 @@
1
+ // src/init/template-patcher-v2.ts
2
+ import { parse } from "@astrojs/compiler";
3
+ var normalizeWs = (s) => s.replace(/\s+/g, " ").trim();
4
+ function findNormalizedText(haystack, needle, startFrom) {
5
+ const exactIdx = haystack.indexOf(needle, startFrom);
6
+ if (exactIdx !== -1) return { start: exactIdx, end: exactIdx + needle.length };
7
+ const normalized = normalizeWs(needle);
8
+ if (!normalized || normalized.length < 2) return null;
9
+ const escaped = normalized.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/ /g, "\\s+");
10
+ const regex = new RegExp(escaped);
11
+ const match = regex.exec(haystack.slice(startFrom));
12
+ if (match) return { start: startFrom + match.index, end: startFrom + match.index + match[0].length };
13
+ return null;
14
+ }
15
+ async function patchTemplateForFields(source, sectionKey, fields, repeatedGroups = [], options) {
16
+ let result;
17
+ try {
18
+ result = await parse(source);
19
+ } catch (err) {
20
+ console.error("[setzkasten] template-patcher: parse() failed:", err);
21
+ return source;
22
+ }
23
+ const ast = result.ast;
24
+ const b2c = buildByteToCharMap(source);
25
+ convertAstPositions(ast, b2c);
26
+ const fmNode = ast.children?.find((c) => c.type === "frontmatter");
27
+ if (!fmNode?.value) return source;
28
+ let varName = extractVarName(fmNode.value);
29
+ if (!varName) {
30
+ varName = "skData";
31
+ const isPageMode = options?.mode === "page";
32
+ let propsBlock;
33
+ if (isPageMode) {
34
+ propsBlock = `
35
+ import { getSection } from 'setzkasten:content'
36
+ const ${varName} = getSection('${sectionKey}')
37
+ `;
38
+ } else {
39
+ propsBlock = `
40
+ interface Props {
41
+ data: Record<string, any> | null
42
+ }
43
+ const { data: ${varName} } = Astro.props
44
+ `;
45
+ }
46
+ const fmEnd = source.indexOf("---", source.indexOf("---") + 3);
47
+ if (fmEnd === -1) return source;
48
+ const modifiedSource = source.slice(0, fmEnd) + propsBlock + source.slice(fmEnd);
49
+ const shift = propsBlock.length;
50
+ for (const g of repeatedGroups) {
51
+ for (const inst of g.instances) {
52
+ inst.start += shift;
53
+ inst.end += shift;
54
+ }
55
+ for (const f of g.fields) {
56
+ for (let i = 0; i < f.positions.length; i++) {
57
+ const p = f.positions[i];
58
+ if (p) p.offset += shift;
59
+ }
60
+ }
61
+ if (g.classAttrs) {
62
+ for (const instAttrs of g.classAttrs) {
63
+ for (const a of instAttrs) a.sourceOffset += shift;
64
+ }
65
+ }
66
+ }
67
+ const patched2 = await patchTemplateForFields(modifiedSource, sectionKey, fields, repeatedGroups, options);
68
+ return removeOldVarDeclarations(patched2, fields, repeatedGroups, varName);
69
+ }
70
+ const existingBindings = /* @__PURE__ */ new Set();
71
+ walkAst(ast, (node) => {
72
+ if (isElement(node) && node.attributes) {
73
+ for (const attr of node.attributes) {
74
+ if (attr.name === "data-sk-field" && attr.value) {
75
+ existingBindings.add(attr.value);
76
+ }
77
+ }
78
+ }
79
+ });
80
+ const fmVarNames = /* @__PURE__ */ new Set();
81
+ const fmVarRegex = /(?:const|let)\s+(\w+)\s*=/g;
82
+ let fmMatch;
83
+ while ((fmMatch = fmVarRegex.exec(fmNode.value)) !== null) {
84
+ fmVarNames.add(fmMatch[1]);
85
+ }
86
+ const cmsExpressions = [];
87
+ const varExpressions = [];
88
+ const textNodes = [];
89
+ const hrefAttrs = [];
90
+ const mapExpressions = [];
91
+ const mixedElements = [];
92
+ walkAst(ast, (node, parent) => {
93
+ if (!parent) return;
94
+ if (isElement(node) && node.children && node.children.length > 1) {
95
+ const hasText = node.children.some((c) => c.type === "text" && c.value?.trim());
96
+ const hasChild = node.children.some((c) => isElement(c) || c.type === "expression");
97
+ if (hasText && hasChild) {
98
+ const fullText = normalizeWs(getElementTextContent(node));
99
+ if (fullText) mixedElements.push({ node, normalizedText: fullText });
100
+ }
101
+ }
102
+ if (node.type === "expression" && node.children) {
103
+ const exprText = node.children.filter((c) => c.type === "text").map((c) => c.value ?? "").join("");
104
+ const cmsMatch = exprText.match(new RegExp(`${varName}\\?\\.\\s*(\\w+)`));
105
+ if (cmsMatch) {
106
+ cmsExpressions.push({ node, parent, fieldKey: cmsMatch[1] });
107
+ }
108
+ const trimmedExpr = exprText.trim();
109
+ if (fmVarNames.has(trimmedExpr) && !trimmedExpr.includes(".") && !trimmedExpr.includes("(")) {
110
+ varExpressions.push({ node, parent, varName: trimmedExpr });
111
+ }
112
+ if (exprText.includes(".map(")) {
113
+ mapExpressions.push({ node });
114
+ }
115
+ }
116
+ if (node.type === "text" && node.value?.trim()) {
117
+ textNodes.push({ node, parent });
118
+ }
119
+ if (isElement(node) && node.attributes) {
120
+ for (const attr of node.attributes) {
121
+ if (attr.name === "href" && attr.kind === "quoted" && attr.value) {
122
+ hrefAttrs.push({ attr, element: node });
123
+ }
124
+ }
125
+ }
126
+ });
127
+ const edits = [];
128
+ patchSectionId(source, ast, sectionKey, edits, options);
129
+ const repeatedFieldKeys = /* @__PURE__ */ new Set();
130
+ if (repeatedGroups.length > 0) {
131
+ const repeatedEdits = patchRepeatedGroups(source, sectionKey, varName, repeatedGroups);
132
+ edits.push(...repeatedEdits);
133
+ for (const g of repeatedGroups) repeatedFieldKeys.add(g.fieldKey);
134
+ for (const g of repeatedGroups) {
135
+ const topField = fields.find((f) => f.key === g.fieldKey);
136
+ if (!topField || !Array.isArray(topField.defaultValue)) continue;
137
+ const items = topField.defaultValue;
138
+ for (const innerField of g.fields) {
139
+ for (let ii = 0; ii < items.length && ii < innerField.defaultValues.length; ii++) {
140
+ const val = innerField.defaultValues[ii];
141
+ if (val != null && items[ii] && typeof items[ii] === "object" && !(innerField.key in items[ii])) {
142
+ items[ii][innerField.key] = val;
143
+ }
144
+ }
145
+ }
146
+ }
147
+ }
148
+ for (const field of fields) {
149
+ if (repeatedFieldKeys.has(field.key)) continue;
150
+ const bindingKey = `${sectionKey}.${field.key}`;
151
+ if (existingBindings.has(bindingKey)) continue;
152
+ if (field.type === "array" && Array.isArray(field.defaultValue)) {
153
+ const editsBefore2 = edits.length;
154
+ patchArrayField(source, sectionKey, field, varName, mapExpressions, edits);
155
+ if (edits.length === editsBefore2) {
156
+ patchStaticListField(source, sectionKey, field, varName, ast, edits);
157
+ }
158
+ continue;
159
+ }
160
+ const cmsExpr = cmsExpressions.find((e) => e.fieldKey === field.key);
161
+ if (cmsExpr) {
162
+ patchCmsBoundField(source, sectionKey, field, cmsExpr, edits);
163
+ continue;
164
+ }
165
+ const varExpr = varExpressions.find((e) => e.varName === field.key);
166
+ if (varExpr) {
167
+ patchVarExpression(source, sectionKey, field, varName, varExpr, edits);
168
+ continue;
169
+ }
170
+ if (field.type === "icon" || field.type === "image") {
171
+ patchIconOrImageField(source, sectionKey, field, varName, ast, edits);
172
+ continue;
173
+ }
174
+ if (!field.defaultValue || typeof field.defaultValue !== "string") continue;
175
+ if (field.defaultValue.length < 2) continue;
176
+ if (/link|url|href/i.test(field.key)) {
177
+ const hrefMatch = hrefAttrs.find((h) => h.attr.value === field.defaultValue);
178
+ if (hrefMatch) {
179
+ patchHrefField(source, sectionKey, field, varName, hrefMatch, edits);
180
+ continue;
181
+ }
182
+ }
183
+ const editsBefore = edits.length;
184
+ patchTextField(source, sectionKey, field, varName, textNodes, edits);
185
+ if (edits.length === editsBefore) {
186
+ const dv = field.defaultValue;
187
+ const mixedMatch = mixedElements.find((m) => m.normalizedText === normalizeWs(dv));
188
+ if (mixedMatch) {
189
+ patchMixedContentField(source, sectionKey, field, varName, mixedMatch.node, edits);
190
+ }
191
+ }
192
+ }
193
+ const patched = applyEdits(source, edits);
194
+ return convertToSetHtml(patched);
195
+ }
196
+ function convertToSetHtml(source) {
197
+ const marker = "data-sk-field";
198
+ let result = source;
199
+ let searchFrom = 0;
200
+ while (true) {
201
+ const idx = result.indexOf(marker, searchFrom);
202
+ if (idx === -1) break;
203
+ searchFrom = idx + marker.length;
204
+ let tagEnd = -1;
205
+ let braceDepth = 0;
206
+ for (let i = idx; i < result.length; i++) {
207
+ const ch = result[i];
208
+ if (ch === "{") braceDepth++;
209
+ else if (ch === "}") braceDepth--;
210
+ else if (ch === ">" && braceDepth === 0) {
211
+ tagEnd = i;
212
+ break;
213
+ }
214
+ }
215
+ if (tagEnd === -1) continue;
216
+ const tagSection = result.slice(idx, tagEnd);
217
+ if (tagSection.includes("set:html")) continue;
218
+ const afterTag = result.slice(tagEnd + 1);
219
+ const innerMatch = afterTag.match(/^(\s*)\{([^{}]+)\}(\s*)<\/(\w+)>/);
220
+ if (!innerMatch) continue;
221
+ const expr = innerMatch[2].trim();
222
+ if (!/^\w+\??\.\w+$/.test(expr)) continue;
223
+ const fullInnerLength = innerMatch[0].length;
224
+ const closeTag = `</${innerMatch[4]}>`;
225
+ const insertion = ` set:html={${expr}}>${closeTag}`;
226
+ result = result.slice(0, tagEnd) + insertion + result.slice(tagEnd + 1 + fullInnerLength);
227
+ searchFrom = tagEnd + insertion.length;
228
+ }
229
+ return result;
230
+ }
231
+ function buildByteToCharMap(source) {
232
+ const buf = Buffer.from(source, "utf-8");
233
+ if (buf.length === source.length) {
234
+ return (offset) => offset;
235
+ }
236
+ const map = new Array(buf.length + 1);
237
+ let byteIdx = 0;
238
+ for (let charIdx = 0; charIdx < source.length; charIdx++) {
239
+ const codePoint = source.codePointAt(charIdx);
240
+ const charByteLen = codePoint <= 127 ? 1 : codePoint <= 2047 ? 2 : codePoint <= 65535 ? 3 : 4;
241
+ for (let b = 0; b < charByteLen; b++) {
242
+ map[byteIdx + b] = charIdx;
243
+ }
244
+ byteIdx += charByteLen;
245
+ if (codePoint > 65535) charIdx++;
246
+ }
247
+ map[byteIdx] = source.length;
248
+ return (offset) => {
249
+ if (offset <= 0) return 0;
250
+ if (offset >= map.length) return source.length;
251
+ return map[offset];
252
+ };
253
+ }
254
+ function convertAstPositions(node, b2c) {
255
+ if (node.position?.start) {
256
+ node.position.start.offset = b2c(node.position.start.offset);
257
+ }
258
+ if (node.position?.end) {
259
+ node.position.end.offset = b2c(node.position.end.offset);
260
+ }
261
+ if (node.attributes) {
262
+ for (const attr of node.attributes) {
263
+ if (attr.position?.start) {
264
+ attr.position.start.offset = b2c(attr.position.start.offset);
265
+ }
266
+ }
267
+ }
268
+ if (node.children) {
269
+ for (const child of node.children) {
270
+ convertAstPositions(child, b2c);
271
+ }
272
+ }
273
+ }
274
+ function findExpressionBounds(source, node) {
275
+ const approxStart = node.position?.start?.offset;
276
+ if (approxStart == null) return null;
277
+ const exprText = (node.children ?? []).filter((c) => c.type === "text").map((c) => c.value ?? "").join("");
278
+ if (!exprText) return null;
279
+ const searchStart = Math.max(0, approxStart - 10);
280
+ const searchEnd = Math.min(source.length, approxStart + 10);
281
+ let braceStart = -1;
282
+ for (let i = searchStart; i < searchEnd; i++) {
283
+ if (source[i] === "{") {
284
+ const afterBrace = source.slice(i + 1, i + 1 + exprText.length + 5);
285
+ if (afterBrace.trimStart().startsWith(exprText.trimStart().slice(0, 20))) {
286
+ braceStart = i;
287
+ break;
288
+ }
289
+ }
290
+ }
291
+ if (braceStart === -1) return null;
292
+ let depth = 1;
293
+ let inStr = null;
294
+ for (let i = braceStart + 1; i < source.length; i++) {
295
+ const ch = source[i];
296
+ if (inStr) {
297
+ if (ch === inStr && source[i - 1] !== "\\") inStr = null;
298
+ continue;
299
+ }
300
+ if (ch === "'" || ch === '"' || ch === "`") {
301
+ inStr = ch;
302
+ continue;
303
+ }
304
+ if (ch === "{") depth++;
305
+ if (ch === "}") {
306
+ depth--;
307
+ if (depth === 0) return { start: braceStart, end: i + 1 };
308
+ }
309
+ }
310
+ return null;
311
+ }
312
+ function findTextBounds(source, node) {
313
+ const value = node.value;
314
+ if (!value) return null;
315
+ const trimmed = value.trim();
316
+ if (!trimmed) return null;
317
+ const approxStart = node.position?.start?.offset;
318
+ if (approxStart == null) return null;
319
+ const searchStart = Math.max(0, approxStart - 20);
320
+ const idx = source.indexOf(trimmed, searchStart);
321
+ if (idx === -1 || idx > approxStart + 50) return null;
322
+ let start = idx;
323
+ while (start > 0 && /\s/.test(source[start - 1]) && source[start - 1] !== ">") {
324
+ start--;
325
+ }
326
+ let end = idx + trimmed.length;
327
+ while (end < source.length && /\s/.test(source[end]) && source[end] !== "<") {
328
+ end++;
329
+ }
330
+ return { start, end };
331
+ }
332
+ function getElementTextContent(node) {
333
+ if (node.type === "text") return node.value ?? "";
334
+ if (node.type === "expression") {
335
+ return (node.children ?? []).filter((c) => c.type === "text").map((c) => c.value ?? "").join("");
336
+ }
337
+ return (node.children ?? []).map(getElementTextContent).join("");
338
+ }
339
+ var PHRASING_ONLY_RTE_WRAPPERS = /* @__PURE__ */ new Set(["p", "span"]);
340
+ function patchMixedContentField(source, sectionKey, field, varName, element, edits) {
341
+ const bindingKey = `${sectionKey}.${field.key}`;
342
+ if (element.attributes?.some((a) => a.name === "data-sk-field")) return;
343
+ const startOffset = element.position?.start?.offset;
344
+ if (startOffset == null) return;
345
+ const tagName = element.name;
346
+ if (!tagName) return;
347
+ const searchStart = Math.max(0, startOffset - 10);
348
+ const tagPattern = `<${tagName}`;
349
+ const tagIdx = source.indexOf(tagPattern, searchStart);
350
+ if (tagIdx === -1 || tagIdx > startOffset + 10) return;
351
+ const openTagEnd = findOpeningTagEnd(source, tagIdx);
352
+ if (openTagEnd === -1) return;
353
+ const endOffset = element.position?.end?.offset;
354
+ if (endOffset == null) return;
355
+ const closeTag = `</${tagName}>`;
356
+ const closeIdx = source.lastIndexOf(closeTag, endOffset);
357
+ if (closeIdx === -1 || closeIdx <= openTagEnd) return;
358
+ const innerStart = openTagEnd + 1;
359
+ const innerHTML = source.slice(innerStart, closeIdx).trim();
360
+ const escapedHTML = innerHTML.replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
361
+ edits.push({
362
+ offset: openTagEnd,
363
+ deleteCount: 0,
364
+ insert: ` data-sk-field="${bindingKey}" set:html={${varName}?.${field.key} ?? \`${escapedHTML}\`}`
365
+ });
366
+ edits.push({
367
+ offset: innerStart,
368
+ deleteCount: closeIdx - innerStart,
369
+ insert: ""
370
+ });
371
+ if (PHRASING_ONLY_RTE_WRAPPERS.has(tagName)) {
372
+ edits.push({
373
+ offset: tagIdx + 1,
374
+ // position of the tag-name char after '<'
375
+ deleteCount: tagName.length,
376
+ insert: "div"
377
+ });
378
+ edits.push({
379
+ offset: closeIdx + 2,
380
+ // position of the tag-name char after '</'
381
+ deleteCount: tagName.length,
382
+ insert: "div"
383
+ });
384
+ }
385
+ }
386
+ function patchIconOrImageField(source, sectionKey, field, _varName, ast, edits) {
387
+ const bindingKey = `${sectionKey}.${field.key}`;
388
+ const defaultStr = typeof field.defaultValue === "string" ? field.defaultValue : "";
389
+ let targetParent = null;
390
+ walkAst(ast, (node, parent) => {
391
+ if (targetParent) return;
392
+ if (!parent || !isElement(parent)) return;
393
+ if (parent.attributes?.some((a) => a.name === "data-sk-field")) return;
394
+ if (field.type === "icon") {
395
+ const isIconComp = isElement(node) && /icon/i.test(node.name ?? "");
396
+ const hasIconAttr = node.attributes?.some(
397
+ (a) => (a.name === "icon" || a.name === "name" || a.name === "data-icon") && (a.value === defaultStr || a.value.includes(field.key))
398
+ );
399
+ if (isIconComp || hasIconAttr) {
400
+ targetParent = parent;
401
+ }
402
+ } else if (field.type === "image") {
403
+ const tag = node.name ?? "";
404
+ const isImgEl = /^(img|Image|picture)$/.test(tag);
405
+ const hasSrcAttr = node.attributes?.some(
406
+ (a) => a.name === "src" && (a.value === defaultStr || a.value.includes(field.key))
407
+ );
408
+ if (isImgEl || hasSrcAttr) {
409
+ targetParent = parent;
410
+ }
411
+ }
412
+ });
413
+ if (!targetParent) return;
414
+ addAttributeToElement(source, targetParent, `data-sk-field="${bindingKey}"`, edits);
415
+ }
416
+ function patchCmsBoundField(source, sectionKey, field, cmsExpr, edits) {
417
+ const bindingKey = `${sectionKey}.${field.key}`;
418
+ const parentEl = cmsExpr.parent;
419
+ if (isElement(parentEl) && parentEl.attributes) {
420
+ for (const attr of parentEl.attributes) {
421
+ if (attr.name === "data-sk-field") return;
422
+ }
423
+ const meaningfulChildren = (parentEl.children ?? []).filter(
424
+ (c) => !(c.type === "text" && !c.value?.trim())
425
+ );
426
+ if (meaningfulChildren.length === 1 && meaningfulChildren[0] === cmsExpr.node) {
427
+ addAttributeToElement(source, parentEl, `data-sk-field="${bindingKey}"`, edits);
428
+ return;
429
+ }
430
+ }
431
+ const bounds = findExpressionBounds(source, cmsExpr.node);
432
+ if (!bounds) return;
433
+ const exprSource = source.slice(bounds.start, bounds.end);
434
+ edits.push({
435
+ offset: bounds.start,
436
+ deleteCount: bounds.end - bounds.start,
437
+ insert: `<span data-sk-field="${bindingKey}">${exprSource}</span>`
438
+ });
439
+ }
440
+ function patchTextField(source, sectionKey, field, varName, textNodes, edits) {
441
+ const defaultValue = field.defaultValue;
442
+ const bindingKey = `${sectionKey}.${field.key}`;
443
+ const cmsExpr = `{${varName}?.${field.key}}`;
444
+ const normalizedDefault = normalizeWs(defaultValue);
445
+ const matches = textNodes.filter((t) => normalizeWs(t.node.value ?? "") === normalizedDefault);
446
+ if (matches.length === 0) return;
447
+ for (const match of matches) {
448
+ const textNode = match.node;
449
+ const parentEl = match.parent;
450
+ const bounds = findTextBounds(source, textNode);
451
+ if (!bounds) continue;
452
+ const { start, end } = bounds;
453
+ if (edits.some((e) => e.offset === start)) continue;
454
+ if (isElement(parentEl)) {
455
+ const meaningfulChildren = (parentEl.children ?? []).filter(
456
+ (c) => !(c.type === "text" && !c.value?.trim())
457
+ );
458
+ const isSoleContent = meaningfulChildren.length === 1 && meaningfulChildren[0] === textNode;
459
+ if (isSoleContent) {
460
+ const hasBinding = parentEl.attributes?.some((a) => a.name === "data-sk-field");
461
+ if (!hasBinding) {
462
+ addAttributeToElement(source, parentEl, `data-sk-field="${bindingKey}"`, edits);
463
+ }
464
+ const originalText2 = source.slice(start, end);
465
+ const leadingWs2 = originalText2.match(/^(\s*)/)?.[1] ?? "";
466
+ const trailingWs2 = originalText2.match(/(\s*)$/)?.[1] ?? "";
467
+ edits.push({
468
+ offset: start,
469
+ deleteCount: end - start,
470
+ insert: `${leadingWs2}${cmsExpr}${trailingWs2}`
471
+ });
472
+ continue;
473
+ }
474
+ }
475
+ const originalText = source.slice(start, end);
476
+ const leadingWs = originalText.match(/^(\s*)/)?.[1] ?? "";
477
+ const trailingWs = originalText.match(/(\s*)$/)?.[1] ?? "";
478
+ edits.push({
479
+ offset: start,
480
+ deleteCount: end - start,
481
+ insert: `${leadingWs}<span data-sk-field="${bindingKey}">${cmsExpr}</span>${trailingWs}`
482
+ });
483
+ }
484
+ }
485
+ function patchVarExpression(source, sectionKey, field, cmsVarName, varExpr, edits) {
486
+ const bindingKey = `${sectionKey}.${field.key}`;
487
+ const bounds = findExpressionBounds(source, varExpr.node);
488
+ if (!bounds) return;
489
+ const cmsExpr = `{${cmsVarName}?.${field.key}}`;
490
+ const parentEl = varExpr.parent;
491
+ if (isElement(parentEl)) {
492
+ const meaningfulChildren = (parentEl.children ?? []).filter(
493
+ (c) => !(c.type === "text" && !c.value?.trim())
494
+ );
495
+ if (meaningfulChildren.length === 1 && meaningfulChildren[0] === varExpr.node) {
496
+ const hasBinding = parentEl.attributes?.some((a) => a.name === "data-sk-field");
497
+ if (!hasBinding) {
498
+ addAttributeToElement(source, parentEl, `data-sk-field="${bindingKey}"`, edits);
499
+ }
500
+ edits.push({ offset: bounds.start, deleteCount: bounds.end - bounds.start, insert: cmsExpr });
501
+ return;
502
+ }
503
+ }
504
+ edits.push({
505
+ offset: bounds.start,
506
+ deleteCount: bounds.end - bounds.start,
507
+ insert: `<span data-sk-field="${bindingKey}">${cmsExpr}</span>`
508
+ });
509
+ }
510
+ function patchHrefField(source, _sectionKey, field, varName, hrefMatch, edits) {
511
+ const attr = hrefMatch.attr;
512
+ const approxStart = attr.position?.start?.offset;
513
+ if (approxStart == null) return;
514
+ const rawAttr = attr.raw ?? `"${attr.value}"`;
515
+ const hrefStr = `href=${rawAttr}`;
516
+ const searchStart = Math.max(0, approxStart - 20);
517
+ const actualOffset = source.indexOf(hrefStr, searchStart);
518
+ if (actualOffset === -1) return;
519
+ edits.push({
520
+ offset: actualOffset,
521
+ deleteCount: hrefStr.length,
522
+ insert: `href={${varName}?.${field.key}}`
523
+ });
524
+ }
525
+ function patchArrayField(source, sectionKey, field, varName, mapExpressions, edits) {
526
+ if (mapExpressions.length === 0) return;
527
+ let mapExpr = mapExpressions[0];
528
+ if (mapExpressions.length > 1 && Array.isArray(field.defaultValue) && field.defaultValue.length > 0) {
529
+ const firstItem = field.defaultValue[0];
530
+ const searchStr = typeof firstItem === "string" ? firstItem.slice(0, 30) : typeof firstItem === "object" && firstItem !== null ? Object.values(firstItem).find((v) => typeof v === "string" && v.length >= 3)?.slice(0, 30) ?? "" : "";
531
+ if (searchStr.length >= 3) {
532
+ for (const expr of mapExpressions) {
533
+ const exprBounds = findExpressionBounds(source, expr.node);
534
+ if (!exprBounds) continue;
535
+ const exprSrc = source.slice(exprBounds.start, exprBounds.end);
536
+ if (exprSrc.includes(searchStr)) {
537
+ mapExpr = expr;
538
+ break;
539
+ }
540
+ }
541
+ }
542
+ }
543
+ const bounds = findExpressionBounds(source, mapExpr.node);
544
+ if (!bounds) return;
545
+ const exprSource = source.slice(bounds.start, bounds.end);
546
+ const mapParamRegex = /\.map\s*\(\s*\(?\s*(\w+)(\s*,\s*(\w+))?\s*\)?/;
547
+ const paramMatch = exprSource.match(mapParamRegex);
548
+ if (!paramMatch) return;
549
+ const itemParam = paramMatch[1];
550
+ let indexParam = paramMatch[3];
551
+ if (!indexParam) {
552
+ indexParam = "_i";
553
+ const sigRegex = new RegExp(`\\.map\\s*\\(\\s*\\(?\\s*${itemParam}(\\s*\\)?)`);
554
+ const sigMatch = exprSource.match(sigRegex);
555
+ if (sigMatch) {
556
+ const sigOffset = bounds.start + exprSource.indexOf(sigMatch[0]);
557
+ const insertPoint = sigOffset + sigMatch[0].length - (sigMatch[1]?.length ?? 0);
558
+ edits.push({
559
+ offset: insertPoint,
560
+ deleteCount: 0,
561
+ insert: `, ${indexParam}`
562
+ });
563
+ }
564
+ }
565
+ const arrayAndMapRegex = /(\[[\s\S]*?\])\s*\.map\s*\(/;
566
+ const arrayMatch = exprSource.match(arrayAndMapRegex);
567
+ if (arrayMatch) {
568
+ const arrayLiteral = arrayMatch[1];
569
+ const arrayStartInExpr = exprSource.indexOf(arrayLiteral);
570
+ const arrayEndInExpr = arrayStartInExpr + arrayLiteral.length;
571
+ const arrayStart = bounds.start + arrayStartInExpr;
572
+ const arrayEnd = bounds.start + arrayEndInExpr;
573
+ edits.push({
574
+ offset: arrayStart,
575
+ deleteCount: arrayEnd - arrayStart,
576
+ insert: `(${varName}?.${field.key} ?? [])`
577
+ });
578
+ } else {
579
+ const varMapRegex = new RegExp(`(\\w+)\\.map\\s*\\(`);
580
+ const varMapMatch = exprSource.match(varMapRegex);
581
+ if (varMapMatch) {
582
+ const varRef = varMapMatch[1];
583
+ const varStartInExpr = exprSource.indexOf(varMapMatch[0]);
584
+ const varEndInExpr = varStartInExpr + varRef.length;
585
+ const varStart = bounds.start + varStartInExpr;
586
+ const varEnd = bounds.start + varEndInExpr;
587
+ edits.push({
588
+ offset: varStart,
589
+ deleteCount: varEnd - varStart,
590
+ insert: `(${varName}?.${field.key} ?? [])`
591
+ });
592
+ }
593
+ }
594
+ const firstChildElement = findFirstChildElement(mapExpr.node);
595
+ if (firstChildElement) {
596
+ const hasSkField = firstChildElement.attributes?.some((a) => a.name === "data-sk-field");
597
+ if (!hasSkField) {
598
+ const skFieldExpr = `data-sk-field={\`${sectionKey}.${field.key}.\${${indexParam}}\`}`;
599
+ addAttributeToElement(source, firstChildElement, skFieldExpr, edits);
600
+ }
601
+ }
602
+ patchArrayItemProperties(source, sectionKey, field, itemParam, indexParam, mapExpr.node, edits);
603
+ }
604
+ function patchStaticListField(source, sectionKey, field, varName, ast, edits) {
605
+ if (!Array.isArray(field.defaultValue) || field.defaultValue.length === 0) return;
606
+ const items = field.defaultValue;
607
+ let matchedUl = null;
608
+ walkAst(ast, (node) => {
609
+ if (matchedUl) return;
610
+ if (node.type !== "element") return;
611
+ if (node.name !== "ul" && node.name !== "ol") return;
612
+ let hasMap = false;
613
+ walkAst(node, (n) => {
614
+ if (hasMap) return;
615
+ if (n.type === "expression") {
616
+ const code = (n.children ?? []).map((c) => c.value ?? "").join("");
617
+ if (/\.map\s*\(/.test(code)) hasMap = true;
618
+ }
619
+ });
620
+ if (hasMap) return;
621
+ const stripHtml = (s) => s.replace(/<[^>]+>/g, "").replace(/\s+/g, " ").trim();
622
+ const liChildren2 = (node.children ?? []).filter((c) => c.type === "element" && c.name === "li");
623
+ if (liChildren2.length !== items.length) return;
624
+ const allMatch = liChildren2.every((li, i) => {
625
+ const text = getElementTextContent(li).replace(/\s+/g, " ").trim();
626
+ return text === stripHtml(items[i]);
627
+ });
628
+ if (allMatch) matchedUl = node;
629
+ });
630
+ if (!matchedUl) return;
631
+ const ulNode = matchedUl;
632
+ const liChildren = (ulNode.children ?? []).filter(
633
+ (c) => c.type === "element" && c.name === "li"
634
+ );
635
+ if (liChildren.length === 0) return;
636
+ const firstLi = liChildren[0];
637
+ const lastLi = liChildren[liChildren.length - 1];
638
+ const rangeStart = firstLi.position?.start?.offset;
639
+ const rangeEnd = lastLi.position?.end?.offset;
640
+ if (rangeStart == null || rangeEnd == null) return;
641
+ const firstLiEnd = firstLi.position?.end?.offset ?? rangeEnd;
642
+ let liTemplate = source.slice(rangeStart, firstLiEnd);
643
+ const liTagEnd = liTemplate.indexOf(">");
644
+ if (liTagEnd === -1) return;
645
+ const bindingKey = `${sectionKey}.${field.key}`;
646
+ liTemplate = liTemplate.slice(0, liTagEnd) + ` data-sk-field={\`${bindingKey}.\${_i}\`}>` + liTemplate.slice(liTagEnd + 1);
647
+ liTemplate = liTemplate.replace(
648
+ /<span([^>]*)>([\s\S]*?)<\/span>/g,
649
+ (_m, attrs, content) => {
650
+ if (!content.trim()) return _m;
651
+ if (attrs.includes("set:html") || attrs.includes("data-sk-field")) return _m;
652
+ return `<span${attrs} set:html={item}></span>`;
653
+ }
654
+ );
655
+ const hasHtmlItems = items.some((s) => /<[a-z]/.test(s));
656
+ const fallbackItems = hasHtmlItems ? items.map((s) => `\`${s.replace(/`/g, "\\`").replace(/\$/g, "\\$")}\``).join(", ") : items.map((s) => `'${s.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`).join(", ");
657
+ const textBeforeFirstLi = source.slice(0, rangeStart);
658
+ const lastNewline = textBeforeFirstLi.lastIndexOf("\n");
659
+ const indent = lastNewline >= 0 ? textBeforeFirstLi.slice(lastNewline + 1).match(/^[ \t]*/)?.[0] ?? "" : "";
660
+ const replacement = `{(${varName}?.${field.key} ?? [${fallbackItems}]).map((item, _i) => (
661
+ ${indent}${liTemplate}
662
+ ${indent}))}`;
663
+ edits.push({
664
+ offset: rangeStart,
665
+ deleteCount: rangeEnd - rangeStart,
666
+ insert: replacement
667
+ });
668
+ }
669
+ function patchArrayItemProperties(source, sectionKey, field, itemParam, indexParam, mapNode, edits) {
670
+ walkAst(mapNode, (node, parent) => {
671
+ if (!parent || node.type !== "expression" || !node.children) return;
672
+ const exprText = node.children.filter((c) => c.type === "text").map((c) => c.value ?? "").join("").trim();
673
+ const nestedMapMatch = exprText.match(new RegExp(`^${itemParam}\\.(\\w+)\\.map\\s*\\(`));
674
+ if (nestedMapMatch) {
675
+ const innerFieldKey = nestedMapMatch[1];
676
+ patchNestedMap(source, sectionKey, field.key, innerFieldKey, indexParam, node, edits);
677
+ return;
678
+ }
679
+ const propMatch = exprText.match(new RegExp(`^${itemParam}\\.(\\w+(?:\\.\\w+)*)$`));
680
+ if (!propMatch) return;
681
+ const propPath = propMatch[1];
682
+ const bindingKey = `${sectionKey}.${field.key}.\${${indexParam}}.${propPath}`;
683
+ if (!isElement(parent) || !parent.attributes) return;
684
+ if (parent.attributes.some((a) => a.name === "data-sk-field")) return;
685
+ const meaningfulChildren = (parent.children ?? []).filter(
686
+ (c) => !(c.type === "text" && !c.value?.trim())
687
+ );
688
+ if (meaningfulChildren.length === 1 && meaningfulChildren[0] === node) {
689
+ const skFieldExpr = `data-sk-field={\`${bindingKey}\`}`;
690
+ addAttributeToElement(source, parent, skFieldExpr, edits);
691
+ return;
692
+ }
693
+ const bounds = findExpressionBounds(source, node);
694
+ if (!bounds) return;
695
+ const exprSource = source.slice(bounds.start, bounds.end);
696
+ edits.push({
697
+ offset: bounds.start,
698
+ deleteCount: bounds.end - bounds.start,
699
+ insert: `<span data-sk-field={\`${bindingKey}\`}>${exprSource}</span>`
700
+ });
701
+ });
702
+ }
703
+ function patchNestedMap(source, sectionKey, outerFieldKey, innerFieldKey, outerIndexParam, exprNode, edits) {
704
+ const bounds = findExpressionBounds(source, exprNode);
705
+ if (!bounds) return;
706
+ const exprSrc = source.slice(bounds.start, bounds.end);
707
+ const mapParamRegex = /\.map\s*\(\s*\(?\s*(\w+)(\s*,\s*(\w+))?\s*\)?/;
708
+ const paramMatch = exprSrc.match(mapParamRegex);
709
+ if (!paramMatch) return;
710
+ const innerItemParam = paramMatch[1];
711
+ let innerIndexParam = paramMatch[3];
712
+ if (!innerIndexParam) {
713
+ innerIndexParam = "_j";
714
+ const sigRegex = new RegExp(`\\.map\\s*\\(\\s*\\(?\\s*${innerItemParam}(\\s*\\)?)`);
715
+ const sigMatch = exprSrc.match(sigRegex);
716
+ if (sigMatch) {
717
+ const insertOffset = bounds.start + exprSrc.indexOf(sigMatch[0]) + sigMatch[0].length - (sigMatch[1]?.length ?? 0);
718
+ edits.push({ offset: insertOffset, deleteCount: 0, insert: `, ${innerIndexParam}` });
719
+ }
720
+ }
721
+ const basePath = `${sectionKey}.${outerFieldKey}.\${${outerIndexParam}}.${innerFieldKey}.\${${innerIndexParam}}`;
722
+ const firstChild = findFirstChildElement(exprNode);
723
+ if (firstChild) {
724
+ const hasSkField = firstChild.attributes?.some((a) => a.name === "data-sk-field");
725
+ if (!hasSkField) {
726
+ addAttributeToElement(source, firstChild, `data-sk-field={\`${basePath}\`}`, edits);
727
+ }
728
+ }
729
+ walkAst(exprNode, (node, parent) => {
730
+ if (!parent || node.type !== "expression" || !node.children) return;
731
+ const exprText = node.children.filter((c) => c.type === "text").map((c) => c.value ?? "").join("").trim();
732
+ const propMatch = exprText.match(new RegExp(`^${innerItemParam}\\.(\\w+(?:\\.\\w+)*)$`));
733
+ if (!propMatch) return;
734
+ const propPath = propMatch[1];
735
+ const bindingKey = `${basePath}.${propPath}`;
736
+ if (!isElement(parent) || !parent.attributes) return;
737
+ if (parent.attributes.some((a) => a.name === "data-sk-field")) return;
738
+ const meaningfulChildren = (parent.children ?? []).filter(
739
+ (c) => !(c.type === "text" && !c.value?.trim())
740
+ );
741
+ if (meaningfulChildren.length === 1 && meaningfulChildren[0] === node) {
742
+ addAttributeToElement(source, parent, `data-sk-field={\`${bindingKey}\`}`, edits);
743
+ return;
744
+ }
745
+ const nodeBounds = findExpressionBounds(source, node);
746
+ if (!nodeBounds) return;
747
+ const nodeSrc = source.slice(nodeBounds.start, nodeBounds.end);
748
+ edits.push({
749
+ offset: nodeBounds.start,
750
+ deleteCount: nodeBounds.end - nodeBounds.start,
751
+ insert: `<span data-sk-field={\`${bindingKey}\`}>${nodeSrc}</span>`
752
+ });
753
+ });
754
+ }
755
+ function collectDynamicClassEdits(innerEdits, source, instances, tmpl, group) {
756
+ const classAttrs = group.classAttrs;
757
+ if (!classAttrs || classAttrs.length < 2 || instances.length < 2) return;
758
+ const tmplAttrs = classAttrs[0];
759
+ if (tmplAttrs.length === 0) return;
760
+ const otherInstAttrs = classAttrs.slice(1);
761
+ function findMatch(tmplAttr, instAttrs, claimed) {
762
+ const tmplParts = new Set(tmplAttr.value.split(/\s+/).filter(Boolean));
763
+ function sharedClassCount(instValue) {
764
+ const instParts = new Set(instValue.split(/\s+/).filter(Boolean));
765
+ if (tmplParts.size === 0 && instParts.size === 0) return 1;
766
+ return [...tmplParts].filter((p) => instParts.has(p)).length;
767
+ }
768
+ for (let i = 0; i < instAttrs.length; i++) {
769
+ if (!claimed.has(i) && instAttrs[i].path === tmplAttr.path) {
770
+ if (sharedClassCount(instAttrs[i].value) > 0) {
771
+ claimed.add(i);
772
+ return instAttrs[i].value;
773
+ }
774
+ }
775
+ }
776
+ const stripIdx = (p) => p.split("/").map((s) => s.replace(/:\d+$/, "")).join("/");
777
+ const tmplTagPath = stripIdx(tmplAttr.path);
778
+ let bestIdx = -1;
779
+ let bestShared = 0;
780
+ for (let i = 0; i < instAttrs.length; i++) {
781
+ if (claimed.has(i)) continue;
782
+ if (stripIdx(instAttrs[i].path) !== tmplTagPath) continue;
783
+ const shared = sharedClassCount(instAttrs[i].value);
784
+ if (shared > bestShared) {
785
+ bestShared = shared;
786
+ bestIdx = i;
787
+ }
788
+ }
789
+ if (bestIdx >= 0 && bestShared > 0) {
790
+ claimed.add(bestIdx);
791
+ return instAttrs[bestIdx].value;
792
+ }
793
+ return null;
794
+ }
795
+ const skipForInstance = tmplAttrs.map(() => /* @__PURE__ */ new Set());
796
+ for (const field of group.fields) {
797
+ if (field.required) continue;
798
+ const tmplPos = field.positions[0];
799
+ if (!tmplPos) continue;
800
+ const fieldTag = field.tag;
801
+ let bestAttrIdx = -1;
802
+ let bestDist = Infinity;
803
+ for (let ai = 0; ai < tmplAttrs.length; ai++) {
804
+ const pathParts = tmplAttrs[ai].path.split("/");
805
+ const leafTag = (pathParts[pathParts.length - 1] ?? "").replace(/:\d+$/, "");
806
+ if (!leafTag && fieldTag !== group.tag) continue;
807
+ if (leafTag && leafTag !== fieldTag) continue;
808
+ const dist = Math.abs(tmplPos.offset - tmplAttrs[ai].sourceOffset);
809
+ if (dist < bestDist) {
810
+ bestDist = dist;
811
+ bestAttrIdx = ai;
812
+ }
813
+ }
814
+ if (bestAttrIdx < 0) continue;
815
+ for (let instIdx = 1; instIdx < instances.length; instIdx++) {
816
+ if (!field.positions[instIdx]) {
817
+ skipForInstance[bestAttrIdx].add(instIdx - 1);
818
+ }
819
+ }
820
+ }
821
+ const claimedPerInstance = otherInstAttrs.map(() => /* @__PURE__ */ new Set());
822
+ let dynamicCount = 0;
823
+ for (let ai = 0; ai < tmplAttrs.length; ai++) {
824
+ const tmplAttr = tmplAttrs[ai];
825
+ const values = [tmplAttr.value];
826
+ let anyDiffers = false;
827
+ for (let ii = 0; ii < otherInstAttrs.length; ii++) {
828
+ if (skipForInstance[ai].has(ii)) {
829
+ values.push(tmplAttr.value);
830
+ continue;
831
+ }
832
+ const match = findMatch(tmplAttr, otherInstAttrs[ii], claimedPerInstance[ii]);
833
+ if (match == null) {
834
+ values.push(tmplAttr.value);
835
+ } else {
836
+ values.push(match);
837
+ if (match !== tmplAttr.value) anyDiffers = true;
838
+ }
839
+ }
840
+ if (!anyDiffers) continue;
841
+ const relOffset = tmplAttr.sourceOffset - tmpl.start;
842
+ if (relOffset < 0) continue;
843
+ const fieldKey = `_class${dynamicCount++}`;
844
+ group.fields.push({
845
+ key: fieldKey,
846
+ type: "text",
847
+ tag: "class",
848
+ required: true,
849
+ positions: instances.map(() => null),
850
+ defaultValues: values
851
+ });
852
+ innerEdits.push({
853
+ offset: relOffset,
854
+ deleteCount: tmplAttr.sourceLength,
855
+ insert: `class={item.${fieldKey}}`
856
+ });
857
+ }
858
+ }
859
+ function patchRepeatedGroups(source, sectionKey, varName, groups) {
860
+ const edits = [];
861
+ for (const group of groups) {
862
+ const { tag, fieldKey, instances, fields } = group;
863
+ if (instances.length < 2) continue;
864
+ const tmpl = instances[0];
865
+ let templateSrc = source.slice(tmpl.start, tmpl.end);
866
+ const innerEdits = [];
867
+ collectDynamicClassEdits(innerEdits, source, instances, tmpl, group);
868
+ for (const field of fields) {
869
+ const pos = field.positions[0];
870
+ if (!pos) continue;
871
+ const relOffset = pos.offset - tmpl.start;
872
+ if (relOffset < 0 || relOffset >= templateSrc.length) continue;
873
+ if (field.type === "array" && pos.source) {
874
+ const mapPattern = `${pos.source}.map(`;
875
+ const mapIdx = templateSrc.indexOf(mapPattern);
876
+ if (mapIdx !== -1) {
877
+ innerEdits.push({
878
+ offset: mapIdx,
879
+ deleteCount: mapPattern.length,
880
+ insert: `(item.${field.key} ?? []).map(`
881
+ });
882
+ }
883
+ } else if (field.type === "link") {
884
+ const fieldSrc = templateSrc.slice(relOffset, relOffset + pos.length);
885
+ const hrefMatch = fieldSrc.match(/href\s*=\s*"([^"]*)"/);
886
+ if (hrefMatch) {
887
+ const hrefStart = relOffset + fieldSrc.indexOf(hrefMatch[0]);
888
+ innerEdits.push({
889
+ offset: hrefStart,
890
+ deleteCount: hrefMatch[0].length,
891
+ insert: `href={item.${field.key}}`
892
+ });
893
+ }
894
+ } else if (field.type === "text") {
895
+ const defaultVal = field.defaultValues[0];
896
+ if (typeof defaultVal === "string" && defaultVal.length >= 1) {
897
+ const searchFrom = relOffset > 10 ? relOffset - 10 : 0;
898
+ const found = findNormalizedText(templateSrc, defaultVal, searchFrom);
899
+ if (found) {
900
+ innerEdits.push({
901
+ offset: found.start,
902
+ deleteCount: found.end - found.start,
903
+ insert: `{item.${field.key}}`
904
+ });
905
+ }
906
+ }
907
+ }
908
+ }
909
+ for (const field of fields) {
910
+ if (field.required) continue;
911
+ const pos = field.positions[0];
912
+ if (pos) continue;
913
+ let donorIdx = -1;
914
+ let donorPos = null;
915
+ for (let i = 1; i < instances.length; i++) {
916
+ if (field.positions[i]) {
917
+ donorIdx = i;
918
+ donorPos = field.positions[i];
919
+ break;
920
+ }
921
+ }
922
+ if (!donorPos || donorIdx < 0) continue;
923
+ const donorInst = instances[donorIdx];
924
+ const donorSrc = source.slice(donorInst.start, donorInst.end);
925
+ const donorRelOffset = donorPos.offset - donorInst.start;
926
+ let elementSrc = donorSrc.slice(donorRelOffset, donorRelOffset + donorPos.length);
927
+ const templateSrcForCheck = source.slice(tmpl.start, tmpl.end);
928
+ const beforeElement = donorSrc.slice(0, donorRelOffset).trimEnd();
929
+ const parentCloseMatch = beforeElement.match(/<(\w+)\s+[^>]*class="([^"]*)"[^>]*>\s*$/);
930
+ if (parentCloseMatch) {
931
+ const parentTag = parentCloseMatch[1];
932
+ const parentClass = parentCloseMatch[2];
933
+ if (!templateSrcForCheck.includes(`class="${parentClass}"`)) {
934
+ const parentStart = beforeElement.lastIndexOf(`<${parentTag}`);
935
+ if (parentStart >= 0) {
936
+ const closingTag = `</${parentTag}>`;
937
+ const afterElement = donorSrc.slice(donorRelOffset + donorPos.length);
938
+ const closingIdx = afterElement.indexOf(closingTag);
939
+ if (closingIdx >= 0) {
940
+ const wrapperEnd = donorRelOffset + donorPos.length + closingIdx + closingTag.length;
941
+ elementSrc = donorSrc.slice(parentStart, wrapperEnd).trim();
942
+ }
943
+ }
944
+ }
945
+ }
946
+ const defaultVal = field.defaultValues[donorIdx];
947
+ if (typeof defaultVal === "string" && defaultVal.length >= 1) {
948
+ const found = findNormalizedText(elementSrc, defaultVal, 0);
949
+ if (found) {
950
+ elementSrc = elementSrc.slice(0, found.start) + `{item.${field.key}}` + elementSrc.slice(found.end);
951
+ }
952
+ }
953
+ const conditionalBlock = `{item.${field.key} && ${elementSrc}}`;
954
+ const openTagEnd = templateSrc.indexOf(">", 0) + 1;
955
+ if (openTagEnd > 0) {
956
+ innerEdits.push({
957
+ offset: openTagEnd,
958
+ deleteCount: 0,
959
+ insert: `
960
+ ${conditionalBlock}`
961
+ });
962
+ }
963
+ }
964
+ innerEdits.sort((a, b) => b.offset - a.offset);
965
+ for (const edit of innerEdits) {
966
+ templateSrc = templateSrc.slice(0, edit.offset) + edit.insert + templateSrc.slice(edit.offset + edit.deleteCount);
967
+ }
968
+ const tagEndIdx = templateSrc.indexOf(">");
969
+ if (tagEndIdx !== -1) {
970
+ const skFieldAttr = ` data-sk-field={\`${sectionKey}.${fieldKey}.\${_i}\`}`;
971
+ templateSrc = templateSrc.slice(0, tagEndIdx) + skFieldAttr + templateSrc.slice(tagEndIdx);
972
+ }
973
+ templateSrc = addInnerFieldBindings(templateSrc, sectionKey, fieldKey, fields);
974
+ const indent = detectIndent(source, tmpl.start);
975
+ const wrapped = `{(${varName}?.${fieldKey} ?? []).map((item, _i) => (
976
+ ${indent} ${templateSrc}
977
+ ${indent}))}`;
978
+ edits.push({
979
+ offset: tmpl.start,
980
+ deleteCount: tmpl.end - tmpl.start,
981
+ insert: wrapped
982
+ });
983
+ for (let i = instances.length - 1; i >= 1; i--) {
984
+ const inst = instances[i];
985
+ let deleteStart = inst.start;
986
+ let deleteEnd = inst.end;
987
+ while (deleteStart > 0 && /\s/.test(source[deleteStart - 1]) && source[deleteStart - 1] !== "\n") {
988
+ deleteStart--;
989
+ }
990
+ if (deleteStart > 0 && source[deleteStart - 1] === "\n") deleteStart--;
991
+ edits.push({
992
+ offset: deleteStart,
993
+ deleteCount: deleteEnd - deleteStart,
994
+ insert: ""
995
+ });
996
+ }
997
+ }
998
+ return edits;
999
+ }
1000
+ function addInnerFieldBindings(templateSrc, sectionKey, groupFieldKey, fields) {
1001
+ const inserts = [];
1002
+ for (const field of fields) {
1003
+ if (field.type === "array" || field.type === "link") continue;
1004
+ const itemRef = `{item.${field.key}}`;
1005
+ const idx = templateSrc.indexOf(itemRef);
1006
+ if (idx === -1) continue;
1007
+ let tagStart = idx - 1;
1008
+ while (tagStart >= 0 && templateSrc[tagStart] !== "<") tagStart--;
1009
+ if (tagStart < 0 || templateSrc[tagStart + 1] === "/") continue;
1010
+ let tagEnd = -1;
1011
+ let inQ = null;
1012
+ let inE = 0;
1013
+ for (let i = tagStart; i < idx; i++) {
1014
+ const ch = templateSrc[i];
1015
+ if (inQ) {
1016
+ if (ch === inQ && templateSrc[i - 1] !== "\\") inQ = null;
1017
+ continue;
1018
+ }
1019
+ if (ch === "{") {
1020
+ inE++;
1021
+ continue;
1022
+ }
1023
+ if (ch === "}" && inE > 0) {
1024
+ inE--;
1025
+ continue;
1026
+ }
1027
+ if (inE > 0) continue;
1028
+ if (ch === '"' || ch === "'") {
1029
+ inQ = ch;
1030
+ continue;
1031
+ }
1032
+ if (ch === ">") tagEnd = i;
1033
+ }
1034
+ if (tagEnd === -1) continue;
1035
+ const tagStr = templateSrc.slice(tagStart, tagEnd + 1);
1036
+ if (tagStr.includes("data-sk-field")) continue;
1037
+ inserts.push({
1038
+ offset: tagEnd,
1039
+ text: ` data-sk-field={\`${sectionKey}.${groupFieldKey}.\${_i}.${field.key}\`}`
1040
+ });
1041
+ }
1042
+ inserts.sort((a, b) => b.offset - a.offset);
1043
+ for (const ins of inserts) {
1044
+ templateSrc = templateSrc.slice(0, ins.offset) + ins.text + templateSrc.slice(ins.offset);
1045
+ }
1046
+ return templateSrc;
1047
+ }
1048
+ function detectIndent(source, offset) {
1049
+ let lineStart = offset;
1050
+ while (lineStart > 0 && source[lineStart - 1] !== "\n") lineStart--;
1051
+ return source.slice(lineStart, offset);
1052
+ }
1053
+ function patchSectionId(source, ast, sectionKey, edits, options) {
1054
+ const templateChildren = (ast.children ?? []).filter((c) => c.type !== "frontmatter");
1055
+ let rootElement = null;
1056
+ if (options?.mode === "page") {
1057
+ let findFirstHtmlElement2 = function(nodes) {
1058
+ for (const child of nodes) {
1059
+ if (!isElement(child)) continue;
1060
+ if (child.name && /^[a-z]/.test(child.name)) return child;
1061
+ const inner = findFirstHtmlElement2(child.children ?? []);
1062
+ if (inner) return inner;
1063
+ }
1064
+ return null;
1065
+ };
1066
+ var findFirstHtmlElement = findFirstHtmlElement2;
1067
+ rootElement = findFirstHtmlElement2(templateChildren);
1068
+ } else {
1069
+ for (const child of templateChildren) {
1070
+ if (isElement(child)) {
1071
+ rootElement = child;
1072
+ break;
1073
+ }
1074
+ }
1075
+ }
1076
+ if (!rootElement) return;
1077
+ const hasId = rootElement.attributes?.some((a) => a.name === "id");
1078
+ if (hasId) return;
1079
+ addAttributeToElement(source, rootElement, `id="section-${sectionKey}"`, edits);
1080
+ }
1081
+ function extractVarName(frontmatter) {
1082
+ const propsMatch = frontmatter.match(/const\s*\{\s*data\s*:\s*(\w+)\s*\}/);
1083
+ if (propsMatch) return propsMatch[1];
1084
+ const getSectionMatch = frontmatter.match(/(?:const|let)\s+(\w+)\s*=.*getSection/);
1085
+ if (getSectionMatch) return getSectionMatch[1];
1086
+ return null;
1087
+ }
1088
+ function isElement(node) {
1089
+ return (node.type === "element" || node.type === "component" || node.type === "custom-element") && Array.isArray(node.attributes);
1090
+ }
1091
+ function walkAst(node, callback, parent = null) {
1092
+ callback(node, parent);
1093
+ if (node.children) {
1094
+ for (const child of node.children) {
1095
+ walkAst(child, callback, node);
1096
+ }
1097
+ }
1098
+ }
1099
+ function findFirstChildElement(node) {
1100
+ if (!node.children) return null;
1101
+ for (const child of node.children) {
1102
+ if (isElement(child)) return child;
1103
+ const deeper = findFirstChildElement(child);
1104
+ if (deeper) return deeper;
1105
+ }
1106
+ return null;
1107
+ }
1108
+ function addAttributeToElement(source, element, attrStr, edits) {
1109
+ const approxStart = element.position?.start?.offset;
1110
+ if (approxStart == null) return;
1111
+ const tagName = element.name;
1112
+ if (!tagName) return;
1113
+ const searchStart = Math.max(0, approxStart - 10);
1114
+ const tagPattern = `<${tagName}`;
1115
+ const tagIdx = source.indexOf(tagPattern, searchStart);
1116
+ if (tagIdx === -1 || tagIdx > approxStart + 10) return;
1117
+ const insertOffset = findOpeningTagEnd(source, tagIdx);
1118
+ if (insertOffset === -1) return;
1119
+ edits.push({
1120
+ offset: insertOffset,
1121
+ deleteCount: 0,
1122
+ insert: ` ${attrStr}`
1123
+ });
1124
+ }
1125
+ function findOpeningTagEnd(source, startOffset) {
1126
+ let inQuote = null;
1127
+ let inExpr = 0;
1128
+ for (let i = startOffset; i < source.length; i++) {
1129
+ const ch = source[i];
1130
+ if (inQuote) {
1131
+ if (ch === inQuote && source[i - 1] !== "\\") inQuote = null;
1132
+ continue;
1133
+ }
1134
+ if (ch === "{") {
1135
+ inExpr++;
1136
+ continue;
1137
+ }
1138
+ if (ch === "}" && inExpr > 0) {
1139
+ inExpr--;
1140
+ continue;
1141
+ }
1142
+ if (inExpr > 0) continue;
1143
+ if (ch === '"' || ch === "'") {
1144
+ inQuote = ch;
1145
+ continue;
1146
+ }
1147
+ if (ch === ">") return i;
1148
+ }
1149
+ return -1;
1150
+ }
1151
+ function applyEdits(source, edits) {
1152
+ if (edits.length === 0) return source;
1153
+ const sorted = [...edits].sort((a, b) => b.offset - a.offset);
1154
+ let result = source;
1155
+ for (const edit of sorted) {
1156
+ result = result.slice(0, edit.offset) + edit.insert + result.slice(edit.offset + edit.deleteCount);
1157
+ }
1158
+ return result;
1159
+ }
1160
+ function removeOldVarDeclarations(source, fields, repeatedGroups, cmsVarName = "skData") {
1161
+ const fmStart = source.indexOf("---");
1162
+ if (fmStart === -1) return source;
1163
+ const fmEnd = source.indexOf("---", fmStart + 3);
1164
+ if (fmEnd === -1) return source;
1165
+ let frontmatter = source.slice(fmStart + 4, fmEnd);
1166
+ const fieldKeys = new Set(fields.map((f) => f.key));
1167
+ if (repeatedGroups) {
1168
+ for (const g of repeatedGroups) {
1169
+ for (const f of g.fields) {
1170
+ for (const pos of f.positions) {
1171
+ if (pos?.source) fieldKeys.add(pos.source);
1172
+ }
1173
+ }
1174
+ }
1175
+ }
1176
+ const removals = [];
1177
+ const aliases = [];
1178
+ const templatePart = source.slice(fmEnd + 3);
1179
+ for (const key of fieldKeys) {
1180
+ const varDeclRegex = new RegExp(`(?:const|let)\\s+${key}\\s*=\\s*`);
1181
+ const declMatch = varDeclRegex.exec(frontmatter);
1182
+ if (!declMatch) continue;
1183
+ const lineStart = frontmatter.lastIndexOf("\n", declMatch.index) + 1;
1184
+ const linePrefix = frontmatter.slice(lineStart, declMatch.index).trim();
1185
+ if (linePrefix === "export") continue;
1186
+ const declStart = declMatch.index;
1187
+ const afterEquals = declStart + declMatch[0].length;
1188
+ const rhsRaw = frontmatter.slice(afterEquals);
1189
+ const leadingWs = rhsRaw.length - rhsRaw.trimStart().length;
1190
+ const rhs = rhsRaw.trimStart();
1191
+ let valueEnd;
1192
+ if (rhs.startsWith("[") || rhs.startsWith("{")) {
1193
+ const open = rhs[0];
1194
+ const close = open === "[" ? "]" : "}";
1195
+ let depth = 1;
1196
+ let inStr = null;
1197
+ let i = 1;
1198
+ for (; i < rhs.length && depth > 0; i++) {
1199
+ const ch = rhs[i];
1200
+ if (inStr) {
1201
+ if (ch === inStr && rhs[i - 1] !== "\\") inStr = null;
1202
+ continue;
1203
+ }
1204
+ if (ch === "'" || ch === '"' || ch === "`") {
1205
+ inStr = ch;
1206
+ continue;
1207
+ }
1208
+ if (ch === open) depth++;
1209
+ if (ch === close) depth--;
1210
+ }
1211
+ valueEnd = i;
1212
+ } else {
1213
+ const lineEnd = rhs.indexOf("\n");
1214
+ valueEnd = lineEnd === -1 ? rhs.length : lineEnd;
1215
+ }
1216
+ let end = afterEquals + leadingWs + valueEnd;
1217
+ if (end < frontmatter.length && frontmatter[end] === ";") end++;
1218
+ if (end < frontmatter.length && frontmatter[end] === "\n") end++;
1219
+ const stillReferenced = new RegExp(`\\b${key}[.([\\[]`).test(templatePart);
1220
+ if (stillReferenced) {
1221
+ aliases.push(`const ${key} = ${cmsVarName}?.${key} ?? []`);
1222
+ }
1223
+ removals.push({ start: declStart, end });
1224
+ }
1225
+ if (removals.length === 0) return source;
1226
+ removals.sort((a, b) => b.start - a.start);
1227
+ for (const { start, end } of removals) {
1228
+ frontmatter = frontmatter.slice(0, start) + frontmatter.slice(end);
1229
+ }
1230
+ if (aliases.length > 0) {
1231
+ frontmatter = frontmatter.trimEnd() + "\n" + aliases.join("\n") + "\n";
1232
+ }
1233
+ frontmatter = frontmatter.replace(/\n{3,}/g, "\n\n");
1234
+ return source.slice(0, fmStart + 4) + frontmatter + source.slice(fmEnd);
1235
+ }
1236
+ function stripTemplateFallbacks(source) {
1237
+ const fallbacks = {};
1238
+ let result = source;
1239
+ result = result.replace(
1240
+ /set:html=\{(\w+)\?\.(\w+)\s*\?\?\s*(?:`([\s\S]*?)`|'([^']*)'|"([^"]*)")\}/g,
1241
+ (_match, varName, fieldKey, tq, sq, dq) => {
1242
+ const value = (tq ?? sq ?? dq ?? "").trim();
1243
+ fallbacks[fieldKey] = value;
1244
+ return `set:html={${varName}?.${fieldKey}}`;
1245
+ }
1246
+ );
1247
+ result = result.replace(
1248
+ /\((\w+)\?\.(\w+)\s*\?\?\s*\[([^\]]*)\]\)\.map\(/gs,
1249
+ (_match, varName, fieldKey, itemsRaw) => {
1250
+ const items = [];
1251
+ const itemRe = /`([^`]*)`|'([^']*)'|"([^"]*)"/g;
1252
+ let m;
1253
+ while ((m = itemRe.exec(itemsRaw)) !== null) {
1254
+ items.push(m[1] ?? m[2] ?? m[3] ?? "");
1255
+ }
1256
+ if (items.length > 0) fallbacks[fieldKey] = items;
1257
+ return `(${varName}?.${fieldKey} ?? []).map(`;
1258
+ }
1259
+ );
1260
+ return { source: result, fallbacks };
1261
+ }
1262
+
1263
+ export {
1264
+ patchTemplateForFields,
1265
+ convertToSetHtml,
1266
+ stripTemplateFallbacks
1267
+ };