@bettercms-ai/convert 0.17.0 → 0.17.2

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.d.ts CHANGED
@@ -409,7 +409,7 @@ interface ConversionReceipt {
409
409
  * reader must treat absent as unknown rather than as `"pending"`.
410
410
  */
411
411
  canvas?: {
412
- lane: "bridge" | "pending";
412
+ lane: "bridge" | "draft-route" | "pending";
413
413
  files?: string[];
414
414
  };
415
415
  /**
@@ -1242,7 +1242,7 @@ interface CanvasSource {
1242
1242
  ast?: unknown;
1243
1243
  }
1244
1244
  interface CanvasResult {
1245
- lane: "bridge" | "pending";
1245
+ lane: "bridge" | "draft-route" | "pending";
1246
1246
  /** Files the bridge was installed in. */
1247
1247
  files: string[];
1248
1248
  /** Rewritten contents, by path. Empty when the lane is pending. */
@@ -1257,8 +1257,8 @@ declare const ASTRO_LANE_PENDING: PendingPath;
1257
1257
  * Install the draft bridge, or say why not.
1258
1258
  *
1259
1259
  * `routes` maps a brief route (`/about`) to its slug, so a route written as a literal in the page
1260
- * can name itself. An Astro project is refused wholesale: bridging it needs a server render, which
1261
- * is a deploy-shaped decision and never a codemod's.
1260
+ * can name itself. An Astro project is never bridged here: it either wired a lane itself (see
1261
+ * {@link astroLane}) or it carries the SSR recipe.
1262
1262
  */
1263
1263
  declare function canvasBridge(sources: CanvasSource[], routes: Map<string, string>): CanvasResult;
1264
1264
 
@@ -1293,10 +1293,11 @@ declare function isSourceCandidate(path: string): boolean;
1293
1293
  * could lift — and the target is a number someone has to be able to reach. They are not copy an
1294
1294
  * author edits, so they are not copy this measures.
1295
1295
  *
1296
- * - a decorative glyph: two characters or fewer, a single non-letter symbol, or an ordinal
1297
- * (`01`, `2.`, `3 /`) that numbers a list rather than saying anything;
1296
+ * - a decorative glyph: no letter and no digit (`✓`, `→`), or an ordinal (`01`, `2.`, `3 /`)
1297
+ * that numbers a list rather than saying anything. Length decides nothing: `OK` and `$1,200`
1298
+ * are copy. The server's `extract-fields.ts` isDecorativeGlyph, verbatim;
1298
1299
  * - the platform badge every free site renders;
1299
- * - a skip link, which exists for keyboards and is never edited.
1300
+ * - a skip link, which exists for keyboards and is never edited. @see isSkipLink
1300
1301
  */
1301
1302
  declare const isDecorativeGlyph: (text: string) => boolean;
1302
1303
  interface XrayBucket {
package/dist/index.js CHANGED
@@ -6090,9 +6090,28 @@ var pend = (reason, hint, file, line) => ({
6090
6090
  hint
6091
6091
  });
6092
6092
  var ASTRO_LANE_PENDING = pend("SSR_DRAFT_ROUTE", SSR_DRAFT_RECIPE);
6093
+ function mounts(content, component) {
6094
+ const from = new RegExp(
6095
+ `import\\s+([A-Za-z_$][\\w$]*)\\s+from\\s+["']@bettercms-ai/astro/components/${component}\\.astro["']`
6096
+ );
6097
+ const local = content.match(from)?.[1];
6098
+ return local !== void 0 && new RegExp(`<${local}[\\s/>]`).test(content);
6099
+ }
6100
+ var ASTRO_CONFIG = /(^|\/)astro\.config\.(mjs|cjs|js|ts|mts)$/;
6101
+ function astroLane(sources) {
6102
+ const astro = sources.filter((s) => s.path.toLowerCase().endsWith(".astro"));
6103
+ const server = sources.some(
6104
+ (s) => ASTRO_CONFIG.test(s.path) && /\boutput\s*:\s*["']server["']/.test(s.content)
6105
+ );
6106
+ if (server && astro.some((s) => mounts(s.content, "BcmsLive"))) return "draft-route";
6107
+ if (astro.some((s) => mounts(s.content, "BcmsLiveBlocks"))) return "bridge";
6108
+ return null;
6109
+ }
6093
6110
  function canvasBridge(sources, routes) {
6094
- if (sources.some((s) => s.path.toLowerCase().endsWith(".astro")))
6095
- return { lane: "pending", files: [], edits: /* @__PURE__ */ new Map(), pending: [ASTRO_LANE_PENDING] };
6111
+ if (sources.some((s) => s.path.toLowerCase().endsWith(".astro"))) {
6112
+ const lane = astroLane(sources);
6113
+ return lane ? { lane, files: [], edits: /* @__PURE__ */ new Map(), pending: [] } : { lane: "pending", files: [], edits: /* @__PURE__ */ new Map(), pending: [ASTRO_LANE_PENDING] };
6114
+ }
6096
6115
  const files = [];
6097
6116
  const edits = /* @__PURE__ */ new Map();
6098
6117
  const pending = [];
@@ -6792,7 +6811,7 @@ function isSourceCandidate(path) {
6792
6811
  }
6793
6812
 
6794
6813
  // src/xray.ts
6795
- var SKIP_TAGS = /* @__PURE__ */ new Set(["svg", "iframe", "head", "title"]);
6814
+ var SKIP_TAGS = /* @__PURE__ */ new Set(["svg", "math", "iframe", "head", "title", "object", "canvas", "select"]);
6796
6815
  var CONTEXT_TAGS = /* @__PURE__ */ new Set([
6797
6816
  "nav",
6798
6817
  "header",
@@ -6810,10 +6829,16 @@ var CONTEXT_TAGS = /* @__PURE__ */ new Set([
6810
6829
  "figure",
6811
6830
  "blockquote"
6812
6831
  ]);
6813
- var BOUND_ATTRS = ["data-bcms-field", "data-bcms-layout-field", "data-bcms-path", "data-bcms-block"];
6814
- var isDecorativeGlyph = (text) => text.length <= 2 || /^\d{1,2}\s*[/.)\-–]?$/.test(text) || !new RegExp("\\p{L}", "u").test(text) || /^made with\b/i.test(text) || /^skip to\b/i.test(text);
6832
+ var BOUND_ATTRS = ["data-bcms-field", "data-bcms-layout-field"];
6833
+ var isDecorativeGlyph = (text) => {
6834
+ const t = flat2(text);
6835
+ return t === "" || !/[\p{L}\p{N}]/u.test(t) || new RegExp("^\\p{Nd}{1,2}\\s*[/.)\\-\u2013\u2014]?$", "u").test(t);
6836
+ };
6837
+ var PLATFORM_BADGE = /^made with\b/iu;
6815
6838
  var MAX_BUCKETS = 12;
6816
6839
  var flat2 = (value) => value.replace(/\s+/g, " ").trim();
6840
+ var textOf = (node) => node.type === "text" ? node.value : node.type === "element" ? node.children.map(textOf).join("") : "";
6841
+ var isSkipLink = (node, attrs) => node.tag === "a" && (attrs.get("href") ?? "").startsWith("#") && (/^skip\b/iu.test(flat2(textOf(node))) || /\bskip[-_]?(link|nav|to|content)\b/iu.test(attrs.get("class") ?? ""));
6817
6842
  function measureUnaddressable(html) {
6818
6843
  const parsed = parse2(html);
6819
6844
  let visible = 0;
@@ -6822,7 +6847,7 @@ function measureUnaddressable(html) {
6822
6847
  const visit = (node, tag, context, bound) => {
6823
6848
  if (node.type === "text") {
6824
6849
  const text = flat2(node.value);
6825
- if (!text || isDecorativeGlyph(text)) return;
6850
+ if (isDecorativeGlyph(text) || PLATFORM_BADGE.test(text)) return;
6826
6851
  visible += text.length;
6827
6852
  if (bound) return;
6828
6853
  unaddressable += text.length;
@@ -6836,7 +6861,7 @@ function measureUnaddressable(html) {
6836
6861
  }
6837
6862
  if (node.type !== "element" || node.raw || SKIP_TAGS.has(node.tag)) return;
6838
6863
  const attrs = new Map(node.attrs.map((a) => [a.name, a.value]));
6839
- if (attrs.has("hidden") || attrs.get("aria-hidden") === "true" || /display\s*:\s*none/.test(attrs.get("style") ?? "")) {
6864
+ if (attrs.has("hidden") || attrs.get("aria-hidden") === "true" || /display\s*:\s*none/i.test(attrs.get("style") ?? "") || isSkipLink(node, attrs)) {
6840
6865
  return;
6841
6866
  }
6842
6867
  const owned = bound || BOUND_ATTRS.some((name) => attrs.has(name));
@@ -6847,7 +6872,7 @@ function measureUnaddressable(html) {
6847
6872
  return {
6848
6873
  visible,
6849
6874
  unaddressable,
6850
- buckets: [...buckets.values()].sort((a, b) => b.chars - a.chars).slice(0, MAX_BUCKETS)
6875
+ buckets: [...buckets.values()].sort((a, b) => b.chars - a.chars || `${a.tag} @ ${a.context}`.localeCompare(`${b.tag} @ ${b.context}`)).slice(0, MAX_BUCKETS)
6851
6876
  };
6852
6877
  }
6853
6878