@bettercms-ai/convert 0.18.1 → 0.19.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,26 @@
1
1
  # @bettercms-ai/convert
2
2
 
3
+ ## 0.19.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 3d022b0: A locale copy placed on its default-locale twin's section component now carries `props.bindPaths` (`{ <propKey>: "<this page's field path>" }`) beside `bind`, because its own field keys differ from the component's. `ComponentProps.bindPaths` is new in types; the convert helper (v9) and the extracted section components and registry read a prop's copy and declare its address through it, so `/fr` renders and edits its own words instead of a field it does not have. Older readers ignore it.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [12b2d62]
12
+ - Updated dependencies [3d022b0]
13
+ - Updated dependencies [a0a6ad9]
14
+ - Updated dependencies [621a70e]
15
+ - Updated dependencies [2729e58]
16
+ - @bettercms-ai/types@1.25.0
17
+
18
+ ## 0.18.2
19
+
20
+ ### Patch Changes
21
+
22
+ - b201770: Rebuild before publishing. 1.22.1 / 0.18.1 shipped a stale dist/ that predated their own changes; this release carries the built fixes.
23
+
3
24
  ## 0.18.1
4
25
 
5
26
  ### Patch Changes
package/dist/cli.js CHANGED
@@ -4190,7 +4190,7 @@ async function bindImages(ctx, targets) {
4190
4190
 
4191
4191
  // src/helper.ts
4192
4192
  import { createHash as createHash2 } from "crypto";
4193
- var HELPER_VERSION = 8;
4193
+ var HELPER_VERSION = 9;
4194
4194
  var HEADER = "// @bettercms-ai/convert helper v";
4195
4195
  var HELPER_PATH = Object.fromEntries(
4196
4196
  Object.keys(DIALECT_RULES).map((d) => [d, DIALECT_RULES[d].helper])
@@ -4355,9 +4355,19 @@ export function bcmsImage<T>(page: unknown, path: string, fallback: T, slug?: st
4355
4355
  * the fallback the template shipped with \u2014 which is why a section renders correctly against the
4356
4356
  * committed \`{}\` stub.
4357
4357
  */
4358
- export function bcmsSection(page: unknown, bind?: string, overrides?: unknown): Record<string, unknown> {
4358
+ export function bcmsSection(
4359
+ page: unknown,
4360
+ bind?: string,
4361
+ overrides?: unknown,
4362
+ bindPaths?: Record<string, string>,
4363
+ ): Record<string, unknown> {
4359
4364
  const group = bind ? readPath(page, bind) : undefined;
4360
4365
  const base: Record<string, unknown> = isRecord(group) ? { ...group } : {};
4366
+ // A locale copy on its twin's component spells its fields its own way: each prop's copy is
4367
+ // read from the path the placement names for it.
4368
+ if (bind && isRecord(bindPaths)) {
4369
+ for (const [key, path] of Object.entries(bindPaths)) base[key] = readPath(page, path);
4370
+ }
4361
4371
  if (!isRecord(overrides)) return base;
4362
4372
  for (const key of Object.keys(overrides)) {
4363
4373
  const value = overrides[key];
@@ -4420,21 +4430,25 @@ export function bcmsSectionPaths(
4420
4430
  overrides: unknown,
4421
4431
  prefix: string,
4422
4432
  map: BcmsPathMap,
4433
+ bindPaths?: Record<string, string>,
4423
4434
  ): Record<string, unknown> {
4424
4435
  const out: Record<string, unknown> = isRecord(page) ? { ...page } : {};
4425
4436
  const own = isRecord(overrides) ? overrides : {};
4426
4437
  const separator = prefix.slice(-1);
4427
- const fromPage = (path: string): unknown =>
4428
- bind ? readPath(page, bind + separator + path.slice(prefix.length)) : undefined;
4438
+ // A re-keyed locale copy names each prop's path on its own page; any other placement re-bases.
4439
+ const fromPage = (path: string, key: string): unknown =>
4440
+ !bind ? undefined
4441
+ : isRecord(bindPaths) && typeof bindPaths[key] === "string" ? readPath(page, bindPaths[key])
4442
+ : readPath(page, bind + separator + path.slice(prefix.length));
4429
4443
  for (const [path, key] of Object.entries(map.scalars ?? {})) {
4430
4444
  const value = own[key];
4431
- out[path] = value !== undefined && value !== null ? value : fromPage(path);
4445
+ out[path] = value !== undefined && value !== null ? value : fromPage(path, key);
4432
4446
  }
4433
4447
  for (const [path, table] of Object.entries(map.tables ?? {})) {
4434
4448
  const held = own[table.key];
4435
4449
  const rows = isRecord(held) && Array.isArray(held.rows) ? held.rows : held;
4436
4450
  if (!Array.isArray(rows)) {
4437
- out[path] = fromPage(path);
4451
+ out[path] = fromPage(path, table.key);
4438
4452
  continue;
4439
4453
  }
4440
4454
  const pageColumn = new Map(Object.entries(table.columns).map(([column, key]) => [key, column]));
@@ -4449,12 +4463,17 @@ export function bcmsSectionPaths(
4449
4463
  }
4450
4464
 
4451
4465
  export function bcmsFields(
4452
- placement: { blockId?: string; bind?: string; overrides?: unknown },
4466
+ placement: { blockId?: string; bind?: string; overrides?: unknown; bindPaths?: Record<string, string> },
4453
4467
  pathPrefix: string,
4454
4468
  map?: BcmsPathMap,
4455
4469
  ): (path: string) => string {
4456
4470
  const id = placement.blockId;
4457
4471
  const own = typeof id === "string" && id !== "" && !placement.bind && isRecord(placement.overrides);
4472
+ /** A re-keyed locale copy's own path for a prop, with whatever follows the prop kept. */
4473
+ const moved = (key: string | undefined, rest: string): string | undefined => {
4474
+ const paths = placement.bind && isRecord(placement.bindPaths) ? placement.bindPaths : undefined;
4475
+ return key !== undefined && paths && typeof paths[key] === "string" ? paths[key] + rest : undefined;
4476
+ };
4458
4477
  /**
4459
4478
  * What replaces the prefix the markup was cut with, or null for "leave every path alone".
4460
4479
  *
@@ -4473,11 +4492,14 @@ export function bcmsFields(
4473
4492
  const separator = pathPrefix.slice(-1);
4474
4493
  return (path: string): string => {
4475
4494
  if (!path.startsWith(pathPrefix) || !(own || placement.bind)) return path;
4476
- if (!own) return placement.bind + separator + path.slice(pathPrefix.length);
4477
4495
  const scalar = map.scalars?.[path];
4478
- if (scalar !== undefined) return id + "__overrides." + scalar;
4479
4496
  const row = /^([^[]+)\\[(\\d+)\\]\\.(.+)$/.exec(path);
4480
4497
  const table = row ? map.tables?.[row[1]] : undefined;
4498
+ if (!own) {
4499
+ return moved(scalar, "") ?? moved(table?.key, row ? "[" + row[2] + "]." + row[3] : "")
4500
+ ?? placement.bind + separator + path.slice(pathPrefix.length);
4501
+ }
4502
+ if (scalar !== undefined) return id + "__overrides." + scalar;
4481
4503
  const column = row && table ? table.columns[row[3]] : undefined;
4482
4504
  return table && column !== undefined ? id + "__overrides." + table.key + "[" + row![2] + "]." + column : path;
4483
4505
  };
@@ -4487,7 +4509,9 @@ export function bcmsFields(
4487
4509
  // it names a key the instance does not hold and a group this placement is not bound to.
4488
4510
  // (An EMPTY prefix is a plan saying the prop key IS the page path, and matches everything.)
4489
4511
  if (lane === null || !path.startsWith(pathPrefix)) return path;
4490
- return lane + path.slice(pathPrefix.length);
4512
+ const rest = path.slice(pathPrefix.length);
4513
+ const key = /^[^.[]+/.exec(rest)?.[0];
4514
+ return (own ? undefined : moved(key, rest.slice(key?.length ?? 0))) ?? lane + rest;
4491
4515
  };
4492
4516
  }
4493
4517
  `;
@@ -4500,7 +4524,8 @@ var HISTORICAL = {
4500
4524
  4: "a5742f64dacb1b26ee6dd5e1469a54871487934c71bc8267df19e3a80e417595",
4501
4525
  5: "0472fe3ffdcfa49e719c7b9da9cb63b6eba637cc22783bb00c3c2cadd3d800da",
4502
4526
  6: "c907e7245578d947c6541b1ab2c5c6659ea7789a1519e895b4a4aed122c6f649",
4503
- 7: "f22514ef48c50230985e605bea6cf9bf41756384a7dc80603e347baba9b65d93"
4527
+ 7: "f22514ef48c50230985e605bea6cf9bf41756384a7dc80603e347baba9b65d93",
4528
+ 8: "484e72852d2b496e016a1f2a2b1a77bb993f8a163b290781522d6114b6519bb9"
4504
4529
  },
4505
4530
  "src/lib/bcms-content.ts": {
4506
4531
  1: "ae72ae14d706943d481c880d70da5789b4816ba1ba319a10fbc63ded4d0b42f2",
@@ -4509,7 +4534,8 @@ var HISTORICAL = {
4509
4534
  4: "0146d027a727ecd3c9e3fc336fcf1299b59ddf75b04091c05703f3f983ec28af",
4510
4535
  5: "c8c93655320fa65664611ba84541f3fa7bfa292cc9a2fd655373440b99049fba",
4511
4536
  6: "63f1fa29e47e70c5193b81ee24addb238ba100247124d55bc17713102d587df5",
4512
- 7: "ed92fb88de953d9951abbe6a1edae1d048f4050eaf6fb607ac08167f94d8bcc7"
4537
+ 7: "ed92fb88de953d9951abbe6a1edae1d048f4050eaf6fb607ac08167f94d8bcc7",
4538
+ 8: "1a6f669d76d42effe77f082436e9d21c0efa6fc499e919e3d116bc3a27a7f3d5"
4513
4539
  },
4514
4540
  "composables/bcms-content.ts": {
4515
4541
  1: "c272412532efed7db64b6cf313b2bed4291f9951430412289c8a588b7f565407",
@@ -4518,7 +4544,8 @@ var HISTORICAL = {
4518
4544
  4: "a5742f64dacb1b26ee6dd5e1469a54871487934c71bc8267df19e3a80e417595",
4519
4545
  5: "0472fe3ffdcfa49e719c7b9da9cb63b6eba637cc22783bb00c3c2cadd3d800da",
4520
4546
  6: "c907e7245578d947c6541b1ab2c5c6659ea7789a1519e895b4a4aed122c6f649",
4521
- 7: "f22514ef48c50230985e605bea6cf9bf41756384a7dc80603e347baba9b65d93"
4547
+ 7: "f22514ef48c50230985e605bea6cf9bf41756384a7dc80603e347baba9b65d93",
4548
+ 8: "484e72852d2b496e016a1f2a2b1a77bb993f8a163b290781522d6114b6519bb9"
4522
4549
  }
4523
4550
  };
4524
4551
  var sha256 = (value) => createHash2("sha256").update(value, "utf8").digest("hex");
@@ -6489,7 +6516,7 @@ function* formsIn(nodes, chain = []) {
6489
6516
  }
6490
6517
  }
6491
6518
  var isComponentTag = (dialect, tag) => dialect !== "html" && /^[A-Z]/.test(tag);
6492
- function candidateOf(file, dialect, form, imported, landmark) {
6519
+ function candidateOf(file, dialect, form, imported, landmark, content) {
6493
6520
  const controls = /* @__PURE__ */ new Map();
6494
6521
  const wrapper = /* @__PURE__ */ new Map();
6495
6522
  const foreign = /* @__PURE__ */ new Set();
@@ -6526,7 +6553,11 @@ function candidateOf(file, dialect, form, imported, landmark) {
6526
6553
  const id = attrOf(control, "id")?.value;
6527
6554
  if (id && !controls.has(id)) controls.set(id, control);
6528
6555
  }
6529
- return { file, dialect, el: form, controls, wrapper, foreign, landmark };
6556
+ const computed = (attr) => !!attr?.expression && !!attr.expressionRange && /[A-Za-z_$]/.test(
6557
+ content.slice(attr.expressionRange.start, attr.expressionRange.end).replace(/\/\*[\s\S]*?\*\/|\/\/[^\n]*|"(?:[^"\\\n]|\\.)*"|'(?:[^'\\\n]|\\.)*'|`(?:[^`\\$]|\\.|\$(?!\{))*`/g, "")
6558
+ );
6559
+ const dynamic = all.some((control) => !foreign.has(control) && computed(attrOf(control, "name")));
6560
+ return { file, dialect, el: form, controls, wrapper, foreign, landmark, dynamic };
6530
6561
  }
6531
6562
  async function importedControls(source, parsed, dialect, project, cache) {
6532
6563
  const out = /* @__PURE__ */ new Map();
@@ -6620,11 +6651,12 @@ async function wireForms(briefIn, sources, options_ = {}) {
6620
6651
  }
6621
6652
  const imported = dialect === "html" ? /* @__PURE__ */ new Map() : await importedControls(source, parsed, dialect, project, componentFiles);
6622
6653
  for (const { el, landmark } of formsIn(parsed.roots)) {
6623
- candidates.push(candidateOf(source.path, dialect, el, imported, landmark));
6654
+ candidates.push(candidateOf(source.path, dialect, el, imported, landmark, source.content));
6624
6655
  }
6625
6656
  }
6626
6657
  const holds = (c, f) => f.fields.filter((field) => c.controls.has(field.key)).length;
6627
- const covers2 = (c, f) => f.fields.length > 0 && holds(c, f) === f.fields.length;
6658
+ const wiredTo = (c, f) => attrOf(c.el, "data-bcms-form")?.value === f.id;
6659
+ const covers2 = (c, f) => c.dynamic && wiredTo(c, f) || f.fields.length > 0 && holds(c, f) === f.fields.length;
6628
6660
  const samePath = (a, b) => a.replace(/\\/g, "/").replace(/^\.\//, "") === b.replace(/\\/g, "/").replace(/^\.\//, "");
6629
6661
  const options = new Map(forms.map((f) => [f.id, candidates.filter((c) => covers2(c, f))]));
6630
6662
  const pinned = /* @__PURE__ */ new Map();
@@ -6707,6 +6739,11 @@ async function wireForms(briefIn, sources, options_ = {}) {
6707
6739
  continue;
6708
6740
  }
6709
6741
  const candidate = list[0];
6742
+ if (wiredTo(candidate, form)) {
6743
+ alreadyWired++;
6744
+ onSite.push({ form, file: candidate.file });
6745
+ continue;
6746
+ }
6710
6747
  if (attrOf(candidate.el, "action")?.expression) {
6711
6748
  pend2(form, "ACTION_IS_EXPRESSION", candidate.file, "its action is an expression, so this run will not replace it.");
6712
6749
  continue;
@@ -6719,11 +6756,6 @@ async function wireForms(briefIn, sources, options_ = {}) {
6719
6756
  pend2(form, "FIELD_UNMATCHED", candidate.file, `the control for ${misnamed.key} posts under an expression, so its name cannot be set to the CMS key.`);
6720
6757
  continue;
6721
6758
  }
6722
- if (attrOf(candidate.el, "data-bcms-form")?.value === form.id) {
6723
- alreadyWired++;
6724
- onSite.push({ form, file: candidate.file });
6725
- continue;
6726
- }
6727
6759
  matched.push({ form, candidate });
6728
6760
  onSite.push({ form, file: candidate.file });
6729
6761
  }
@@ -6844,7 +6876,8 @@ var TEMPLATE_DIRS = /* @__PURE__ */ new Set([
6844
6876
  "_includes",
6845
6877
  "_layouts"
6846
6878
  ]);
6847
- var isAboutTheSite = (lower) => /\.(test|spec)\.[^./]+$/.test(lower) || /^[^/]*\.config\.[^./]+$/.test(lower);
6879
+ var READ_CONFIG = /^astro\.config\.[^./]+$/;
6880
+ var isAboutTheSite = (lower) => /\.(test|spec)\.[^./]+$/.test(lower) || /^[^/]*\.config\.[^./]+$/.test(lower) && !READ_CONFIG.test(lower);
6848
6881
  function isSourceCandidate(path) {
6849
6882
  const lower = path.toLowerCase();
6850
6883
  if (SKIP_DIRS.some((dir) => lower === dir.slice(0, -1) || lower.startsWith(dir) || lower.includes(`/${dir}`)))
@@ -6891,7 +6924,7 @@ function measureUnaddressable(html) {
6891
6924
  let visible = 0;
6892
6925
  let unaddressable = 0;
6893
6926
  const buckets = /* @__PURE__ */ new Map();
6894
- const visit = (node, tag, context, bound) => {
6927
+ const visit = (node, tag, context, bound, inForm = false) => {
6895
6928
  if (node.type === "text") {
6896
6929
  const text = flat2(node.value);
6897
6930
  if (isDecorativeGlyph(text) || PLATFORM_BADGE.test(text)) return;
@@ -6911,9 +6944,10 @@ function measureUnaddressable(html) {
6911
6944
  if (attrs.has("hidden") || attrs.get("aria-hidden") === "true" || /display\s*:\s*none/i.test(attrs.get("style") ?? "") || isSkipLink(node, attrs)) {
6912
6945
  return;
6913
6946
  }
6914
- const owned = bound || BOUND_ATTRS.some((name) => attrs.has(name));
6947
+ const form = inForm || attrs.has("data-bcms-form");
6948
+ const owned = bound || BOUND_ATTRS.some((name) => attrs.has(name)) || form && attrs.has("data-bcms-form-part");
6915
6949
  const chain = CONTEXT_TAGS.has(node.tag) ? context === "body" ? node.tag : `${context}>${node.tag}` : context;
6916
- for (const child of node.children) visit(child, node.tag, chain, owned);
6950
+ for (const child of node.children) visit(child, node.tag, chain, owned, form);
6917
6951
  };
6918
6952
  for (const root of parsed.roots) visit(root, "body", "body", false);
6919
6953
  return {
@@ -8107,6 +8141,7 @@ function componentFile(dialect, path, fromFile, content, parsed, resolved, pathP
8107
8141
  provided: [
8108
8142
  "blockId",
8109
8143
  "bind",
8144
+ "bindPaths",
8110
8145
  "overrides",
8111
8146
  "page",
8112
8147
  fieldName,
@@ -8121,8 +8156,8 @@ function componentFile(dialect, path, fromFile, content, parsed, resolved, pathP
8121
8156
  const key = /^[A-Za-z_$][\w$]*$/.test(groupKey) ? groupKey : JSON.stringify(groupKey);
8122
8157
  const carriedDeclarations = carry.declarations.length > 0 ? `${carry.declarations.join("\n")}
8123
8158
  ` : "";
8124
- const copy = !snapshot ? "" : flat3 ? `const ${snapshot} = bcmsSectionPaths(page, bind, overrides, ${JSON.stringify(pathPrefix)}, ${JSON.stringify(pathMap)});` : `const ${snapshot} = { ${key}: bcmsSection(page, bind, overrides) };`;
8125
- const field = bound > 0 ? `const ${fieldName} = ${fieldsName}({ blockId, bind, overrides }, ${JSON.stringify(pathPrefix)}${flat3 ? `, ${JSON.stringify(pathMap)}` : ""});` : "";
8159
+ const copy = !snapshot ? "" : flat3 ? `const ${snapshot} = bcmsSectionPaths(page, bind, overrides, ${JSON.stringify(pathPrefix)}, ${JSON.stringify(pathMap)}, bindPaths);` : `const ${snapshot} = { ${key}: bcmsSection(page, bind, overrides, bindPaths) };`;
8160
+ const field = bound > 0 ? `const ${fieldName} = ${fieldsName}({ blockId, bind, bindPaths, overrides }, ${JSON.stringify(pathPrefix)}${flat3 ? `, ${JSON.stringify(pathMap)}` : ""});` : "";
8126
8161
  const declared = [copy, field].filter(Boolean).join("\n");
8127
8162
  const styles = dialect === "astro" ? carriedStyles(content) : "";
8128
8163
  if (dialect === "astro") {
@@ -8135,12 +8170,14 @@ interface Props {
8135
8170
  blockId?: string;
8136
8171
  /** The page field group this placement renders \u2014 Phase A keeps the copy where P2 put it. */
8137
8172
  bind?: string;
8173
+ /** A locale copy's own field path for each prop, when its keys differ from this component's. */
8174
+ bindPaths?: Record<string, string>;
8138
8175
  /** The instance's own copy, when an editor gave it any. Wins over the bound group. */
8139
8176
  overrides?: Record<string, unknown>;
8140
8177
  /** The page snapshot, so one component can serve every page whose group has this shape. */
8141
8178
  page?: unknown;
8142
8179
  }
8143
- const { blockId, bind, overrides, page } = Astro.props;
8180
+ const { blockId, bind, bindPaths, overrides, page } = Astro.props;
8144
8181
  ${carriedDeclarations}${declared}
8145
8182
  ---
8146
8183
  ${markup}
@@ -8152,7 +8189,7 @@ ${styles}
8152
8189
  ${[...carried, `import { ${helpers.join(", ")} } from "${helperSpecifier}";`, `import type { BcmsSectionProps } from "${relativeImport(path, SECTIONS_LIB).replace(/\.tsx?$/, "")}";`].join("\n")}
8153
8190
  ${carriedDeclarations === "" ? "" : `
8154
8191
  ${carriedDeclarations}`}
8155
- export default function ${resolved.name}({ blockId, bind, overrides, page }: BcmsSectionProps) {
8192
+ export default function ${resolved.name}({ blockId, bind, bindPaths, overrides, page }: BcmsSectionProps) {
8156
8193
  ${declared.split("\n").join("\n ")}
8157
8194
  return (
8158
8195
  ${markup}
@@ -8514,6 +8551,7 @@ const isRecord = (v: unknown): v is Record<string, unknown> =>
8514
8551
  export interface BcmsSectionProps {
8515
8552
  blockId?: string;
8516
8553
  bind?: string;
8554
+ bindPaths?: Record<string, string>;
8517
8555
  overrides?: Record<string, unknown>;
8518
8556
  page?: unknown;
8519
8557
  }
@@ -8523,6 +8561,8 @@ export interface BcmsPlacement {
8523
8561
  blockId?: string;
8524
8562
  slug: string;
8525
8563
  bind?: string;
8564
+ /** A locale copy's own field path for each prop, when its keys differ from the component's. */
8565
+ bindPaths?: Record<string, string>;
8526
8566
  overrides?: Record<string, unknown>;
8527
8567
  }
8528
8568
 
@@ -8583,6 +8623,7 @@ export function pageSections(pageSlug: string): BcmsPlacement[] {
8583
8623
  blockId: typeof block.id === "string" ? block.id : undefined,
8584
8624
  slug,
8585
8625
  bind: typeof props.bind === "string" ? props.bind : undefined,
8626
+ bindPaths: isRecord(props.bindPaths) ? (props.bindPaths as Record<string, string>) : undefined,
8586
8627
  overrides: isRecord(props.overrides) ? props.overrides : undefined,
8587
8628
  });
8588
8629
  }
@@ -8648,6 +8689,7 @@ const snapshot = pageSnapshot(page);
8648
8689
  <Component
8649
8690
  blockId={placement.blockId}
8650
8691
  bind={placement.bind}
8692
+ bindPaths={placement.bindPaths}
8651
8693
  overrides={placement.overrides}
8652
8694
  page={snapshot}
8653
8695
  />
@@ -8670,6 +8712,7 @@ export default function Sections({ page }: { page: string }) {
8670
8712
  key={placement.blockId ?? \`\${placement.slug}-\${i}\`}
8671
8713
  blockId={placement.blockId}
8672
8714
  bind={placement.bind}
8715
+ bindPaths={placement.bindPaths}
8673
8716
  overrides={placement.overrides}
8674
8717
  page={snapshot}
8675
8718
  />
@@ -9403,7 +9446,23 @@ async function plan(file, content, dialect, targets, sites, importAt, outcomes,
9403
9446
  continue;
9404
9447
  }
9405
9448
  const resolved = sharing.length === 1 ? { assigned: /* @__PURE__ */ new Map([[sharing[0].key, forLiteral]]), ambiguous: /* @__PURE__ */ new Set() } : resolvePositionally(sharing, forLiteral);
9449
+ const exactly = new Map(
9450
+ sharing.length > 1 ? sharing.flatMap((target) => {
9451
+ const own = forLiteral.filter(
9452
+ (site) => site.where === "text" && !site.partial && !site.mixed && isIntrinsic(site.tag) && declarations.some((d) => d.scope === target.scope && d.path === target.path && d.at === site.attrInsertAt)
9453
+ );
9454
+ return own.length > 0 ? [[target.key, own]] : [];
9455
+ }) : []
9456
+ );
9457
+ const unmarked = forLiteral.filter((site) => ![...exactly.values()].some((own) => own.includes(site)));
9458
+ const settled = unmarked.length <= sharing.length - exactly.size ? exactly : /* @__PURE__ */ new Map();
9406
9459
  for (const target of sharing) {
9460
+ const own = settled.get(target.key);
9461
+ if (own) {
9462
+ outcomes.set(target.key, "declared");
9463
+ located += own.length;
9464
+ continue;
9465
+ }
9407
9466
  if (resolved.ambiguous.has(target.key)) {
9408
9467
  outcomes.set(target.key, "AMBIGUOUS_LITERAL");
9409
9468
  continue;
@@ -9929,7 +9988,7 @@ function styleEdits(before, after) {
9929
9988
  // package.json
9930
9989
  var package_default = {
9931
9990
  name: "@bettercms-ai/convert",
9932
- version: "0.18.0",
9991
+ version: "0.19.0",
9933
9992
  type: "module",
9934
9993
  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.",
9935
9994
  bin: {
@@ -9956,12 +10015,13 @@ var package_default = {
9956
10015
  build: "tsup",
9957
10016
  test: "vitest run",
9958
10017
  "test:watch": "vitest",
9959
- briefs: "bun --conditions=development scripts/build-briefs.ts"
10018
+ briefs: "bun --conditions=development scripts/build-briefs.ts",
10019
+ prepublishOnly: "bun run build"
9960
10020
  },
9961
10021
  dependencies: {
9962
10022
  "@astrojs/compiler": "^2.13.1",
9963
10023
  "@babel/parser": "^7.29.2",
9964
- "@bettercms-ai/types": "^1.12.0",
10024
+ "@bettercms-ai/types": "^1.25.0",
9965
10025
  "@vue/compiler-sfc": "^3.5.0",
9966
10026
  "magic-string": "^0.30.21",
9967
10027
  parse5: "^7.3.0",