@barefootjs/xslate 0.16.0 → 0.17.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/adapter/analysis/component-tree.d.ts +30 -0
- package/dist/adapter/analysis/component-tree.d.ts.map +1 -0
- package/dist/adapter/emit-context.d.ts +94 -0
- package/dist/adapter/emit-context.d.ts.map +1 -0
- package/dist/adapter/expr/array-method.d.ts +63 -0
- package/dist/adapter/expr/array-method.d.ts.map +1 -0
- package/dist/adapter/expr/emitters.d.ts +97 -0
- package/dist/adapter/expr/emitters.d.ts.map +1 -0
- package/dist/adapter/expr/operand.d.ts +25 -0
- package/dist/adapter/expr/operand.d.ts.map +1 -0
- package/dist/adapter/index.js +1428 -1326
- package/dist/adapter/lib/constants.d.ts +22 -0
- package/dist/adapter/lib/constants.d.ts.map +1 -0
- package/dist/adapter/lib/ir-scope.d.ts +34 -0
- package/dist/adapter/lib/ir-scope.d.ts.map +1 -0
- package/dist/adapter/lib/kolon-naming.d.ts +22 -0
- package/dist/adapter/lib/kolon-naming.d.ts.map +1 -0
- package/dist/adapter/lib/types.d.ts +27 -0
- package/dist/adapter/lib/types.d.ts.map +1 -0
- package/dist/adapter/memo/seed.d.ts +38 -0
- package/dist/adapter/memo/seed.d.ts.map +1 -0
- package/dist/adapter/props/prop-classes.d.ts +32 -0
- package/dist/adapter/props/prop-classes.d.ts.map +1 -0
- package/dist/adapter/spread/spread-codegen.d.ts +69 -0
- package/dist/adapter/spread/spread-codegen.d.ts.map +1 -0
- package/dist/adapter/value/parsed-literal.d.ts +32 -0
- package/dist/adapter/value/parsed-literal.d.ts.map +1 -0
- package/dist/adapter/xslate-adapter.d.ts +38 -92
- package/dist/adapter/xslate-adapter.d.ts.map +1 -1
- package/dist/build.js +1428 -1326
- package/dist/index.js +1428 -1326
- package/lib/BarefootJS/Backend/Xslate.pm +1 -1
- package/package.json +3 -3
- package/src/__tests__/query-href.test.ts +96 -0
- package/src/__tests__/xslate-adapter.test.ts +55 -0
- package/src/adapter/analysis/component-tree.ts +123 -0
- package/src/adapter/emit-context.ts +104 -0
- package/src/adapter/expr/array-method.ts +311 -0
- package/src/adapter/expr/emitters.ts +530 -0
- package/src/adapter/expr/operand.ts +35 -0
- package/src/adapter/lib/constants.ts +34 -0
- package/src/adapter/lib/ir-scope.ts +64 -0
- package/src/adapter/lib/kolon-naming.ts +27 -0
- package/src/adapter/lib/types.ts +30 -0
- package/src/adapter/memo/seed.ts +125 -0
- package/src/adapter/props/prop-classes.ts +64 -0
- package/src/adapter/spread/spread-codegen.ts +179 -0
- package/src/adapter/value/parsed-literal.ts +80 -0
- package/src/adapter/xslate-adapter.ts +169 -1233
- package/src/test-render.ts +3 -0
package/dist/index.js
CHANGED
|
@@ -187291,25 +187291,23 @@ Additional information: BADCLIENT: Bad error code, ${badCode} not found in range
|
|
|
187291
187291
|
import {
|
|
187292
187292
|
BaseAdapter,
|
|
187293
187293
|
isBooleanAttr,
|
|
187294
|
-
parseExpression as
|
|
187294
|
+
parseExpression as parseExpression3,
|
|
187295
|
+
stringifyParsedExpr as stringifyParsedExpr2,
|
|
187295
187296
|
exprToString,
|
|
187296
187297
|
parseProviderObjectLiteral,
|
|
187297
187298
|
parseStyleObjectEntries,
|
|
187298
|
-
isSupported,
|
|
187299
|
-
identifierPath,
|
|
187299
|
+
isSupported as isSupported2,
|
|
187300
187300
|
emitParsedExpr,
|
|
187301
187301
|
emitIRNode,
|
|
187302
187302
|
emitAttrValue,
|
|
187303
187303
|
augmentInheritedPropAccesses,
|
|
187304
|
-
parseRecordIndexAccess,
|
|
187305
|
-
evalStringArrayJoin,
|
|
187306
187304
|
collectModuleStringConsts,
|
|
187307
|
-
extractArrowBodyExpression,
|
|
187308
|
-
collectContextConsumers,
|
|
187309
187305
|
isLowerableObjectRestDestructure,
|
|
187310
187306
|
lookupStaticRecordLiteral,
|
|
187311
187307
|
searchParamsLocalNames,
|
|
187312
|
-
|
|
187308
|
+
prepareLoweringMatchers,
|
|
187309
|
+
queryHrefArgs,
|
|
187310
|
+
sortComparatorFromArrow as sortComparatorFromArrow2
|
|
187313
187311
|
} from "@barefootjs/jsx";
|
|
187314
187312
|
|
|
187315
187313
|
// src/adapter/boolean-result.ts
|
|
@@ -187366,8 +187364,9 @@ function isAriaBooleanAttr(name) {
|
|
|
187366
187364
|
}
|
|
187367
187365
|
|
|
187368
187366
|
// src/adapter/xslate-adapter.ts
|
|
187369
|
-
var import_typescript = __toESM(require_typescript(), 1);
|
|
187370
187367
|
import { BF_SLOT, BF_COND, BF_REGION } from "@barefootjs/shared";
|
|
187368
|
+
|
|
187369
|
+
// src/adapter/lib/constants.ts
|
|
187371
187370
|
var XSLATE_TEMPLATE_PRIMITIVES = {
|
|
187372
187371
|
"JSON.stringify": { arity: 1, emit: (args) => `$bf.json(${args[0]})` },
|
|
187373
187372
|
String: { arity: 1, emit: (args) => `$bf.string(${args[0]})` },
|
|
@@ -187377,12 +187376,16 @@ var XSLATE_TEMPLATE_PRIMITIVES = {
|
|
|
187377
187376
|
"Math.round": { arity: 1, emit: (args) => `$bf.round(${args[0]})` }
|
|
187378
187377
|
};
|
|
187379
187378
|
var XSLATE_PRIMITIVE_EMIT_MAP = Object.fromEntries(Object.entries(XSLATE_TEMPLATE_PRIMITIVES).map(([k, v]) => [k, v.emit]));
|
|
187379
|
+
|
|
187380
|
+
// src/adapter/lib/kolon-naming.ts
|
|
187380
187381
|
function escapeKolonSingleQuoted(s) {
|
|
187381
187382
|
return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
187382
187383
|
}
|
|
187383
187384
|
function kolonHashKey(name) {
|
|
187384
187385
|
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(name) ? name : `'${escapeKolonSingleQuoted(name)}'`;
|
|
187385
187386
|
}
|
|
187387
|
+
|
|
187388
|
+
// src/adapter/lib/ir-scope.ts
|
|
187386
187389
|
function resolveJsxChildrenProp(props) {
|
|
187387
187390
|
const prop = props.find((p) => p.name === "children");
|
|
187388
187391
|
if (!prop)
|
|
@@ -187422,1166 +187425,737 @@ function referencedVarsAreAvailable(expr, available) {
|
|
|
187422
187425
|
return true;
|
|
187423
187426
|
}
|
|
187424
187427
|
|
|
187425
|
-
|
|
187426
|
-
|
|
187427
|
-
|
|
187428
|
-
|
|
187429
|
-
|
|
187430
|
-
|
|
187431
|
-
|
|
187432
|
-
|
|
187433
|
-
|
|
187434
|
-
|
|
187435
|
-
|
|
187436
|
-
|
|
187437
|
-
|
|
187438
|
-
|
|
187439
|
-
|
|
187440
|
-
|
|
187441
|
-
|
|
187442
|
-
|
|
187443
|
-
|
|
187444
|
-
|
|
187445
|
-
|
|
187446
|
-
|
|
187447
|
-
|
|
187448
|
-
|
|
187449
|
-
|
|
187450
|
-
|
|
187451
|
-
|
|
187452
|
-
|
|
187453
|
-
|
|
187454
|
-
|
|
187455
|
-
|
|
187456
|
-
|
|
187457
|
-
this.localConstants = ir.metadata.localConstants ?? [];
|
|
187458
|
-
this.nullableOptionalProps = new Set(ir.metadata.propsParams.filter((p) => p.defaultValue === undefined && !p.isRest && p.type?.kind !== "primitive").map((p) => p.name));
|
|
187459
|
-
this.stringValueNames = new Set;
|
|
187460
|
-
for (const s of ir.metadata.signals) {
|
|
187461
|
-
if (isStringTypeInfo(s.type) || isBareStringLiteral(s.initialValue)) {
|
|
187462
|
-
this.stringValueNames.add(s.getter);
|
|
187428
|
+
// src/adapter/expr/array-method.ts
|
|
187429
|
+
import {
|
|
187430
|
+
serializeParsedExpr,
|
|
187431
|
+
freeVarsInBody
|
|
187432
|
+
} from "@barefootjs/jsx";
|
|
187433
|
+
function renderArrayMethod(method, object, args, emit) {
|
|
187434
|
+
switch (method) {
|
|
187435
|
+
case "join": {
|
|
187436
|
+
const obj = emit(object);
|
|
187437
|
+
const sep = args.length >= 1 ? emit(args[0]) : `','`;
|
|
187438
|
+
return `$bf.join(${obj}, ${sep})`;
|
|
187439
|
+
}
|
|
187440
|
+
case "includes": {
|
|
187441
|
+
const obj = emit(object);
|
|
187442
|
+
const needle = emit(args[0]);
|
|
187443
|
+
return `$bf.includes(${obj}, ${needle})`;
|
|
187444
|
+
}
|
|
187445
|
+
case "indexOf":
|
|
187446
|
+
case "lastIndexOf": {
|
|
187447
|
+
const fn = method === "indexOf" ? "index_of" : "last_index_of";
|
|
187448
|
+
const obj = emit(object);
|
|
187449
|
+
const needle = emit(args[0]);
|
|
187450
|
+
return `$bf.${fn}(${obj}, ${needle})`;
|
|
187451
|
+
}
|
|
187452
|
+
case "at": {
|
|
187453
|
+
const obj = emit(object);
|
|
187454
|
+
const idx = args.length >= 1 ? emit(args[0]) : "0";
|
|
187455
|
+
return `$bf.at(${obj}, ${idx})`;
|
|
187456
|
+
}
|
|
187457
|
+
case "concat": {
|
|
187458
|
+
if (args.length === 0) {
|
|
187459
|
+
return emit(object);
|
|
187463
187460
|
}
|
|
187461
|
+
const a = emit(object);
|
|
187462
|
+
const b = emit(args[0]);
|
|
187463
|
+
return `$bf.concat(${a}, ${b})`;
|
|
187464
187464
|
}
|
|
187465
|
-
|
|
187466
|
-
|
|
187467
|
-
|
|
187465
|
+
case "slice": {
|
|
187466
|
+
const recv = emit(object);
|
|
187467
|
+
const start = args.length >= 1 ? emit(args[0]) : "0";
|
|
187468
|
+
const end = args.length >= 2 ? emit(args[1]) : "nil";
|
|
187469
|
+
return `$bf.slice(${recv}, ${start}, ${end})`;
|
|
187468
187470
|
}
|
|
187469
|
-
|
|
187470
|
-
|
|
187471
|
-
|
|
187472
|
-
|
|
187473
|
-
if (!options?.siblingTemplatesRegistered) {
|
|
187474
|
-
this.checkImportedLoopChildComponents(ir);
|
|
187471
|
+
case "reverse":
|
|
187472
|
+
case "toReversed": {
|
|
187473
|
+
const recv = emit(object);
|
|
187474
|
+
return `$bf.reverse(${recv})`;
|
|
187475
187475
|
}
|
|
187476
|
-
|
|
187477
|
-
|
|
187478
|
-
|
|
187479
|
-
const ctxSeed = this.generateContextConsumerSeed(ir);
|
|
187480
|
-
const memoSeed = this.generateDerivedMemoSeed(ir);
|
|
187481
|
-
const template = `${scriptReg}${ctxSeed}${memoSeed}${templateBody}
|
|
187482
|
-
`;
|
|
187483
|
-
if (this.errors.length > 0) {
|
|
187484
|
-
ir.errors.push(...this.errors);
|
|
187476
|
+
case "toLowerCase": {
|
|
187477
|
+
const recv = emit(object);
|
|
187478
|
+
return `$bf.lc(${recv})`;
|
|
187485
187479
|
}
|
|
187486
|
-
|
|
187487
|
-
|
|
187488
|
-
|
|
187489
|
-
component: template,
|
|
187490
|
-
defaultExport: ""
|
|
187491
|
-
};
|
|
187492
|
-
return {
|
|
187493
|
-
template,
|
|
187494
|
-
sections,
|
|
187495
|
-
extension: this.extension
|
|
187496
|
-
};
|
|
187497
|
-
}
|
|
187498
|
-
generateScriptRegistrations(ir, scriptBaseName) {
|
|
187499
|
-
const hasInteractivity = this.hasClientInteractivity(ir);
|
|
187500
|
-
if (!hasInteractivity)
|
|
187501
|
-
return "";
|
|
187502
|
-
const name = scriptBaseName ?? ir.metadata.componentName;
|
|
187503
|
-
const runtimePath = this.options.barefootJsPath;
|
|
187504
|
-
const clientJsPath = `${this.options.clientJsBasePath}${name}.client.js`;
|
|
187505
|
-
const lines = [];
|
|
187506
|
-
lines.push(`: my $_bf_reg0 = $bf.register_script('${runtimePath}');`);
|
|
187507
|
-
lines.push(`: my $_bf_reg1 = $bf.register_script('${clientJsPath}');`);
|
|
187508
|
-
lines.push("");
|
|
187509
|
-
return lines.join(`
|
|
187510
|
-
`);
|
|
187511
|
-
}
|
|
187512
|
-
hasClientInteractivity(ir) {
|
|
187513
|
-
return ir.metadata.signals.length > 0 || ir.metadata.effects.length > 0 || ir.metadata.onMounts.length > 0 || (ir.metadata.clientAnalysis?.needsInit ?? false);
|
|
187514
|
-
}
|
|
187515
|
-
renderNode(node) {
|
|
187516
|
-
return emitIRNode(node, this, {});
|
|
187517
|
-
}
|
|
187518
|
-
emitElement(node, _ctx, _emit) {
|
|
187519
|
-
return this.renderElement(node);
|
|
187520
|
-
}
|
|
187521
|
-
emitText(node) {
|
|
187522
|
-
return node.value;
|
|
187523
|
-
}
|
|
187524
|
-
emitExpression(node) {
|
|
187525
|
-
return this.renderExpression(node);
|
|
187526
|
-
}
|
|
187527
|
-
emitConditional(node, _ctx, _emit) {
|
|
187528
|
-
return this.renderConditional(node);
|
|
187529
|
-
}
|
|
187530
|
-
emitLoop(node, _ctx, _emit) {
|
|
187531
|
-
return this.renderLoop(node);
|
|
187532
|
-
}
|
|
187533
|
-
emitComponent(node, _ctx, _emit) {
|
|
187534
|
-
return this.renderComponent(node);
|
|
187535
|
-
}
|
|
187536
|
-
emitFragment(node, _ctx, _emit) {
|
|
187537
|
-
return this.renderFragment(node);
|
|
187538
|
-
}
|
|
187539
|
-
emitSlot(node) {
|
|
187540
|
-
return this.renderSlot(node);
|
|
187541
|
-
}
|
|
187542
|
-
emitIfStatement(node, _ctx, _emit) {
|
|
187543
|
-
return this.renderIfStatement(node);
|
|
187544
|
-
}
|
|
187545
|
-
emitProvider(node, _ctx, _emit) {
|
|
187546
|
-
const value = this.providerValueKolon(node.valueProp);
|
|
187547
|
-
const children = this.renderChildren(node.children);
|
|
187548
|
-
const name = node.contextName;
|
|
187549
|
-
return `<: $bf.provide_context('${name}', ${value}) :>` + children + `<: $bf.revoke_context('${name}') :>`;
|
|
187550
|
-
}
|
|
187551
|
-
providerValueKolon(valueProp) {
|
|
187552
|
-
const v = valueProp.value;
|
|
187553
|
-
if (v.kind === "literal") {
|
|
187554
|
-
return typeof v.value === "string" ? `'${v.value.replace(/[\\']/g, (m) => `\\${m}`)}'` : String(v.value);
|
|
187480
|
+
case "toUpperCase": {
|
|
187481
|
+
const recv = emit(object);
|
|
187482
|
+
return `$bf.uc(${recv})`;
|
|
187555
187483
|
}
|
|
187556
|
-
|
|
187557
|
-
const
|
|
187558
|
-
|
|
187559
|
-
return hashref;
|
|
187560
|
-
return this.convertExpressionToKolon(v.expr);
|
|
187484
|
+
case "trim": {
|
|
187485
|
+
const recv = emit(object);
|
|
187486
|
+
return `$bf.trim(${recv})`;
|
|
187561
187487
|
}
|
|
187562
|
-
|
|
187563
|
-
|
|
187564
|
-
|
|
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
|
-
}
|
|
187579
|
-
contextDefaultKolon(c) {
|
|
187580
|
-
const d = c.defaultValue;
|
|
187581
|
-
if (d === null || d === undefined)
|
|
187582
|
-
return "nil";
|
|
187583
|
-
if (typeof d === "string")
|
|
187584
|
-
return `'${d.replace(/[\\']/g, (m) => `\\${m}`)}'`;
|
|
187585
|
-
if (typeof d === "boolean")
|
|
187586
|
-
return d ? "1" : "0";
|
|
187587
|
-
return String(d);
|
|
187588
|
-
}
|
|
187589
|
-
generateContextConsumerSeed(ir) {
|
|
187590
|
-
const consumers = collectContextConsumers(ir.metadata);
|
|
187591
|
-
if (consumers.length === 0)
|
|
187592
|
-
return "";
|
|
187593
|
-
return consumers.map((c) => `: my $${c.localName} = $bf.use_context('${c.contextName}', ${this.contextDefaultKolon(c)});`).join(`
|
|
187594
|
-
`) + `
|
|
187595
|
-
`;
|
|
187596
|
-
}
|
|
187597
|
-
generateDerivedMemoSeed(ir) {
|
|
187598
|
-
const memos = ir.metadata.memos ?? [];
|
|
187599
|
-
const signals = ir.metadata.signals ?? [];
|
|
187600
|
-
if (memos.length === 0 && signals.length === 0)
|
|
187601
|
-
return "";
|
|
187602
|
-
const available = new Set(ir.metadata.propsParams.map((p) => p.name));
|
|
187603
|
-
const lines = [];
|
|
187604
|
-
for (const signal of signals) {
|
|
187605
|
-
const kolon = this.tryLowerToKolon(signal.initialValue, available);
|
|
187606
|
-
const refsSelf = kolon !== null && new RegExp(`\\$${signal.getter}\\b`).test(kolon);
|
|
187607
|
-
if (kolon !== null && !refsSelf)
|
|
187608
|
-
lines.push(`: my $${signal.getter} = ${kolon};`);
|
|
187609
|
-
available.add(signal.getter);
|
|
187488
|
+
case "toFixed": {
|
|
187489
|
+
const recv = emit(object);
|
|
187490
|
+
const digits = args.length >= 1 ? emit(args[0]) : "0";
|
|
187491
|
+
return `$bf.to_fixed(${recv}, ${digits})`;
|
|
187610
187492
|
}
|
|
187611
|
-
|
|
187612
|
-
const
|
|
187613
|
-
if (
|
|
187614
|
-
|
|
187615
|
-
const refsSelf = kolon !== null && new RegExp(`\\$${memo.name}\\b`).test(kolon);
|
|
187616
|
-
if (kolon !== null && !refsSelf)
|
|
187617
|
-
lines.push(`: my $${memo.name} = ${kolon};`);
|
|
187493
|
+
case "split": {
|
|
187494
|
+
const recv = emit(object);
|
|
187495
|
+
if (args.length === 0) {
|
|
187496
|
+
return `$bf.split(${recv})`;
|
|
187618
187497
|
}
|
|
187619
|
-
|
|
187620
|
-
|
|
187621
|
-
|
|
187622
|
-
|
|
187623
|
-
|
|
187624
|
-
|
|
187625
|
-
tryLowerToKolon(expr, available) {
|
|
187626
|
-
const trimmed = expr.trim();
|
|
187627
|
-
if (!trimmed)
|
|
187628
|
-
return null;
|
|
187629
|
-
if (!isSupported(parseExpression2(trimmed)).supported)
|
|
187630
|
-
return null;
|
|
187631
|
-
const kolon = this.convertExpressionToKolon(trimmed);
|
|
187632
|
-
if (kolon === "" || !/\$[A-Za-z_]\w*/.test(kolon))
|
|
187633
|
-
return null;
|
|
187634
|
-
return referencedVarsAreAvailable(kolon, available) ? kolon : null;
|
|
187635
|
-
}
|
|
187636
|
-
emitAsync(node, _ctx, _emit) {
|
|
187637
|
-
return this.renderAsync(node);
|
|
187638
|
-
}
|
|
187639
|
-
renderElement(element) {
|
|
187640
|
-
const tag = element.tag;
|
|
187641
|
-
const attrs = this.renderAttributes(element);
|
|
187642
|
-
const children = this.renderChildren(element.children);
|
|
187643
|
-
let hydrationAttrs = "";
|
|
187644
|
-
if (element.needsScope) {
|
|
187645
|
-
hydrationAttrs += ` ${this.renderScopeMarker("")}`;
|
|
187498
|
+
const sep = emit(args[0]);
|
|
187499
|
+
if (args.length === 1) {
|
|
187500
|
+
return `$bf.split(${recv}, ${sep})`;
|
|
187501
|
+
}
|
|
187502
|
+
const limit = emit(args[1]);
|
|
187503
|
+
return `$bf.split(${recv}, ${sep}, ${limit})`;
|
|
187646
187504
|
}
|
|
187647
|
-
|
|
187648
|
-
|
|
187505
|
+
case "startsWith":
|
|
187506
|
+
case "endsWith": {
|
|
187507
|
+
const fn = method === "startsWith" ? "starts_with" : "ends_with";
|
|
187508
|
+
const recv = emit(object);
|
|
187509
|
+
const arg = emit(args[0]);
|
|
187510
|
+
if (args.length >= 2) {
|
|
187511
|
+
return `$bf.${fn}(${recv}, ${arg}, ${emit(args[1])})`;
|
|
187512
|
+
}
|
|
187513
|
+
return `$bf.${fn}(${recv}, ${arg})`;
|
|
187649
187514
|
}
|
|
187650
|
-
|
|
187651
|
-
|
|
187515
|
+
case "replace": {
|
|
187516
|
+
const recv = emit(object);
|
|
187517
|
+
const oldS = emit(args[0]);
|
|
187518
|
+
const newS = emit(args[1]);
|
|
187519
|
+
return `$bf.replace(${recv}, ${oldS}, ${newS})`;
|
|
187652
187520
|
}
|
|
187653
|
-
|
|
187654
|
-
|
|
187521
|
+
case "repeat": {
|
|
187522
|
+
const recv = emit(object);
|
|
187523
|
+
const count = args.length === 0 ? "0" : emit(args[0]);
|
|
187524
|
+
return `$bf.repeat(${recv}, ${count})`;
|
|
187655
187525
|
}
|
|
187656
|
-
|
|
187657
|
-
|
|
187658
|
-
"
|
|
187659
|
-
|
|
187660
|
-
|
|
187661
|
-
|
|
187662
|
-
"hr",
|
|
187663
|
-
"img",
|
|
187664
|
-
"input",
|
|
187665
|
-
"link",
|
|
187666
|
-
"meta",
|
|
187667
|
-
"param",
|
|
187668
|
-
"source",
|
|
187669
|
-
"track",
|
|
187670
|
-
"wbr"
|
|
187671
|
-
];
|
|
187672
|
-
if (voidElements.includes(tag.toLowerCase())) {
|
|
187673
|
-
return `<${tag}${attrs}${hydrationAttrs}>`;
|
|
187674
|
-
}
|
|
187675
|
-
return `<${tag}${attrs}${hydrationAttrs}>${children}</${tag}>`;
|
|
187676
|
-
}
|
|
187677
|
-
renderExpression(expr) {
|
|
187678
|
-
if (expr.clientOnly) {
|
|
187679
|
-
if (expr.slotId) {
|
|
187680
|
-
return `<: $bf.comment("client:${expr.slotId}") | mark_raw :>`;
|
|
187526
|
+
case "padStart":
|
|
187527
|
+
case "padEnd": {
|
|
187528
|
+
const fn = method === "padStart" ? "pad_start" : "pad_end";
|
|
187529
|
+
const recv = emit(object);
|
|
187530
|
+
if (args.length === 0) {
|
|
187531
|
+
return `$bf.${fn}(${recv}, 0)`;
|
|
187681
187532
|
}
|
|
187682
|
-
|
|
187533
|
+
const target = emit(args[0]);
|
|
187534
|
+
if (args.length === 1) {
|
|
187535
|
+
return `$bf.${fn}(${recv}, ${target})`;
|
|
187536
|
+
}
|
|
187537
|
+
const pad = emit(args[1]);
|
|
187538
|
+
return `$bf.${fn}(${recv}, ${target}, ${pad})`;
|
|
187683
187539
|
}
|
|
187684
|
-
|
|
187685
|
-
|
|
187686
|
-
|
|
187540
|
+
default: {
|
|
187541
|
+
const _exhaustive = method;
|
|
187542
|
+
throw new Error(`renderArrayMethod: unhandled ArrayMethod '${_exhaustive}'`);
|
|
187687
187543
|
}
|
|
187688
|
-
return `<: ${perlExpr} :>`;
|
|
187689
187544
|
}
|
|
187690
|
-
|
|
187691
|
-
|
|
187692
|
-
|
|
187693
|
-
|
|
187694
|
-
|
|
187695
|
-
|
|
187696
|
-
|
|
187697
|
-
|
|
187698
|
-
|
|
187699
|
-
|
|
187700
|
-
|
|
187701
|
-
|
|
187702
|
-
|
|
187703
|
-
|
|
187704
|
-
|
|
187705
|
-
|
|
187706
|
-
|
|
187707
|
-
|
|
187708
|
-
|
|
187709
|
-
${
|
|
187710
|
-
|
|
187711
|
-
|
|
187712
|
-
|
|
187713
|
-
|
|
187714
|
-
|
|
187715
|
-
|
|
187716
|
-
|
|
187717
|
-
|
|
187718
|
-
|
|
187719
|
-
|
|
187720
|
-
|
|
187721
|
-
|
|
187722
|
-
|
|
187723
|
-
|
|
187724
|
-
${
|
|
187725
|
-
|
|
187726
|
-
|
|
187727
|
-
|
|
187728
|
-
|
|
187729
|
-
|
|
187730
|
-
|
|
187731
|
-
: }
|
|
187732
|
-
|
|
187733
|
-
|
|
187734
|
-
|
|
187735
|
-
|
|
187736
|
-
|
|
187737
|
-
|
|
187738
|
-
|
|
187739
|
-
|
|
187740
|
-
|
|
187545
|
+
}
|
|
187546
|
+
function escapePerlSingleQuote(s) {
|
|
187547
|
+
return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
187548
|
+
}
|
|
187549
|
+
function emitEvalEnvArg(body, params, emit) {
|
|
187550
|
+
const free = freeVarsInBody(body, new Set(params));
|
|
187551
|
+
if (free.length === 0)
|
|
187552
|
+
return "{}";
|
|
187553
|
+
const pairs = free.map((n) => `'${escapePerlSingleQuote(n)}' => ${emit({ kind: "identifier", name: n })}`);
|
|
187554
|
+
return `{ ${pairs.join(", ")} }`;
|
|
187555
|
+
}
|
|
187556
|
+
function renderSortEval(recv, body, params, emit) {
|
|
187557
|
+
const json = serializeParsedExpr(body);
|
|
187558
|
+
if (json === null)
|
|
187559
|
+
return null;
|
|
187560
|
+
if (params.length < 2)
|
|
187561
|
+
return null;
|
|
187562
|
+
const [paramA, paramB] = params;
|
|
187563
|
+
const env = emitEvalEnvArg(body, params, emit);
|
|
187564
|
+
return `$bf.sort_eval(${recv}, '${escapePerlSingleQuote(json)}', '${paramA}', '${paramB}', ${env})`;
|
|
187565
|
+
}
|
|
187566
|
+
function renderReduceEval(recv, body, params, init, direction, emit) {
|
|
187567
|
+
const json = serializeParsedExpr(body);
|
|
187568
|
+
if (json === null)
|
|
187569
|
+
return null;
|
|
187570
|
+
if (init.kind !== "literal")
|
|
187571
|
+
return null;
|
|
187572
|
+
const initOut = init.literalType === "string" ? `'${escapePerlSingleQuote(String(init.value))}'` : init.literalType === "number" ? String(init.value) : null;
|
|
187573
|
+
if (initOut === null)
|
|
187574
|
+
return null;
|
|
187575
|
+
if (params.length < 2)
|
|
187576
|
+
return null;
|
|
187577
|
+
const [paramAcc, paramItem] = params;
|
|
187578
|
+
const env = emitEvalEnvArg(body, params, emit);
|
|
187579
|
+
return `$bf.reduce_eval(${recv}, '${escapePerlSingleQuote(json)}', '${paramAcc}', '${paramItem}', ${initOut}, '${direction}', ${env})`;
|
|
187580
|
+
}
|
|
187581
|
+
function renderPredicateEval(funcName, recv, predicate, param, emit, forward) {
|
|
187582
|
+
const json = serializeParsedExpr(predicate);
|
|
187583
|
+
if (json === null)
|
|
187584
|
+
return null;
|
|
187585
|
+
const env = emitEvalEnvArg(predicate, [param], emit);
|
|
187586
|
+
const fwd = forward === undefined ? "" : `, ${forward ? 1 : 0}`;
|
|
187587
|
+
return `$bf.${funcName}(${recv}, '${escapePerlSingleQuote(json)}', '${param}'${fwd}, ${env})`;
|
|
187588
|
+
}
|
|
187589
|
+
function renderFlatMapEval(recv, body, param, emit) {
|
|
187590
|
+
const json = serializeParsedExpr(body);
|
|
187591
|
+
if (json === null)
|
|
187592
|
+
return null;
|
|
187593
|
+
const env = emitEvalEnvArg(body, [param], emit);
|
|
187594
|
+
return `$bf.flat_map_eval(${recv}, '${escapePerlSingleQuote(json)}', '${param}', ${env})`;
|
|
187595
|
+
}
|
|
187596
|
+
function renderSortMethod(recv, c) {
|
|
187597
|
+
const keyHashes = c.keys.map((k) => {
|
|
187598
|
+
const keyEntry = k.key.kind === "self" ? `key_kind => 'self'` : `key_kind => 'field', key => '${k.key.field}'`;
|
|
187599
|
+
return `{ ${keyEntry}, compare_type => '${k.type}', direction => '${k.direction}' }`;
|
|
187600
|
+
});
|
|
187601
|
+
return `$bf.sort(${recv}, { keys => [${keyHashes.join(", ")}] })`;
|
|
187602
|
+
}
|
|
187603
|
+
function renderFlatMethod(recv, depth) {
|
|
187604
|
+
const d = depth === "infinity" ? -1 : depth;
|
|
187605
|
+
return `$bf.flat(${recv}, ${d})`;
|
|
187606
|
+
}
|
|
187607
|
+
|
|
187608
|
+
// src/adapter/expr/emitters.ts
|
|
187609
|
+
import {
|
|
187610
|
+
identifierPath,
|
|
187611
|
+
matchSearchParamsMethodCall,
|
|
187612
|
+
sortComparatorFromArrow
|
|
187613
|
+
} from "@barefootjs/jsx";
|
|
187614
|
+
var PREDICATE_METHODS = new Set([
|
|
187615
|
+
"filter",
|
|
187616
|
+
"find",
|
|
187617
|
+
"findIndex",
|
|
187618
|
+
"findLast",
|
|
187619
|
+
"findLastIndex",
|
|
187620
|
+
"every",
|
|
187621
|
+
"some"
|
|
187622
|
+
]);
|
|
187623
|
+
|
|
187624
|
+
class XslateFilterEmitter {
|
|
187625
|
+
param;
|
|
187626
|
+
localVarMap;
|
|
187627
|
+
isStringName;
|
|
187628
|
+
constructor(param, localVarMap, isStringName = () => false) {
|
|
187629
|
+
this.param = param;
|
|
187630
|
+
this.localVarMap = localVarMap;
|
|
187631
|
+
this.isStringName = isStringName;
|
|
187741
187632
|
}
|
|
187742
|
-
|
|
187743
|
-
if (
|
|
187744
|
-
return
|
|
187633
|
+
identifier(name) {
|
|
187634
|
+
if (name === this.param)
|
|
187635
|
+
return `$${this.param}`;
|
|
187636
|
+
const signal = this.localVarMap.get(name);
|
|
187637
|
+
if (signal)
|
|
187638
|
+
return `$${signal}`;
|
|
187639
|
+
return `$${name}`;
|
|
187640
|
+
}
|
|
187641
|
+
literal(value, literalType) {
|
|
187642
|
+
if (literalType === "string")
|
|
187643
|
+
return `'${value}'`;
|
|
187644
|
+
if (literalType === "boolean")
|
|
187645
|
+
return value ? "1" : "0";
|
|
187646
|
+
if (literalType === "null")
|
|
187647
|
+
return "nil";
|
|
187648
|
+
return String(value);
|
|
187649
|
+
}
|
|
187650
|
+
member(object, property, _computed, emit) {
|
|
187651
|
+
if (property === "length") {
|
|
187652
|
+
return `$bf.length(${emit(object)})`;
|
|
187745
187653
|
}
|
|
187746
|
-
return
|
|
187654
|
+
return `${emit(object)}.${property}`;
|
|
187747
187655
|
}
|
|
187748
|
-
|
|
187749
|
-
|
|
187750
|
-
|
|
187751
|
-
|
|
187656
|
+
indexAccess(object, index, emit) {
|
|
187657
|
+
return `${emit(object)}[${emit(index)}]`;
|
|
187658
|
+
}
|
|
187659
|
+
call(callee, args, emit) {
|
|
187660
|
+
if (callee.kind === "identifier" && args.length === 0) {
|
|
187661
|
+
return `$${callee.name}`;
|
|
187752
187662
|
}
|
|
187753
|
-
return
|
|
187663
|
+
return emit(callee);
|
|
187754
187664
|
}
|
|
187755
|
-
|
|
187756
|
-
const
|
|
187757
|
-
|
|
187758
|
-
|
|
187759
|
-
|
|
187760
|
-
if (imp.isTypeOnly)
|
|
187761
|
-
continue;
|
|
187762
|
-
for (const spec of imp.specifiers) {
|
|
187763
|
-
relativeImports.add(spec.alias ?? spec.name);
|
|
187764
|
-
}
|
|
187665
|
+
unary(op, argument, emit) {
|
|
187666
|
+
const arg = emit(argument);
|
|
187667
|
+
if (op === "!") {
|
|
187668
|
+
const needsParens = argument.kind === "binary" || argument.kind === "logical";
|
|
187669
|
+
return needsParens ? `!(${arg})` : `!${arg}`;
|
|
187765
187670
|
}
|
|
187766
|
-
if (
|
|
187767
|
-
return
|
|
187768
|
-
|
|
187769
|
-
|
|
187770
|
-
|
|
187771
|
-
|
|
187772
|
-
|
|
187773
|
-
|
|
187774
|
-
|
|
187775
|
-
|
|
187776
|
-
|
|
187777
|
-
|
|
187778
|
-
|
|
187779
|
-
|
|
187780
|
-
|
|
187781
|
-
|
|
187782
|
-
|
|
187783
|
-
|
|
187784
|
-
}
|
|
187785
|
-
});
|
|
187786
|
-
}
|
|
187787
|
-
for (const child of comp.children)
|
|
187788
|
-
visit(child, inLoop);
|
|
187789
|
-
break;
|
|
187790
|
-
}
|
|
187791
|
-
case "element":
|
|
187792
|
-
for (const child of node.children)
|
|
187793
|
-
visit(child, inLoop);
|
|
187794
|
-
break;
|
|
187795
|
-
case "fragment":
|
|
187796
|
-
for (const child of node.children)
|
|
187797
|
-
visit(child, inLoop);
|
|
187798
|
-
break;
|
|
187799
|
-
case "conditional": {
|
|
187800
|
-
const cond = node;
|
|
187801
|
-
visit(cond.whenTrue, inLoop);
|
|
187802
|
-
if (cond.whenFalse)
|
|
187803
|
-
visit(cond.whenFalse, inLoop);
|
|
187804
|
-
break;
|
|
187805
|
-
}
|
|
187806
|
-
case "loop":
|
|
187807
|
-
for (const child of node.children)
|
|
187808
|
-
visit(child, true);
|
|
187809
|
-
break;
|
|
187810
|
-
case "if-statement": {
|
|
187811
|
-
const stmt = node;
|
|
187812
|
-
visit(stmt.consequent, inLoop);
|
|
187813
|
-
if (stmt.alternate)
|
|
187814
|
-
visit(stmt.alternate, inLoop);
|
|
187815
|
-
break;
|
|
187816
|
-
}
|
|
187817
|
-
case "provider":
|
|
187818
|
-
for (const child of node.children)
|
|
187819
|
-
visit(child, inLoop);
|
|
187820
|
-
break;
|
|
187821
|
-
case "async": {
|
|
187822
|
-
const a = node;
|
|
187823
|
-
visit(a.fallback, inLoop);
|
|
187824
|
-
for (const child of a.children)
|
|
187825
|
-
visit(child, inLoop);
|
|
187826
|
-
break;
|
|
187827
|
-
}
|
|
187828
|
-
}
|
|
187671
|
+
if (op === "-")
|
|
187672
|
+
return `-${arg}`;
|
|
187673
|
+
return arg;
|
|
187674
|
+
}
|
|
187675
|
+
binary(op, left, right, emit) {
|
|
187676
|
+
const l = emit(left);
|
|
187677
|
+
const r = emit(right);
|
|
187678
|
+
const opMap = {
|
|
187679
|
+
"===": "==",
|
|
187680
|
+
"!==": "!=",
|
|
187681
|
+
">": ">",
|
|
187682
|
+
"<": "<",
|
|
187683
|
+
">=": ">=",
|
|
187684
|
+
"<=": "<=",
|
|
187685
|
+
"+": "+",
|
|
187686
|
+
"-": "-",
|
|
187687
|
+
"*": "*",
|
|
187688
|
+
"/": "/"
|
|
187829
187689
|
};
|
|
187830
|
-
|
|
187690
|
+
return `${l} ${opMap[op] ?? op} ${r}`;
|
|
187831
187691
|
}
|
|
187832
|
-
|
|
187833
|
-
|
|
187834
|
-
|
|
187835
|
-
|
|
187836
|
-
|
|
187837
|
-
if (
|
|
187838
|
-
|
|
187839
|
-
|
|
187840
|
-
severity: "error",
|
|
187841
|
-
message: `Loop callback uses an array/object destructure pattern (\`${loop.param}\`) that the Xslate adapter cannot lower — Kolon \`for LIST -> $item\` binds a single scalar and can't unpack a tuple.`,
|
|
187842
|
-
loc: loop.loc ?? { file: this.componentName + ".tsx", start: { line: 1, column: 0 }, end: { line: 1, column: 0 } },
|
|
187843
|
-
suggestion: {
|
|
187844
|
-
message: `Options:
|
|
187845
|
-
` + ` 1. Rename the parameter to a single name and access tuple elements with index syntax in the body (e.g. \`entry => entry[0]\` instead of \`([k, v]) => ...\`).
|
|
187846
|
-
` + ` 2. Mark the loop position as @client-only so the destructure runs in JS on the client.
|
|
187847
|
-
` + ` 3. Move the loop into a primitive that the adapter registers explicitly.`
|
|
187848
|
-
}
|
|
187849
|
-
});
|
|
187850
|
-
}
|
|
187851
|
-
const rawArray = this.convertExpressionToKolon(loop.array);
|
|
187852
|
-
let array = rawArray;
|
|
187853
|
-
if (loop.sortComparator) {
|
|
187854
|
-
array = renderSortMethod(rawArray, loop.sortComparator);
|
|
187855
|
-
}
|
|
187856
|
-
const param = loop.param;
|
|
187857
|
-
const renderedChildren = this.renderChildren(loop.children);
|
|
187858
|
-
const loopVar = loop.iterationShape === "keys" ? "__bf_item" : supportableDestructure ? "__bf_item" : param;
|
|
187859
|
-
const indexLocalLines = [];
|
|
187860
|
-
if (loop.iterationShape === "keys") {
|
|
187861
|
-
indexLocalLines.push(`: my $${param} = $~${loopVar}.index;`);
|
|
187862
|
-
} else if (loop.index) {
|
|
187863
|
-
indexLocalLines.push(`: my $${loop.index} = $~${loopVar}.index;`);
|
|
187864
|
-
}
|
|
187865
|
-
if (supportableDestructure) {
|
|
187866
|
-
for (const b of loop.paramBindings ?? []) {
|
|
187867
|
-
indexLocalLines.push(b.rest ? `: my $${b.name} = $${loopVar};` : `: my $${b.name} = $${loopVar}${b.path};`);
|
|
187868
|
-
}
|
|
187869
|
-
}
|
|
187870
|
-
const prevInLoop = this.inLoop;
|
|
187871
|
-
this.inLoop = true;
|
|
187872
|
-
const childrenUnderLoop = this.renderChildren(loop.children);
|
|
187873
|
-
this.inLoop = prevInLoop;
|
|
187874
|
-
const bodyChildren = loop.bodyIsItemConditional && loop.key ? `<: $bf.comment("loop-i:" ~ ${this.convertExpressionToKolon(loop.key)}) | mark_raw :>
|
|
187875
|
-
${childrenUnderLoop}` : childrenUnderLoop;
|
|
187876
|
-
const lines = [];
|
|
187877
|
-
lines.push(`<: $bf.comment("loop:${loop.markerId}") | mark_raw :>`);
|
|
187878
|
-
lines.push(`: for ${array} -> $${loopVar} {`);
|
|
187879
|
-
for (const il of indexLocalLines)
|
|
187880
|
-
lines.push(il);
|
|
187881
|
-
if (loop.filterPredicate) {
|
|
187882
|
-
let filterCond;
|
|
187883
|
-
if (loop.filterPredicate.blockBody) {
|
|
187884
|
-
filterCond = this.renderBlockBodyCondition(loop.filterPredicate.blockBody, loop.filterPredicate.param);
|
|
187885
|
-
} else if (loop.filterPredicate.predicate) {
|
|
187886
|
-
filterCond = this.renderKolonFilterExpr(loop.filterPredicate.predicate, loop.filterPredicate.param);
|
|
187887
|
-
} else {
|
|
187888
|
-
filterCond = "1";
|
|
187889
|
-
}
|
|
187890
|
-
if (loop.filterPredicate.param !== param) {
|
|
187891
|
-
filterCond = filterCond.replace(new RegExp(`\\$${loop.filterPredicate.param}\\b`, "g"), `$${param}`);
|
|
187892
|
-
}
|
|
187893
|
-
lines.push(`: if (${filterCond}) {`);
|
|
187894
|
-
lines.push(bodyChildren);
|
|
187895
|
-
lines.push(`: }`);
|
|
187896
|
-
} else {
|
|
187897
|
-
lines.push(bodyChildren);
|
|
187898
|
-
}
|
|
187899
|
-
lines.push(`: }`);
|
|
187900
|
-
lines.push(`<: $bf.comment("/loop:${loop.markerId}") | mark_raw :>`);
|
|
187901
|
-
return lines.join(`
|
|
187902
|
-
`);
|
|
187692
|
+
logical(op, left, right, emit) {
|
|
187693
|
+
const l = emit(left);
|
|
187694
|
+
const r = emit(right);
|
|
187695
|
+
if (op === "&&")
|
|
187696
|
+
return `(${l} && ${r})`;
|
|
187697
|
+
if (op === "||")
|
|
187698
|
+
return `(${l} || ${r})`;
|
|
187699
|
+
return `(${l} // ${r})`;
|
|
187903
187700
|
}
|
|
187904
|
-
|
|
187905
|
-
|
|
187906
|
-
|
|
187907
|
-
|
|
187908
|
-
|
|
187909
|
-
|
|
187910
|
-
|
|
187911
|
-
|
|
187912
|
-
|
|
187913
|
-
|
|
187914
|
-
|
|
187915
|
-
|
|
187916
|
-
|
|
187917
|
-
|
|
187918
|
-
|
|
187919
|
-
|
|
187920
|
-
|
|
187921
|
-
|
|
187922
|
-
|
|
187923
|
-
|
|
187924
|
-
|
|
187925
|
-
|
|
187926
|
-
|
|
187927
|
-
|
|
187928
|
-
|
|
187929
|
-
|
|
187930
|
-
|
|
187931
|
-
|
|
187932
|
-
|
|
187933
|
-
|
|
187934
|
-
|
|
187935
|
-
|
|
187936
|
-
|
|
187937
|
-
|
|
187938
|
-
|
|
187939
|
-
|
|
187940
|
-
|
|
187941
|
-
|
|
187942
|
-
|
|
187943
|
-
|
|
187944
|
-
|
|
187945
|
-
|
|
187946
|
-
|
|
187947
|
-
|
|
187948
|
-
if (
|
|
187949
|
-
|
|
187701
|
+
callbackMethod(method, object, _arrow, _restArgs, emit) {
|
|
187702
|
+
return emit(object);
|
|
187703
|
+
}
|
|
187704
|
+
arrayLiteral(elements, emit) {
|
|
187705
|
+
return `[${elements.map(emit).join(", ")}]`;
|
|
187706
|
+
}
|
|
187707
|
+
arrayMethod(method, object, args, emit) {
|
|
187708
|
+
return renderArrayMethod(method, object, args, emit);
|
|
187709
|
+
}
|
|
187710
|
+
flatMethod(object, depth, emit) {
|
|
187711
|
+
return renderFlatMethod(emit(object), depth);
|
|
187712
|
+
}
|
|
187713
|
+
conditional(_test, _consequent, _alternate) {
|
|
187714
|
+
return "1";
|
|
187715
|
+
}
|
|
187716
|
+
templateLiteral(_parts) {
|
|
187717
|
+
return "1";
|
|
187718
|
+
}
|
|
187719
|
+
arrow(_params, _body) {
|
|
187720
|
+
return "1";
|
|
187721
|
+
}
|
|
187722
|
+
regex(_raw) {
|
|
187723
|
+
return "1";
|
|
187724
|
+
}
|
|
187725
|
+
unsupported(_raw, _reason) {
|
|
187726
|
+
return "1";
|
|
187727
|
+
}
|
|
187728
|
+
objectLiteral(_properties, _raw, _emit) {
|
|
187729
|
+
return "1";
|
|
187730
|
+
}
|
|
187731
|
+
}
|
|
187732
|
+
|
|
187733
|
+
class XslateTopLevelEmitter {
|
|
187734
|
+
ctx;
|
|
187735
|
+
constructor(ctx) {
|
|
187736
|
+
this.ctx = ctx;
|
|
187737
|
+
}
|
|
187738
|
+
identifier(name) {
|
|
187739
|
+
if (name === "undefined" || name === "null")
|
|
187740
|
+
return "nil";
|
|
187741
|
+
const inlined = this.ctx._resolveModuleStringConst(name);
|
|
187742
|
+
if (inlined !== null)
|
|
187743
|
+
return inlined;
|
|
187744
|
+
const literalConst = this.ctx._resolveLiteralConst(name);
|
|
187745
|
+
if (literalConst !== null)
|
|
187746
|
+
return literalConst;
|
|
187747
|
+
return `$${name}`;
|
|
187748
|
+
}
|
|
187749
|
+
literal(value, literalType) {
|
|
187750
|
+
if (literalType === "string")
|
|
187751
|
+
return `'${value}'`;
|
|
187752
|
+
if (literalType === "boolean")
|
|
187753
|
+
return value ? "1" : "0";
|
|
187754
|
+
if (literalType === "null")
|
|
187755
|
+
return "nil";
|
|
187756
|
+
return String(value);
|
|
187757
|
+
}
|
|
187758
|
+
member(object, property, _computed, emit) {
|
|
187759
|
+
if (object.kind === "identifier" && object.name === "props") {
|
|
187760
|
+
return `$${property}`;
|
|
187950
187761
|
}
|
|
187951
|
-
|
|
187952
|
-
|
|
187953
|
-
|
|
187954
|
-
|
|
187955
|
-
this.inLoop = false;
|
|
187956
|
-
const childrenBody = this.renderChildren(effectiveChildren);
|
|
187957
|
-
this.inLoop = prevInLoop;
|
|
187958
|
-
const macroName = `bf_children_${comp.slotId ?? "c" + this.childrenCaptureCounter++}`;
|
|
187959
|
-
const childrenEntry = `children => ${macroName}()`;
|
|
187960
|
-
const allParts = [...propParts, childrenEntry];
|
|
187961
|
-
return `<: macro ${macroName} -> () { :>${childrenBody}<: } :><: $bf.render_child('${tplName}', { ${allParts.join(", ")} }) | mark_raw :>`;
|
|
187762
|
+
if (object.kind === "identifier") {
|
|
187763
|
+
const staticValue = this.ctx._resolveStaticRecordLiteral(object.name, property);
|
|
187764
|
+
if (staticValue !== null)
|
|
187765
|
+
return staticValue;
|
|
187962
187766
|
}
|
|
187963
|
-
const
|
|
187964
|
-
|
|
187767
|
+
const obj = emit(object);
|
|
187768
|
+
if (property === "length")
|
|
187769
|
+
return `$bf.length(${obj})`;
|
|
187770
|
+
return `${obj}.${property}`;
|
|
187965
187771
|
}
|
|
187966
|
-
|
|
187967
|
-
|
|
187968
|
-
toTemplateName(componentName) {
|
|
187969
|
-
return componentName.replace(/([A-Z])/g, "_$1").toLowerCase().replace(/^_/, "");
|
|
187772
|
+
indexAccess(object, index, emit) {
|
|
187773
|
+
return `${emit(object)}[${emit(index)}]`;
|
|
187970
187774
|
}
|
|
187971
|
-
|
|
187972
|
-
|
|
187973
|
-
|
|
187974
|
-
|
|
187975
|
-
|
|
187976
|
-
|
|
187977
|
-
|
|
187978
|
-
|
|
187979
|
-
const altResult = this.renderIfStatement(ifStmt.alternate);
|
|
187980
|
-
result += altResult.replace(/^: if/, ": } elsif");
|
|
187981
|
-
} else {
|
|
187982
|
-
const alternate = this.renderNode(ifStmt.alternate);
|
|
187983
|
-
result += `: } else {
|
|
187984
|
-
${alternate}
|
|
187985
|
-
`;
|
|
187775
|
+
call(callee, args, emit) {
|
|
187776
|
+
if (callee.kind === "identifier" && args.length === 0) {
|
|
187777
|
+
return `$${callee.name}`;
|
|
187778
|
+
}
|
|
187779
|
+
if (this.ctx._searchParamsLocals.size > 0) {
|
|
187780
|
+
const sp = matchSearchParamsMethodCall(callee, args, this.ctx._searchParamsLocals);
|
|
187781
|
+
if (sp) {
|
|
187782
|
+
return `$searchParams.${sp.method}(${sp.args.map(emit).join(", ")})`;
|
|
187986
187783
|
}
|
|
187987
187784
|
}
|
|
187988
|
-
|
|
187989
|
-
|
|
187785
|
+
const path = identifierPath(callee);
|
|
187786
|
+
const spec = path ? XSLATE_TEMPLATE_PRIMITIVES[path] : undefined;
|
|
187787
|
+
if (path && spec) {
|
|
187788
|
+
if (args.length === spec.arity) {
|
|
187789
|
+
return spec.emit(args.map(emit));
|
|
187790
|
+
}
|
|
187791
|
+
this.ctx._recordExprBF101(`templatePrimitive '${path}' expects ${spec.arity} arg(s), got ${args.length}`, `Call '${path}' with exactly ${spec.arity} argument(s).`);
|
|
187792
|
+
return "''";
|
|
187793
|
+
}
|
|
187794
|
+
return emit(callee);
|
|
187990
187795
|
}
|
|
187991
|
-
|
|
187992
|
-
const
|
|
187993
|
-
if (
|
|
187994
|
-
return
|
|
187796
|
+
unary(op, argument, emit) {
|
|
187797
|
+
const arg = emit(argument);
|
|
187798
|
+
if (op === "!")
|
|
187799
|
+
return `!${arg}`;
|
|
187800
|
+
if (op === "-")
|
|
187801
|
+
return `-${arg}`;
|
|
187802
|
+
return arg;
|
|
187803
|
+
}
|
|
187804
|
+
binary(op, left, right, emit) {
|
|
187805
|
+
const l = emit(left);
|
|
187806
|
+
const r = emit(right);
|
|
187807
|
+
const opMap = {
|
|
187808
|
+
"===": "==",
|
|
187809
|
+
"!==": "!=",
|
|
187810
|
+
">": ">",
|
|
187811
|
+
"<": "<",
|
|
187812
|
+
">=": ">=",
|
|
187813
|
+
"<=": "<=",
|
|
187814
|
+
"+": "+",
|
|
187815
|
+
"-": "-",
|
|
187816
|
+
"*": "*"
|
|
187817
|
+
};
|
|
187818
|
+
return `${l} ${opMap[op] ?? op} ${r}`;
|
|
187819
|
+
}
|
|
187820
|
+
logical(op, left, right, emit) {
|
|
187821
|
+
const l = emit(left);
|
|
187822
|
+
const r = emit(right);
|
|
187823
|
+
if (op === "&&")
|
|
187824
|
+
return `(${l} && ${r})`;
|
|
187825
|
+
if (op === "||")
|
|
187826
|
+
return `(${l} || ${r})`;
|
|
187827
|
+
return `(${l} // ${r})`;
|
|
187828
|
+
}
|
|
187829
|
+
callbackMethod(method, object, arrow, restArgs, emit) {
|
|
187830
|
+
const recv = emit(object);
|
|
187831
|
+
const body = arrow.body;
|
|
187832
|
+
const params = arrow.params;
|
|
187833
|
+
if (PREDICATE_METHODS.has(method)) {
|
|
187834
|
+
return this._emitPredicateCallback({ method, object, param: params[0], predicate: body }, recv, emit);
|
|
187835
|
+
}
|
|
187836
|
+
if (method === "sort" || method === "toSorted") {
|
|
187837
|
+
const evalForm = renderSortEval(recv, body, params, emit);
|
|
187838
|
+
if (evalForm !== null)
|
|
187839
|
+
return evalForm;
|
|
187840
|
+
const structured = sortComparatorFromArrow(arrow);
|
|
187841
|
+
if (structured !== null)
|
|
187842
|
+
return renderSortMethod(recv, structured);
|
|
187843
|
+
this.ctx._recordExprBF101(`'.${method}(...)' comparator is outside the Xslate adapter's evaluable / structured surface`, `Pre-compute the sorted array, or move this position to a '/* @client */' boundary.`);
|
|
187844
|
+
return "''";
|
|
187995
187845
|
}
|
|
187996
|
-
|
|
187846
|
+
if (method === "reduce" || method === "reduceRight") {
|
|
187847
|
+
const direction = method === "reduceRight" ? "right" : "left";
|
|
187848
|
+
const init = restArgs[0];
|
|
187849
|
+
const evalForm = init !== undefined ? renderReduceEval(recv, body, params, init, direction, emit) : null;
|
|
187850
|
+
if (evalForm !== null)
|
|
187851
|
+
return evalForm;
|
|
187852
|
+
this.ctx._recordExprBF101(`'.${method}(...)' is outside the Xslate adapter's evaluable surface (needs a literal initial value and an evaluable reducer body)`, `Pre-compute the reduced value, or move this position to a '/* @client */' boundary.`);
|
|
187853
|
+
return "''";
|
|
187854
|
+
}
|
|
187855
|
+
if (method === "flatMap") {
|
|
187856
|
+
const evalForm = renderFlatMapEval(recv, body, params[0], emit);
|
|
187857
|
+
if (evalForm !== null)
|
|
187858
|
+
return evalForm;
|
|
187859
|
+
this.ctx._recordExprBF101(`'.flatMap(...)' projection is outside the Xslate adapter's evaluable surface`, `Pre-compute the projected array, or move this position to a '/* @client */' boundary.`);
|
|
187860
|
+
return "''";
|
|
187861
|
+
}
|
|
187862
|
+
return recv;
|
|
187997
187863
|
}
|
|
187998
|
-
|
|
187999
|
-
|
|
187864
|
+
_emitPredicateCallback(call, arrayExpr, emit) {
|
|
187865
|
+
const { method, object, param, predicate } = call;
|
|
187866
|
+
const evalFn = {
|
|
187867
|
+
filter: ["filter_eval"],
|
|
187868
|
+
every: ["every_eval"],
|
|
187869
|
+
some: ["some_eval"],
|
|
187870
|
+
find: ["find_eval", true],
|
|
187871
|
+
findLast: ["find_eval", false],
|
|
187872
|
+
findIndex: ["find_index_eval", true],
|
|
187873
|
+
findLastIndex: ["find_index_eval", false]
|
|
187874
|
+
};
|
|
187875
|
+
const isIdentity = method === "filter" && predicate.kind === "identifier" && predicate.name === param;
|
|
187876
|
+
const spec = evalFn[method];
|
|
187877
|
+
if (spec && !isIdentity) {
|
|
187878
|
+
const evalForm = renderPredicateEval(spec[0], arrayExpr, predicate, param, emit, spec[1]);
|
|
187879
|
+
if (evalForm !== null)
|
|
187880
|
+
return evalForm;
|
|
187881
|
+
}
|
|
187882
|
+
const predBody = this.ctx._renderKolonFilterExprPublic(predicate, param);
|
|
187883
|
+
const lambda = `-> $${param} { ${predBody} }`;
|
|
187884
|
+
const fn = {
|
|
187885
|
+
filter: "filter",
|
|
187886
|
+
every: "every",
|
|
187887
|
+
some: "some",
|
|
187888
|
+
find: "find",
|
|
187889
|
+
findIndex: "find_index",
|
|
187890
|
+
findLast: "find_last",
|
|
187891
|
+
findLastIndex: "find_last_index"
|
|
187892
|
+
};
|
|
187893
|
+
if (fn[method])
|
|
187894
|
+
return `$bf.${fn[method]}(${arrayExpr}, ${lambda})`;
|
|
187895
|
+
return emit(object);
|
|
188000
187896
|
}
|
|
188001
|
-
|
|
188002
|
-
|
|
188003
|
-
const children = this.renderChildren(node.children);
|
|
188004
|
-
const macroName = `bf_async_fallback_${node.id}`;
|
|
188005
|
-
return `<: macro ${macroName} -> () { :>${fallback}<: } :><: $bf.async_boundary('${node.id}', ${macroName}()) | mark_raw :>
|
|
188006
|
-
${children}`;
|
|
187897
|
+
arrayLiteral(elements, emit) {
|
|
187898
|
+
return `[${elements.map(emit).join(", ")}]`;
|
|
188007
187899
|
}
|
|
188008
|
-
|
|
188009
|
-
|
|
188010
|
-
emitExpression: (value, name) => {
|
|
188011
|
-
if (name === "style") {
|
|
188012
|
-
const css = this.tryLowerStyleObject(value.expr);
|
|
188013
|
-
if (css !== null)
|
|
188014
|
-
return `style="${css}"`;
|
|
188015
|
-
}
|
|
188016
|
-
if (this.refuseUnsupportedAttrExpression(value.expr, name)) {
|
|
188017
|
-
return "";
|
|
188018
|
-
}
|
|
188019
|
-
const bareId = value.expr.trim();
|
|
188020
|
-
const normalizedBareId = this.propsObjectName && bareId.startsWith(`${this.propsObjectName}.`) ? bareId.slice(this.propsObjectName.length + 1) : bareId;
|
|
188021
|
-
if (!isBooleanAttr(name) && !value.presenceOrUndefined && /^[A-Za-z_$][\w$]*$/.test(normalizedBareId) && this.nullableOptionalProps.has(normalizedBareId)) {
|
|
188022
|
-
const perl2 = this.convertExpressionToKolon(value.expr);
|
|
188023
|
-
const body = isBooleanResultExpr(value.expr) || isAriaBooleanAttr(name) || this.isBooleanTypedPropRef(value.expr) ? `${name}="<: $bf.bool_str(${perl2}) :>"` : `${name}="<: ${perl2} :>"`;
|
|
188024
|
-
return `
|
|
188025
|
-
: if (defined ${perl2}) {
|
|
188026
|
-
${body}
|
|
188027
|
-
: }
|
|
188028
|
-
`;
|
|
188029
|
-
}
|
|
188030
|
-
if (isBooleanAttr(name)) {
|
|
188031
|
-
return `<: ${this.convertExpressionToKolon(value.expr)} ? '${name}' : '' :>`;
|
|
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
|
-
}
|
|
188056
|
-
const perl = this.convertExpressionToKolon(value.expr);
|
|
188057
|
-
if (isBooleanResultExpr(value.expr) || isAriaBooleanAttr(name) || this.isBooleanTypedPropRef(value.expr)) {
|
|
188058
|
-
return `${name}="<: $bf.bool_str(${perl}) :>"`;
|
|
188059
|
-
}
|
|
188060
|
-
return `${name}="<: ${perl} :>"`;
|
|
188061
|
-
},
|
|
188062
|
-
emitBooleanAttr: (_value, name) => name,
|
|
188063
|
-
emitTemplate: (value, name) => `${name}="<: ${this.convertTemplateLiteralPartsToKolon(value.parts)} :>"`,
|
|
188064
|
-
emitSpread: (value) => {
|
|
188065
|
-
if (this.refuseUnsupportedAttrExpression(value.expr, "...")) {
|
|
188066
|
-
return "";
|
|
188067
|
-
}
|
|
188068
|
-
const trimmed = value.expr.trim();
|
|
188069
|
-
if (this.propsObjectName && this.propsObjectName === trimmed) {
|
|
188070
|
-
const entries = this.propsParams.map((p) => `${JSON.stringify(p.name)} => $${p.name}`);
|
|
188071
|
-
return `<: $bf.spread_attrs({${entries.join(", ")}}) | mark_raw :>`;
|
|
188072
|
-
}
|
|
188073
|
-
const ternaryHashref = this.conditionalSpreadToKolon(trimmed);
|
|
188074
|
-
if (ternaryHashref !== null) {
|
|
188075
|
-
return `<: $bf.spread_attrs(${ternaryHashref}) | mark_raw :>`;
|
|
188076
|
-
}
|
|
188077
|
-
if (/^[A-Za-z_$][\w$]*$/.test(trimmed)) {
|
|
188078
|
-
const localConst = (this.localConstants ?? []).find((c) => c.name === trimmed && !c.isModule);
|
|
188079
|
-
if (localConst?.value !== undefined) {
|
|
188080
|
-
const initTrimmed = localConst.value.trim();
|
|
188081
|
-
if (!/^[A-Za-z_$][\w$]*$/.test(initTrimmed)) {
|
|
188082
|
-
const resolved = this.conditionalSpreadToKolon(initTrimmed);
|
|
188083
|
-
if (resolved !== null) {
|
|
188084
|
-
return `<: $bf.spread_attrs(${resolved}) | mark_raw :>`;
|
|
188085
|
-
}
|
|
188086
|
-
}
|
|
188087
|
-
}
|
|
188088
|
-
}
|
|
188089
|
-
const perlExpr = this.convertExpressionToKolon(value.expr);
|
|
188090
|
-
return `<: $bf.spread_attrs(${perlExpr}) | mark_raw :>`;
|
|
188091
|
-
},
|
|
188092
|
-
emitBooleanShorthand: () => "",
|
|
188093
|
-
emitJsxChildren: () => ""
|
|
188094
|
-
};
|
|
188095
|
-
tryLowerStyleObject(expr) {
|
|
188096
|
-
const entries = parseStyleObjectEntries(expr);
|
|
188097
|
-
if (!entries)
|
|
188098
|
-
return null;
|
|
188099
|
-
for (const e of entries) {
|
|
188100
|
-
if (e.kind === "expr" && !isSupported(parseExpression2(e.expr)).supported)
|
|
188101
|
-
return null;
|
|
188102
|
-
}
|
|
188103
|
-
return entries.map((e) => e.kind === "literal" ? `${this.escapeAttrText(e.cssKey)}:${this.escapeAttrText(e.value)}` : `${this.escapeAttrText(e.cssKey)}:<: ${this.convertExpressionToKolon(e.expr)} :>`).join(";");
|
|
187900
|
+
arrayMethod(method, object, args, emit) {
|
|
187901
|
+
return renderArrayMethod(method, object, args, emit);
|
|
188104
187902
|
}
|
|
188105
|
-
|
|
188106
|
-
return
|
|
187903
|
+
flatMethod(object, depth, emit) {
|
|
187904
|
+
return renderFlatMethod(emit(object), depth);
|
|
188107
187905
|
}
|
|
188108
|
-
|
|
188109
|
-
|
|
188110
|
-
|
|
188111
|
-
|
|
188112
|
-
|
|
188113
|
-
|
|
188114
|
-
if (
|
|
188115
|
-
|
|
188116
|
-
|
|
188117
|
-
|
|
188118
|
-
else
|
|
188119
|
-
|
|
188120
|
-
|
|
188121
|
-
|
|
188122
|
-
|
|
187906
|
+
conditional(test, consequent, alternate, emit) {
|
|
187907
|
+
return `(${emit(test)} ? ${emit(consequent)} : ${emit(alternate)})`;
|
|
187908
|
+
}
|
|
187909
|
+
templateLiteral(parts, emit) {
|
|
187910
|
+
const terms = [];
|
|
187911
|
+
for (const part of parts) {
|
|
187912
|
+
if (part.type === "string") {
|
|
187913
|
+
if (part.value !== "") {
|
|
187914
|
+
terms.push(`'${part.value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`);
|
|
187915
|
+
}
|
|
187916
|
+
} else {
|
|
187917
|
+
const rendered = emit(part.expr);
|
|
187918
|
+
const needsParens = part.expr.kind === "binary" || part.expr.kind === "logical" || part.expr.kind === "conditional";
|
|
187919
|
+
terms.push(needsParens ? `(${rendered})` : rendered);
|
|
187920
|
+
}
|
|
188123
187921
|
}
|
|
188124
|
-
|
|
187922
|
+
if (terms.length === 0)
|
|
187923
|
+
return `''`;
|
|
187924
|
+
return terms.join(" ~ ");
|
|
188125
187925
|
}
|
|
188126
|
-
|
|
188127
|
-
return
|
|
187926
|
+
arrow(_params, _body) {
|
|
187927
|
+
return "''";
|
|
188128
187928
|
}
|
|
188129
|
-
|
|
188130
|
-
return
|
|
187929
|
+
regex(_raw) {
|
|
187930
|
+
return "''";
|
|
188131
187931
|
}
|
|
188132
|
-
|
|
188133
|
-
return
|
|
187932
|
+
unsupported(_raw, _reason) {
|
|
187933
|
+
return "''";
|
|
188134
187934
|
}
|
|
188135
|
-
|
|
188136
|
-
return
|
|
187935
|
+
objectLiteral(_properties, _raw, _emit) {
|
|
187936
|
+
return "''";
|
|
188137
187937
|
}
|
|
188138
|
-
|
|
188139
|
-
|
|
188140
|
-
|
|
188141
|
-
|
|
188142
|
-
|
|
188143
|
-
|
|
188144
|
-
|
|
188145
|
-
|
|
188146
|
-
|
|
188147
|
-
|
|
188148
|
-
|
|
188149
|
-
|
|
188150
|
-
|
|
188151
|
-
|
|
188152
|
-
|
|
188153
|
-
|
|
188154
|
-
|
|
188155
|
-
if (parts.length === 1)
|
|
188156
|
-
return parts[0];
|
|
188157
|
-
return `(${parts.join(" || ")})`;
|
|
187938
|
+
}
|
|
187939
|
+
|
|
187940
|
+
// src/adapter/analysis/component-tree.ts
|
|
187941
|
+
function hasClientInteractivity(ir) {
|
|
187942
|
+
return ir.metadata.signals.length > 0 || ir.metadata.effects.length > 0 || ir.metadata.onMounts.length > 0 || (ir.metadata.clientAnalysis?.needsInit ?? false);
|
|
187943
|
+
}
|
|
187944
|
+
function collectImportedLoopChildComponentErrors(ir, componentName) {
|
|
187945
|
+
const errors = [];
|
|
187946
|
+
const relativeImports = new Set;
|
|
187947
|
+
for (const imp of ir.metadata.templateImports ?? ir.metadata.imports ?? []) {
|
|
187948
|
+
if (!imp.source.startsWith("./") && !imp.source.startsWith("../"))
|
|
187949
|
+
continue;
|
|
187950
|
+
if (imp.isTypeOnly)
|
|
187951
|
+
continue;
|
|
187952
|
+
for (const spec of imp.specifiers) {
|
|
187953
|
+
relativeImports.add(spec.alias ?? spec.name);
|
|
187954
|
+
}
|
|
188158
187955
|
}
|
|
188159
|
-
|
|
188160
|
-
|
|
188161
|
-
|
|
188162
|
-
|
|
188163
|
-
|
|
188164
|
-
|
|
188165
|
-
|
|
188166
|
-
|
|
188167
|
-
|
|
188168
|
-
|
|
188169
|
-
|
|
188170
|
-
|
|
188171
|
-
|
|
188172
|
-
|
|
188173
|
-
|
|
188174
|
-
|
|
188175
|
-
|
|
188176
|
-
|
|
188177
|
-
|
|
187956
|
+
if (relativeImports.size === 0)
|
|
187957
|
+
return errors;
|
|
187958
|
+
const loc = { file: componentName + ".tsx", start: { line: 1, column: 0 }, end: { line: 1, column: 0 } };
|
|
187959
|
+
const visit = (node, inLoop) => {
|
|
187960
|
+
switch (node.type) {
|
|
187961
|
+
case "component": {
|
|
187962
|
+
const comp = node;
|
|
187963
|
+
if (inLoop && relativeImports.has(comp.name)) {
|
|
187964
|
+
errors.push({
|
|
187965
|
+
code: "BF103",
|
|
187966
|
+
severity: "error",
|
|
187967
|
+
message: `Component <${comp.name}> is imported from a sibling module and used inside a loop. The Xslate adapter emits a cross-template call; the child template must be registered alongside the parent at render time.`,
|
|
187968
|
+
loc: comp.loc ?? loc,
|
|
187969
|
+
suggestion: {
|
|
187970
|
+
message: `Options:
|
|
187971
|
+
` + ` 1. Compile '${comp.name}' (its source file) with the same adapter and register the resulting Xslate template alongside the parent at render time.
|
|
187972
|
+
` + ` 2. Inline <${comp.name}> directly inside the loop body so no cross-file template lookup is needed.
|
|
187973
|
+
` + ` 3. Mark the loop position as @client-only so the template is materialised on the client instead of at SSR time.`
|
|
187974
|
+
}
|
|
187975
|
+
});
|
|
188178
187976
|
}
|
|
187977
|
+
for (const child of comp.children)
|
|
187978
|
+
visit(child, inLoop);
|
|
187979
|
+
break;
|
|
188179
187980
|
}
|
|
188180
|
-
|
|
188181
|
-
|
|
188182
|
-
|
|
188183
|
-
|
|
188184
|
-
|
|
188185
|
-
|
|
188186
|
-
|
|
188187
|
-
|
|
188188
|
-
|
|
187981
|
+
case "element":
|
|
187982
|
+
for (const child of node.children)
|
|
187983
|
+
visit(child, inLoop);
|
|
187984
|
+
break;
|
|
187985
|
+
case "fragment":
|
|
187986
|
+
for (const child of node.children)
|
|
187987
|
+
visit(child, inLoop);
|
|
187988
|
+
break;
|
|
187989
|
+
case "conditional": {
|
|
187990
|
+
const cond = node;
|
|
187991
|
+
visit(cond.whenTrue, inLoop);
|
|
187992
|
+
if (cond.whenFalse)
|
|
187993
|
+
visit(cond.whenFalse, inLoop);
|
|
187994
|
+
break;
|
|
188189
187995
|
}
|
|
188190
|
-
|
|
188191
|
-
|
|
188192
|
-
|
|
188193
|
-
|
|
188194
|
-
|
|
188195
|
-
|
|
188196
|
-
|
|
188197
|
-
|
|
188198
|
-
|
|
188199
|
-
|
|
188200
|
-
if (conditions.length === 0)
|
|
188201
|
-
return "1";
|
|
188202
|
-
if (conditions.length === 1)
|
|
188203
|
-
return this.renderKolonFilterExpr(conditions[0], param, localVarMap);
|
|
188204
|
-
const parts = conditions.map((c) => this.renderKolonFilterExpr(c, param, localVarMap));
|
|
188205
|
-
return `(${parts.join(" && ")})`;
|
|
188206
|
-
}
|
|
188207
|
-
convertTemplateLiteralPartsToKolon(literalParts) {
|
|
188208
|
-
const parts = [];
|
|
188209
|
-
for (const part of literalParts) {
|
|
188210
|
-
if (part.type === "string") {
|
|
188211
|
-
parts.push(this.substituteJsInterpolationsToKolon(part.value));
|
|
188212
|
-
} else if (part.type === "ternary") {
|
|
188213
|
-
const cond = this.convertExpressionToKolon(part.condition);
|
|
188214
|
-
parts.push(`(${cond} ? '${part.whenTrue}' : '${part.whenFalse}')`);
|
|
188215
|
-
} else if (part.type === "lookup") {
|
|
188216
|
-
const keyExpr = this.convertExpressionToKolon(part.key);
|
|
188217
|
-
const entries = Object.entries(part.cases).map(([k, v]) => `'${k}' => '${v}'`).join(", ");
|
|
188218
|
-
parts.push(`({ ${entries} }[${keyExpr}] // '')`);
|
|
187996
|
+
case "loop":
|
|
187997
|
+
for (const child of node.children)
|
|
187998
|
+
visit(child, true);
|
|
187999
|
+
break;
|
|
188000
|
+
case "if-statement": {
|
|
188001
|
+
const stmt = node;
|
|
188002
|
+
visit(stmt.consequent, inLoop);
|
|
188003
|
+
if (stmt.alternate)
|
|
188004
|
+
visit(stmt.alternate, inLoop);
|
|
188005
|
+
break;
|
|
188219
188006
|
}
|
|
188220
|
-
|
|
188221
|
-
|
|
188222
|
-
|
|
188223
|
-
|
|
188224
|
-
|
|
188225
|
-
|
|
188226
|
-
|
|
188227
|
-
|
|
188228
|
-
|
|
188229
|
-
|
|
188230
|
-
segments.push(`'${s.slice(lastIndex, m.index)}'`);
|
|
188007
|
+
case "provider":
|
|
188008
|
+
for (const child of node.children)
|
|
188009
|
+
visit(child, inLoop);
|
|
188010
|
+
break;
|
|
188011
|
+
case "async": {
|
|
188012
|
+
const a = node;
|
|
188013
|
+
visit(a.fallback, inLoop);
|
|
188014
|
+
for (const child of a.children)
|
|
188015
|
+
visit(child, inLoop);
|
|
188016
|
+
break;
|
|
188231
188017
|
}
|
|
188232
|
-
segments.push(this.convertExpressionToKolon(m[1].trim()));
|
|
188233
|
-
lastIndex = re.lastIndex;
|
|
188234
|
-
}
|
|
188235
|
-
if (lastIndex < s.length) {
|
|
188236
|
-
segments.push(`'${s.slice(lastIndex)}'`);
|
|
188237
188018
|
}
|
|
188238
|
-
|
|
188239
|
-
|
|
188240
|
-
|
|
188241
|
-
|
|
188242
|
-
|
|
188243
|
-
|
|
188244
|
-
|
|
188245
|
-
|
|
188246
|
-
|
|
188247
|
-
|
|
188248
|
-
|
|
188249
|
-
|
|
188250
|
-
|
|
188251
|
-
|
|
188252
|
-
|
|
188253
|
-
return false;
|
|
188254
|
-
const reason = support.reason ?? (parsed.kind === "unsupported" ? parsed.reason : undefined);
|
|
188255
|
-
const reasonLine = reason ? `
|
|
188256
|
-
${reason}` : "";
|
|
188257
|
-
this.errors.push({
|
|
188258
|
-
code: "BF101",
|
|
188259
|
-
severity: "error",
|
|
188260
|
-
message: `Expression not supported on attribute '${attrName}': ${expr.trim()}${reasonLine}`,
|
|
188261
|
-
loc: { file: this.componentName + ".tsx", start: { line: 1, column: 0 }, end: { line: 1, column: 0 } },
|
|
188262
|
-
suggestion: {
|
|
188263
|
-
message: "The Xslate adapter cannot lower JS object literals or tagged-template-literal expressions into Kolon. Move the expression into a `'use client'` component (so hydration computes it), or expand it into discrete attributes whose values are values the adapter can lower."
|
|
188264
|
-
}
|
|
188265
|
-
});
|
|
188266
|
-
return true;
|
|
188019
|
+
};
|
|
188020
|
+
visit(ir.root, false);
|
|
188021
|
+
return errors;
|
|
188022
|
+
}
|
|
188023
|
+
|
|
188024
|
+
// src/adapter/spread/spread-codegen.ts
|
|
188025
|
+
var import_typescript = __toESM(require_typescript(), 1);
|
|
188026
|
+
import { parseRecordIndexAccess, stringifyParsedExpr } from "@barefootjs/jsx";
|
|
188027
|
+
function conditionalSpreadToKolon(ctx, expr) {
|
|
188028
|
+
if (!expr || expr.kind !== "conditional")
|
|
188029
|
+
return null;
|
|
188030
|
+
const whenTrue = expr.consequent;
|
|
188031
|
+
const whenFalse = expr.alternate;
|
|
188032
|
+
if (whenTrue.kind !== "object-literal" || whenFalse.kind !== "object-literal") {
|
|
188033
|
+
return null;
|
|
188267
188034
|
}
|
|
188268
|
-
|
|
188269
|
-
|
|
188270
|
-
|
|
188271
|
-
|
|
188272
|
-
|
|
188273
|
-
|
|
188035
|
+
const condKolon = ctx.convertExpressionToKolon("", expr.test);
|
|
188036
|
+
const trueKolon = objectLiteralToKolonHashref(ctx, whenTrue);
|
|
188037
|
+
const falseKolon = objectLiteralToKolonHashref(ctx, whenFalse);
|
|
188038
|
+
if (trueKolon === null || falseKolon === null)
|
|
188039
|
+
return null;
|
|
188040
|
+
return `${condKolon} ? ${trueKolon} : ${falseKolon}`;
|
|
188041
|
+
}
|
|
188042
|
+
function objectLiteralExprToKolonHashref(ctx, expr) {
|
|
188043
|
+
if (!expr || expr.kind !== "object-literal")
|
|
188044
|
+
return null;
|
|
188045
|
+
return objectLiteralToKolonHashref(ctx, expr);
|
|
188046
|
+
}
|
|
188047
|
+
function objectLiteralToKolonHashref(ctx, obj) {
|
|
188048
|
+
const entries = [];
|
|
188049
|
+
for (const prop of obj.properties) {
|
|
188050
|
+
if (prop.shorthand)
|
|
188274
188051
|
return null;
|
|
188275
|
-
|
|
188276
|
-
while (import_typescript.default.isParenthesizedExpression(node))
|
|
188277
|
-
node = node.expression;
|
|
188278
|
-
if (!import_typescript.default.isConditionalExpression(node))
|
|
188052
|
+
if (prop.keyKind === "numeric")
|
|
188279
188053
|
return null;
|
|
188280
|
-
const
|
|
188281
|
-
|
|
188282
|
-
|
|
188283
|
-
|
|
188284
|
-
|
|
188285
|
-
|
|
188286
|
-
|
|
188287
|
-
|
|
188288
|
-
|
|
188054
|
+
const key = prop.key;
|
|
188055
|
+
const val = prop.value;
|
|
188056
|
+
const indexed = recordIndexAccessToKolon(ctx, val);
|
|
188057
|
+
if (indexed === null && val.kind === "index-access" && !isLiteralIndex(val.index)) {
|
|
188058
|
+
ctx.errors.push({
|
|
188059
|
+
code: "BF101",
|
|
188060
|
+
severity: "error",
|
|
188061
|
+
message: `Spread object value '${stringifyParsedExpr(val)}' indexes a record map whose values aren't scalar literals — it can't lower to an inline Kolon hashref.`,
|
|
188062
|
+
loc: { file: ctx.componentName + ".tsx", start: { line: 1, column: 0 }, end: { line: 1, column: 0 } },
|
|
188063
|
+
suggestion: {
|
|
188064
|
+
message: "Index a record whose values are number/string literals, or move the spread into a `'use client'` component so hydration computes it."
|
|
188065
|
+
}
|
|
188066
|
+
});
|
|
188289
188067
|
return null;
|
|
188290
188068
|
}
|
|
188291
|
-
const
|
|
188292
|
-
|
|
188293
|
-
const falsePerl = this.objectLiteralToKolonHashref(whenFalse, sf);
|
|
188294
|
-
if (truePerl === null || falsePerl === null)
|
|
188295
|
-
return null;
|
|
188296
|
-
return `${condPerl} ? ${truePerl} : ${falsePerl}`;
|
|
188297
|
-
}
|
|
188298
|
-
objectLiteralToKolonHashref(obj, sf) {
|
|
188299
|
-
const entries = [];
|
|
188300
|
-
for (const prop of obj.properties) {
|
|
188301
|
-
if (!import_typescript.default.isPropertyAssignment(prop))
|
|
188302
|
-
return null;
|
|
188303
|
-
let key;
|
|
188304
|
-
if (import_typescript.default.isIdentifier(prop.name)) {
|
|
188305
|
-
key = prop.name.text;
|
|
188306
|
-
} else if (import_typescript.default.isStringLiteral(prop.name) || import_typescript.default.isNoSubstitutionTemplateLiteral(prop.name)) {
|
|
188307
|
-
key = prop.name.text;
|
|
188308
|
-
} else {
|
|
188309
|
-
return null;
|
|
188310
|
-
}
|
|
188311
|
-
const initNode = (() => {
|
|
188312
|
-
let n = prop.initializer;
|
|
188313
|
-
while (import_typescript.default.isParenthesizedExpression(n))
|
|
188314
|
-
n = n.expression;
|
|
188315
|
-
return n;
|
|
188316
|
-
})();
|
|
188317
|
-
const indexed = this.recordIndexAccessToKolon(initNode);
|
|
188318
|
-
if (indexed === null && import_typescript.default.isElementAccessExpression(initNode) && initNode.argumentExpression && !import_typescript.default.isNumericLiteral(initNode.argumentExpression) && !import_typescript.default.isStringLiteral(initNode.argumentExpression)) {
|
|
188319
|
-
this.errors.push({
|
|
188320
|
-
code: "BF101",
|
|
188321
|
-
severity: "error",
|
|
188322
|
-
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.`,
|
|
188323
|
-
loc: { file: this.componentName + ".tsx", start: { line: 1, column: 0 }, end: { line: 1, column: 0 } },
|
|
188324
|
-
suggestion: {
|
|
188325
|
-
message: "Index a record whose values are number/string literals, or move the spread into a `'use client'` component so hydration computes it."
|
|
188326
|
-
}
|
|
188327
|
-
});
|
|
188328
|
-
return null;
|
|
188329
|
-
}
|
|
188330
|
-
const valPerl = indexed !== null ? indexed : this.convertExpressionToKolon(prop.initializer.getText(sf));
|
|
188331
|
-
entries.push(`'${escapeKolonSingleQuoted(key)}' => ${valPerl}`);
|
|
188332
|
-
}
|
|
188333
|
-
return entries.length === 0 ? "{}" : `{ ${entries.join(", ")} }`;
|
|
188334
|
-
}
|
|
188335
|
-
recordIndexAccessToKolon(val) {
|
|
188336
|
-
const parsed = parseRecordIndexAccess(val, this.localConstants ?? [], this.propsParams);
|
|
188337
|
-
if (!parsed)
|
|
188338
|
-
return null;
|
|
188339
|
-
const entries = parsed.entries.map((e) => {
|
|
188340
|
-
const mapVal = e.value.kind === "number" ? e.value.text : `'${escapeKolonSingleQuoted(e.value.text)}'`;
|
|
188341
|
-
return `'${escapeKolonSingleQuoted(e.key)}' => ${mapVal}`;
|
|
188342
|
-
});
|
|
188343
|
-
return `{ ${entries.join(", ")} }[$${parsed.indexPropName}]`;
|
|
188344
|
-
}
|
|
188345
|
-
convertExpressionToKolon(expr) {
|
|
188346
|
-
const trimmed = expr.trim();
|
|
188347
|
-
if (trimmed === "")
|
|
188348
|
-
return "''";
|
|
188349
|
-
const parsed = parseExpression2(trimmed);
|
|
188350
|
-
const support = isSupported(parsed);
|
|
188351
|
-
if (!support.supported) {
|
|
188352
|
-
this.errors.push({
|
|
188353
|
-
code: "BF101",
|
|
188354
|
-
severity: "error",
|
|
188355
|
-
message: `Expression not supported: ${trimmed}`,
|
|
188356
|
-
loc: { file: this.componentName + ".tsx", start: { line: 1, column: 0 }, end: { line: 1, column: 0 } },
|
|
188357
|
-
suggestion: {
|
|
188358
|
-
message: support.reason ? `${support.reason}
|
|
188359
|
-
|
|
188360
|
-
Options:
|
|
188361
|
-
1. Use /* @client */ for client-side evaluation
|
|
188362
|
-
2. Pre-compute the value in the backend` : `Options:
|
|
188363
|
-
1. Use /* @client */ for client-side evaluation
|
|
188364
|
-
2. Pre-compute the value in the backend`
|
|
188365
|
-
}
|
|
188366
|
-
});
|
|
188367
|
-
return "''";
|
|
188368
|
-
}
|
|
188369
|
-
return this.renderParsedExprToKolon(parsed);
|
|
188370
|
-
}
|
|
188371
|
-
renderParsedExprToKolon(expr) {
|
|
188372
|
-
return emitParsedExpr(expr, new XslateTopLevelEmitter(this));
|
|
188373
|
-
}
|
|
188374
|
-
_isStringValueName(name) {
|
|
188375
|
-
return this.stringValueNames.has(name);
|
|
188376
|
-
}
|
|
188377
|
-
parseUndefinedAlternateTernary(expr) {
|
|
188378
|
-
const parsed = parseExpression2(expr.trim());
|
|
188379
|
-
if (parsed?.kind !== "conditional")
|
|
188380
|
-
return null;
|
|
188381
|
-
const alt = parsed.alternate;
|
|
188382
|
-
const isUndef = alt.kind === "identifier" && (alt.name === "undefined" || alt.name === "null") || alt.kind === "literal" && (alt.value === null || alt.value === undefined);
|
|
188383
|
-
if (!isUndef)
|
|
188384
|
-
return null;
|
|
188385
|
-
return {
|
|
188386
|
-
condition: exprToString(parsed.test),
|
|
188387
|
-
consequent: exprToString(parsed.consequent)
|
|
188388
|
-
};
|
|
188389
|
-
}
|
|
188390
|
-
isBooleanTypedPropRef(expr) {
|
|
188391
|
-
let bare = expr.trim();
|
|
188392
|
-
if (this.propsObjectName && bare.startsWith(`${this.propsObjectName}.`)) {
|
|
188393
|
-
bare = bare.slice(this.propsObjectName.length + 1);
|
|
188394
|
-
}
|
|
188395
|
-
if (!/^[A-Za-z_$][\w$]*$/.test(bare))
|
|
188396
|
-
return false;
|
|
188397
|
-
return this.booleanTypedProps.has(bare);
|
|
188398
|
-
}
|
|
188399
|
-
_resolveLiteralConst(name) {
|
|
188400
|
-
const c = (this.localConstants ?? []).find((lc) => lc.name === name);
|
|
188401
|
-
if (c?.value === undefined)
|
|
188402
|
-
return null;
|
|
188403
|
-
const v = c.value.trim();
|
|
188404
|
-
if (/^-?\d+(\.\d+)?$/.test(v))
|
|
188405
|
-
return v;
|
|
188406
|
-
const strLit = /^'([^'\\]*)'$/.exec(v) ?? /^"([^"\\]*)"$/.exec(v);
|
|
188407
|
-
if (strLit)
|
|
188408
|
-
return `'${strLit[1].replace(/[\\']/g, (m) => `\\${m}`)}'`;
|
|
188409
|
-
return null;
|
|
188410
|
-
}
|
|
188411
|
-
_resolveStaticRecordLiteral(objectName, key) {
|
|
188412
|
-
const hit = lookupStaticRecordLiteral(objectName, key, this.localConstants);
|
|
188413
|
-
if (!hit)
|
|
188414
|
-
return null;
|
|
188415
|
-
return hit.kind === "number" ? hit.text : `'${hit.text.replace(/[\\']/g, (m) => `\\${m}`)}'`;
|
|
188416
|
-
}
|
|
188417
|
-
_resolveModuleStringConst(name) {
|
|
188418
|
-
if (this.inLoop)
|
|
188419
|
-
return null;
|
|
188420
|
-
const value = this.moduleStringConsts.get(name);
|
|
188421
|
-
if (value === undefined)
|
|
188422
|
-
return null;
|
|
188423
|
-
return `'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
|
|
188424
|
-
}
|
|
188425
|
-
_recordExprBF101(message, reason) {
|
|
188426
|
-
this.errors.push({
|
|
188427
|
-
code: "BF101",
|
|
188428
|
-
severity: "error",
|
|
188429
|
-
message,
|
|
188430
|
-
loc: { file: this.componentName + ".tsx", start: { line: 1, column: 0 }, end: { line: 1, column: 0 } },
|
|
188431
|
-
suggestion: {
|
|
188432
|
-
message: reason ? `${reason}
|
|
188433
|
-
|
|
188434
|
-
Options:
|
|
188435
|
-
1. Use /* @client */ for client-side evaluation
|
|
188436
|
-
2. Pre-compute the value in the backend` : `Options:
|
|
188437
|
-
1. Use /* @client */ for client-side evaluation
|
|
188438
|
-
2. Pre-compute the value in the backend`
|
|
188439
|
-
}
|
|
188440
|
-
});
|
|
188441
|
-
}
|
|
188442
|
-
_renderKolonFilterExprPublic(expr, param) {
|
|
188443
|
-
return this.renderKolonFilterExpr(expr, param);
|
|
188069
|
+
const valKolon = indexed !== null ? indexed : ctx.convertExpressionToKolon("", val);
|
|
188070
|
+
entries.push(`'${escapeKolonSingleQuoted(key)}' => ${valKolon}`);
|
|
188444
188071
|
}
|
|
188072
|
+
return entries.length === 0 ? "{}" : `{ ${entries.join(", ")} }`;
|
|
188445
188073
|
}
|
|
188446
|
-
function
|
|
188447
|
-
|
|
188448
|
-
case "join": {
|
|
188449
|
-
const obj = emit(object);
|
|
188450
|
-
const sep2 = args.length >= 1 ? emit(args[0]) : `','`;
|
|
188451
|
-
return `$bf.join(${obj}, ${sep2})`;
|
|
188452
|
-
}
|
|
188453
|
-
case "includes": {
|
|
188454
|
-
const obj = emit(object);
|
|
188455
|
-
const needle = emit(args[0]);
|
|
188456
|
-
return `$bf.includes(${obj}, ${needle})`;
|
|
188457
|
-
}
|
|
188458
|
-
case "indexOf":
|
|
188459
|
-
case "lastIndexOf": {
|
|
188460
|
-
const fn = method === "indexOf" ? "index_of" : "last_index_of";
|
|
188461
|
-
const obj = emit(object);
|
|
188462
|
-
const needle = emit(args[0]);
|
|
188463
|
-
return `$bf.${fn}(${obj}, ${needle})`;
|
|
188464
|
-
}
|
|
188465
|
-
case "at": {
|
|
188466
|
-
const obj = emit(object);
|
|
188467
|
-
const idx = args.length >= 1 ? emit(args[0]) : "0";
|
|
188468
|
-
return `$bf.at(${obj}, ${idx})`;
|
|
188469
|
-
}
|
|
188470
|
-
case "concat": {
|
|
188471
|
-
if (args.length === 0) {
|
|
188472
|
-
return emit(object);
|
|
188473
|
-
}
|
|
188474
|
-
const a = emit(object);
|
|
188475
|
-
const b = emit(args[0]);
|
|
188476
|
-
return `$bf.concat(${a}, ${b})`;
|
|
188477
|
-
}
|
|
188478
|
-
case "slice": {
|
|
188479
|
-
const recv = emit(object);
|
|
188480
|
-
const start = args.length >= 1 ? emit(args[0]) : "0";
|
|
188481
|
-
const end = args.length >= 2 ? emit(args[1]) : "nil";
|
|
188482
|
-
return `$bf.slice(${recv}, ${start}, ${end})`;
|
|
188483
|
-
}
|
|
188484
|
-
case "reverse":
|
|
188485
|
-
case "toReversed": {
|
|
188486
|
-
const recv = emit(object);
|
|
188487
|
-
return `$bf.reverse(${recv})`;
|
|
188488
|
-
}
|
|
188489
|
-
case "toLowerCase": {
|
|
188490
|
-
const recv = emit(object);
|
|
188491
|
-
return `$bf.lc(${recv})`;
|
|
188492
|
-
}
|
|
188493
|
-
case "toUpperCase": {
|
|
188494
|
-
const recv = emit(object);
|
|
188495
|
-
return `$bf.uc(${recv})`;
|
|
188496
|
-
}
|
|
188497
|
-
case "trim": {
|
|
188498
|
-
const recv = emit(object);
|
|
188499
|
-
return `$bf.trim(${recv})`;
|
|
188500
|
-
}
|
|
188501
|
-
case "toFixed": {
|
|
188502
|
-
const recv = emit(object);
|
|
188503
|
-
const digits = args.length >= 1 ? emit(args[0]) : "0";
|
|
188504
|
-
return `$bf.to_fixed(${recv}, ${digits})`;
|
|
188505
|
-
}
|
|
188506
|
-
case "split": {
|
|
188507
|
-
const recv = emit(object);
|
|
188508
|
-
if (args.length === 0) {
|
|
188509
|
-
return `$bf.split(${recv})`;
|
|
188510
|
-
}
|
|
188511
|
-
const sep2 = emit(args[0]);
|
|
188512
|
-
if (args.length === 1) {
|
|
188513
|
-
return `$bf.split(${recv}, ${sep2})`;
|
|
188514
|
-
}
|
|
188515
|
-
const limit = emit(args[1]);
|
|
188516
|
-
return `$bf.split(${recv}, ${sep2}, ${limit})`;
|
|
188517
|
-
}
|
|
188518
|
-
case "startsWith":
|
|
188519
|
-
case "endsWith": {
|
|
188520
|
-
const fn = method === "startsWith" ? "starts_with" : "ends_with";
|
|
188521
|
-
const recv = emit(object);
|
|
188522
|
-
const arg = emit(args[0]);
|
|
188523
|
-
if (args.length >= 2) {
|
|
188524
|
-
return `$bf.${fn}(${recv}, ${arg}, ${emit(args[1])})`;
|
|
188525
|
-
}
|
|
188526
|
-
return `$bf.${fn}(${recv}, ${arg})`;
|
|
188527
|
-
}
|
|
188528
|
-
case "replace": {
|
|
188529
|
-
const recv = emit(object);
|
|
188530
|
-
const oldS = emit(args[0]);
|
|
188531
|
-
const newS = emit(args[1]);
|
|
188532
|
-
return `$bf.replace(${recv}, ${oldS}, ${newS})`;
|
|
188533
|
-
}
|
|
188534
|
-
case "repeat": {
|
|
188535
|
-
const recv = emit(object);
|
|
188536
|
-
const count = args.length === 0 ? "0" : emit(args[0]);
|
|
188537
|
-
return `$bf.repeat(${recv}, ${count})`;
|
|
188538
|
-
}
|
|
188539
|
-
case "padStart":
|
|
188540
|
-
case "padEnd": {
|
|
188541
|
-
const fn = method === "padStart" ? "pad_start" : "pad_end";
|
|
188542
|
-
const recv = emit(object);
|
|
188543
|
-
if (args.length === 0) {
|
|
188544
|
-
return `$bf.${fn}(${recv}, 0)`;
|
|
188545
|
-
}
|
|
188546
|
-
const target = emit(args[0]);
|
|
188547
|
-
if (args.length === 1) {
|
|
188548
|
-
return `$bf.${fn}(${recv}, ${target})`;
|
|
188549
|
-
}
|
|
188550
|
-
const pad2 = emit(args[1]);
|
|
188551
|
-
return `$bf.${fn}(${recv}, ${target}, ${pad2})`;
|
|
188552
|
-
}
|
|
188553
|
-
default: {
|
|
188554
|
-
const _exhaustive = method;
|
|
188555
|
-
throw new Error(`renderArrayMethod: unhandled ArrayMethod '${_exhaustive}'`);
|
|
188556
|
-
}
|
|
188557
|
-
}
|
|
188074
|
+
function isLiteralIndex(index) {
|
|
188075
|
+
return index.kind === "literal" && (index.literalType === "number" || index.literalType === "string");
|
|
188558
188076
|
}
|
|
188559
|
-
function
|
|
188560
|
-
|
|
188561
|
-
|
|
188562
|
-
|
|
188077
|
+
function recordIndexAccessToKolon(ctx, val) {
|
|
188078
|
+
if (val.kind !== "index-access" || val.object.kind !== "identifier" || val.index.kind !== "identifier") {
|
|
188079
|
+
return null;
|
|
188080
|
+
}
|
|
188081
|
+
const tsVal = import_typescript.default.factory.createElementAccessExpression(import_typescript.default.factory.createIdentifier(val.object.name), import_typescript.default.factory.createIdentifier(val.index.name));
|
|
188082
|
+
const parsed = parseRecordIndexAccess(tsVal, ctx.localConstants ?? [], ctx.propsParams);
|
|
188083
|
+
if (!parsed)
|
|
188084
|
+
return null;
|
|
188085
|
+
const entries = parsed.entries.map((e) => {
|
|
188086
|
+
const mapVal = e.value.kind === "number" ? e.value.text : `'${escapeKolonSingleQuoted(e.value.text)}'`;
|
|
188087
|
+
return `'${escapeKolonSingleQuoted(e.key)}' => ${mapVal}`;
|
|
188563
188088
|
});
|
|
188564
|
-
return
|
|
188089
|
+
return `{ ${entries.join(", ")} }[$${parsed.indexPropName}]`;
|
|
188565
188090
|
}
|
|
188566
|
-
|
|
188567
|
-
|
|
188568
|
-
|
|
188569
|
-
|
|
188091
|
+
|
|
188092
|
+
// src/adapter/memo/seed.ts
|
|
188093
|
+
import {
|
|
188094
|
+
collectContextConsumers,
|
|
188095
|
+
extractArrowBodyExpression,
|
|
188096
|
+
isSupported,
|
|
188097
|
+
parseExpression as parseExpression2
|
|
188098
|
+
} from "@barefootjs/jsx";
|
|
188099
|
+
function contextDefaultKolon(c) {
|
|
188100
|
+
const d = c.defaultValue;
|
|
188101
|
+
if (d === null || d === undefined)
|
|
188102
|
+
return "nil";
|
|
188103
|
+
if (typeof d === "string")
|
|
188104
|
+
return `'${d.replace(/[\\']/g, (m) => `\\${m}`)}'`;
|
|
188105
|
+
if (typeof d === "boolean")
|
|
188106
|
+
return d ? "1" : "0";
|
|
188107
|
+
return String(d);
|
|
188570
188108
|
}
|
|
188571
|
-
function
|
|
188572
|
-
const
|
|
188573
|
-
|
|
188109
|
+
function generateContextConsumerSeed(ir) {
|
|
188110
|
+
const consumers = collectContextConsumers(ir.metadata);
|
|
188111
|
+
if (consumers.length === 0)
|
|
188112
|
+
return "";
|
|
188113
|
+
return consumers.map((c) => `: my $${c.localName} = $bf.use_context('${c.contextName}', ${contextDefaultKolon(c)});`).join(`
|
|
188114
|
+
`) + `
|
|
188115
|
+
`;
|
|
188574
188116
|
}
|
|
188575
|
-
function
|
|
188576
|
-
const
|
|
188577
|
-
|
|
188578
|
-
|
|
188579
|
-
return
|
|
188117
|
+
function generateDerivedMemoSeed(ctx, ir) {
|
|
188118
|
+
const memos = ir.metadata.memos ?? [];
|
|
188119
|
+
const signals = ir.metadata.signals ?? [];
|
|
188120
|
+
if (memos.length === 0 && signals.length === 0)
|
|
188121
|
+
return "";
|
|
188122
|
+
const available = new Set(ir.metadata.propsParams.map((p) => p.name));
|
|
188123
|
+
const lines = [];
|
|
188124
|
+
for (const signal of signals) {
|
|
188125
|
+
const kolon = tryLowerToKolon(ctx, signal.initialValue, available);
|
|
188126
|
+
const refsSelf = kolon !== null && new RegExp(`\\$${signal.getter}\\b`).test(kolon);
|
|
188127
|
+
if (kolon !== null && !refsSelf)
|
|
188128
|
+
lines.push(`: my $${signal.getter} = ${kolon};`);
|
|
188129
|
+
available.add(signal.getter);
|
|
188130
|
+
}
|
|
188131
|
+
for (const memo of memos) {
|
|
188132
|
+
const body = extractArrowBodyExpression(memo.computation);
|
|
188133
|
+
if (body !== null) {
|
|
188134
|
+
const kolon = tryLowerToKolon(ctx, body, available);
|
|
188135
|
+
const refsSelf = kolon !== null && new RegExp(`\\$${memo.name}\\b`).test(kolon);
|
|
188136
|
+
if (kolon !== null && !refsSelf)
|
|
188137
|
+
lines.push(`: my $${memo.name} = ${kolon};`);
|
|
188138
|
+
}
|
|
188139
|
+
available.add(memo.name);
|
|
188580
188140
|
}
|
|
188581
|
-
|
|
188582
|
-
|
|
188583
|
-
|
|
188141
|
+
return lines.length > 0 ? lines.join(`
|
|
188142
|
+
`) + `
|
|
188143
|
+
` : "";
|
|
188144
|
+
}
|
|
188145
|
+
function tryLowerToKolon(ctx, expr, available) {
|
|
188146
|
+
const trimmed = expr.trim();
|
|
188147
|
+
if (!trimmed)
|
|
188148
|
+
return null;
|
|
188149
|
+
if (!isSupported(parseExpression2(trimmed)).supported)
|
|
188150
|
+
return null;
|
|
188151
|
+
const kolon = ctx.convertExpressionToKolon(trimmed);
|
|
188152
|
+
if (kolon === "" || !/\$[A-Za-z_]\w*/.test(kolon))
|
|
188153
|
+
return null;
|
|
188154
|
+
return referencedVarsAreAvailable(kolon, available) ? kolon : null;
|
|
188584
188155
|
}
|
|
188156
|
+
|
|
188157
|
+
// src/adapter/value/parsed-literal.ts
|
|
188158
|
+
import { evalStringArrayJoin } from "@barefootjs/jsx";
|
|
188585
188159
|
function isStringTypeInfo(type2) {
|
|
188586
188160
|
return type2?.kind === "primitive" && type2.primitive === "string";
|
|
188587
188161
|
}
|
|
@@ -188591,274 +188165,802 @@ function isBareStringLiteral(initialValue) {
|
|
|
188591
188165
|
const v = initialValue.trim();
|
|
188592
188166
|
return v.startsWith("'") && v.endsWith("'") || v.startsWith('"') && v.endsWith('"');
|
|
188593
188167
|
}
|
|
188594
|
-
|
|
188595
|
-
|
|
188596
|
-
|
|
188597
|
-
|
|
188598
|
-
|
|
188599
|
-
|
|
188600
|
-
|
|
188601
|
-
|
|
188168
|
+
|
|
188169
|
+
// src/adapter/props/prop-classes.ts
|
|
188170
|
+
function collectBooleanTypedProps(ir) {
|
|
188171
|
+
return new Set(ir.metadata.propsParams.filter((prop) => prop.type?.primitive === "boolean" || prop.type?.raw === "boolean").map((prop) => prop.name));
|
|
188172
|
+
}
|
|
188173
|
+
function collectNullableOptionalProps(ir) {
|
|
188174
|
+
return new Set(ir.metadata.propsParams.filter((p) => p.defaultValue === undefined && !p.isRest && p.type?.kind !== "primitive").map((p) => p.name));
|
|
188175
|
+
}
|
|
188176
|
+
function collectStringValueNames(ir) {
|
|
188177
|
+
const names = new Set;
|
|
188178
|
+
for (const s of ir.metadata.signals) {
|
|
188179
|
+
if (isStringTypeInfo(s.type) || isBareStringLiteral(s.initialValue)) {
|
|
188180
|
+
names.add(s.getter);
|
|
188181
|
+
}
|
|
188602
188182
|
}
|
|
188603
|
-
|
|
188604
|
-
if (
|
|
188605
|
-
|
|
188606
|
-
const signal = this.localVarMap.get(name);
|
|
188607
|
-
if (signal)
|
|
188608
|
-
return `$${signal}`;
|
|
188609
|
-
return `$${name}`;
|
|
188183
|
+
for (const p of ir.metadata.propsParams) {
|
|
188184
|
+
if (isStringTypeInfo(p.type))
|
|
188185
|
+
names.add(p.name);
|
|
188610
188186
|
}
|
|
188611
|
-
|
|
188612
|
-
|
|
188613
|
-
|
|
188614
|
-
|
|
188615
|
-
|
|
188616
|
-
|
|
188617
|
-
|
|
188618
|
-
|
|
188187
|
+
return names;
|
|
188188
|
+
}
|
|
188189
|
+
|
|
188190
|
+
// src/adapter/xslate-adapter.ts
|
|
188191
|
+
class XslateAdapter extends BaseAdapter {
|
|
188192
|
+
name = "xslate";
|
|
188193
|
+
extension = ".tx";
|
|
188194
|
+
templatesPerComponent = true;
|
|
188195
|
+
importMapInjection = "html-snippet";
|
|
188196
|
+
templatePrimitives = XSLATE_PRIMITIVE_EMIT_MAP;
|
|
188197
|
+
componentName = "";
|
|
188198
|
+
rootScopeNodes = new Set;
|
|
188199
|
+
options;
|
|
188200
|
+
errors = [];
|
|
188201
|
+
inLoop = false;
|
|
188202
|
+
propsObjectName = null;
|
|
188203
|
+
propsParams = [];
|
|
188204
|
+
booleanTypedProps = new Set;
|
|
188205
|
+
stringValueNames = new Set;
|
|
188206
|
+
moduleStringConsts = new Map;
|
|
188207
|
+
_searchParamsLocals = new Set;
|
|
188208
|
+
_loweringMatchers = [];
|
|
188209
|
+
localConstants = [];
|
|
188210
|
+
nullableOptionalProps = new Set;
|
|
188211
|
+
constructor(options = {}) {
|
|
188212
|
+
super();
|
|
188213
|
+
this.options = {
|
|
188214
|
+
clientJsBasePath: options.clientJsBasePath ?? "/static/components/",
|
|
188215
|
+
barefootJsPath: options.barefootJsPath ?? "/static/components/barefoot.js"
|
|
188216
|
+
};
|
|
188619
188217
|
}
|
|
188620
|
-
|
|
188621
|
-
|
|
188622
|
-
|
|
188218
|
+
generate(ir, options) {
|
|
188219
|
+
this.componentName = ir.metadata.componentName;
|
|
188220
|
+
this.propsObjectName = ir.metadata.propsObjectName ?? null;
|
|
188221
|
+
augmentInheritedPropAccesses(ir);
|
|
188222
|
+
this.propsParams = ir.metadata.propsParams.map((p) => ({ name: p.name }));
|
|
188223
|
+
this.booleanTypedProps = collectBooleanTypedProps(ir);
|
|
188224
|
+
this.localConstants = ir.metadata.localConstants ?? [];
|
|
188225
|
+
this.nullableOptionalProps = collectNullableOptionalProps(ir);
|
|
188226
|
+
this.stringValueNames = collectStringValueNames(ir);
|
|
188227
|
+
this.moduleStringConsts = collectModuleStringConsts(ir.metadata.localConstants);
|
|
188228
|
+
this._searchParamsLocals = searchParamsLocalNames(ir.metadata);
|
|
188229
|
+
this._loweringMatchers = prepareLoweringMatchers(ir.metadata);
|
|
188230
|
+
this.errors = [];
|
|
188231
|
+
this.childrenCaptureCounter = 0;
|
|
188232
|
+
if (!options?.siblingTemplatesRegistered) {
|
|
188233
|
+
this.errors.push(...collectImportedLoopChildComponentErrors(ir, this.componentName));
|
|
188623
188234
|
}
|
|
188624
|
-
|
|
188235
|
+
this.rootScopeNodes = collectRootScopeNodes(ir.root);
|
|
188236
|
+
const templateBody = ir.root.type === "if-statement" ? this.renderIfStatement(ir.root) : this.renderNode(ir.root);
|
|
188237
|
+
const scriptReg = options?.skipScriptRegistration ? "" : this.generateScriptRegistrations(ir, options?.scriptBaseName);
|
|
188238
|
+
const ctxSeed = generateContextConsumerSeed(ir);
|
|
188239
|
+
const memoSeed = generateDerivedMemoSeed(this.memoCtx, ir);
|
|
188240
|
+
const template = `${scriptReg}${ctxSeed}${memoSeed}${templateBody}
|
|
188241
|
+
`;
|
|
188242
|
+
if (this.errors.length > 0) {
|
|
188243
|
+
ir.errors.push(...this.errors);
|
|
188244
|
+
}
|
|
188245
|
+
const sections = {
|
|
188246
|
+
imports: "",
|
|
188247
|
+
types: "",
|
|
188248
|
+
component: template,
|
|
188249
|
+
defaultExport: ""
|
|
188250
|
+
};
|
|
188251
|
+
return {
|
|
188252
|
+
template,
|
|
188253
|
+
sections,
|
|
188254
|
+
extension: this.extension
|
|
188255
|
+
};
|
|
188625
188256
|
}
|
|
188626
|
-
|
|
188627
|
-
|
|
188257
|
+
generateScriptRegistrations(ir, scriptBaseName) {
|
|
188258
|
+
const hasInteractivity = hasClientInteractivity(ir);
|
|
188259
|
+
if (!hasInteractivity)
|
|
188260
|
+
return "";
|
|
188261
|
+
const name = scriptBaseName ?? ir.metadata.componentName;
|
|
188262
|
+
const runtimePath = this.options.barefootJsPath;
|
|
188263
|
+
const clientJsPath = `${this.options.clientJsBasePath}${name}.client.js`;
|
|
188264
|
+
const lines = [];
|
|
188265
|
+
lines.push(`: my $_bf_reg0 = $bf.register_script('${runtimePath}');`);
|
|
188266
|
+
lines.push(`: my $_bf_reg1 = $bf.register_script('${clientJsPath}');`);
|
|
188267
|
+
lines.push("");
|
|
188268
|
+
return lines.join(`
|
|
188269
|
+
`);
|
|
188628
188270
|
}
|
|
188629
|
-
|
|
188630
|
-
|
|
188631
|
-
return `$${callee.name}`;
|
|
188632
|
-
}
|
|
188633
|
-
return emit(callee);
|
|
188271
|
+
renderNode(node) {
|
|
188272
|
+
return emitIRNode(node, this, {});
|
|
188634
188273
|
}
|
|
188635
|
-
|
|
188636
|
-
|
|
188637
|
-
if (op === "!") {
|
|
188638
|
-
const needsParens = argument.kind === "binary" || argument.kind === "logical";
|
|
188639
|
-
return needsParens ? `!(${arg})` : `!${arg}`;
|
|
188640
|
-
}
|
|
188641
|
-
if (op === "-")
|
|
188642
|
-
return `-${arg}`;
|
|
188643
|
-
return arg;
|
|
188274
|
+
emitElement(node, _ctx, _emit) {
|
|
188275
|
+
return this.renderElement(node);
|
|
188644
188276
|
}
|
|
188645
|
-
|
|
188646
|
-
|
|
188647
|
-
const r = emit(right);
|
|
188648
|
-
const opMap = {
|
|
188649
|
-
"===": "==",
|
|
188650
|
-
"!==": "!=",
|
|
188651
|
-
">": ">",
|
|
188652
|
-
"<": "<",
|
|
188653
|
-
">=": ">=",
|
|
188654
|
-
"<=": "<=",
|
|
188655
|
-
"+": "+",
|
|
188656
|
-
"-": "-",
|
|
188657
|
-
"*": "*",
|
|
188658
|
-
"/": "/"
|
|
188659
|
-
};
|
|
188660
|
-
return `${l} ${opMap[op] ?? op} ${r}`;
|
|
188277
|
+
emitText(node) {
|
|
188278
|
+
return node.value;
|
|
188661
188279
|
}
|
|
188662
|
-
|
|
188663
|
-
|
|
188664
|
-
const r = emit(right);
|
|
188665
|
-
if (op === "&&")
|
|
188666
|
-
return `(${l} && ${r})`;
|
|
188667
|
-
if (op === "||")
|
|
188668
|
-
return `(${l} || ${r})`;
|
|
188669
|
-
return `(${l} // ${r})`;
|
|
188280
|
+
emitExpression(node) {
|
|
188281
|
+
return this.renderExpression(node);
|
|
188670
188282
|
}
|
|
188671
|
-
|
|
188672
|
-
return
|
|
188283
|
+
emitConditional(node, _ctx, _emit) {
|
|
188284
|
+
return this.renderConditional(node);
|
|
188673
188285
|
}
|
|
188674
|
-
|
|
188675
|
-
return
|
|
188286
|
+
emitLoop(node, _ctx, _emit) {
|
|
188287
|
+
return this.renderLoop(node);
|
|
188676
188288
|
}
|
|
188677
|
-
|
|
188678
|
-
return
|
|
188289
|
+
emitComponent(node, _ctx, _emit) {
|
|
188290
|
+
return this.renderComponent(node);
|
|
188679
188291
|
}
|
|
188680
|
-
|
|
188681
|
-
return
|
|
188292
|
+
emitFragment(node, _ctx, _emit) {
|
|
188293
|
+
return this.renderFragment(node);
|
|
188682
188294
|
}
|
|
188683
|
-
|
|
188684
|
-
return
|
|
188295
|
+
emitSlot(node) {
|
|
188296
|
+
return this.renderSlot(node);
|
|
188685
188297
|
}
|
|
188686
|
-
|
|
188687
|
-
return
|
|
188298
|
+
emitIfStatement(node, _ctx, _emit) {
|
|
188299
|
+
return this.renderIfStatement(node);
|
|
188688
188300
|
}
|
|
188689
|
-
|
|
188690
|
-
|
|
188301
|
+
emitProvider(node, _ctx, _emit) {
|
|
188302
|
+
const value = this.providerValueKolon(node.valueProp);
|
|
188303
|
+
const children = this.renderChildren(node.children);
|
|
188304
|
+
const name = node.contextName;
|
|
188305
|
+
return `<: $bf.provide_context('${name}', ${value}) :>` + children + `<: $bf.revoke_context('${name}') :>`;
|
|
188691
188306
|
}
|
|
188692
|
-
|
|
188693
|
-
|
|
188307
|
+
providerValueKolon(valueProp) {
|
|
188308
|
+
const v = valueProp.value;
|
|
188309
|
+
if (v.kind === "literal") {
|
|
188310
|
+
return typeof v.value === "string" ? `'${v.value.replace(/[\\']/g, (m) => `\\${m}`)}'` : String(v.value);
|
|
188311
|
+
}
|
|
188312
|
+
if (v.kind === "expression") {
|
|
188313
|
+
const hashref = this.providerObjectLiteralKolon(v.expr);
|
|
188314
|
+
if (hashref !== null)
|
|
188315
|
+
return hashref;
|
|
188316
|
+
return this.convertExpressionToKolon(v.expr);
|
|
188317
|
+
}
|
|
188318
|
+
if (v.kind === "template")
|
|
188319
|
+
return this.convertTemplateLiteralPartsToKolon(v.parts);
|
|
188320
|
+
return "nil";
|
|
188694
188321
|
}
|
|
188695
|
-
|
|
188696
|
-
|
|
188322
|
+
providerObjectLiteralKolon(expr) {
|
|
188323
|
+
const members = parseProviderObjectLiteral(expr.trim());
|
|
188324
|
+
if (members === null)
|
|
188325
|
+
return null;
|
|
188326
|
+
const entries = members.map((m) => {
|
|
188327
|
+
const key = `'${m.name.replace(/[\\']/g, (c) => `\\${c}`)}'`;
|
|
188328
|
+
if (m.kind === "function" || /^on[A-Z]/.test(m.name))
|
|
188329
|
+
return `${key} => nil`;
|
|
188330
|
+
const src = m.kind === "getter" ? m.body : m.expr;
|
|
188331
|
+
return `${key} => ${this.convertExpressionToKolon(src)}`;
|
|
188332
|
+
});
|
|
188333
|
+
return `{ ${entries.join(", ")} }`;
|
|
188697
188334
|
}
|
|
188698
|
-
|
|
188699
|
-
return
|
|
188335
|
+
emitAsync(node, _ctx, _emit) {
|
|
188336
|
+
return this.renderAsync(node);
|
|
188700
188337
|
}
|
|
188701
|
-
|
|
188702
|
-
|
|
188338
|
+
renderElement(element) {
|
|
188339
|
+
const tag = element.tag;
|
|
188340
|
+
const attrs = this.renderAttributes(element);
|
|
188341
|
+
const children = this.renderChildren(element.children);
|
|
188342
|
+
let hydrationAttrs = "";
|
|
188343
|
+
if (element.needsScope) {
|
|
188344
|
+
hydrationAttrs += ` ${this.renderScopeMarker("")}`;
|
|
188345
|
+
}
|
|
188346
|
+
if (this.rootScopeNodes.has(element) && element.needsScope) {
|
|
188347
|
+
hydrationAttrs += ` <: $bf.data_key_attr() | mark_raw :>`;
|
|
188348
|
+
}
|
|
188349
|
+
if (element.slotId) {
|
|
188350
|
+
hydrationAttrs += ` ${this.renderSlotMarker(element.slotId)}`;
|
|
188351
|
+
}
|
|
188352
|
+
if (element.regionId) {
|
|
188353
|
+
hydrationAttrs += ` ${BF_REGION}="${element.regionId}"`;
|
|
188354
|
+
}
|
|
188355
|
+
const voidElements = [
|
|
188356
|
+
"area",
|
|
188357
|
+
"base",
|
|
188358
|
+
"br",
|
|
188359
|
+
"col",
|
|
188360
|
+
"embed",
|
|
188361
|
+
"hr",
|
|
188362
|
+
"img",
|
|
188363
|
+
"input",
|
|
188364
|
+
"link",
|
|
188365
|
+
"meta",
|
|
188366
|
+
"param",
|
|
188367
|
+
"source",
|
|
188368
|
+
"track",
|
|
188369
|
+
"wbr"
|
|
188370
|
+
];
|
|
188371
|
+
if (voidElements.includes(tag.toLowerCase())) {
|
|
188372
|
+
return `<${tag}${attrs}${hydrationAttrs}>`;
|
|
188373
|
+
}
|
|
188374
|
+
return `<${tag}${attrs}${hydrationAttrs}>${children}</${tag}>`;
|
|
188703
188375
|
}
|
|
188704
|
-
|
|
188705
|
-
|
|
188706
|
-
|
|
188707
|
-
|
|
188708
|
-
|
|
188709
|
-
|
|
188376
|
+
renderExpression(expr) {
|
|
188377
|
+
if (expr.clientOnly) {
|
|
188378
|
+
if (expr.slotId) {
|
|
188379
|
+
return `<: $bf.comment("client:${expr.slotId}") | mark_raw :>`;
|
|
188380
|
+
}
|
|
188381
|
+
return "";
|
|
188382
|
+
}
|
|
188383
|
+
const perlExpr = this.convertExpressionToKolon(expr.expr);
|
|
188384
|
+
if (expr.slotId) {
|
|
188385
|
+
return `<: $bf.text_start("${expr.slotId}") | mark_raw :><: ${perlExpr} :><: $bf.text_end() | mark_raw :>`;
|
|
188386
|
+
}
|
|
188387
|
+
return `<: ${perlExpr} :>`;
|
|
188710
188388
|
}
|
|
188711
|
-
|
|
188712
|
-
if (
|
|
188713
|
-
return "
|
|
188714
|
-
|
|
188715
|
-
|
|
188716
|
-
|
|
188717
|
-
const
|
|
188718
|
-
|
|
188719
|
-
|
|
188720
|
-
|
|
188389
|
+
renderConditional(cond) {
|
|
188390
|
+
if (cond.clientOnly && cond.slotId) {
|
|
188391
|
+
return `<: $bf.comment("cond-start:${cond.slotId}") | mark_raw :><: $bf.comment("cond-end:${cond.slotId}") | mark_raw :>`;
|
|
188392
|
+
}
|
|
188393
|
+
const condition = this.convertExpressionToKolon(cond.condition);
|
|
188394
|
+
const whenTrue = this.renderNode(cond.whenTrue);
|
|
188395
|
+
const whenFalse = this.renderNodeOrNull(cond.whenFalse);
|
|
188396
|
+
const isFragmentBranch = cond.whenTrue.type === "fragment" || cond.whenFalse.type === "fragment";
|
|
188397
|
+
const useCommentMarkers = cond.slotId && isFragmentBranch;
|
|
188398
|
+
let markedTrue = whenTrue;
|
|
188399
|
+
let markedFalse = whenFalse;
|
|
188400
|
+
if (cond.slotId && !useCommentMarkers) {
|
|
188401
|
+
markedTrue = this.addCondMarkerToFirstElement(whenTrue, cond.slotId);
|
|
188402
|
+
markedFalse = whenFalse ? this.addCondMarkerToFirstElement(whenFalse, cond.slotId) : whenFalse;
|
|
188403
|
+
}
|
|
188404
|
+
let result;
|
|
188405
|
+
if (useCommentMarkers) {
|
|
188406
|
+
const inner = whenFalse ? `
|
|
188407
|
+
: if (${condition}) {
|
|
188408
|
+
${whenTrue}
|
|
188409
|
+
: } else {
|
|
188410
|
+
${whenFalse}
|
|
188411
|
+
: }
|
|
188412
|
+
` : `
|
|
188413
|
+
: if (${condition}) {
|
|
188414
|
+
${whenTrue}
|
|
188415
|
+
: }
|
|
188416
|
+
`;
|
|
188417
|
+
result = `<: $bf.comment("cond-start:${cond.slotId}") | mark_raw :>${inner}<: $bf.comment("cond-end:${cond.slotId}") | mark_raw :>`;
|
|
188418
|
+
} else if (markedFalse) {
|
|
188419
|
+
result = `
|
|
188420
|
+
: if (${condition}) {
|
|
188421
|
+
${markedTrue}
|
|
188422
|
+
: } else {
|
|
188423
|
+
${markedFalse}
|
|
188424
|
+
: }
|
|
188425
|
+
`;
|
|
188426
|
+
} else if (cond.slotId) {
|
|
188427
|
+
result = `<: $bf.comment("cond-start:${cond.slotId}") | mark_raw :>
|
|
188428
|
+
: if (${condition}) {
|
|
188429
|
+
${whenTrue}
|
|
188430
|
+
: }
|
|
188431
|
+
<: $bf.comment("cond-end:${cond.slotId}") | mark_raw :>`;
|
|
188432
|
+
} else {
|
|
188433
|
+
result = `
|
|
188434
|
+
: if (${condition}) {
|
|
188435
|
+
${whenTrue}
|
|
188436
|
+
: }
|
|
188437
|
+
`;
|
|
188438
|
+
}
|
|
188439
|
+
return result;
|
|
188440
|
+
}
|
|
188441
|
+
renderNodeOrNull(node) {
|
|
188442
|
+
if (node.type === "expression" && (node.expr === "null" || node.expr === "undefined")) {
|
|
188443
|
+
return null;
|
|
188444
|
+
}
|
|
188445
|
+
return this.renderNode(node);
|
|
188446
|
+
}
|
|
188447
|
+
addCondMarkerToFirstElement(content, condId) {
|
|
188448
|
+
const match = content.match(/^(<\w+)([\s>])/);
|
|
188449
|
+
if (match) {
|
|
188450
|
+
return content.replace(/^(<\w+)([\s>])/, `$1 ${BF_COND}="${condId}"$2`);
|
|
188451
|
+
}
|
|
188452
|
+
return `<: $bf.comment("cond-start:${condId}") | mark_raw :>${content}<: $bf.comment("cond-end:${condId}") | mark_raw :>`;
|
|
188453
|
+
}
|
|
188454
|
+
renderLoop(loop) {
|
|
188455
|
+
if (loop.clientOnly)
|
|
188456
|
+
return "";
|
|
188457
|
+
const destructure = !!(loop.paramBindings && loop.paramBindings.length > 0);
|
|
188458
|
+
const supportableDestructure = destructure && isLowerableObjectRestDestructure(loop);
|
|
188459
|
+
if (destructure && !supportableDestructure) {
|
|
188460
|
+
this.errors.push({
|
|
188461
|
+
code: "BF104",
|
|
188462
|
+
severity: "error",
|
|
188463
|
+
message: `Loop callback uses an array/object destructure pattern (\`${loop.param}\`) that the Xslate adapter cannot lower — Kolon \`for LIST -> $item\` binds a single scalar and can't unpack a tuple.`,
|
|
188464
|
+
loc: loop.loc ?? { file: this.componentName + ".tsx", start: { line: 1, column: 0 }, end: { line: 1, column: 0 } },
|
|
188465
|
+
suggestion: {
|
|
188466
|
+
message: `Options:
|
|
188467
|
+
` + ` 1. Rename the parameter to a single name and access tuple elements with index syntax in the body (e.g. \`entry => entry[0]\` instead of \`([k, v]) => ...\`).
|
|
188468
|
+
` + ` 2. Mark the loop position as @client-only so the destructure runs in JS on the client.
|
|
188469
|
+
` + ` 3. Move the loop into a primitive that the adapter registers explicitly.`
|
|
188470
|
+
}
|
|
188471
|
+
});
|
|
188472
|
+
}
|
|
188473
|
+
const rawArray = this.convertExpressionToKolon(loop.array);
|
|
188474
|
+
let array = rawArray;
|
|
188475
|
+
if (loop.sortComparator) {
|
|
188476
|
+
const sort = loop.sortComparator;
|
|
188477
|
+
const sortEmit = (e) => this.convertExpressionToKolon("", e);
|
|
188478
|
+
const arrow = sort.arrow;
|
|
188479
|
+
const params = arrow.kind === "arrow" ? arrow.params : [sort.paramA, sort.paramB];
|
|
188480
|
+
const structured = sortComparatorFromArrow2(arrow);
|
|
188481
|
+
array = renderSortEval(rawArray, arrow.kind === "arrow" ? arrow.body : arrow, params, sortEmit) ?? (structured !== null ? renderSortMethod(rawArray, structured) : rawArray);
|
|
188482
|
+
}
|
|
188483
|
+
const param = loop.param;
|
|
188484
|
+
const renderedChildren = this.renderChildren(loop.children);
|
|
188485
|
+
const loopVar = loop.iterationShape === "keys" ? "__bf_item" : supportableDestructure ? "__bf_item" : param;
|
|
188486
|
+
const indexLocalLines = [];
|
|
188487
|
+
if (loop.iterationShape === "keys") {
|
|
188488
|
+
indexLocalLines.push(`: my $${param} = $~${loopVar}.index;`);
|
|
188489
|
+
} else if (loop.index) {
|
|
188490
|
+
indexLocalLines.push(`: my $${loop.index} = $~${loopVar}.index;`);
|
|
188491
|
+
}
|
|
188492
|
+
if (supportableDestructure) {
|
|
188493
|
+
for (const b of loop.paramBindings ?? []) {
|
|
188494
|
+
indexLocalLines.push(b.rest ? `: my $${b.name} = $${loopVar};` : `: my $${b.name} = $${loopVar}${b.path};`);
|
|
188495
|
+
}
|
|
188496
|
+
}
|
|
188497
|
+
const prevInLoop = this.inLoop;
|
|
188498
|
+
this.inLoop = true;
|
|
188499
|
+
const childrenUnderLoop = this.renderChildren(loop.children);
|
|
188500
|
+
this.inLoop = prevInLoop;
|
|
188501
|
+
const bodyChildren = loop.bodyIsItemConditional && loop.key ? `<: $bf.comment("loop-i:" ~ ${this.convertExpressionToKolon(loop.key)}) | mark_raw :>
|
|
188502
|
+
${childrenUnderLoop}` : childrenUnderLoop;
|
|
188503
|
+
const lines = [];
|
|
188504
|
+
lines.push(`<: $bf.comment("loop:${loop.markerId}") | mark_raw :>`);
|
|
188505
|
+
lines.push(`: for ${array} -> $${loopVar} {`);
|
|
188506
|
+
for (const il of indexLocalLines)
|
|
188507
|
+
lines.push(il);
|
|
188508
|
+
if (loop.filterPredicate) {
|
|
188509
|
+
let filterCond;
|
|
188510
|
+
if (loop.filterPredicate.predicate) {
|
|
188511
|
+
filterCond = this.renderKolonFilterExpr(loop.filterPredicate.predicate, loop.filterPredicate.param);
|
|
188512
|
+
} else {
|
|
188513
|
+
filterCond = "1";
|
|
188514
|
+
}
|
|
188515
|
+
if (loop.filterPredicate.param !== param) {
|
|
188516
|
+
filterCond = filterCond.replace(new RegExp(`\\$${loop.filterPredicate.param}\\b`, "g"), `$${param}`);
|
|
188517
|
+
}
|
|
188518
|
+
lines.push(`: if (${filterCond}) {`);
|
|
188519
|
+
lines.push(bodyChildren);
|
|
188520
|
+
lines.push(`: }`);
|
|
188521
|
+
} else {
|
|
188522
|
+
lines.push(bodyChildren);
|
|
188523
|
+
}
|
|
188524
|
+
lines.push(`: }`);
|
|
188525
|
+
lines.push(`<: $bf.comment("/loop:${loop.markerId}") | mark_raw :>`);
|
|
188526
|
+
return lines.join(`
|
|
188527
|
+
`);
|
|
188528
|
+
}
|
|
188529
|
+
componentPropEmitter = {
|
|
188530
|
+
emitLiteral: (value, name) => `${kolonHashKey(name)} => '${value.value}'`,
|
|
188531
|
+
emitExpression: (value, name) => {
|
|
188532
|
+
if (value.parts) {
|
|
188533
|
+
return `${kolonHashKey(name)} => ${this.convertTemplateLiteralPartsToKolon(value.parts)}`;
|
|
188534
|
+
}
|
|
188535
|
+
if (value.parsed) {
|
|
188536
|
+
const hashref = objectLiteralExprToKolonHashref(this.spreadCtx, value.parsed);
|
|
188537
|
+
if (hashref !== null)
|
|
188538
|
+
return `${kolonHashKey(name)} => ${hashref}`;
|
|
188539
|
+
}
|
|
188540
|
+
return `${kolonHashKey(name)} => ${this.convertExpressionToKolon(value.expr)}`;
|
|
188541
|
+
},
|
|
188542
|
+
emitSpread: (value) => {
|
|
188543
|
+
return this.convertExpressionToKolon(value.expr);
|
|
188544
|
+
},
|
|
188545
|
+
emitTemplate: (value, name) => `${kolonHashKey(name)} => ${this.convertTemplateLiteralPartsToKolon(value.parts)}`,
|
|
188546
|
+
emitBooleanAttr: (_value, name) => `${kolonHashKey(name)} => 1`,
|
|
188547
|
+
emitBooleanShorthand: (_value, name) => `${kolonHashKey(name)} => 1`,
|
|
188548
|
+
emitJsxChildren: () => ""
|
|
188549
|
+
};
|
|
188550
|
+
renderComponent(comp) {
|
|
188551
|
+
const propParts = [];
|
|
188552
|
+
for (const p of comp.props) {
|
|
188553
|
+
if ((p.name.match(/^on[A-Z]/) || p.name === "ref") && p.value.kind === "expression")
|
|
188554
|
+
continue;
|
|
188555
|
+
if (p.value.kind === "spread") {
|
|
188556
|
+
const trimmed = p.value.expr.trim();
|
|
188557
|
+
if (this.propsObjectName && this.propsObjectName === trimmed) {
|
|
188558
|
+
for (const pp of this.propsParams) {
|
|
188559
|
+
propParts.push(`${pp.name} => $${pp.name}`);
|
|
188560
|
+
}
|
|
188561
|
+
continue;
|
|
188562
|
+
}
|
|
188563
|
+
this.errors.push({
|
|
188564
|
+
code: "BF101",
|
|
188565
|
+
severity: "error",
|
|
188566
|
+
message: `Spread props (\`{...${trimmed}}\`) on a child component cannot be lowered to Kolon — Kolon hashref method args can't splat a runtime hash into named entries.`,
|
|
188567
|
+
loc: comp.loc ?? { file: this.componentName + ".tsx", start: { line: 1, column: 0 }, end: { line: 1, column: 0 } },
|
|
188568
|
+
suggestion: {
|
|
188569
|
+
message: "Pass the child component its props explicitly rather than spreading a runtime object."
|
|
188570
|
+
}
|
|
188571
|
+
});
|
|
188572
|
+
continue;
|
|
188573
|
+
}
|
|
188574
|
+
const lowered = emitAttrValue(p.value, this.componentPropEmitter, p.name);
|
|
188575
|
+
if (lowered)
|
|
188576
|
+
propParts.push(lowered);
|
|
188577
|
+
}
|
|
188578
|
+
if (comp.slotId && !this.inLoop) {
|
|
188579
|
+
propParts.push(`_bf_slot => '${comp.slotId}'`);
|
|
188580
|
+
}
|
|
188581
|
+
const tplName = this.toTemplateName(comp.name);
|
|
188582
|
+
const effectiveChildren = comp.children.length > 0 ? comp.children : resolveJsxChildrenProp(comp.props);
|
|
188583
|
+
if (effectiveChildren.length > 0) {
|
|
188584
|
+
const prevInLoop = this.inLoop;
|
|
188585
|
+
this.inLoop = false;
|
|
188586
|
+
const childrenBody = this.renderChildren(effectiveChildren);
|
|
188587
|
+
this.inLoop = prevInLoop;
|
|
188588
|
+
const macroName = `bf_children_${comp.slotId ?? "c" + this.childrenCaptureCounter++}`;
|
|
188589
|
+
const childrenEntry = `children => ${macroName}()`;
|
|
188590
|
+
const allParts = [...propParts, childrenEntry];
|
|
188591
|
+
return `<: macro ${macroName} -> () { :>${childrenBody}<: } :><: $bf.render_child('${tplName}', { ${allParts.join(", ")} }) | mark_raw :>`;
|
|
188592
|
+
}
|
|
188593
|
+
const hashEntries = propParts.length > 0 ? `, { ${propParts.join(", ")} }` : "";
|
|
188594
|
+
return `<: $bf.render_child('${tplName}'${hashEntries}) | mark_raw :>`;
|
|
188595
|
+
}
|
|
188596
|
+
childrenCaptureCounter = 0;
|
|
188597
|
+
presenceVarCounter = 0;
|
|
188598
|
+
toTemplateName(componentName) {
|
|
188599
|
+
return componentName.replace(/([A-Z])/g, "_$1").toLowerCase().replace(/^_/, "");
|
|
188600
|
+
}
|
|
188601
|
+
renderIfStatement(ifStmt) {
|
|
188602
|
+
const condition = this.convertExpressionToKolon(ifStmt.condition);
|
|
188603
|
+
const consequent = ifStmt.consequent.type === "if-statement" ? this.renderIfStatement(ifStmt.consequent) : this.renderNode(ifStmt.consequent);
|
|
188604
|
+
let result = `: if (${condition}) {
|
|
188605
|
+
${consequent}
|
|
188606
|
+
`;
|
|
188607
|
+
if (ifStmt.alternate) {
|
|
188608
|
+
if (ifStmt.alternate.type === "if-statement") {
|
|
188609
|
+
const altResult = this.renderIfStatement(ifStmt.alternate);
|
|
188610
|
+
result += altResult.replace(/^: if/, ": } elsif");
|
|
188611
|
+
} else {
|
|
188612
|
+
const alternate = this.renderNode(ifStmt.alternate);
|
|
188613
|
+
result += `: } else {
|
|
188614
|
+
${alternate}
|
|
188615
|
+
`;
|
|
188616
|
+
}
|
|
188617
|
+
}
|
|
188618
|
+
result += `: }`;
|
|
188619
|
+
return result;
|
|
188620
|
+
}
|
|
188621
|
+
renderFragment(fragment) {
|
|
188622
|
+
const children = this.renderChildren(fragment.children);
|
|
188623
|
+
if (fragment.needsScopeComment) {
|
|
188624
|
+
return `<: $bf.scope_comment() | mark_raw :>${children}`;
|
|
188625
|
+
}
|
|
188626
|
+
return children;
|
|
188627
|
+
}
|
|
188628
|
+
renderSlot(_slot) {
|
|
188629
|
+
return `<: $children | mark_raw :>`;
|
|
188630
|
+
}
|
|
188631
|
+
renderAsync(node) {
|
|
188632
|
+
const fallback = this.renderNode(node.fallback);
|
|
188633
|
+
const children = this.renderChildren(node.children);
|
|
188634
|
+
const macroName = `bf_async_fallback_${node.id}`;
|
|
188635
|
+
return `<: macro ${macroName} -> () { :>${fallback}<: } :><: $bf.async_boundary('${node.id}', ${macroName}()) | mark_raw :>
|
|
188636
|
+
${children}`;
|
|
188637
|
+
}
|
|
188638
|
+
elementAttrEmitter = {
|
|
188639
|
+
emitLiteral: (value, name) => `${name}="${value.value}"`,
|
|
188640
|
+
emitExpression: (value, name) => {
|
|
188641
|
+
if (name === "style") {
|
|
188642
|
+
const css = this.tryLowerStyleObject(value.expr);
|
|
188643
|
+
if (css !== null)
|
|
188644
|
+
return `style="${css}"`;
|
|
188645
|
+
}
|
|
188646
|
+
if (this.refuseUnsupportedAttrExpression(value.expr, name)) {
|
|
188647
|
+
return "";
|
|
188648
|
+
}
|
|
188649
|
+
const bareId = value.expr.trim();
|
|
188650
|
+
const normalizedBareId = this.propsObjectName && bareId.startsWith(`${this.propsObjectName}.`) ? bareId.slice(this.propsObjectName.length + 1) : bareId;
|
|
188651
|
+
if (!isBooleanAttr(name) && !value.presenceOrUndefined && /^[A-Za-z_$][\w$]*$/.test(normalizedBareId) && this.nullableOptionalProps.has(normalizedBareId)) {
|
|
188652
|
+
const perl2 = this.convertExpressionToKolon(value.expr);
|
|
188653
|
+
const body = isBooleanResultExpr(value.expr) || isAriaBooleanAttr(name) || this.isBooleanTypedPropRef(value.expr) ? `${name}="<: $bf.bool_str(${perl2}) :>"` : `${name}="<: ${perl2} :>"`;
|
|
188654
|
+
return `
|
|
188655
|
+
: if (defined ${perl2}) {
|
|
188656
|
+
${body}
|
|
188657
|
+
: }
|
|
188658
|
+
`;
|
|
188659
|
+
}
|
|
188660
|
+
if (isBooleanAttr(name)) {
|
|
188661
|
+
return `<: ${this.convertExpressionToKolon(value.expr)} ? '${name}' : '' :>`;
|
|
188662
|
+
}
|
|
188663
|
+
if (value.presenceOrUndefined) {
|
|
188664
|
+
const perl2 = this.convertExpressionToKolon(value.expr);
|
|
188665
|
+
const tmp = `$bf_pu${this.presenceVarCounter++}`;
|
|
188666
|
+
const body = isBooleanResultExpr(value.expr) || isAriaBooleanAttr(name) || this.isBooleanTypedPropRef(value.expr) ? `${name}="<: $bf.bool_str(${tmp}) :>"` : `${name}="<: ${tmp} :>"`;
|
|
188667
|
+
return `
|
|
188668
|
+
: my ${tmp} = ${perl2};
|
|
188669
|
+
: if (${tmp}) {
|
|
188670
|
+
${body}
|
|
188671
|
+
: }
|
|
188672
|
+
`;
|
|
188673
|
+
}
|
|
188674
|
+
{
|
|
188675
|
+
const m = this.parseUndefinedAlternateTernary(value.expr);
|
|
188676
|
+
if (m) {
|
|
188677
|
+
const cond = this.convertExpressionToKolon(m.condition);
|
|
188678
|
+
const val = this.convertExpressionToKolon(m.consequent);
|
|
188679
|
+
return `
|
|
188680
|
+
: if (${cond}) {
|
|
188681
|
+
${name}="<: ${val} :>"
|
|
188682
|
+
: }
|
|
188683
|
+
`;
|
|
188684
|
+
}
|
|
188685
|
+
}
|
|
188686
|
+
const perl = this.convertExpressionToKolon(value.expr);
|
|
188687
|
+
if (isBooleanResultExpr(value.expr) || isAriaBooleanAttr(name) || this.isBooleanTypedPropRef(value.expr)) {
|
|
188688
|
+
return `${name}="<: $bf.bool_str(${perl}) :>"`;
|
|
188689
|
+
}
|
|
188690
|
+
return `${name}="<: ${perl} :>"`;
|
|
188691
|
+
},
|
|
188692
|
+
emitBooleanAttr: (_value, name) => name,
|
|
188693
|
+
emitTemplate: (value, name) => `${name}="<: ${this.convertTemplateLiteralPartsToKolon(value.parts)} :>"`,
|
|
188694
|
+
emitSpread: (value) => {
|
|
188695
|
+
if (this.refuseUnsupportedAttrExpression(value.expr, "...")) {
|
|
188696
|
+
return "";
|
|
188697
|
+
}
|
|
188698
|
+
const trimmed = value.expr.trim();
|
|
188699
|
+
if (this.propsObjectName && this.propsObjectName === trimmed) {
|
|
188700
|
+
const entries = this.propsParams.map((p) => `${JSON.stringify(p.name)} => $${p.name}`);
|
|
188701
|
+
return `<: $bf.spread_attrs({${entries.join(", ")}}) | mark_raw :>`;
|
|
188702
|
+
}
|
|
188703
|
+
const ternaryHashref = conditionalSpreadToKolon(this.spreadCtx, value.parsed);
|
|
188704
|
+
if (ternaryHashref !== null) {
|
|
188705
|
+
return `<: $bf.spread_attrs(${ternaryHashref}) | mark_raw :>`;
|
|
188706
|
+
}
|
|
188707
|
+
if (/^[A-Za-z_$][\w$]*$/.test(trimmed)) {
|
|
188708
|
+
const localConst = (this.localConstants ?? []).find((c) => c.name === trimmed && !c.isModule);
|
|
188709
|
+
if (localConst?.value !== undefined) {
|
|
188710
|
+
const initTrimmed = localConst.value.trim();
|
|
188711
|
+
if (!/^[A-Za-z_$][\w$]*$/.test(initTrimmed)) {
|
|
188712
|
+
const resolved = conditionalSpreadToKolon(this.spreadCtx, parseExpression3(initTrimmed));
|
|
188713
|
+
if (resolved !== null) {
|
|
188714
|
+
return `<: $bf.spread_attrs(${resolved}) | mark_raw :>`;
|
|
188715
|
+
}
|
|
188716
|
+
}
|
|
188717
|
+
}
|
|
188718
|
+
}
|
|
188719
|
+
const perlExpr = this.convertExpressionToKolon(value.expr);
|
|
188720
|
+
return `<: $bf.spread_attrs(${perlExpr}) | mark_raw :>`;
|
|
188721
|
+
},
|
|
188722
|
+
emitBooleanShorthand: () => "",
|
|
188723
|
+
emitJsxChildren: () => ""
|
|
188724
|
+
};
|
|
188725
|
+
tryLowerStyleObject(expr) {
|
|
188726
|
+
const entries = parseStyleObjectEntries(expr);
|
|
188727
|
+
if (!entries)
|
|
188728
|
+
return null;
|
|
188729
|
+
for (const e of entries) {
|
|
188730
|
+
if (e.kind === "expr" && !isSupported2(parseExpression3(e.expr)).supported)
|
|
188731
|
+
return null;
|
|
188732
|
+
}
|
|
188733
|
+
return entries.map((e) => e.kind === "literal" ? `${this.escapeAttrText(e.cssKey)}:${this.escapeAttrText(e.value)}` : `${this.escapeAttrText(e.cssKey)}:<: ${this.convertExpressionToKolon(e.expr)} :>`).join(";");
|
|
188734
|
+
}
|
|
188735
|
+
escapeAttrText(s) {
|
|
188736
|
+
return s.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">");
|
|
188737
|
+
}
|
|
188738
|
+
renderAttributes(element) {
|
|
188739
|
+
const parts = [];
|
|
188740
|
+
for (const attr of element.attrs) {
|
|
188741
|
+
if (attr.clientOnly)
|
|
188742
|
+
continue;
|
|
188743
|
+
let attrName;
|
|
188744
|
+
if (attr.name === "className")
|
|
188745
|
+
attrName = "class";
|
|
188746
|
+
else if (attr.name === "key")
|
|
188747
|
+
attrName = "data-key";
|
|
188748
|
+
else
|
|
188749
|
+
attrName = attr.name;
|
|
188750
|
+
const lowered = emitAttrValue(attr.value, this.elementAttrEmitter, attrName);
|
|
188751
|
+
if (lowered)
|
|
188752
|
+
parts.push(lowered);
|
|
188753
|
+
}
|
|
188754
|
+
return parts.length > 0 ? " " + parts.join(" ") : "";
|
|
188721
188755
|
}
|
|
188722
|
-
|
|
188723
|
-
|
|
188724
|
-
return `'${value}'`;
|
|
188725
|
-
if (literalType === "boolean")
|
|
188726
|
-
return value ? "1" : "0";
|
|
188727
|
-
if (literalType === "null")
|
|
188728
|
-
return "nil";
|
|
188729
|
-
return String(value);
|
|
188756
|
+
renderScopeMarker(_instanceIdExpr) {
|
|
188757
|
+
return `bf-s="<: $bf.scope_attr() :>" <: $bf.hydration_attrs() | mark_raw :> <: $bf.props_attr() | mark_raw :>`;
|
|
188730
188758
|
}
|
|
188731
|
-
|
|
188732
|
-
|
|
188733
|
-
return `$${property}`;
|
|
188734
|
-
}
|
|
188735
|
-
if (object.kind === "identifier") {
|
|
188736
|
-
const staticValue = this.adapter._resolveStaticRecordLiteral(object.name, property);
|
|
188737
|
-
if (staticValue !== null)
|
|
188738
|
-
return staticValue;
|
|
188739
|
-
}
|
|
188740
|
-
const obj = emit(object);
|
|
188741
|
-
if (property === "length")
|
|
188742
|
-
return `$bf.length(${obj})`;
|
|
188743
|
-
return `${obj}.${property}`;
|
|
188759
|
+
renderSlotMarker(slotId) {
|
|
188760
|
+
return `${BF_SLOT}="${slotId}"`;
|
|
188744
188761
|
}
|
|
188745
|
-
|
|
188746
|
-
return `${
|
|
188762
|
+
renderCondMarker(condId) {
|
|
188763
|
+
return `${BF_COND}="${condId}"`;
|
|
188747
188764
|
}
|
|
188748
|
-
|
|
188749
|
-
|
|
188750
|
-
|
|
188751
|
-
|
|
188752
|
-
|
|
188753
|
-
|
|
188754
|
-
if (
|
|
188755
|
-
|
|
188765
|
+
renderKolonFilterExpr(expr, param, localVarMap = new Map) {
|
|
188766
|
+
return emitParsedExpr(expr, new XslateFilterEmitter(param, localVarMap, (n) => this._isStringValueName(n)));
|
|
188767
|
+
}
|
|
188768
|
+
convertTemplateLiteralPartsToKolon(literalParts) {
|
|
188769
|
+
const parts = [];
|
|
188770
|
+
for (const part of literalParts) {
|
|
188771
|
+
if (part.type === "string") {
|
|
188772
|
+
parts.push(this.substituteJsInterpolationsToKolon(part.value));
|
|
188773
|
+
} else if (part.type === "ternary") {
|
|
188774
|
+
const cond = this.convertExpressionToKolon(part.condition);
|
|
188775
|
+
parts.push(`(${cond} ? '${part.whenTrue}' : '${part.whenFalse}')`);
|
|
188776
|
+
} else if (part.type === "lookup") {
|
|
188777
|
+
const keyExpr = this.convertExpressionToKolon(part.key);
|
|
188778
|
+
const entries = Object.entries(part.cases).map(([k, v]) => `'${k}' => '${v}'`).join(", ");
|
|
188779
|
+
parts.push(`({ ${entries} }[${keyExpr}] // '')`);
|
|
188756
188780
|
}
|
|
188757
188781
|
}
|
|
188758
|
-
|
|
188759
|
-
|
|
188760
|
-
|
|
188761
|
-
|
|
188762
|
-
|
|
188782
|
+
return parts.length === 1 ? parts[0] : parts.join(" ~ ");
|
|
188783
|
+
}
|
|
188784
|
+
substituteJsInterpolationsToKolon(s) {
|
|
188785
|
+
const segments = [];
|
|
188786
|
+
const re = /\$\{([^}]+)\}/g;
|
|
188787
|
+
let lastIndex = 0;
|
|
188788
|
+
let m;
|
|
188789
|
+
while ((m = re.exec(s)) !== null) {
|
|
188790
|
+
if (m.index > lastIndex) {
|
|
188791
|
+
segments.push(`'${s.slice(lastIndex, m.index)}'`);
|
|
188763
188792
|
}
|
|
188764
|
-
|
|
188765
|
-
|
|
188793
|
+
segments.push(this.convertExpressionToKolon(m[1].trim()));
|
|
188794
|
+
lastIndex = re.lastIndex;
|
|
188766
188795
|
}
|
|
188767
|
-
|
|
188796
|
+
if (lastIndex < s.length) {
|
|
188797
|
+
segments.push(`'${s.slice(lastIndex)}'`);
|
|
188798
|
+
}
|
|
188799
|
+
if (segments.length === 0)
|
|
188800
|
+
return `''`;
|
|
188801
|
+
return segments.length === 1 ? segments[0] : `(${segments.join(" ~ ")})`;
|
|
188768
188802
|
}
|
|
188769
|
-
|
|
188770
|
-
|
|
188771
|
-
|
|
188772
|
-
|
|
188773
|
-
|
|
188774
|
-
|
|
188775
|
-
|
|
188803
|
+
refuseUnsupportedAttrExpression(expr, attrName) {
|
|
188804
|
+
let probe = expr.trim();
|
|
188805
|
+
while (probe.startsWith("("))
|
|
188806
|
+
probe = probe.slice(1).trimStart();
|
|
188807
|
+
const startsAsObjectLiteral = probe.startsWith("{");
|
|
188808
|
+
const hasTaggedTemplate = /[A-Za-z_$][\w$]*\s*`/.test(probe);
|
|
188809
|
+
if (!startsAsObjectLiteral && !hasTaggedTemplate)
|
|
188810
|
+
return false;
|
|
188811
|
+
const parsed = parseExpression3(expr.trim());
|
|
188812
|
+
const support = isSupported2(parsed);
|
|
188813
|
+
if (parsed.kind !== "unsupported" && support.supported)
|
|
188814
|
+
return false;
|
|
188815
|
+
const reason = support.reason ?? (parsed.kind === "unsupported" ? parsed.reason : undefined);
|
|
188816
|
+
const reasonLine = reason ? `
|
|
188817
|
+
${reason}` : "";
|
|
188818
|
+
this.errors.push({
|
|
188819
|
+
code: "BF101",
|
|
188820
|
+
severity: "error",
|
|
188821
|
+
message: `Expression not supported on attribute '${attrName}': ${expr.trim()}${reasonLine}`,
|
|
188822
|
+
loc: { file: this.componentName + ".tsx", start: { line: 1, column: 0 }, end: { line: 1, column: 0 } },
|
|
188823
|
+
suggestion: {
|
|
188824
|
+
message: "The Xslate adapter cannot lower JS object literals or tagged-template-literal expressions into Kolon. Move the expression into a `'use client'` component (so hydration computes it), or expand it into discrete attributes whose values are values the adapter can lower."
|
|
188825
|
+
}
|
|
188826
|
+
});
|
|
188827
|
+
return true;
|
|
188776
188828
|
}
|
|
188777
|
-
|
|
188778
|
-
|
|
188779
|
-
|
|
188780
|
-
|
|
188781
|
-
|
|
188782
|
-
|
|
188783
|
-
|
|
188784
|
-
|
|
188785
|
-
">=": ">=",
|
|
188786
|
-
"<=": "<=",
|
|
188787
|
-
"+": "+",
|
|
188788
|
-
"-": "-",
|
|
188789
|
-
"*": "*"
|
|
188829
|
+
get emitCtx() {
|
|
188830
|
+
return {
|
|
188831
|
+
_searchParamsLocals: this._searchParamsLocals,
|
|
188832
|
+
_resolveModuleStringConst: (name) => this._resolveModuleStringConst(name),
|
|
188833
|
+
_resolveLiteralConst: (name) => this._resolveLiteralConst(name),
|
|
188834
|
+
_resolveStaticRecordLiteral: (o, k) => this._resolveStaticRecordLiteral(o, k),
|
|
188835
|
+
_recordExprBF101: (message, reason) => this._recordExprBF101(message, reason),
|
|
188836
|
+
_renderKolonFilterExprPublic: (e, p) => this._renderKolonFilterExprPublic(e, p)
|
|
188790
188837
|
};
|
|
188791
|
-
return `${l} ${opMap[op] ?? op} ${r}`;
|
|
188792
|
-
}
|
|
188793
|
-
logical(op, left, right, emit) {
|
|
188794
|
-
const l = emit(left);
|
|
188795
|
-
const r = emit(right);
|
|
188796
|
-
if (op === "&&")
|
|
188797
|
-
return `(${l} && ${r})`;
|
|
188798
|
-
if (op === "||")
|
|
188799
|
-
return `(${l} || ${r})`;
|
|
188800
|
-
return `(${l} // ${r})`;
|
|
188801
188838
|
}
|
|
188802
|
-
|
|
188803
|
-
|
|
188804
|
-
|
|
188805
|
-
|
|
188806
|
-
|
|
188807
|
-
|
|
188808
|
-
|
|
188809
|
-
some: "some",
|
|
188810
|
-
find: "find",
|
|
188811
|
-
findIndex: "find_index",
|
|
188812
|
-
findLast: "find_last",
|
|
188813
|
-
findLastIndex: "find_last_index"
|
|
188839
|
+
get spreadCtx() {
|
|
188840
|
+
return {
|
|
188841
|
+
componentName: this.componentName,
|
|
188842
|
+
errors: this.errors,
|
|
188843
|
+
localConstants: this.localConstants,
|
|
188844
|
+
propsParams: this.propsParams,
|
|
188845
|
+
convertExpressionToKolon: (e, preParsed) => this.convertExpressionToKolon(e, preParsed)
|
|
188814
188846
|
};
|
|
188815
|
-
if (fn[method])
|
|
188816
|
-
return `$bf.${fn[method]}(${arrayExpr}, ${lambda})`;
|
|
188817
|
-
return emit(object);
|
|
188818
188847
|
}
|
|
188819
|
-
|
|
188820
|
-
return
|
|
188848
|
+
get memoCtx() {
|
|
188849
|
+
return { convertExpressionToKolon: (e, preParsed) => this.convertExpressionToKolon(e, preParsed) };
|
|
188821
188850
|
}
|
|
188822
|
-
|
|
188823
|
-
|
|
188851
|
+
convertExpressionToKolon(expr, preParsed) {
|
|
188852
|
+
let parsed;
|
|
188853
|
+
if (preParsed) {
|
|
188854
|
+
parsed = preParsed;
|
|
188855
|
+
} else {
|
|
188856
|
+
const trimmed = expr.trim();
|
|
188857
|
+
if (trimmed === "")
|
|
188858
|
+
return "''";
|
|
188859
|
+
parsed = parseExpression3(trimmed);
|
|
188860
|
+
}
|
|
188861
|
+
if (parsed.kind === "call") {
|
|
188862
|
+
for (const matcher of this._loweringMatchers) {
|
|
188863
|
+
const node = matcher(parsed.callee, parsed.args);
|
|
188864
|
+
if (node?.kind === "guard-list" && node.helper === "query") {
|
|
188865
|
+
const qArgs = queryHrefArgs(node, (n) => this.renderParsedExprToKolon(n));
|
|
188866
|
+
return `$bf.query(${qArgs.join(", ")})`;
|
|
188867
|
+
}
|
|
188868
|
+
}
|
|
188869
|
+
}
|
|
188870
|
+
const support = isSupported2(parsed);
|
|
188871
|
+
if (!support.supported) {
|
|
188872
|
+
this.errors.push({
|
|
188873
|
+
code: "BF101",
|
|
188874
|
+
severity: "error",
|
|
188875
|
+
message: `Expression not supported: ${preParsed ? stringifyParsedExpr2(parsed) : expr.trim()}`,
|
|
188876
|
+
loc: { file: this.componentName + ".tsx", start: { line: 1, column: 0 }, end: { line: 1, column: 0 } },
|
|
188877
|
+
suggestion: {
|
|
188878
|
+
message: support.reason ? `${support.reason}
|
|
188879
|
+
|
|
188880
|
+
Options:
|
|
188881
|
+
1. Use /* @client */ for client-side evaluation
|
|
188882
|
+
2. Pre-compute the value in the backend` : `Options:
|
|
188883
|
+
1. Use /* @client */ for client-side evaluation
|
|
188884
|
+
2. Pre-compute the value in the backend`
|
|
188885
|
+
}
|
|
188886
|
+
});
|
|
188887
|
+
return "''";
|
|
188888
|
+
}
|
|
188889
|
+
return this.renderParsedExprToKolon(parsed);
|
|
188824
188890
|
}
|
|
188825
|
-
|
|
188826
|
-
return
|
|
188891
|
+
renderParsedExprToKolon(expr) {
|
|
188892
|
+
return emitParsedExpr(expr, new XslateTopLevelEmitter(this.emitCtx));
|
|
188827
188893
|
}
|
|
188828
|
-
|
|
188829
|
-
return
|
|
188894
|
+
_isStringValueName(name) {
|
|
188895
|
+
return this.stringValueNames.has(name);
|
|
188830
188896
|
}
|
|
188831
|
-
|
|
188832
|
-
|
|
188897
|
+
parseUndefinedAlternateTernary(expr) {
|
|
188898
|
+
const parsed = parseExpression3(expr.trim());
|
|
188899
|
+
if (parsed?.kind !== "conditional")
|
|
188900
|
+
return null;
|
|
188901
|
+
const alt = parsed.alternate;
|
|
188902
|
+
const isUndef = alt.kind === "identifier" && (alt.name === "undefined" || alt.name === "null") || alt.kind === "literal" && (alt.value === null || alt.value === undefined);
|
|
188903
|
+
if (!isUndef)
|
|
188904
|
+
return null;
|
|
188905
|
+
return {
|
|
188906
|
+
condition: exprToString(parsed.test),
|
|
188907
|
+
consequent: exprToString(parsed.consequent)
|
|
188908
|
+
};
|
|
188909
|
+
}
|
|
188910
|
+
isBooleanTypedPropRef(expr) {
|
|
188911
|
+
let bare = expr.trim();
|
|
188912
|
+
if (this.propsObjectName && bare.startsWith(`${this.propsObjectName}.`)) {
|
|
188913
|
+
bare = bare.slice(this.propsObjectName.length + 1);
|
|
188914
|
+
}
|
|
188915
|
+
if (!/^[A-Za-z_$][\w$]*$/.test(bare))
|
|
188916
|
+
return false;
|
|
188917
|
+
return this.booleanTypedProps.has(bare);
|
|
188833
188918
|
}
|
|
188834
|
-
|
|
188835
|
-
|
|
188919
|
+
_resolveLiteralConst(name) {
|
|
188920
|
+
const c = (this.localConstants ?? []).find((lc) => lc.name === name);
|
|
188921
|
+
if (c?.value === undefined)
|
|
188922
|
+
return null;
|
|
188923
|
+
const v = c.value.trim();
|
|
188924
|
+
if (/^-?\d+(\.\d+)?$/.test(v))
|
|
188925
|
+
return v;
|
|
188926
|
+
const strLit = /^'([^'\\]*)'$/.exec(v) ?? /^"([^"\\]*)"$/.exec(v);
|
|
188927
|
+
if (strLit)
|
|
188928
|
+
return `'${strLit[1].replace(/[\\']/g, (m) => `\\${m}`)}'`;
|
|
188929
|
+
return null;
|
|
188836
188930
|
}
|
|
188837
|
-
|
|
188838
|
-
|
|
188931
|
+
_resolveStaticRecordLiteral(objectName, key) {
|
|
188932
|
+
const hit = lookupStaticRecordLiteral(objectName, key, this.localConstants);
|
|
188933
|
+
if (!hit)
|
|
188934
|
+
return null;
|
|
188935
|
+
return hit.kind === "number" ? hit.text : `'${hit.text.replace(/[\\']/g, (m) => `\\${m}`)}'`;
|
|
188839
188936
|
}
|
|
188840
|
-
|
|
188841
|
-
|
|
188842
|
-
|
|
188843
|
-
|
|
188844
|
-
|
|
188845
|
-
|
|
188846
|
-
|
|
188847
|
-
} else {
|
|
188848
|
-
const rendered = emit(part.expr);
|
|
188849
|
-
const needsParens = part.expr.kind === "binary" || part.expr.kind === "logical" || part.expr.kind === "conditional";
|
|
188850
|
-
terms.push(needsParens ? `(${rendered})` : rendered);
|
|
188851
|
-
}
|
|
188852
|
-
}
|
|
188853
|
-
if (terms.length === 0)
|
|
188854
|
-
return `''`;
|
|
188855
|
-
return terms.join(" ~ ");
|
|
188937
|
+
_resolveModuleStringConst(name) {
|
|
188938
|
+
if (this.inLoop)
|
|
188939
|
+
return null;
|
|
188940
|
+
const value = this.moduleStringConsts.get(name);
|
|
188941
|
+
if (value === undefined)
|
|
188942
|
+
return null;
|
|
188943
|
+
return `'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
|
|
188856
188944
|
}
|
|
188857
|
-
|
|
188858
|
-
|
|
188945
|
+
_recordExprBF101(message, reason) {
|
|
188946
|
+
this.errors.push({
|
|
188947
|
+
code: "BF101",
|
|
188948
|
+
severity: "error",
|
|
188949
|
+
message,
|
|
188950
|
+
loc: { file: this.componentName + ".tsx", start: { line: 1, column: 0 }, end: { line: 1, column: 0 } },
|
|
188951
|
+
suggestion: {
|
|
188952
|
+
message: reason ? `${reason}
|
|
188953
|
+
|
|
188954
|
+
Options:
|
|
188955
|
+
1. Use /* @client */ for client-side evaluation
|
|
188956
|
+
2. Pre-compute the value in the backend` : `Options:
|
|
188957
|
+
1. Use /* @client */ for client-side evaluation
|
|
188958
|
+
2. Pre-compute the value in the backend`
|
|
188959
|
+
}
|
|
188960
|
+
});
|
|
188859
188961
|
}
|
|
188860
|
-
|
|
188861
|
-
return
|
|
188962
|
+
_renderKolonFilterExprPublic(expr, param) {
|
|
188963
|
+
return this.renderKolonFilterExpr(expr, param);
|
|
188862
188964
|
}
|
|
188863
188965
|
}
|
|
188864
188966
|
var xslateAdapter = new XslateAdapter;
|