@nuasite/cli 0.64.1 → 0.64.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -39590,6 +39590,311 @@ var init_snippet_utils = __esm(() => {
39590
39590
  CMS_PLACEHOLDER_PATTERN = /\{\{cms:[^}]+\}\}/;
39591
39591
  });
39592
39592
 
39593
+ // ../cms/src/handlers/element-span.ts
39594
+ function tagEnd(text, start) {
39595
+ let quote = null;
39596
+ let braces = 0;
39597
+ for (let i2 = start + 1;i2 < text.length; i2++) {
39598
+ const ch = text[i2];
39599
+ if (quote) {
39600
+ if (ch === quote)
39601
+ quote = null;
39602
+ continue;
39603
+ }
39604
+ if (braces > 0) {
39605
+ if (ch === "{")
39606
+ braces++;
39607
+ else if (ch === "}")
39608
+ braces--;
39609
+ else if (ch === '"' || ch === "'" || ch === "`")
39610
+ quote = ch;
39611
+ continue;
39612
+ }
39613
+ if (ch === '"' || ch === "'")
39614
+ quote = ch;
39615
+ else if (ch === "{")
39616
+ braces++;
39617
+ else if (ch === ">")
39618
+ return i2 + 1;
39619
+ }
39620
+ return -1;
39621
+ }
39622
+ function lineStart(text, line) {
39623
+ let offset = 0;
39624
+ for (let current2 = 1;current2 < line; current2++) {
39625
+ const next = text.indexOf(`
39626
+ `, offset);
39627
+ if (next < 0)
39628
+ return -1;
39629
+ offset = next + 1;
39630
+ }
39631
+ return offset;
39632
+ }
39633
+ function lineEnd(text, offset) {
39634
+ const next = text.indexOf(`
39635
+ `, offset);
39636
+ return next < 0 ? text.length : next;
39637
+ }
39638
+ function locateSnippet(content, snippet, line) {
39639
+ const first = content.indexOf(snippet);
39640
+ if (first < 0 || !line || line < 1)
39641
+ return first;
39642
+ const span = snippet.match(/\n/g)?.length ?? 0;
39643
+ let best = first;
39644
+ let bestDistance = Number.POSITIVE_INFINITY;
39645
+ let countedTo = 0;
39646
+ let startLine = 1;
39647
+ for (let at = first;at >= 0; at = content.indexOf(snippet, at + 1)) {
39648
+ for (let i2 = content.indexOf(`
39649
+ `, countedTo);i2 >= 0 && i2 < at; i2 = content.indexOf(`
39650
+ `, i2 + 1))
39651
+ startLine++;
39652
+ countedTo = at;
39653
+ const endLine = startLine + span;
39654
+ const distance = line < startLine ? startLine - line : line > endLine ? line - endLine : 0;
39655
+ if (distance < bestDistance) {
39656
+ best = at;
39657
+ bestDistance = distance;
39658
+ }
39659
+ if (distance === 0 || startLine > line)
39660
+ break;
39661
+ }
39662
+ return best;
39663
+ }
39664
+ function selfClosingSlash(text, start, end) {
39665
+ const slash = end - 2;
39666
+ if (slash <= start || text[slash] !== "/")
39667
+ return -1;
39668
+ const before = text[slash - 1];
39669
+ if (/[\s"'}]/.test(before))
39670
+ return slash;
39671
+ const head = text.slice(start, slash);
39672
+ const run = head.slice(Math.max(head.lastIndexOf(" "), head.lastIndexOf("\t"), head.lastIndexOf(`
39673
+ `)) + 1);
39674
+ return run.includes("=") ? -1 : slash;
39675
+ }
39676
+ function tokenize(text) {
39677
+ const tokens = [];
39678
+ const tagName = /^<(\/?)([a-zA-Z][\w:.-]*)/;
39679
+ let lower;
39680
+ const skipped = [];
39681
+ const fence = /^(?:\uFEFF)?---\r?\n[\s\S]*?\r?\n---(?=\r?\n|$)/.exec(text);
39682
+ if (fence)
39683
+ skipped.push([0, fence[0].length]);
39684
+ for (const comment of text.matchAll(/\{\s*\/\*[\s\S]*?\*\/\s*\}/g))
39685
+ skipped.push([comment.index, comment.index + comment[0].length]);
39686
+ let nextSkip = 0;
39687
+ let i2 = text.indexOf("<");
39688
+ while (i2 >= 0 && i2 < text.length) {
39689
+ while (nextSkip < skipped.length && skipped[nextSkip][1] <= i2)
39690
+ nextSkip++;
39691
+ const region = skipped[nextSkip];
39692
+ if (region && region[0] <= i2) {
39693
+ i2 = text.indexOf("<", region[1]);
39694
+ continue;
39695
+ }
39696
+ if (text.startsWith("<!--", i2)) {
39697
+ const close = text.indexOf("-->", i2 + 4);
39698
+ if (close < 0)
39699
+ break;
39700
+ i2 = text.indexOf("<", close + 3);
39701
+ continue;
39702
+ }
39703
+ const match = tagName.exec(text.slice(i2, i2 + 64));
39704
+ if (!match) {
39705
+ i2 = text.indexOf("<", i2 + 1);
39706
+ continue;
39707
+ }
39708
+ const end = tagEnd(text, i2);
39709
+ if (end < 0)
39710
+ break;
39711
+ const name = match[2];
39712
+ const closing = match[1] === "/";
39713
+ tokens.push({ start: i2, end, name, closing, selfClosing: !closing && selfClosingSlash(text, i2, end) >= 0 });
39714
+ i2 = end;
39715
+ if (!closing && RAW_TEXT_ELEMENTS.has(name)) {
39716
+ lower ??= text.toLowerCase();
39717
+ const close = lower.indexOf(`</${name.toLowerCase()}`, end);
39718
+ if (close < 0)
39719
+ break;
39720
+ i2 = close;
39721
+ continue;
39722
+ }
39723
+ i2 = text.indexOf("<", i2);
39724
+ }
39725
+ return tokens;
39726
+ }
39727
+ function findElements(text, from, to, tag) {
39728
+ const wanted = tag?.toLowerCase();
39729
+ return allElements(text).filter((e) => e.start >= from && e.start < to && (!wanted || e.tag === wanted));
39730
+ }
39731
+ function allElements(text) {
39732
+ if (text === lastText)
39733
+ return lastElements;
39734
+ const open = new Map;
39735
+ const found2 = [];
39736
+ for (const token of tokenize(text)) {
39737
+ const lower = token.name.toLowerCase();
39738
+ const key = token.name === lower ? lower : token.name;
39739
+ if (token.closing) {
39740
+ const opener = open.get(key)?.pop();
39741
+ if (opener) {
39742
+ found2.push({ start: opener.start, openEnd: opener.end, closeStart: token.start, end: token.end, tag: lower, name: opener.name, void: false });
39743
+ }
39744
+ continue;
39745
+ }
39746
+ if (token.selfClosing || token.name === lower && VOID_ELEMENTS.has(lower)) {
39747
+ found2.push({ start: token.start, openEnd: token.end, closeStart: token.end, end: token.end, tag: lower, name: token.name, void: true });
39748
+ continue;
39749
+ }
39750
+ if (!open.has(key))
39751
+ open.set(key, []);
39752
+ open.get(key).push(token);
39753
+ }
39754
+ lastText = text;
39755
+ lastElements = found2.sort((a, b2) => a.start - b2.start);
39756
+ return lastElements;
39757
+ }
39758
+ function decodeEntities(text) {
39759
+ return text.replace(/&(?:#x([0-9a-f]+)|#(\d+)|([a-z]+));/gi, (whole, hex, dec, name) => {
39760
+ const code = hex ? parseInt(hex, 16) : dec ? parseInt(dec, 10) : -1;
39761
+ if (code >= 0)
39762
+ return code <= 1114111 ? String.fromCodePoint(code) : whole;
39763
+ return NAMED_ENTITIES[name.toLowerCase()] ?? whole;
39764
+ });
39765
+ }
39766
+ function renderedCased(markup) {
39767
+ return decodeEntities(markup.replace(/<br\b[^>]*>/gi, " ").replace(/<!--[\s\S]*?-->/g, "").replace(/<[^>]+>/g, "").replace(/\\(['"])/g, "$1")).replace(/\u00AD/g, "").replace(/\s+/g, " ").trim();
39768
+ }
39769
+ function renderedText(innerMarkup) {
39770
+ return renderedCased(innerMarkup).toLowerCase();
39771
+ }
39772
+ function withHoles(text) {
39773
+ return renderedCased(text.replace(PLACEHOLDER, HOLE).replace(EXPRESSION, HOLE)).replace(new RegExp(`\\s*${HOLE}\\s*`, "g"), HOLE);
39774
+ }
39775
+ function matchesWithHoles(pattern, text) {
39776
+ if (!pattern.includes(HOLE))
39777
+ return pattern === text;
39778
+ const parts = pattern.split(HOLE);
39779
+ const first = parts[0];
39780
+ const last = parts[parts.length - 1];
39781
+ if (first.length + last.length > text.length || !text.startsWith(first) || !text.endsWith(last))
39782
+ return false;
39783
+ let at = first.length;
39784
+ const limit = text.length - last.length;
39785
+ for (const middle of parts.slice(1, -1)) {
39786
+ const found2 = text.indexOf(middle, at);
39787
+ if (found2 < 0 || found2 + middle.length > limit)
39788
+ return false;
39789
+ at = found2 + middle.length;
39790
+ }
39791
+ return true;
39792
+ }
39793
+ function textFit(innerMarkup, elementText) {
39794
+ const source = withHoles(innerMarkup);
39795
+ const wanted = withHoles(elementText);
39796
+ const fits = (a, b2) => matchesWithHoles(a, b2) || matchesWithHoles(b2, a);
39797
+ if (!fits(source.toLowerCase(), wanted.toLowerCase()))
39798
+ return 0;
39799
+ if (!source.replaceAll(HOLE, "").trim())
39800
+ return 1;
39801
+ return fits(source, wanted) ? 3 : 2;
39802
+ }
39803
+ function attributeValues(openingTag) {
39804
+ return [...openingTag.matchAll(/\s[\w:-]+\s*=\s*(?:"([^"]*)"|'([^']*)')/g)].map((m2) => decodeEntities(m2[1] ?? m2[2] ?? ""));
39805
+ }
39806
+ function candidateFit(text, candidate, elementText) {
39807
+ if (candidate.void)
39808
+ return attributeValues(text.slice(candidate.start, candidate.openEnd)).includes(elementText.trim()) ? 3 : 0;
39809
+ return textFit(text.slice(candidate.openEnd, candidate.closeStart), elementText);
39810
+ }
39811
+ function pickElement(text, candidates, elementText, rivals) {
39812
+ const known = !!elementText && !!renderedText(elementText.replace(PLACEHOLDER, ""));
39813
+ if (!known)
39814
+ return candidates.length === 1 ? candidates[0] : null;
39815
+ const fits = candidates.map((c) => ({ candidate: c, fit: candidateFit(text, c, elementText) }));
39816
+ const best = Math.max(0, ...fits.map((f2) => f2.fit));
39817
+ const matching = fits.filter((f2) => f2.fit === best && best > 0);
39818
+ if (matching.length !== 1)
39819
+ return null;
39820
+ const picked = matching[0].candidate;
39821
+ const from = rivals?.from ?? 0;
39822
+ const to = rivals?.to ?? text.length;
39823
+ const components = findElements(text, from, to).filter((c) => c.name !== c.tag && c.start !== picked.start);
39824
+ if (components.some((c) => candidateFit(text, c, elementText) >= best))
39825
+ return null;
39826
+ const wanted = withHoles(elementText).toLowerCase();
39827
+ for (const link of text.slice(from, to).matchAll(/\[([^\]\n]+)\]\(/g)) {
39828
+ if (withHoles(link[1]).toLowerCase() === wanted)
39829
+ return null;
39830
+ }
39831
+ return picked;
39832
+ }
39833
+ function elementOnLine(content, line, tag, elementText) {
39834
+ if (!tag)
39835
+ return null;
39836
+ const from = lineStart(content, line);
39837
+ if (from < 0)
39838
+ return null;
39839
+ const to = lineEnd(content, from);
39840
+ const candidates = findElements(content, from, to, tag);
39841
+ const texts = Array.isArray(elementText) ? elementText : [elementText];
39842
+ for (const text of texts) {
39843
+ const picked = pickElement(content, candidates, text, { from, to });
39844
+ if (picked)
39845
+ return picked;
39846
+ }
39847
+ return null;
39848
+ }
39849
+ var VOID_ELEMENTS, RAW_TEXT_ELEMENTS, lastText, lastElements, NAMED_ENTITIES, PLACEHOLDER, EXPRESSION, HOLE = "\x00";
39850
+ var init_element_span = __esm(() => {
39851
+ VOID_ELEMENTS = new Set([
39852
+ "area",
39853
+ "base",
39854
+ "br",
39855
+ "col",
39856
+ "embed",
39857
+ "hr",
39858
+ "img",
39859
+ "input",
39860
+ "link",
39861
+ "meta",
39862
+ "param",
39863
+ "source",
39864
+ "track",
39865
+ "wbr"
39866
+ ]);
39867
+ RAW_TEXT_ELEMENTS = new Set(["script", "style", "textarea", "title"]);
39868
+ lastElements = [];
39869
+ NAMED_ENTITIES = {
39870
+ amp: "&",
39871
+ lt: "<",
39872
+ gt: ">",
39873
+ quot: '"',
39874
+ apos: "'",
39875
+ nbsp: "\xA0",
39876
+ ndash: "\u2013",
39877
+ mdash: "\u2014",
39878
+ hellip: "\u2026",
39879
+ laquo: "\xAB",
39880
+ raquo: "\xBB",
39881
+ lsquo: "\u2018",
39882
+ rsquo: "\u2019",
39883
+ ldquo: "\u201C",
39884
+ rdquo: "\u201D",
39885
+ bdquo: "\u201E",
39886
+ sbquo: "\u201A",
39887
+ shy: "\xAD",
39888
+ copy: "\xA9",
39889
+ reg: "\xAE",
39890
+ trade: "\u2122",
39891
+ times: "\xD7",
39892
+ euro: "\u20AC"
39893
+ };
39894
+ PLACEHOLDER = /\{\{cms:[^}]+\}\}/g;
39895
+ EXPRESSION = /\{[^{}]*\}/g;
39896
+ });
39897
+
39593
39898
  // ../cms/src/handlers/source-writer.ts
39594
39899
  import path21 from "path";
39595
39900
  async function handleUpdate(request, manifestWriter) {
@@ -39651,7 +39956,16 @@ async function applyChanges(content, changes, manifest, absFilePath, originUrl)
39651
39956
  let appliedCount = 0;
39652
39957
  const failedChanges = [];
39653
39958
  const fileOps = [];
39654
- const sortedChanges = [...changes].sort((a, b2) => (b2.sourceLine ?? 0) - (a.sourceLine ?? 0));
39959
+ const isText = (change) => !change.imageChange && !change.styleChange && !change.attributeChanges?.length;
39960
+ const sortedChanges = [...changes].sort((a, b2) => (b2.sourceLine ?? 0) - (a.sourceLine ?? 0) || Number(isText(b2)) - Number(isText(a)));
39961
+ const newTextOf = new Map(changes.filter(isText).map((change) => [change.cmsId, change.newValue]));
39962
+ const identityOf = (cmsId) => {
39963
+ const entry = manifest.entries[cmsId];
39964
+ if (!entry)
39965
+ return;
39966
+ const edited = newTextOf.get(cmsId);
39967
+ return { tag: entry.tag, text: edited !== undefined ? [entry.text, edited] : entry.text };
39968
+ };
39655
39969
  for (const change of sortedChanges) {
39656
39970
  if (change.imageChange) {
39657
39971
  const result2 = await applyImageChange(newContent, change, absFilePath, originUrl);
@@ -39666,7 +39980,7 @@ async function applyChanges(content, changes, manifest, absFilePath, originUrl)
39666
39980
  continue;
39667
39981
  }
39668
39982
  if (change.styleChange) {
39669
- const result2 = applyColorChange(newContent, change);
39983
+ const result2 = applyColorChange(newContent, change, identityOf(change.cmsId));
39670
39984
  if (result2.success) {
39671
39985
  newContent = result2.content;
39672
39986
  appliedCount++;
@@ -39676,7 +39990,7 @@ async function applyChanges(content, changes, manifest, absFilePath, originUrl)
39676
39990
  continue;
39677
39991
  }
39678
39992
  if (change.attributeChanges && change.attributeChanges.length > 0) {
39679
- const result2 = applyAttributeChanges(newContent, change);
39993
+ const result2 = applyAttributeChanges(newContent, change, identityOf(change.cmsId));
39680
39994
  if (result2.appliedCount > 0) {
39681
39995
  newContent = result2.content;
39682
39996
  appliedCount++;
@@ -39736,19 +40050,12 @@ async function applyImageChange(content, change, absFilePath, originUrl) {
39736
40050
  let newContent = content;
39737
40051
  let replacedIndex = -1;
39738
40052
  for (const srcToFind of srcCandidates) {
39739
- const srcPatternDouble = new RegExp(`src="${escapeRegex(srcToFind)}"`);
39740
- const srcPatternSingle = new RegExp(`src='${escapeRegex(srcToFind)}'`);
39741
- const escapedNewSrc = escapeReplacement(newSrc);
39742
- const doubleMatch = newContent.match(srcPatternDouble);
39743
- if (doubleMatch && doubleMatch.index !== undefined) {
39744
- replacedIndex = doubleMatch.index;
39745
- newContent = newContent.slice(0, replacedIndex) + newContent.slice(replacedIndex).replace(srcPatternDouble, `src="${escapedNewSrc}"`);
39746
- break;
39747
- }
39748
- const singleMatch = newContent.match(srcPatternSingle);
39749
- if (singleMatch && singleMatch.index !== undefined) {
39750
- replacedIndex = singleMatch.index;
39751
- newContent = newContent.slice(0, replacedIndex) + newContent.slice(replacedIndex).replace(srcPatternSingle, `src='${escapedNewSrc}'`);
40053
+ const escaped = escapeRegex(srcToFind);
40054
+ const match = nearestMatch(newContent, new RegExp(`(?<![\\w:-])(src\\s*=\\s*)(["'])${escaped}\\2`, "g"), change.sourceLine) ?? nearestMatch(newContent, new RegExp(`(src\\s*=\\s*)(["'])${escaped}\\2`, "g"), change.sourceLine);
40055
+ if (match) {
40056
+ const quote = match[2];
40057
+ replacedIndex = match.index;
40058
+ newContent = newContent.slice(0, replacedIndex) + `${match[1]}${quote}${newSrc}${quote}` + newContent.slice(replacedIndex + match[0].length);
39752
40059
  break;
39753
40060
  }
39754
40061
  }
@@ -39791,13 +40098,16 @@ async function applyImageChange(content, change, absFilePath, originUrl) {
39791
40098
  const lines = newContent.split(`
39792
40099
  `);
39793
40100
  const targetLineIdx = change.sourceLine - 1;
39794
- const regionStart = Math.max(0, targetLineIdx - 3);
39795
40101
  const regionEnd = Math.min(lines.length, targetLineIdx + 10);
39796
- const regionLines = lines.slice(regionStart, regionEnd);
39797
- const regionText = regionLines.join(`
40102
+ const regionAt = (start) => {
40103
+ const text = lines.slice(start, regionEnd).join(`
39798
40104
  `);
39799
- if (/<img\b/i.test(regionText) || /<Image\b/.test(regionText)) {
39800
- const exprMatch = findExpressionSrcAttribute(regionText);
40105
+ return { start, text, expr: findExpressionSrcAttribute(text) };
40106
+ };
40107
+ const wide = regionAt(Math.max(0, targetLineIdx - 3));
40108
+ const tight = regionAt(Math.max(0, targetLineIdx));
40109
+ const { start: regionStart, text: regionText, expr: exprMatch } = tight.expr ? tight : wide;
40110
+ if (/<img\b/i.test(wide.text) || /<Image\b/.test(wide.text)) {
39801
40111
  if (exprMatch) {
39802
40112
  const exprContent = regionText.slice(exprMatch.index + regionText.slice(exprMatch.index).indexOf("{") + 1, exprMatch.index + exprMatch.length - 1).trim();
39803
40113
  const importInfo = /^\w+$/.test(exprContent) ? findFrontmatterAssetImport(content, exprContent) : null;
@@ -39821,14 +40131,16 @@ async function applyImageChange(content, change, absFilePath, originUrl) {
39821
40131
  return { success: false, error: `Image src not found in source file: ${originalSrc}` };
39822
40132
  }
39823
40133
  if (newAlt !== undefined) {
39824
- const searchStart = Math.max(0, replacedIndex - 200);
39825
- const searchEnd = Math.min(newContent.length, replacedIndex + 300);
40134
+ const srcInTag = /^src\s*=/.test(newContent.slice(replacedIndex, replacedIndex + 8));
40135
+ const ownTag = (srcInTag ? enclosingTag(newContent, replacedIndex) : null) ?? imageTagOnLine(newContent, change.sourceLine);
40136
+ const searchStart = ownTag ? ownTag.start : Math.max(0, replacedIndex - 200);
40137
+ const searchEnd = ownTag ? ownTag.end : Math.min(newContent.length, replacedIndex + 300);
39826
40138
  const region = newContent.slice(searchStart, searchEnd);
39827
40139
  let altIndex = -1;
39828
40140
  let altLength = 0;
39829
40141
  let altQuote = '"';
39830
- const altPatternDouble = /alt="[^"]*"/;
39831
- const altPatternSingle = /alt='[^']*'/;
40142
+ const altPatternDouble = /(?<![\w:-])alt="[^"]*"/;
40143
+ const altPatternSingle = /(?<![\w:-])alt='[^']*'/;
39832
40144
  const altDoubleMatch = region.match(altPatternDouble);
39833
40145
  const altSingleMatch = region.match(altPatternSingle);
39834
40146
  if (altDoubleMatch && altDoubleMatch.index !== undefined) {
@@ -39847,14 +40159,53 @@ async function applyImageChange(content, change, absFilePath, originUrl) {
39847
40159
  altQuote = '"';
39848
40160
  }
39849
40161
  }
40162
+ const escapedAlt = altQuote === '"' ? newAlt.replace(/"/g, "&quot;") : newAlt.replace(/'/g, "&#39;");
39850
40163
  if (altIndex >= 0) {
39851
40164
  const altAbsoluteIndex = searchStart + altIndex;
39852
- const escapedAlt = altQuote === '"' ? newAlt.replace(/"/g, "&quot;") : newAlt.replace(/'/g, "&#39;");
39853
40165
  newContent = newContent.slice(0, altAbsoluteIndex) + `alt=${altQuote}${escapedAlt}${altQuote}` + newContent.slice(altAbsoluteIndex + altLength);
40166
+ } else if (ownTag && newAlt && IMAGE_TAG.test(ownTag.name)) {
40167
+ newContent = insertAttribute(newContent, ownTag.start, ownTag.end, `alt="${escapedAlt}"`);
39854
40168
  }
39855
40169
  }
39856
40170
  return { success: true, content: newContent, fileOp: pendingFileOp };
39857
40171
  }
40172
+ function nearestMatch(content, pattern, line) {
40173
+ pattern.lastIndex = 0;
40174
+ let best = null;
40175
+ let bestDistance = Number.POSITIVE_INFINITY;
40176
+ let matchLine = 1;
40177
+ let countedTo = 0;
40178
+ for (let match = pattern.exec(content);match; match = pattern.exec(content)) {
40179
+ if (!line || line < 1)
40180
+ return match;
40181
+ for (let i2 = content.indexOf(`
40182
+ `, countedTo);i2 >= 0 && i2 < match.index; i2 = content.indexOf(`
40183
+ `, i2 + 1))
40184
+ matchLine++;
40185
+ countedTo = match.index;
40186
+ const distance = Math.abs(matchLine - line);
40187
+ if (distance < bestDistance) {
40188
+ best = match;
40189
+ bestDistance = distance;
40190
+ }
40191
+ if (distance === 0 || matchLine > line)
40192
+ break;
40193
+ }
40194
+ return best;
40195
+ }
40196
+ function enclosingTag(content, offset) {
40197
+ const element = findElements(content, 0, offset + 1).filter((e) => e.start <= offset && offset < e.openEnd).pop();
40198
+ return element ? { start: element.start, end: element.openEnd, name: element.name } : null;
40199
+ }
40200
+ function imageTagOnLine(content, line) {
40201
+ if (!line || line < 1)
40202
+ return null;
40203
+ const from = lineStart(content, line);
40204
+ if (from < 0)
40205
+ return null;
40206
+ const images = findElements(content, from, lineEnd(content, from)).filter((e) => IMAGE_TAG.test(e.name));
40207
+ return images.length === 1 ? { start: images[0].start, end: images[0].openEnd, name: images[0].name } : null;
40208
+ }
39858
40209
  function findFrontmatterAssetImport(content, varName) {
39859
40210
  const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
39860
40211
  if (!fmMatch)
@@ -39885,7 +40236,7 @@ function inlineJsxLiteralReplace(content, lines, regionStart, exprMatch, newSrc)
39885
40236
  regionStartOffset += lines[i2].length + 1;
39886
40237
  const absIndex = regionStartOffset + exprMatch.index;
39887
40238
  return {
39888
- content: content.slice(0, absIndex) + `src="${escapeReplacement(newSrc)}"` + content.slice(absIndex + exprMatch.length),
40239
+ content: content.slice(0, absIndex) + `src="${newSrc}"` + content.slice(absIndex + exprMatch.length),
39889
40240
  replacedIndex: absIndex
39890
40241
  };
39891
40242
  }
@@ -39949,103 +40300,93 @@ async function readBoundedBody(res, maxBytes) {
39949
40300
  }
39950
40301
  return Buffer.concat(chunks, total);
39951
40302
  }
39952
- function applyColorChange(content, change) {
40303
+ function applyColorChange(content, change, entry) {
39953
40304
  const { oldClass, newClass } = change.styleChange;
39954
40305
  const sourceLine = change.styleChange.sourceLine ?? change.sourceLine;
39955
40306
  if (!oldClass) {
39956
- return appendClassToAttribute(content, newClass, sourceLine);
39957
- }
39958
- return replaceClassInAttribute(content, oldClass, newClass, sourceLine);
39959
- }
39960
- function replaceClassInAttribute(content, oldClass, newClass, sourceLine) {
39961
- const replaceOnLine = (line) => {
39962
- const dqMatch = line.match(/(class\s*=\s*)(")([^"]*)"/);
39963
- const sqMatch = line.match(/(class\s*=\s*)(')([^']*)'/);
39964
- const match = dqMatch || sqMatch;
39965
- if (!match)
39966
- return null;
39967
- const prefix = match[1];
39968
- const quote = match[2];
39969
- const classContent = match[3];
39970
- const classes = classContent.split(/\s+/).filter(Boolean);
39971
- const idx = classes.indexOf(oldClass);
39972
- if (idx === -1)
39973
- return null;
39974
- if (newClass) {
39975
- classes[idx] = newClass;
39976
- } else {
39977
- classes.splice(idx, 1);
40307
+ return appendClassToAttribute(content, newClass, sourceLine, entry);
40308
+ }
40309
+ return replaceClassInAttribute(content, oldClass, newClass, sourceLine, entry);
40310
+ }
40311
+ function classAttributes(text, from, to) {
40312
+ const found2 = [];
40313
+ const pattern = /(?<![\w:-])(class\s*=\s*)(?:"([^"]*)"|'([^']*)')/g;
40314
+ const region = text.slice(from, to);
40315
+ for (let match = pattern.exec(region);match; match = pattern.exec(region)) {
40316
+ const quote = match[2] !== undefined ? '"' : "'";
40317
+ found2.push({ index: from + match.index, prefix: match[1], quote, value: match[2] ?? match[3], length: match[0].length });
40318
+ }
40319
+ return found2;
40320
+ }
40321
+ function rewriteClassAttribute(content, attr, classes) {
40322
+ return content.slice(0, attr.index) + `${attr.prefix}${attr.quote}${classes.join(" ")}${attr.quote}` + content.slice(attr.index + attr.length);
40323
+ }
40324
+ function insertAttribute(content, tagStart, openEnd, attribute) {
40325
+ const slash = selfClosingSlash(content, tagStart, openEnd);
40326
+ let at = slash >= 0 ? slash : openEnd - 1;
40327
+ while (at > tagStart && /\s/.test(content[at - 1]))
40328
+ at--;
40329
+ return content.slice(0, at) + ` ${attribute}` + content.slice(at);
40330
+ }
40331
+ function replaceClassInAttribute(content, oldClass, newClass, sourceLine, entry) {
40332
+ const replaceIn = (from, to) => {
40333
+ for (const attr of classAttributes(content, from, to)) {
40334
+ const classes = attr.value.split(/\s+/).filter(Boolean);
40335
+ const idx = classes.indexOf(oldClass);
40336
+ if (idx === -1)
40337
+ continue;
40338
+ if (newClass) {
40339
+ classes[idx] = newClass;
40340
+ } else {
40341
+ classes.splice(idx, 1);
40342
+ }
40343
+ return rewriteClassAttribute(content, attr, classes);
39978
40344
  }
39979
- return line.replace(match[0], `${prefix}${quote}${classes.join(" ")}${quote}`);
40345
+ return null;
39980
40346
  };
39981
40347
  if (sourceLine) {
39982
- const lines2 = content.split(`
39983
- `);
39984
- const lineIndex = sourceLine - 1;
39985
- if (lineIndex >= 0 && lineIndex < lines2.length) {
39986
- const result = replaceOnLine(lines2[lineIndex]);
39987
- if (result !== null) {
39988
- lines2[lineIndex] = result;
39989
- return { success: true, content: lines2.join(`
39990
- `) };
39991
- }
39992
- return { success: false, error: `Color class '${oldClass}' not found on line ${sourceLine}` };
39993
- }
39994
- return { success: false, error: `Invalid source line ${sourceLine}` };
39995
- }
39996
- const lines = content.split(`
39997
- `);
39998
- for (let i2 = 0;i2 < lines.length; i2++) {
39999
- const result = replaceOnLine(lines[i2]);
40000
- if (result !== null) {
40001
- lines[i2] = result;
40002
- return { success: true, content: lines.join(`
40003
- `) };
40004
- }
40005
- }
40348
+ const from = lineStart(content, sourceLine);
40349
+ if (from < 0)
40350
+ return { success: false, error: `Invalid source line ${sourceLine}` };
40351
+ const element = elementOnLine(content, sourceLine, entry?.tag, entry?.text);
40352
+ const result2 = element ? replaceIn(element.start, element.openEnd) : replaceIn(from, lineEnd(content, from));
40353
+ if (result2 !== null)
40354
+ return { success: true, content: result2 };
40355
+ return { success: false, error: `Color class '${oldClass}' not found on line ${sourceLine}` };
40356
+ }
40357
+ const result = replaceIn(0, content.length);
40358
+ if (result !== null)
40359
+ return { success: true, content: result };
40006
40360
  return { success: false, error: `Color class '${oldClass}' not found in source file` };
40007
40361
  }
40008
- function appendClassToAttribute(content, newClass, sourceLine) {
40009
- const matchClassAttr = (line) => {
40010
- return line.match(/(class\s*=\s*")(([^"]*))(")/) || line.match(/(class\s*=\s*')(([^']*))(')/);
40011
- };
40012
- const doAppendOnLine = (line) => {
40013
- const match = matchClassAttr(line);
40014
- if (!match)
40015
- return null;
40016
- const open = match[1];
40017
- const classes = match[2];
40018
- const close = match[4];
40019
- const trimmed = classes.trimEnd();
40020
- const separator = trimmed ? " " : "";
40021
- const replacement = `${open}${trimmed}${separator}${escapeReplacement(newClass)}${close}`;
40022
- return line.replace(match[0], replacement);
40362
+ function appendClassToAttribute(content, newClass, sourceLine, entry) {
40363
+ const appendTo = (attr) => {
40364
+ const kept = attr.value.trimEnd();
40365
+ return rewriteClassAttribute(content, attr, kept ? [kept, newClass] : [newClass]);
40023
40366
  };
40024
40367
  if (sourceLine) {
40025
- const lines2 = content.split(`
40026
- `);
40027
- const lineIndex = sourceLine - 1;
40028
- if (lineIndex >= 0 && lineIndex < lines2.length) {
40029
- const result = doAppendOnLine(lines2[lineIndex]);
40030
- if (result !== null) {
40031
- lines2[lineIndex] = result;
40032
- return { success: true, content: lines2.join(`
40033
- `) };
40034
- }
40035
- return { success: false, error: `No class attribute found on line ${sourceLine}` };
40036
- }
40037
- return { success: false, error: `Invalid source line ${sourceLine}` };
40038
- }
40039
- const lines = content.split(`
40040
- `);
40041
- for (let i2 = 0;i2 < lines.length; i2++) {
40042
- const result = doAppendOnLine(lines[i2]);
40043
- if (result !== null) {
40044
- lines[i2] = result;
40045
- return { success: true, content: lines.join(`
40046
- `) };
40047
- }
40048
- }
40368
+ const from = lineStart(content, sourceLine);
40369
+ if (from < 0)
40370
+ return { success: false, error: `Invalid source line ${sourceLine}` };
40371
+ const element = elementOnLine(content, sourceLine, entry?.tag, entry?.text);
40372
+ if (element) {
40373
+ const [own] = classAttributes(content, element.start, element.openEnd);
40374
+ if (own)
40375
+ return { success: true, content: appendTo(own) };
40376
+ const openingTag = content.slice(element.start, element.openEnd);
40377
+ if (/(?<![\w-])class(?::list)?\s*=/.test(openingTag)) {
40378
+ return { success: false, error: `The class of this element is an expression on line ${sourceLine}; edit it in the source` };
40379
+ }
40380
+ return { success: true, content: insertAttribute(content, element.start, element.openEnd, `class="${newClass}"`) };
40381
+ }
40382
+ const [first2] = classAttributes(content, from, lineEnd(content, from));
40383
+ if (first2)
40384
+ return { success: true, content: appendTo(first2) };
40385
+ return { success: false, error: `No class attribute found on line ${sourceLine}` };
40386
+ }
40387
+ const [first] = classAttributes(content, 0, content.length);
40388
+ if (first)
40389
+ return { success: true, content: appendTo(first) };
40049
40390
  return { success: false, error: "No class attribute found in source file" };
40050
40391
  }
40051
40392
  function replaceLiteralInSnippet(content, snippet, oldValue, newValue) {
@@ -40063,7 +40404,7 @@ function replaceLiteralInSnippet(content, snippet, oldValue, newValue) {
40063
40404
  }
40064
40405
  return;
40065
40406
  }
40066
- function applyAttributeChanges(content, change) {
40407
+ function applyAttributeChanges(content, change, entry) {
40067
40408
  let newContent = content;
40068
40409
  let attrApplied = 0;
40069
40410
  const failedChanges = [];
@@ -40083,15 +40424,22 @@ function applyAttributeChanges(content, change) {
40083
40424
  const lineIndex = targetLine - 1;
40084
40425
  if (lineIndex >= 0 && lineIndex < lines.length) {
40085
40426
  const line = lines[lineIndex];
40086
- const doubleQuotePattern = new RegExp(`(${escapeRegex(attributeName)}\\s*=\\s*)"(${escapeRegex(attrOldValue)})"`);
40087
- const singleQuotePattern = new RegExp(`(${escapeRegex(attributeName)}\\s*=\\s*)'(${escapeRegex(attrOldValue)})'`);
40427
+ const doubleQuotePattern = new RegExp(`(?<![\\w:-])(${escapeRegex(attributeName)}\\s*=\\s*)"(${escapeRegex(attrOldValue)})"`);
40428
+ const singleQuotePattern = new RegExp(`(?<![\\w:-])(${escapeRegex(attributeName)}\\s*=\\s*)'(${escapeRegex(attrOldValue)})'`);
40088
40429
  const safeNewValue = escapeReplacement(attrNewValue);
40089
- if (doubleQuotePattern.test(line)) {
40430
+ const element = elementOnLine(newContent, targetLine, entry?.tag, entry?.text);
40431
+ const openingTag = element ? newContent.slice(element.start, element.openEnd) : "";
40432
+ const ownPattern = doubleQuotePattern.test(openingTag) ? doubleQuotePattern : singleQuotePattern.test(openingTag) ? singleQuotePattern : null;
40433
+ if (element && ownPattern) {
40434
+ const quote = ownPattern === doubleQuotePattern ? '"' : "'";
40435
+ newContent = newContent.slice(0, element.start) + openingTag.replace(ownPattern, `$1${quote}${safeNewValue}${quote}`) + newContent.slice(element.openEnd);
40436
+ attrApplied++;
40437
+ } else if (!element && doubleQuotePattern.test(line)) {
40090
40438
  lines[lineIndex] = line.replace(doubleQuotePattern, `$1"${safeNewValue}"`);
40091
40439
  newContent = lines.join(`
40092
40440
  `);
40093
40441
  attrApplied++;
40094
- } else if (singleQuotePattern.test(line)) {
40442
+ } else if (!element && singleQuotePattern.test(line)) {
40095
40443
  lines[lineIndex] = line.replace(singleQuotePattern, `$1'${safeNewValue}'`);
40096
40444
  newContent = lines.join(`
40097
40445
  `);
@@ -40123,8 +40471,8 @@ function applyAttributeChanges(content, change) {
40123
40471
  });
40124
40472
  }
40125
40473
  } else {
40126
- const doubleQuotePattern = new RegExp(`(${escapeRegex(attributeName)}\\s*=\\s*)"(${escapeRegex(attrOldValue)})"`);
40127
- const singleQuotePattern = new RegExp(`(${escapeRegex(attributeName)}\\s*=\\s*)'(${escapeRegex(attrOldValue)})'`);
40474
+ const doubleQuotePattern = new RegExp(`(?<![\\w:-])(${escapeRegex(attributeName)}\\s*=\\s*)"(${escapeRegex(attrOldValue)})"`);
40475
+ const singleQuotePattern = new RegExp(`(?<![\\w:-])(${escapeRegex(attributeName)}\\s*=\\s*)'(${escapeRegex(attrOldValue)})'`);
40128
40476
  const safeNewValue = escapeReplacement(attrNewValue);
40129
40477
  if (doubleQuotePattern.test(newContent)) {
40130
40478
  newContent = newContent.replace(doubleQuotePattern, `$1"${safeNewValue}"`);
@@ -40143,28 +40491,49 @@ function applyAttributeChanges(content, change) {
40143
40491
  return { content: newContent, appliedCount: attrApplied, failedChanges };
40144
40492
  }
40145
40493
  function applyTextChange(content, change, manifest) {
40146
- const { sourceSnippet, originalValue, newValue, htmlValue } = change;
40147
- if (!sourceSnippet || !originalValue) {
40494
+ const { sourceSnippet: recordedSnippet, originalValue, newValue, htmlValue } = change;
40495
+ if (!recordedSnippet || !originalValue) {
40148
40496
  if (change.attributeChanges && change.attributeChanges.length > 0) {
40149
40497
  return { success: true, content };
40150
40498
  }
40151
40499
  return { success: false, error: "Missing sourceSnippet or originalValue in change payload" };
40152
40500
  }
40153
- if (!content.includes(sourceSnippet)) {
40154
- return { success: false, error: "Source snippet not found in file" };
40155
- }
40156
40501
  const entry = manifest.entries[change.cmsId];
40157
- const insideYaml = isYamlValueSource(content, sourceSnippet, change.sourcePath);
40502
+ const pickIn = (snippet) => entry?.tag ? pickElement(snippet, findElements(snippet, 0, snippet.length, entry.tag), entry.text) : null;
40503
+ let lineSnippet = recordedSnippet;
40504
+ let lineSnippetAt = locateSnippet(content, lineSnippet, change.sourceLine);
40505
+ if (lineSnippetAt < 0) {
40506
+ const stale = pickIn(lineSnippet);
40507
+ const elementSource = stale ? lineSnippet.slice(stale.start, stale.end) : "";
40508
+ const at = elementSource ? locateSnippet(content, elementSource, change.sourceLine) : -1;
40509
+ if (at < 0) {
40510
+ return { success: false, error: "Source snippet not found in file" };
40511
+ }
40512
+ lineSnippet = elementSource;
40513
+ lineSnippetAt = at;
40514
+ }
40515
+ const insideYaml = isYamlValueSource(content, lineSnippet, change.sourcePath, lineSnippetAt);
40516
+ const snippetIsJavaScript = isJavaScriptSource(change.sourcePath) && (!!entry?.variableName || !looksLikeMarkup(lineSnippet));
40517
+ const element = !insideYaml && !snippetIsJavaScript ? pickIn(lineSnippet) : null;
40518
+ const sourceSnippet = element ? lineSnippet.slice(element.start, element.end) : lineSnippet;
40519
+ const snippetAt = lineSnippetAt + (element?.start ?? 0);
40520
+ const innerFrom = element && !element.void ? element.openEnd - element.start : 0;
40521
+ const innerTo = element && !element.void ? element.closeStart - element.start : sourceSnippet.length;
40522
+ const replaceInText = (search, replacement) => {
40523
+ const at = sourceSnippet.indexOf(search, innerFrom);
40524
+ if (at < 0 || at + search.length > innerTo)
40525
+ return sourceSnippet;
40526
+ return sourceSnippet.slice(0, at) + replacement + sourceSnippet.slice(at + search.length);
40527
+ };
40158
40528
  const stylingAllowed = entry?.allowStyling !== false && !insideYaml;
40159
40529
  const newText = stylingAllowed ? htmlValue ?? newValue : newValue;
40160
40530
  const placeholderPattern = /\{\{cms:[^}]+\}\}/g;
40161
40531
  if (placeholderPattern.test(originalValue)) {
40162
- return applyTextChangeWithPlaceholders(insideYaml, content, sourceSnippet, originalValue, newText);
40532
+ return applyTextChangeWithPlaceholders(insideYaml, content, snippetAt, sourceSnippet, originalValue, newText, innerFrom, innerTo);
40163
40533
  }
40164
40534
  const resolvedNewText = resolveCmsPlaceholders(newText, manifest);
40165
40535
  const resolvedOriginal = resolveCmsPlaceholders(originalValue, manifest);
40166
- const write = (updatedSnippet2) => writeSnippet(content, sourceSnippet, updatedSnippet2, insideYaml, resolvedNewText);
40167
- const snippetIsJavaScript = isJavaScriptSource(change.sourcePath) && (!!entry?.variableName || !looksLikeMarkup(sourceSnippet));
40536
+ const write = (updatedSnippet2) => writeSnippet(content, snippetAt, sourceSnippet, updatedSnippet2, insideYaml, resolvedNewText);
40168
40537
  if (snippetIsJavaScript) {
40169
40538
  const literalResult = tryJsStringLiteralChange(sourceSnippet, resolvedOriginal, resolvedNewText);
40170
40539
  if (literalResult !== null) {
@@ -40177,7 +40546,7 @@ function applyTextChange(content, change, manifest) {
40177
40546
  return write(frontmatterResult);
40178
40547
  }
40179
40548
  }
40180
- const updatedSnippet = sourceSnippet.replace(resolvedOriginal, escapeReplacement(resolvedNewText));
40549
+ const updatedSnippet = replaceInText(resolvedOriginal, resolvedNewText);
40181
40550
  if (updatedSnippet === sourceSnippet) {
40182
40551
  const brResult = tryBrNormalizedChange(sourceSnippet, resolvedOriginal, resolvedNewText);
40183
40552
  if (brResult !== null) {
@@ -40189,31 +40558,30 @@ function applyTextChange(content, change, manifest) {
40189
40558
  return write(literalResult);
40190
40559
  }
40191
40560
  }
40192
- const matchedText = findTextInSnippet(sourceSnippet, resolvedOriginal);
40561
+ const matchedText = findTextInSnippet(sourceSnippet.slice(innerFrom, innerTo), resolvedOriginal);
40193
40562
  if (matchedText) {
40194
40563
  const spliced = /<[^>]+>/.test(resolvedNewText) ? null : spliceIntoEntitySource(matchedText, resolvedOriginal, resolvedNewText);
40195
40564
  const replacement = spliced ?? (/<[^>]+>/.test(resolvedNewText) ? encodeNbspLike(resolvedNewText, matchedText) : encodeEntitiesLike(resolvedNewText, matchedText));
40196
- const updatedWithEntity = sourceSnippet.replace(matchedText, escapeReplacement(replacement));
40565
+ const updatedWithEntity = replaceInText(matchedText, replacement);
40197
40566
  return write(updatedWithEntity);
40198
40567
  }
40199
- const innerMatch = sourceSnippet.match(/^(\s*<(\w+)\b[^>]*>)([\s\S]*)(<\/\2>\s*)$/);
40200
- if (innerMatch) {
40201
- const [, openTag, , innerContent, closeTag] = innerMatch;
40202
- const textOnly = innerContent.replace(/<[^>]+>/g, "");
40203
- if (textOnly === resolvedOriginal) {
40204
- const isHtmlReplacement = /<[^>]+>/.test(resolvedNewText);
40205
- if (isHtmlReplacement || !/<[^>]+>/.test(innerContent)) {
40206
- return write(openTag + resolvedNewText + closeTag);
40207
- }
40208
- const splicedInner = spliceTextAcrossInlineMarkup(innerContent, resolvedOriginal, resolvedNewText);
40209
- if (splicedInner !== null) {
40210
- return write(openTag + splicedInner + closeTag);
40211
- }
40212
- return {
40213
- success: false,
40214
- error: "Cannot apply this edit without dropping the inline markup inside the element " + "\u2014 edit the styled part separately"
40215
- };
40568
+ const element2 = findElementByText(sourceSnippet, resolvedOriginal, entry?.tag);
40569
+ if (element2) {
40570
+ const { before, openTag, innerContent, closeTag, after } = element2;
40571
+ const writeElement = (updatedElement) => write(before + updatedElement + after);
40572
+ const isHtmlReplacement = /<[^>]+>/.test(resolvedNewText);
40573
+ if (isHtmlReplacement || !/<[^>]+>/.test(innerContent)) {
40574
+ const spliced = isHtmlReplacement ? null : spliceIntoEntitySource(innerContent, resolvedOriginal, resolvedNewText);
40575
+ return writeElement(openTag + (spliced ?? resolvedNewText) + closeTag);
40216
40576
  }
40577
+ const splicedInner = spliceTextAcrossInlineMarkup(innerContent, resolvedOriginal, resolvedNewText);
40578
+ if (splicedInner !== null) {
40579
+ return writeElement(openTag + splicedInner + closeTag);
40580
+ }
40581
+ return {
40582
+ success: false,
40583
+ error: "Cannot apply this edit without dropping the inline markup inside the element. " + "Edit the styled part separately"
40584
+ };
40217
40585
  }
40218
40586
  return {
40219
40587
  success: false,
@@ -40222,14 +40590,14 @@ function applyTextChange(content, change, manifest) {
40222
40590
  }
40223
40591
  return write(updatedSnippet);
40224
40592
  }
40225
- function applyTextChangeWithPlaceholders(insideYaml, content, sourceSnippet, originalValue, newText) {
40593
+ function applyTextChangeWithPlaceholders(insideYaml, content, snippetAt, sourceSnippet, originalValue, newText, innerFrom = 0, innerTo = sourceSnippet.length) {
40226
40594
  const placeholderPattern = /\{\{cms:[^}]+\}\}/g;
40227
40595
  const originalParts = originalValue.split(placeholderPattern);
40228
40596
  const newParts = newText.split(placeholderPattern);
40229
40597
  if (originalParts.length !== newParts.length) {
40230
40598
  return { success: false, error: "Placeholder structure mismatch between original and new values" };
40231
40599
  }
40232
- let updatedSnippet = sourceSnippet;
40600
+ let updatedInner = sourceSnippet.slice(innerFrom, innerTo);
40233
40601
  let anyChange = false;
40234
40602
  for (let i2 = 0;i2 < originalParts.length; i2++) {
40235
40603
  const oldPart = originalParts[i2];
@@ -40237,12 +40605,12 @@ function applyTextChangeWithPlaceholders(insideYaml, content, sourceSnippet, ori
40237
40605
  if (oldPart === newPart || oldPart.length === 0) {
40238
40606
  continue;
40239
40607
  }
40240
- const matchedText = findTextInSnippet(updatedSnippet, oldPart);
40608
+ const matchedText = findTextInSnippet(updatedInner, oldPart);
40241
40609
  if (matchedText) {
40242
40610
  if (matchedText !== oldPart) {
40243
40611
  newPart = encodeEntitiesLike(newPart, matchedText);
40244
40612
  }
40245
- updatedSnippet = updatedSnippet.replace(matchedText, escapeReplacement(newPart));
40613
+ updatedInner = updatedInner.replace(matchedText, escapeReplacement(newPart));
40246
40614
  anyChange = true;
40247
40615
  } else {
40248
40616
  return {
@@ -40254,12 +40622,13 @@ function applyTextChangeWithPlaceholders(insideYaml, content, sourceSnippet, ori
40254
40622
  if (!anyChange) {
40255
40623
  return { success: false, error: "No text changes detected between original and new values" };
40256
40624
  }
40257
- return writeSnippet(content, sourceSnippet, updatedSnippet, insideYaml, newText);
40625
+ const updatedSnippet = sourceSnippet.slice(0, innerFrom) + updatedInner + sourceSnippet.slice(innerTo);
40626
+ return writeSnippet(content, snippetAt, sourceSnippet, updatedSnippet, insideYaml, newText);
40258
40627
  }
40259
40628
  function isJavaScriptSource(sourcePath) {
40260
40629
  return !!sourcePath && JAVASCRIPT_SOURCE.test(sourcePath);
40261
40630
  }
40262
- function isYamlValueSource(content, sourceSnippet, sourcePath) {
40631
+ function isYamlValueSource(content, sourceSnippet, sourcePath, snippetAt) {
40263
40632
  if (!sourcePath)
40264
40633
  return false;
40265
40634
  if (YAML_DOCUMENT_SOURCE.test(sourcePath))
@@ -40269,7 +40638,7 @@ function isYamlValueSource(content, sourceSnippet, sourcePath) {
40269
40638
  const block = frontmatterBlock(content);
40270
40639
  if (!block)
40271
40640
  return false;
40272
- const snippetStart = content.indexOf(sourceSnippet);
40641
+ const snippetStart = snippetAt ?? content.indexOf(sourceSnippet);
40273
40642
  return snippetStart >= block.start && snippetStart + sourceSnippet.length <= block.end;
40274
40643
  }
40275
40644
  function frontmatterBlock(content) {
@@ -40299,7 +40668,7 @@ function decodeJsString(raw) {
40299
40668
  const close = raw.indexOf("}", i2 + 2);
40300
40669
  if (close !== -1) {
40301
40670
  const code2 = parseInt(raw.slice(i2 + 2, close), 16);
40302
- if (!Number.isNaN(code2)) {
40671
+ if (!Number.isNaN(code2) && code2 <= 1114111) {
40303
40672
  out += String.fromCodePoint(code2);
40304
40673
  i2 = close;
40305
40674
  continue;
@@ -40402,8 +40771,9 @@ function spliceTextAcrossInlineMarkup(innerContent, originalText, newText) {
40402
40771
  const token2 = tokens[i2];
40403
40772
  if (isTag(token2) || !token2)
40404
40773
  continue;
40405
- runs.push({ index: i2, start: plain.length, end: plain.length + token2.length });
40406
- plain += token2;
40774
+ const { text, offsets } = decodeEntitiesWithOffsets(token2);
40775
+ runs.push({ index: i2, start: plain.length, end: plain.length + text.length, offsets });
40776
+ plain += text;
40407
40777
  }
40408
40778
  if (plain !== originalText)
40409
40779
  return null;
@@ -40429,9 +40799,29 @@ function spliceTextAcrossInlineMarkup(innerContent, originalText, newText) {
40429
40799
  if (!run)
40430
40800
  return null;
40431
40801
  const token = tokens[run.index];
40432
- tokens[run.index] = token.slice(0, start - run.start) + newText.slice(start, newEnd) + token.slice(originalEnd - run.start);
40802
+ tokens[run.index] = token.slice(0, run.offsets[start - run.start]) + newText.slice(start, newEnd) + token.slice(run.offsets[originalEnd - run.start]);
40433
40803
  return tokens.join("");
40434
40804
  }
40805
+ function findElementByText(snippet, text, tag) {
40806
+ const whole = snippet.match(/^(\s*<(\w+)\b[^>]*>)([\s\S]*)(<\/\2>\s*)$/);
40807
+ if (whole && plainTextOf(whole[3]) === text) {
40808
+ return { before: "", openTag: whole[1], innerContent: whole[3], closeTag: whole[4], after: "" };
40809
+ }
40810
+ const exact = findElements(snippet, 0, snippet.length, tag).filter((span) => !span.void && plainTextOf(snippet.slice(span.openEnd, span.closeStart)) === text);
40811
+ const match = pickElement(snippet, exact, text);
40812
+ if (!match)
40813
+ return null;
40814
+ return {
40815
+ before: snippet.slice(0, match.start),
40816
+ openTag: snippet.slice(match.start, match.openEnd),
40817
+ innerContent: snippet.slice(match.openEnd, match.closeStart),
40818
+ closeTag: snippet.slice(match.closeStart, match.end),
40819
+ after: snippet.slice(match.end)
40820
+ };
40821
+ }
40822
+ function plainTextOf(markup) {
40823
+ return decodeEntitiesWithOffsets(markup.replace(/<[^>]+>/g, "")).text;
40824
+ }
40435
40825
  function findTextInSnippet(snippet, decodedText) {
40436
40826
  if (snippet.includes(decodedText)) {
40437
40827
  return decodedText;
@@ -40480,10 +40870,9 @@ function decodeEntitiesWithOffsets(source) {
40480
40870
  }
40481
40871
  function decodeEntityMatch(match) {
40482
40872
  const [, hex, dec, name] = match;
40483
- if (hex)
40484
- return String.fromCodePoint(parseInt(hex, 16));
40485
- if (dec)
40486
- return String.fromCodePoint(parseInt(dec, 10));
40873
+ const code = hex ? parseInt(hex, 16) : dec ? parseInt(dec, 10) : -1;
40874
+ if (code >= 0)
40875
+ return code <= 1114111 ? String.fromCodePoint(code) : undefined;
40487
40876
  return name ? NAMED_ENTITIES[name.toLowerCase()] : undefined;
40488
40877
  }
40489
40878
  function spliceIntoEntitySource(source, original, replacement) {
@@ -40550,7 +40939,7 @@ function resolveCmsPlaceholders(text, manifest) {
40550
40939
  });
40551
40940
  }
40552
40941
  function findExpressionAttribute(text, attr) {
40553
- const exprStart = new RegExp(`${attr}\\s*=\\s*\\{`);
40942
+ const exprStart = new RegExp(`(?<![\\w:-])${attr}\\s*=\\s*\\{`);
40554
40943
  const match = text.match(exprStart);
40555
40944
  if (!match || match.index === undefined)
40556
40945
  return null;
@@ -40663,14 +41052,14 @@ function scalarSays(actual, expected) {
40663
41052
  const parsed = tryParseYaml(expected);
40664
41053
  return typeof parsed === "number" && parsed === actual;
40665
41054
  }
40666
- function writeSnippet(content, sourceSnippet, updatedSnippet, insideYaml, expected) {
41055
+ function writeSnippet(content, snippetAt, sourceSnippet, updatedSnippet, insideYaml, expected) {
40667
41056
  if (insideYaml && !yamlSnippetSurvives(sourceSnippet, updatedSnippet, expected)) {
40668
41057
  return {
40669
41058
  success: false,
40670
41059
  error: `"${expected.substring(0, 50)}" cannot be written into this frontmatter field without breaking it`
40671
41060
  };
40672
41061
  }
40673
- return { success: true, content: content.replace(sourceSnippet, escapeReplacement(updatedSnippet)) };
41062
+ return { success: true, content: content.slice(0, snippetAt) + updatedSnippet + content.slice(snippetAt + sourceSnippet.length) };
40674
41063
  }
40675
41064
  function yamlSnippetSurvives(sourceSnippet, updatedSnippet, expected) {
40676
41065
  const before = yamlScalars(sourceSnippet);
@@ -40841,15 +41230,17 @@ function tryBrNormalizedChange(sourceSnippet, resolvedOriginal, resolvedNewText)
40841
41230
  }
40842
41231
  return result !== sourceSnippet ? result : null;
40843
41232
  }
40844
- var import_node_html_parser, import_yaml6, ASSET_IMPORT_EXT_RE, REMOTE_FETCH_TIMEOUT_MS = 15000, REMOTE_FETCH_MAX_BYTES, QUOTED_LITERAL_DELIMITERS, JAVASCRIPT_SOURCE, YAML_DOCUMENT_SOURCE, YAML_FRONTMATTER_SOURCE, JS_ESCAPES, ENTITY_ALTERNATIVES, NAMED_ENTITIES, YAML_PLAIN_NUMBER;
41233
+ var import_node_html_parser, import_yaml6, IMAGE_TAG, ASSET_IMPORT_EXT_RE, REMOTE_FETCH_TIMEOUT_MS = 15000, REMOTE_FETCH_MAX_BYTES, QUOTED_LITERAL_DELIMITERS, JAVASCRIPT_SOURCE, YAML_DOCUMENT_SOURCE, YAML_FRONTMATTER_SOURCE, JS_ESCAPES, ENTITY_ALTERNATIVES, YAML_PLAIN_NUMBER;
40845
41234
  var init_source_writer = __esm(() => {
40846
41235
  init_astro_image_paths();
40847
41236
  init_config();
40848
41237
  init_context();
40849
41238
  init_snippet_utils();
40850
41239
  init_utils2();
41240
+ init_element_span();
40851
41241
  import_node_html_parser = __toESM(require_dist2(), 1);
40852
41242
  import_yaml6 = __toESM(require_dist(), 1);
41243
+ IMAGE_TAG = /^(img|Image|Picture)$/;
40853
41244
  ASSET_IMPORT_EXT_RE = /\.(jpe?g|png|gif|webp|avif|svg|ico|bmp|tiff?)$/i;
40854
41245
  REMOTE_FETCH_MAX_BYTES = 50 * 1024 * 1024;
40855
41246
  QUOTED_LITERAL_DELIMITERS = [`'`, `"`, "`"];
@@ -40875,14 +41266,6 @@ var init_source_writer = __esm(() => {
40875
41266
  ['"', ["&quot;"]],
40876
41267
  ["'", ["&#39;", "&apos;"]]
40877
41268
  ]);
40878
- NAMED_ENTITIES = {
40879
- amp: "&",
40880
- lt: "<",
40881
- gt: ">",
40882
- quot: '"',
40883
- apos: "'",
40884
- nbsp: "\xA0"
40885
- };
40886
41269
  YAML_PLAIN_NUMBER = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/;
40887
41270
  });
40888
41271
 
@@ -42565,7 +42948,7 @@ async function processHtml(html, fileId, options, getNextId, sourcePath) {
42565
42948
  let markdownWrapperNode = null;
42566
42949
  const markdownWrapperNodes = new Set;
42567
42950
  if (collectionInfo) {
42568
- const allElements = root.querySelectorAll("*");
42951
+ const allElements2 = root.querySelectorAll("*");
42569
42952
  let foundWrapper = false;
42570
42953
  const markerEls = root.querySelectorAll("[data-cms-markdown-content]");
42571
42954
  for (const markerEl of markerEls) {
@@ -42590,7 +42973,7 @@ async function processHtml(html, fileId, options, getNextId, sourcePath) {
42590
42973
  }
42591
42974
  if (!foundWrapper) {
42592
42975
  const SKIP_WRAPPER_TAGS = new Set(["html", "head", "body", "script", "style", "meta", "link"]);
42593
- for (const node of allElements) {
42976
+ for (const node of allElements2) {
42594
42977
  const tag = node.tagName?.toLowerCase?.() ?? "";
42595
42978
  if (SKIP_WRAPPER_TAGS.has(tag))
42596
42979
  continue;
@@ -42637,7 +43020,7 @@ async function processHtml(html, fileId, options, getNextId, sourcePath) {
42637
43020
  }
42638
43021
  return depth;
42639
43022
  };
42640
- for (const node of allElements) {
43023
+ for (const node of allElements2) {
42641
43024
  const tag = node.tagName?.toLowerCase?.() ?? "";
42642
43025
  if (["script", "style", "head", "meta", "link", "html"].includes(tag))
42643
43026
  continue;
@@ -42668,7 +43051,7 @@ async function processHtml(html, fileId, options, getNextId, sourcePath) {
42668
43051
  const bodyStart = collectionInfo.bodyFirstLine.replace(/^\*\*|\*\*$/g, "").replace(/\*\*/g, "").replace(/\*/g, "").replace(/^#+ /, "").replace(/^\s*[-*+]\s+/, "").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").trim().substring(0, 50);
42669
43052
  if (bodyStart.length > 3) {
42670
43053
  const candidates = [];
42671
- for (const node of allElements) {
43054
+ for (const node of allElements2) {
42672
43055
  const tag = node.tagName?.toLowerCase?.() ?? "";
42673
43056
  if (["script", "style", "head", "meta", "link"].includes(tag))
42674
43057
  continue;
@@ -43920,6 +44303,9 @@ var init_vite_plugin = __esm(() => {
43920
44303
  init_vite_plugin_array_transform();
43921
44304
  });
43922
44305
 
44306
+ // ../cms/src/vite-plugin-inline-nuasite.ts
44307
+ var init_vite_plugin_inline_nuasite = () => {};
44308
+
43923
44309
  // ../../node_modules/ccount/index.js
43924
44310
  function ccount(value2, character) {
43925
44311
  const source = String(value2);
@@ -47584,9 +47970,9 @@ function factoryLabel(effects, ok3, nok, type, markerType, stringType, disallowE
47584
47970
  return ok3;
47585
47971
  }
47586
47972
  effects.enter(stringType);
47587
- return lineStart(code);
47973
+ return lineStart2(code);
47588
47974
  }
47589
- function lineStart(code) {
47975
+ function lineStart2(code) {
47590
47976
  if (code === codes.rightSquareBracket && !balance) {
47591
47977
  return atClosingBrace(code);
47592
47978
  }
@@ -47616,7 +48002,7 @@ function factoryLabel(effects, ok3, nok, type, markerType, stringType, disallowE
47616
48002
  }
47617
48003
  effects.consume(code);
47618
48004
  effects.exit(types.chunkText);
47619
- return lineStart;
48005
+ return lineStart2;
47620
48006
  }
47621
48007
  effects.consume(code);
47622
48008
  return code === codes.backslash ? dataEscape : data;
@@ -47727,9 +48113,9 @@ function tokenizeDirectiveContainer(effects, ok3, nok) {
47727
48113
  return effects.check(nonLazyLine, emptyContentNonLazyLineAfter, after)(code);
47728
48114
  }
47729
48115
  effects.enter("directiveContainerContent");
47730
- return lineStart(code);
48116
+ return lineStart2(code);
47731
48117
  }
47732
- function lineStart(code) {
48118
+ function lineStart2(code) {
47733
48119
  return effects.attempt({ tokenize: tokenizeClosingFence, partial: true }, afterContent, initialSize ? factorySpace(effects, chunkStart, types.linePrefix, initialSize + 1) : chunkStart)(code);
47734
48120
  }
47735
48121
  function chunkStart(code) {
@@ -47765,13 +48151,13 @@ function tokenizeDirectiveContainer(effects, ok3, nok) {
47765
48151
  }
47766
48152
  function emptyContentNonLazyLineAfter(code) {
47767
48153
  effects.enter("directiveContainerContent");
47768
- return lineStart(code);
48154
+ return lineStart2(code);
47769
48155
  }
47770
48156
  function nonLazyLineAfter(code) {
47771
48157
  effects.consume(code);
47772
48158
  const t = effects.exit(types.chunkDocument);
47773
48159
  self2.parser.lazy[t.start.line] = false;
47774
- return lineStart;
48160
+ return lineStart2;
47775
48161
  }
47776
48162
  function lineAfter(code) {
47777
48163
  const t = effects.exit(types.chunkDocument);
@@ -47829,9 +48215,9 @@ function tokenizeNonLazyLine(effects, ok3, nok) {
47829
48215
  effects.enter(types.lineEnding);
47830
48216
  effects.consume(code);
47831
48217
  effects.exit(types.lineEnding);
47832
- return lineStart;
48218
+ return lineStart2;
47833
48219
  }
47834
- function lineStart(code) {
48220
+ function lineStart2(code) {
47835
48221
  return self2.parser.lazy[self2.now().line] ? nok(code) : ok3(code);
47836
48222
  }
47837
48223
  }
@@ -85547,7 +85933,7 @@ var init_build = __esm(() => {
85547
85933
  var _b;
85548
85934
  Object.defineProperty(exports, "__esModule", { value: true });
85549
85935
  exports.FIELDS = undefined;
85550
- exports.default = tokenize;
85936
+ exports.default = tokenize2;
85551
85937
  var t4 = __importStar(require_tokenTypes());
85552
85938
  var unescapable = (_a3 = {}, _a3[t4.tab] = true, _a3[t4.newline] = true, _a3[t4.cr] = true, _a3[t4.feed] = true, _a3);
85553
85939
  var wordDelimiters = (_b = {}, _b[t4.space] = true, _b[t4.tab] = true, _b[t4.newline] = true, _b[t4.cr] = true, _b[t4.feed] = true, _b[t4.ampersand] = true, _b[t4.asterisk] = true, _b[t4.bang] = true, _b[t4.comma] = true, _b[t4.colon] = true, _b[t4.semicolon] = true, _b[t4.openParenthesis] = true, _b[t4.closeParenthesis] = true, _b[t4.openSquare] = true, _b[t4.closeSquare] = true, _b[t4.singleQuote] = true, _b[t4.doubleQuote] = true, _b[t4.plus] = true, _b[t4.pipe] = true, _b[t4.tilde] = true, _b[t4.greaterThan] = true, _b[t4.equals] = true, _b[t4.dollar] = true, _b[t4.caret] = true, _b[t4.slash] = true, _b);
@@ -85594,7 +85980,7 @@ var init_build = __esm(() => {
85594
85980
  START_POS: 5,
85595
85981
  END_POS: 6
85596
85982
  };
85597
- function tokenize(input) {
85983
+ function tokenize2(input) {
85598
85984
  var tokens = [];
85599
85985
  var css = input.css.valueOf();
85600
85986
  var length = css.length;
@@ -112013,6 +112399,7 @@ var init_src3 = __esm(async () => {
112013
112399
  init_redirects2();
112014
112400
  init_scan_cache();
112015
112401
  init_vite_plugin();
112402
+ init_vite_plugin_inline_nuasite();
112016
112403
  init_remark();
112017
112404
  init_src2();
112018
112405
  init_fields();
@@ -112025,6 +112412,7 @@ var init_src3 = __esm(async () => {
112025
112412
  init_migrate_astro_image();
112026
112413
  init_at_root();
112027
112414
  init_redirects2();
112415
+ init_vite_plugin_inline_nuasite();
112028
112416
  init_scan_cache();
112029
112417
  init_source_finder();
112030
112418
  DEFAULT_MAX_UPLOAD_SIZE = 20 * 1024 * 1024;