@barefootjs/xslate 0.14.0 → 0.15.1

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/build.js CHANGED
@@ -187292,6 +187292,8 @@ import {
187292
187292
  BaseAdapter,
187293
187293
  isBooleanAttr,
187294
187294
  parseExpression as parseExpression2,
187295
+ exprToString,
187296
+ parseProviderObjectLiteral,
187295
187297
  parseStyleObjectEntries,
187296
187298
  isSupported,
187297
187299
  identifierPath,
@@ -187301,10 +187303,13 @@ import {
187301
187303
  augmentInheritedPropAccesses,
187302
187304
  parseRecordIndexAccess,
187303
187305
  evalStringArrayJoin,
187306
+ collectModuleStringConsts,
187304
187307
  extractArrowBodyExpression,
187305
187308
  collectContextConsumers,
187306
187309
  isLowerableObjectRestDestructure,
187307
- extractSsrDefaults
187310
+ lookupStaticRecordLiteral,
187311
+ searchParamsLocalNames,
187312
+ matchSearchParamsMethodCall
187308
187313
  } from "@barefootjs/jsx";
187309
187314
 
187310
187315
  // src/adapter/boolean-result.ts
@@ -187351,6 +187356,8 @@ var ARIA_BOOLEAN_ATTRS = new Set([
187351
187356
  "aria-multiselectable",
187352
187357
  "aria-readonly",
187353
187358
  "aria-required",
187359
+ "aria-selected",
187360
+ "aria-expanded",
187354
187361
  "aria-checked",
187355
187362
  "aria-pressed"
187356
187363
  ]);
@@ -187360,7 +187367,7 @@ function isAriaBooleanAttr(name) {
187360
187367
 
187361
187368
  // src/adapter/xslate-adapter.ts
187362
187369
  var import_typescript = __toESM(require_typescript(), 1);
187363
- import { BF_SLOT, BF_COND } from "@barefootjs/shared";
187370
+ import { BF_SLOT, BF_COND, BF_REGION } from "@barefootjs/shared";
187364
187371
  var XSLATE_TEMPLATE_PRIMITIVES = {
187365
187372
  "JSON.stringify": { arity: 1, emit: (args) => `$bf.json(${args[0]})` },
187366
187373
  String: { arity: 1, emit: (args) => `$bf.string(${args[0]})` },
@@ -187428,8 +187435,10 @@ class XslateAdapter extends BaseAdapter {
187428
187435
  inLoop = false;
187429
187436
  propsObjectName = null;
187430
187437
  propsParams = [];
187438
+ booleanTypedProps = new Set;
187431
187439
  stringValueNames = new Set;
187432
187440
  moduleStringConsts = new Map;
187441
+ _searchParamsLocals = new Set;
187433
187442
  localConstants = [];
187434
187443
  nullableOptionalProps = new Set;
187435
187444
  constructor(options = {}) {
@@ -187444,6 +187453,7 @@ class XslateAdapter extends BaseAdapter {
187444
187453
  this.propsObjectName = ir.metadata.propsObjectName ?? null;
187445
187454
  augmentInheritedPropAccesses(ir);
187446
187455
  this.propsParams = ir.metadata.propsParams.map((p) => ({ name: p.name }));
187456
+ this.booleanTypedProps = new Set(ir.metadata.propsParams.filter((prop) => prop.type?.primitive === "boolean" || prop.type?.raw === "boolean").map((prop) => prop.name));
187447
187457
  this.localConstants = ir.metadata.localConstants ?? [];
187448
187458
  this.nullableOptionalProps = new Set(ir.metadata.propsParams.filter((p) => p.defaultValue === undefined && !p.isRest && p.type?.kind !== "primitive").map((p) => p.name));
187449
187459
  this.stringValueNames = new Set;
@@ -187457,6 +187467,7 @@ class XslateAdapter extends BaseAdapter {
187457
187467
  this.stringValueNames.add(p.name);
187458
187468
  }
187459
187469
  this.moduleStringConsts = collectModuleStringConsts(ir.metadata.localConstants);
187470
+ this._searchParamsLocals = searchParamsLocalNames(ir.metadata);
187460
187471
  this.errors = [];
187461
187472
  this.childrenCaptureCounter = 0;
187462
187473
  if (!options?.siblingTemplatesRegistered) {
@@ -187542,12 +187553,29 @@ class XslateAdapter extends BaseAdapter {
187542
187553
  if (v.kind === "literal") {
187543
187554
  return typeof v.value === "string" ? `'${v.value.replace(/[\\']/g, (m) => `\\${m}`)}'` : String(v.value);
187544
187555
  }
187545
- if (v.kind === "expression")
187556
+ if (v.kind === "expression") {
187557
+ const hashref = this.providerObjectLiteralKolon(v.expr);
187558
+ if (hashref !== null)
187559
+ return hashref;
187546
187560
  return this.convertExpressionToKolon(v.expr);
187561
+ }
187547
187562
  if (v.kind === "template")
187548
187563
  return this.convertTemplateLiteralPartsToKolon(v.parts);
187549
187564
  return "nil";
187550
187565
  }
187566
+ providerObjectLiteralKolon(expr) {
187567
+ const members = parseProviderObjectLiteral(expr.trim());
187568
+ if (members === null)
187569
+ return null;
187570
+ const entries = members.map((m) => {
187571
+ const key = `'${m.name.replace(/[\\']/g, (c) => `\\${c}`)}'`;
187572
+ if (m.kind === "function" || /^on[A-Z]/.test(m.name))
187573
+ return `${key} => nil`;
187574
+ const src = m.kind === "getter" ? m.body : m.expr;
187575
+ return `${key} => ${this.convertExpressionToKolon(src)}`;
187576
+ });
187577
+ return `{ ${entries.join(", ")} }`;
187578
+ }
187551
187579
  contextDefaultKolon(c) {
187552
187580
  const d = c.defaultValue;
187553
187581
  if (d === null || d === undefined)
@@ -187571,7 +187599,6 @@ class XslateAdapter extends BaseAdapter {
187571
187599
  const signals = ir.metadata.signals ?? [];
187572
187600
  if (memos.length === 0 && signals.length === 0)
187573
187601
  return "";
187574
- const ssrDefaults = extractSsrDefaults(ir.metadata) ?? {};
187575
187602
  const available = new Set(ir.metadata.propsParams.map((p) => p.name));
187576
187603
  const lines = [];
187577
187604
  for (const signal of signals) {
@@ -187582,18 +187609,13 @@ class XslateAdapter extends BaseAdapter {
187582
187609
  available.add(signal.getter);
187583
187610
  }
187584
187611
  for (const memo of memos) {
187585
- const def = ssrDefaults[memo.name];
187586
- const isNull2 = !def || typeof def === "object" && "value" in def && def.value === null;
187587
- if (!isNull2) {
187588
- available.add(memo.name);
187589
- continue;
187590
- }
187591
187612
  const body = extractArrowBodyExpression(memo.computation);
187592
- if (body === null)
187593
- continue;
187594
- const kolon = this.tryLowerToKolon(body, available);
187595
- if (kolon !== null)
187596
- lines.push(`: my $${memo.name} = ${kolon};`);
187613
+ if (body !== null) {
187614
+ const kolon = this.tryLowerToKolon(body, available);
187615
+ const refsSelf = kolon !== null && new RegExp(`\\$${memo.name}\\b`).test(kolon);
187616
+ if (kolon !== null && !refsSelf)
187617
+ lines.push(`: my $${memo.name} = ${kolon};`);
187618
+ }
187597
187619
  available.add(memo.name);
187598
187620
  }
187599
187621
  return lines.length > 0 ? lines.join(`
@@ -187628,6 +187650,9 @@ class XslateAdapter extends BaseAdapter {
187628
187650
  if (element.slotId) {
187629
187651
  hydrationAttrs += ` ${this.renderSlotMarker(element.slotId)}`;
187630
187652
  }
187653
+ if (element.regionId) {
187654
+ hydrationAttrs += ` ${BF_REGION}="${element.regionId}"`;
187655
+ }
187631
187656
  const voidElements = [
187632
187657
  "area",
187633
187658
  "base",
@@ -187895,7 +187920,7 @@ ${childrenUnderLoop}` : childrenUnderLoop;
187895
187920
  renderComponent(comp) {
187896
187921
  const propParts = [];
187897
187922
  for (const p of comp.props) {
187898
- if (p.name.match(/^on[A-Z]/) && p.value.kind === "expression")
187923
+ if ((p.name.match(/^on[A-Z]/) || p.name === "ref") && p.value.kind === "expression")
187899
187924
  continue;
187900
187925
  if (p.value.kind === "spread") {
187901
187926
  const trimmed = p.value.expr.trim();
@@ -187926,7 +187951,10 @@ ${childrenUnderLoop}` : childrenUnderLoop;
187926
187951
  const tplName = this.toTemplateName(comp.name);
187927
187952
  const effectiveChildren = comp.children.length > 0 ? comp.children : resolveJsxChildrenProp(comp.props);
187928
187953
  if (effectiveChildren.length > 0) {
187954
+ const prevInLoop = this.inLoop;
187955
+ this.inLoop = false;
187929
187956
  const childrenBody = this.renderChildren(effectiveChildren);
187957
+ this.inLoop = prevInLoop;
187930
187958
  const macroName = `bf_children_${comp.slotId ?? "c" + this.childrenCaptureCounter++}`;
187931
187959
  const childrenEntry = `children => ${macroName}()`;
187932
187960
  const allParts = [...propParts, childrenEntry];
@@ -187936,6 +187964,7 @@ ${childrenUnderLoop}` : childrenUnderLoop;
187936
187964
  return `<: $bf.render_child('${tplName}'${hashEntries}) | mark_raw :>`;
187937
187965
  }
187938
187966
  childrenCaptureCounter = 0;
187967
+ presenceVarCounter = 0;
187939
187968
  toTemplateName(componentName) {
187940
187969
  return componentName.replace(/([A-Z])/g, "_$1").toLowerCase().replace(/^_/, "");
187941
187970
  }
@@ -187991,18 +188020,41 @@ ${children}`;
187991
188020
  const normalizedBareId = this.propsObjectName && bareId.startsWith(`${this.propsObjectName}.`) ? bareId.slice(this.propsObjectName.length + 1) : bareId;
187992
188021
  if (!isBooleanAttr(name) && !value.presenceOrUndefined && /^[A-Za-z_$][\w$]*$/.test(normalizedBareId) && this.nullableOptionalProps.has(normalizedBareId)) {
187993
188022
  const perl2 = this.convertExpressionToKolon(value.expr);
187994
- const body = isBooleanResultExpr(value.expr) || isAriaBooleanAttr(name) ? `${name}="<: $bf.bool_str(${perl2}) :>"` : `${name}="<: ${perl2} :>"`;
188023
+ const body = isBooleanResultExpr(value.expr) || isAriaBooleanAttr(name) || this.isBooleanTypedPropRef(value.expr) ? `${name}="<: $bf.bool_str(${perl2}) :>"` : `${name}="<: ${perl2} :>"`;
187995
188024
  return `
187996
188025
  : if (defined ${perl2}) {
187997
188026
  ${body}
187998
188027
  : }
187999
188028
  `;
188000
188029
  }
188001
- if (isBooleanAttr(name) || value.presenceOrUndefined) {
188030
+ if (isBooleanAttr(name)) {
188002
188031
  return `<: ${this.convertExpressionToKolon(value.expr)} ? '${name}' : '' :>`;
188003
188032
  }
188033
+ if (value.presenceOrUndefined) {
188034
+ const perl2 = this.convertExpressionToKolon(value.expr);
188035
+ const tmp = `$bf_pu${this.presenceVarCounter++}`;
188036
+ const body = isBooleanResultExpr(value.expr) || isAriaBooleanAttr(name) || this.isBooleanTypedPropRef(value.expr) ? `${name}="<: $bf.bool_str(${tmp}) :>"` : `${name}="<: ${tmp} :>"`;
188037
+ return `
188038
+ : my ${tmp} = ${perl2};
188039
+ : if (${tmp}) {
188040
+ ${body}
188041
+ : }
188042
+ `;
188043
+ }
188044
+ {
188045
+ const m = this.parseUndefinedAlternateTernary(value.expr);
188046
+ if (m) {
188047
+ const cond = this.convertExpressionToKolon(m.condition);
188048
+ const val = this.convertExpressionToKolon(m.consequent);
188049
+ return `
188050
+ : if (${cond}) {
188051
+ ${name}="<: ${val} :>"
188052
+ : }
188053
+ `;
188054
+ }
188055
+ }
188004
188056
  const perl = this.convertExpressionToKolon(value.expr);
188005
- if (isBooleanResultExpr(value.expr) || isAriaBooleanAttr(name)) {
188057
+ if (isBooleanResultExpr(value.expr) || isAriaBooleanAttr(name) || this.isBooleanTypedPropRef(value.expr)) {
188006
188058
  return `${name}="<: $bf.bool_str(${perl}) :>"`;
188007
188059
  }
188008
188060
  return `${name}="<: ${perl} :>"`;
@@ -188261,6 +188313,18 @@ ${reason}` : "";
188261
188313
  return n;
188262
188314
  })();
188263
188315
  const indexed = this.recordIndexAccessToKolon(initNode);
188316
+ if (indexed === null && import_typescript.default.isElementAccessExpression(initNode) && initNode.argumentExpression && !import_typescript.default.isNumericLiteral(initNode.argumentExpression) && !import_typescript.default.isStringLiteral(initNode.argumentExpression)) {
188317
+ this.errors.push({
188318
+ code: "BF101",
188319
+ severity: "error",
188320
+ message: `Spread object value '${initNode.getText(sf)}' indexes a record map whose values aren't scalar literals — it can't lower to an inline Kolon hashref.`,
188321
+ loc: { file: this.componentName + ".tsx", start: { line: 1, column: 0 }, end: { line: 1, column: 0 } },
188322
+ suggestion: {
188323
+ message: "Index a record whose values are number/string literals, or move the spread into a `'use client'` component so hydration computes it."
188324
+ }
188325
+ });
188326
+ return null;
188327
+ }
188264
188328
  const valPerl = indexed !== null ? indexed : this.convertExpressionToKolon(prop.initializer.getText(sf));
188265
188329
  entries.push(`'${escapeKolonSingleQuoted(key)}' => ${valPerl}`);
188266
188330
  }
@@ -188308,6 +188372,46 @@ Options:
188308
188372
  _isStringValueName(name) {
188309
188373
  return this.stringValueNames.has(name);
188310
188374
  }
188375
+ parseUndefinedAlternateTernary(expr) {
188376
+ const parsed = parseExpression2(expr.trim());
188377
+ if (parsed?.kind !== "conditional")
188378
+ return null;
188379
+ const alt = parsed.alternate;
188380
+ const isUndef = alt.kind === "identifier" && (alt.name === "undefined" || alt.name === "null") || alt.kind === "literal" && (alt.value === null || alt.value === undefined);
188381
+ if (!isUndef)
188382
+ return null;
188383
+ return {
188384
+ condition: exprToString(parsed.test),
188385
+ consequent: exprToString(parsed.consequent)
188386
+ };
188387
+ }
188388
+ isBooleanTypedPropRef(expr) {
188389
+ let bare = expr.trim();
188390
+ if (this.propsObjectName && bare.startsWith(`${this.propsObjectName}.`)) {
188391
+ bare = bare.slice(this.propsObjectName.length + 1);
188392
+ }
188393
+ if (!/^[A-Za-z_$][\w$]*$/.test(bare))
188394
+ return false;
188395
+ return this.booleanTypedProps.has(bare);
188396
+ }
188397
+ _resolveLiteralConst(name) {
188398
+ const c = (this.localConstants ?? []).find((lc) => lc.name === name);
188399
+ if (c?.value === undefined)
188400
+ return null;
188401
+ const v = c.value.trim();
188402
+ if (/^-?\d+(\.\d+)?$/.test(v))
188403
+ return v;
188404
+ const strLit = /^'([^'\\]*)'$/.exec(v) ?? /^"([^"\\]*)"$/.exec(v);
188405
+ if (strLit)
188406
+ return `'${strLit[1].replace(/[\\']/g, (m) => `\\${m}`)}'`;
188407
+ return null;
188408
+ }
188409
+ _resolveStaticRecordLiteral(objectName, key) {
188410
+ const hit = lookupStaticRecordLiteral(objectName, key, this.localConstants);
188411
+ if (!hit)
188412
+ return null;
188413
+ return hit.kind === "number" ? hit.text : `'${hit.text.replace(/[\\']/g, (m) => `\\${m}`)}'`;
188414
+ }
188311
188415
  _resolveModuleStringConst(name) {
188312
188416
  if (this.inLoop)
188313
188417
  return null;
@@ -188392,6 +188496,11 @@ function renderArrayMethod(method, object, args, emit) {
188392
188496
  const recv = emit(object);
188393
188497
  return `$bf.trim(${recv})`;
188394
188498
  }
188499
+ case "toFixed": {
188500
+ const recv = emit(object);
188501
+ const digits = args.length >= 1 ? emit(args[0]) : "0";
188502
+ return `$bf.to_fixed(${recv}, ${digits})`;
188503
+ }
188395
188504
  case "split": {
188396
188505
  const recv = emit(object);
188397
188506
  if (args.length === 0) {
@@ -188471,68 +188580,6 @@ function renderFlatMapMethod(recv, op) {
188471
188580
  return `$bf.flat_map(${recv}, 'self', '')`;
188472
188581
  return `$bf.flat_map(${recv}, 'field', '${proj.field}')`;
188473
188582
  }
188474
- function parsePureStringLiteral(source) {
188475
- let s = source.trim();
188476
- while (s.startsWith("(") && s.endsWith(")"))
188477
- s = s.slice(1, -1).trim();
188478
- const quote = s[0];
188479
- if ((quote === "'" || quote === '"') && s[s.length - 1] === quote) {
188480
- const body = s.slice(1, -1);
188481
- if (containsUnescaped(body, quote))
188482
- return null;
188483
- return unescapeStringLiteralBody(body);
188484
- }
188485
- if (quote === "`" && s[s.length - 1] === "`") {
188486
- const body = s.slice(1, -1);
188487
- if (body.includes("${"))
188488
- return null;
188489
- if (containsUnescaped(body, "`"))
188490
- return null;
188491
- return unescapeStringLiteralBody(body);
188492
- }
188493
- return evalStringArrayJoin(source);
188494
- }
188495
- function containsUnescaped(s, ch) {
188496
- for (let i2 = 0;i2 < s.length; i2++) {
188497
- if (s[i2] === "\\") {
188498
- i2++;
188499
- continue;
188500
- }
188501
- if (s[i2] === ch)
188502
- return true;
188503
- }
188504
- return false;
188505
- }
188506
- function unescapeStringLiteralBody(s) {
188507
- return s.replace(/\\(.)/g, (_, c) => {
188508
- switch (c) {
188509
- case "n":
188510
- return `
188511
- `;
188512
- case "r":
188513
- return "\r";
188514
- case "t":
188515
- return "\t";
188516
- case "0":
188517
- return "\x00";
188518
- default:
188519
- return c;
188520
- }
188521
- });
188522
- }
188523
- function collectModuleStringConsts(constants3) {
188524
- const map = new Map;
188525
- for (const c of constants3 ?? []) {
188526
- if (!c.isModule)
188527
- continue;
188528
- if (c.value === undefined)
188529
- continue;
188530
- const literal = parsePureStringLiteral(c.value);
188531
- if (literal !== null)
188532
- map.set(c.name, literal);
188533
- }
188534
- return map;
188535
- }
188536
188583
  function isStringTypeInfo(type2) {
188537
188584
  return type2?.kind === "primitive" && type2.primitive === "string";
188538
188585
  }
@@ -188574,6 +188621,9 @@ class XslateFilterEmitter {
188574
188621
  }
188575
188622
  return `${emit(object)}.${property}`;
188576
188623
  }
188624
+ indexAccess(object, index, emit) {
188625
+ return `${emit(object)}[${emit(index)}]`;
188626
+ }
188577
188627
  call(callee, args, emit) {
188578
188628
  if (callee.kind === "identifier" && args.length === 0) {
188579
188629
  return `$${callee.name}`;
@@ -188657,9 +188707,14 @@ class XslateTopLevelEmitter {
188657
188707
  this.adapter = adapter;
188658
188708
  }
188659
188709
  identifier(name) {
188710
+ if (name === "undefined" || name === "null")
188711
+ return "nil";
188660
188712
  const inlined = this.adapter._resolveModuleStringConst(name);
188661
188713
  if (inlined !== null)
188662
188714
  return inlined;
188715
+ const literalConst = this.adapter._resolveLiteralConst(name);
188716
+ if (literalConst !== null)
188717
+ return literalConst;
188663
188718
  return `$${name}`;
188664
188719
  }
188665
188720
  literal(value, literalType) {
@@ -188675,15 +188730,29 @@ class XslateTopLevelEmitter {
188675
188730
  if (object.kind === "identifier" && object.name === "props") {
188676
188731
  return `$${property}`;
188677
188732
  }
188733
+ if (object.kind === "identifier") {
188734
+ const staticValue = this.adapter._resolveStaticRecordLiteral(object.name, property);
188735
+ if (staticValue !== null)
188736
+ return staticValue;
188737
+ }
188678
188738
  const obj = emit(object);
188679
188739
  if (property === "length")
188680
188740
  return `$bf.length(${obj})`;
188681
188741
  return `${obj}.${property}`;
188682
188742
  }
188743
+ indexAccess(object, index, emit) {
188744
+ return `${emit(object)}[${emit(index)}]`;
188745
+ }
188683
188746
  call(callee, args, emit) {
188684
188747
  if (callee.kind === "identifier" && args.length === 0) {
188685
188748
  return `$${callee.name}`;
188686
188749
  }
188750
+ if (this.adapter._searchParamsLocals.size > 0) {
188751
+ const sp = matchSearchParamsMethodCall(callee, args, this.adapter._searchParamsLocals);
188752
+ if (sp) {
188753
+ return `$searchParams.${sp.method}(${sp.args.map(emit).join(", ")})`;
188754
+ }
188755
+ }
188687
188756
  const path = identifierPath(callee);
188688
188757
  const spec = path ? XSLATE_TEMPLATE_PRIMITIVES[path] : undefined;
188689
188758
  if (path && spec) {