@bettercms-ai/convert 0.18.2 → 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,20 @@
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
+
3
18
  ## 0.18.2
4
19
 
5
20
  ### 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");
@@ -8114,6 +8141,7 @@ function componentFile(dialect, path, fromFile, content, parsed, resolved, pathP
8114
8141
  provided: [
8115
8142
  "blockId",
8116
8143
  "bind",
8144
+ "bindPaths",
8117
8145
  "overrides",
8118
8146
  "page",
8119
8147
  fieldName,
@@ -8128,8 +8156,8 @@ function componentFile(dialect, path, fromFile, content, parsed, resolved, pathP
8128
8156
  const key = /^[A-Za-z_$][\w$]*$/.test(groupKey) ? groupKey : JSON.stringify(groupKey);
8129
8157
  const carriedDeclarations = carry.declarations.length > 0 ? `${carry.declarations.join("\n")}
8130
8158
  ` : "";
8131
- const copy = !snapshot ? "" : flat3 ? `const ${snapshot} = bcmsSectionPaths(page, bind, overrides, ${JSON.stringify(pathPrefix)}, ${JSON.stringify(pathMap)});` : `const ${snapshot} = { ${key}: bcmsSection(page, bind, overrides) };`;
8132
- 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)}` : ""});` : "";
8133
8161
  const declared = [copy, field].filter(Boolean).join("\n");
8134
8162
  const styles = dialect === "astro" ? carriedStyles(content) : "";
8135
8163
  if (dialect === "astro") {
@@ -8142,12 +8170,14 @@ interface Props {
8142
8170
  blockId?: string;
8143
8171
  /** The page field group this placement renders \u2014 Phase A keeps the copy where P2 put it. */
8144
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>;
8145
8175
  /** The instance's own copy, when an editor gave it any. Wins over the bound group. */
8146
8176
  overrides?: Record<string, unknown>;
8147
8177
  /** The page snapshot, so one component can serve every page whose group has this shape. */
8148
8178
  page?: unknown;
8149
8179
  }
8150
- const { blockId, bind, overrides, page } = Astro.props;
8180
+ const { blockId, bind, bindPaths, overrides, page } = Astro.props;
8151
8181
  ${carriedDeclarations}${declared}
8152
8182
  ---
8153
8183
  ${markup}
@@ -8159,7 +8189,7 @@ ${styles}
8159
8189
  ${[...carried, `import { ${helpers.join(", ")} } from "${helperSpecifier}";`, `import type { BcmsSectionProps } from "${relativeImport(path, SECTIONS_LIB).replace(/\.tsx?$/, "")}";`].join("\n")}
8160
8190
  ${carriedDeclarations === "" ? "" : `
8161
8191
  ${carriedDeclarations}`}
8162
- export default function ${resolved.name}({ blockId, bind, overrides, page }: BcmsSectionProps) {
8192
+ export default function ${resolved.name}({ blockId, bind, bindPaths, overrides, page }: BcmsSectionProps) {
8163
8193
  ${declared.split("\n").join("\n ")}
8164
8194
  return (
8165
8195
  ${markup}
@@ -8521,6 +8551,7 @@ const isRecord = (v: unknown): v is Record<string, unknown> =>
8521
8551
  export interface BcmsSectionProps {
8522
8552
  blockId?: string;
8523
8553
  bind?: string;
8554
+ bindPaths?: Record<string, string>;
8524
8555
  overrides?: Record<string, unknown>;
8525
8556
  page?: unknown;
8526
8557
  }
@@ -8530,6 +8561,8 @@ export interface BcmsPlacement {
8530
8561
  blockId?: string;
8531
8562
  slug: string;
8532
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>;
8533
8566
  overrides?: Record<string, unknown>;
8534
8567
  }
8535
8568
 
@@ -8590,6 +8623,7 @@ export function pageSections(pageSlug: string): BcmsPlacement[] {
8590
8623
  blockId: typeof block.id === "string" ? block.id : undefined,
8591
8624
  slug,
8592
8625
  bind: typeof props.bind === "string" ? props.bind : undefined,
8626
+ bindPaths: isRecord(props.bindPaths) ? (props.bindPaths as Record<string, string>) : undefined,
8593
8627
  overrides: isRecord(props.overrides) ? props.overrides : undefined,
8594
8628
  });
8595
8629
  }
@@ -8655,6 +8689,7 @@ const snapshot = pageSnapshot(page);
8655
8689
  <Component
8656
8690
  blockId={placement.blockId}
8657
8691
  bind={placement.bind}
8692
+ bindPaths={placement.bindPaths}
8658
8693
  overrides={placement.overrides}
8659
8694
  page={snapshot}
8660
8695
  />
@@ -8677,6 +8712,7 @@ export default function Sections({ page }: { page: string }) {
8677
8712
  key={placement.blockId ?? \`\${placement.slug}-\${i}\`}
8678
8713
  blockId={placement.blockId}
8679
8714
  bind={placement.bind}
8715
+ bindPaths={placement.bindPaths}
8680
8716
  overrides={placement.overrides}
8681
8717
  page={snapshot}
8682
8718
  />
@@ -9952,7 +9988,7 @@ function styleEdits(before, after) {
9952
9988
  // package.json
9953
9989
  var package_default = {
9954
9990
  name: "@bettercms-ai/convert",
9955
- version: "0.18.2",
9991
+ version: "0.19.0",
9956
9992
  type: "module",
9957
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.",
9958
9994
  bin: {
@@ -9985,7 +10021,7 @@ var package_default = {
9985
10021
  dependencies: {
9986
10022
  "@astrojs/compiler": "^2.13.1",
9987
10023
  "@babel/parser": "^7.29.2",
9988
- "@bettercms-ai/types": "^1.12.0",
10024
+ "@bettercms-ai/types": "^1.25.0",
9989
10025
  "@vue/compiler-sfc": "^3.5.0",
9990
10026
  "magic-string": "^0.30.21",
9991
10027
  parse5: "^7.3.0",