@bettercms-ai/convert 0.14.0 → 0.15.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/dist/index.js CHANGED
@@ -6401,9 +6401,11 @@ function* elementsIn(nodes) {
6401
6401
  var childElements = (node) => node.children.flatMap(
6402
6402
  (child) => child.type === "element" ? [child] : child.type === "expr" ? childElements(child) : []
6403
6403
  );
6404
- function candidateOf(file, dialect, form) {
6404
+ var isComponentTag = (dialect, tag) => dialect !== "html" && /^[A-Z]/.test(tag);
6405
+ function candidateOf(file, dialect, form, imported) {
6405
6406
  const controls = /* @__PURE__ */ new Map();
6406
6407
  const wrapper = /* @__PURE__ */ new Map();
6408
+ const foreign = /* @__PURE__ */ new Set();
6407
6409
  const all = [];
6408
6410
  const walk = (parent) => {
6409
6411
  const own = [];
@@ -6411,6 +6413,17 @@ function candidateOf(file, dialect, form) {
6411
6413
  if (CONTROLS.has(child.tag.toLowerCase())) {
6412
6414
  own.push(child);
6413
6415
  all.push(child);
6416
+ } else if (isComponentTag(dialect, child.tag)) {
6417
+ const inFile = attrOf(child, "name") ? [] : imported.get(child.tag) ?? [];
6418
+ if (inFile.length) {
6419
+ for (const control of inFile) {
6420
+ all.push(control);
6421
+ foreign.add(control);
6422
+ }
6423
+ } else if (attrOf(child, "name") ?? attrOf(child, "id")) {
6424
+ own.push(child);
6425
+ all.push(child);
6426
+ }
6414
6427
  }
6415
6428
  walk(child);
6416
6429
  }
@@ -6422,10 +6435,32 @@ function candidateOf(file, dialect, form) {
6422
6435
  if (name && !controls.has(name)) controls.set(name, control);
6423
6436
  }
6424
6437
  for (const control of all) {
6438
+ if (foreign.has(control)) continue;
6425
6439
  const id = attrOf(control, "id")?.value;
6426
6440
  if (id && !controls.has(id)) controls.set(id, control);
6427
6441
  }
6428
- return { file, dialect, el: form, controls, wrapper };
6442
+ return { file, dialect, el: form, controls, wrapper, foreign };
6443
+ }
6444
+ async function importedControls(source, parsed, dialect, project, cache) {
6445
+ const out = /* @__PURE__ */ new Map();
6446
+ for (const [local, { specifier }] of importsIn(parsed, dialect)) {
6447
+ const path = resolveSpecifier(source.path, specifier, project.files, project.aliases);
6448
+ const child = path === null || path === source.path ? void 0 : project.byPath.get(path);
6449
+ if (!child) continue;
6450
+ let controls = cache.get(child.path);
6451
+ if (!controls) {
6452
+ controls = (async () => {
6453
+ const own = dialectOf(child.path, child.content);
6454
+ if (!own || !WIRABLE.includes(own)) return [];
6455
+ const file = await parseFile4(own, child.content);
6456
+ return file.error ? [] : [...elementsIn(file.roots)].filter((el) => CONTROLS.has(el.tag.toLowerCase()));
6457
+ })();
6458
+ cache.set(child.path, controls);
6459
+ }
6460
+ const found = await controls;
6461
+ if (found.length) out.set(local, found);
6462
+ }
6463
+ return out;
6429
6464
  }
6430
6465
  var hasMessage = (form) => [...elementsIn([form])].some((el) => (attrOf(el, "class") ?? attrOf(el, "classname"))?.value.includes("bcms-form-msg") ?? false);
6431
6466
  function wire(source, candidate, form) {
@@ -6444,6 +6479,7 @@ function wire(source, candidate, form) {
6444
6479
  source.appendLeft(el.attrInsertAt, ` ${inserts.join(" ")}`);
6445
6480
  for (const field of form.fields) {
6446
6481
  const control = candidate.controls.get(field.key);
6482
+ if (candidate.foreign.has(control)) continue;
6447
6483
  const host = candidate.wrapper.get(control) ?? control;
6448
6484
  source.appendLeft(host.attrInsertAt, ` data-bcms-form-field="${escapeAttr(field.key)}"`);
6449
6485
  const name = attrOf(control, "name");
@@ -6462,6 +6498,12 @@ async function wireForms(briefIn, sources) {
6462
6498
  const forms = brief.forms ?? [];
6463
6499
  const candidates = [];
6464
6500
  const unreadable = [];
6501
+ const componentFiles = /* @__PURE__ */ new Map();
6502
+ const project = {
6503
+ byPath: new Map(sources.map((s) => [s.path, s])),
6504
+ files: new Set(sources.map((s) => s.path)),
6505
+ aliases: aliasesFrom(sources)
6506
+ };
6465
6507
  for (const source of sources) {
6466
6508
  if (!/<form[\s/>]/i.test(source.content)) continue;
6467
6509
  const dialect = dialectOf(source.path, source.content);
@@ -6482,8 +6524,9 @@ async function wireForms(briefIn, sources) {
6482
6524
  });
6483
6525
  continue;
6484
6526
  }
6527
+ const imported = dialect === "html" ? /* @__PURE__ */ new Map() : await importedControls(source, parsed, dialect, project, componentFiles);
6485
6528
  for (const el of elementsIn(parsed.roots)) {
6486
- if (el.tag.toLowerCase() === "form") candidates.push(candidateOf(source.path, dialect, el));
6529
+ if (el.tag.toLowerCase() === "form") candidates.push(candidateOf(source.path, dialect, el, imported));
6487
6530
  }
6488
6531
  }
6489
6532
  const holds = (c, f) => f.fields.filter((field) => c.controls.has(field.key)).length;
@@ -6556,6 +6599,14 @@ ${indent}${scriptFor(last.candidate.dialect)}`);
6556
6599
  files.push({ path, operation: "modify", content: source.toString() });
6557
6600
  }
6558
6601
  const notes = onSite.filter(({ form }) => form.status === "draft").map(({ form }) => `publish form ${form.name} in the Forms tab \u2014 a draft form rejects every submission with 403.`);
6602
+ for (const { form, candidate } of matched) {
6603
+ const keys = form.fields.filter((field) => candidate.foreign.has(candidate.controls.get(field.key))).map((field) => field.key);
6604
+ if (keys.length > 0) {
6605
+ notes.push(
6606
+ `form ${form.name}: ${keys.join(", ")} matched a control inside an imported component, so no data-bcms-form-field marker was written for ${keys.length === 1 ? "it" : "them"}.`
6607
+ );
6608
+ }
6609
+ }
6559
6610
  return {
6560
6611
  files,
6561
6612
  receipt: {