@bettercms-ai/convert 0.13.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # @bettercms-ai/convert
2
2
 
3
+ ## 0.14.0
4
+
5
+ ### Minor Changes
6
+
7
+ - df570ef: Receipt and brief shapes a consumer reads:
8
+ - `BriefPath.kind` is typed `BindingKind` rather than `BindingKind | ""` — the brief always names a kind, and the empty case only forced callers to handle a value it never sends.
9
+ - New `PendingReason` `"DOCUMENT_DECLARE_ONLY"`: a `document` path is never located and never rewritten, because its `original` is only the body's FIRST block and declaring the paragraph that renders it is what publish refuses. The row's fix names the wrapper to declare instead.
10
+ - `PendingFix.kind` (`"document" | "richtext"`, optional) accompanies a `declare-richtext` action to say WHICH container is meant.
11
+
3
12
  ## 0.13.0
4
13
 
5
14
  ### Minor Changes
package/dist/cli.js CHANGED
@@ -2562,7 +2562,11 @@ var IMAGE_SOURCES = {
2562
2562
  video: ["poster"]
2563
2563
  };
2564
2564
  var isImageSource = (site) => site.where === "attr" && (IMAGE_SOURCES[site.tag.toLowerCase()] ?? []).includes(site.attr ?? "");
2565
- var ownsSubtree = (kind, site, inline) => kind === "richtext" && (inline === true ? !site.nonHtml : !(site.mixed && site.textRange !== null));
2565
+ var ownsSubtree = (kind, site, inline) => (
2566
+ // A `document` is the same claim at article scale: the element renders EVERY block of the Body,
2567
+ // so its children come from the stored value and nothing else may be bound inside it.
2568
+ (kind === "richtext" || kind === "document") && (inline === true ? !site.nonHtml : !(site.mixed && site.textRange !== null))
2569
+ );
2566
2570
  var norm = (value) => decodeEntities(value.normalize("NFC")).replace(/\s+/g, " ").trim();
2567
2571
  var BCMS_ATTRS = [
2568
2572
  "data-bcms-field",
@@ -5363,7 +5367,7 @@ function actionFor(reason, input, file, inData) {
5363
5367
  return chosen;
5364
5368
  }
5365
5369
  function reasonAction(reason, input, file, inData) {
5366
- const richtext = input.kind === "richtext" || input.inline === true;
5370
+ const richtext = input.kind === "richtext" || input.kind === "document" || input.inline === true;
5367
5371
  switch (reason) {
5368
5372
  case "AMBIGUOUS_LITERAL":
5369
5373
  if (input.routes > 1) {
@@ -5414,6 +5418,11 @@ function reasonAction(reason, input, file, inData) {
5414
5418
  return { action: "wrap-span", why: "Only part of the element's text is this value \u2014 give it its own span." };
5415
5419
  case "SUBTREE_NOT_HTML":
5416
5420
  return { action: "declare-richtext", why: "The value is the element's whole subtree \u2014 bind it as richtext." };
5421
+ case "DOCUMENT_DECLARE_ONLY":
5422
+ return {
5423
+ action: "declare-richtext",
5424
+ why: 'This is a Body: bind the ONE element that renders every block of it \u2014 an article, a main or a div \u2014 with the richtext helper and data-bcms-kind="document". Never the paragraph holding its first block: that is where the whole article would be published.'
5425
+ };
5417
5426
  default:
5418
5427
  return { action: "manual", why: `${reason} \u2014 see the receipt's message for what blocked this path.` };
5419
5428
  }
@@ -5430,7 +5439,10 @@ function fixFor(row, input) {
5430
5439
  ...place.col !== void 0 ? { col: place.col } : {},
5431
5440
  snippet: place.snippet,
5432
5441
  action,
5433
- ...why ? { why } : {}
5442
+ ...why ? { why } : {},
5443
+ // Only `declare-richtext` carries it, and only to say WHICH container is meant: one element
5444
+ // for a rich-text value, the whole Body's wrapper for a document.
5445
+ ...action === "declare-richtext" && (input.kind === "document" || input.kind === "richtext") ? { kind: input.kind } : {}
5434
5446
  };
5435
5447
  }
5436
5448
  function routeCounts(pending) {
@@ -8771,10 +8783,28 @@ async function convertSources(briefIn, sources, options) {
8771
8783
  }
8772
8784
  pend2(target, miss.reason === "no-original" ? "NO_ORIGINAL" : "NOT_IN_SOURCE");
8773
8785
  }
8786
+ const documents = /* @__PURE__ */ new Set();
8787
+ for (const [key, hit] of locatedByKey) {
8788
+ const target = targets.get(key);
8789
+ if (!target || target.kind !== "document") continue;
8790
+ documents.add(key);
8791
+ const byHand = declaredByHand(target, hit.files);
8792
+ if (byHand !== null) {
8793
+ declaredOnly++;
8794
+ declaredRow(target, false, byHand);
8795
+ continue;
8796
+ }
8797
+ pend2(
8798
+ target,
8799
+ "DOCUMENT_DECLARE_ONLY",
8800
+ hit.files[0],
8801
+ "A Body is bound by declaring the one element that renders every block of it, never the paragraph that happens to hold its first block."
8802
+ );
8803
+ }
8774
8804
  const byFile = /* @__PURE__ */ new Map();
8775
8805
  for (const [key, hit] of locatedByKey) {
8776
8806
  const target = targets.get(key);
8777
- if (!target) continue;
8807
+ if (!target || documents.has(key)) continue;
8778
8808
  for (const file of hit.files) byFile.set(file, [...byFile.get(file) ?? [], target]);
8779
8809
  }
8780
8810
  const drill = {
@@ -8823,6 +8853,7 @@ async function convertSources(briefIn, sources, options) {
8823
8853
  let rewritten = 0;
8824
8854
  let alreadyDeclared = declaredOnly;
8825
8855
  for (const [key, hit] of locatedByKey) {
8856
+ if (documents.has(key)) continue;
8826
8857
  const target = targets.get(key);
8827
8858
  const outcomes = hit.files.map((file) => results.get(file)?.get(target.key) ?? "NOT_IN_SOURCE");
8828
8859
  const worst = outcomes.find((o) => o !== "rewritten" && o !== "declared");
@@ -9607,7 +9638,7 @@ function emitStubs2(dialects, slugs, contents, out) {
9607
9638
  // package.json
9608
9639
  var package_default = {
9609
9640
  name: "@bettercms-ai/convert",
9610
- version: "0.13.0",
9641
+ version: "0.14.0",
9611
9642
  type: "module",
9612
9643
  description: "Deterministic codemod that turns an imported site's text-matched bindings into declared ones \u2014 parser offsets in, one magic-string pass out, plus a receipt of everything it could not do.",
9613
9644
  bin: {