@barefootjs/mojolicious 0.14.0 → 0.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter/boolean-result.d.ts.map +1 -1
- package/dist/adapter/index.js +147 -47
- package/dist/adapter/mojo-adapter.d.ts +62 -10
- package/dist/adapter/mojo-adapter.d.ts.map +1 -1
- package/dist/build.js +147 -47
- package/dist/index.js +147 -47
- package/dist/test-render.d.ts.map +1 -1
- package/lib/BarefootJS/Backend/Mojo.pm +1 -1
- package/lib/Mojolicious/Plugin/BarefootJS/DevReload.pm +1 -1
- package/lib/Mojolicious/Plugin/BarefootJS.pm +11 -1
- package/package.json +3 -3
- package/src/__tests__/mojo-adapter.test.ts +53 -4
- package/src/adapter/boolean-result.ts +5 -0
- package/src/adapter/mojo-adapter.ts +287 -39
- package/src/test-render.ts +98 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"boolean-result.d.ts","sourceRoot":"","sources":["../../src/adapter/boolean-result.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAwCH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAIzD;
|
|
1
|
+
{"version":3,"file":"boolean-result.d.ts","sourceRoot":"","sources":["../../src/adapter/boolean-result.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAwCH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAIzD;AA8CD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEvD"}
|
package/dist/adapter/index.js
CHANGED
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
parseExpression as parseExpression2,
|
|
7
7
|
parseStyleObjectEntries,
|
|
8
8
|
isSupported,
|
|
9
|
+
exprToString,
|
|
10
|
+
parseProviderObjectLiteral,
|
|
9
11
|
identifierPath,
|
|
10
12
|
emitParsedExpr,
|
|
11
13
|
emitIRNode,
|
|
@@ -16,7 +18,10 @@ import {
|
|
|
16
18
|
extractArrowBodyExpression,
|
|
17
19
|
collectContextConsumers,
|
|
18
20
|
isLowerableObjectRestDestructure,
|
|
19
|
-
|
|
21
|
+
collectModuleStringConsts,
|
|
22
|
+
lookupStaticRecordLiteral,
|
|
23
|
+
searchParamsLocalNames,
|
|
24
|
+
matchSearchParamsMethodCall
|
|
20
25
|
} from "@barefootjs/jsx";
|
|
21
26
|
|
|
22
27
|
// src/adapter/boolean-result.ts
|
|
@@ -63,6 +68,8 @@ var ARIA_BOOLEAN_ATTRS = new Set([
|
|
|
63
68
|
"aria-multiselectable",
|
|
64
69
|
"aria-readonly",
|
|
65
70
|
"aria-required",
|
|
71
|
+
"aria-selected",
|
|
72
|
+
"aria-expanded",
|
|
66
73
|
"aria-checked",
|
|
67
74
|
"aria-pressed"
|
|
68
75
|
]);
|
|
@@ -71,7 +78,7 @@ function isAriaBooleanAttr(name) {
|
|
|
71
78
|
}
|
|
72
79
|
|
|
73
80
|
// src/adapter/mojo-adapter.ts
|
|
74
|
-
import { BF_SLOT, BF_COND } from "@barefootjs/shared";
|
|
81
|
+
import { BF_SLOT, BF_COND, BF_REGION } from "@barefootjs/shared";
|
|
75
82
|
var MOJO_TEMPLATE_PRIMITIVES = {
|
|
76
83
|
"JSON.stringify": { arity: 1, emit: (args) => `bf->json(${args[0]})` },
|
|
77
84
|
String: { arity: 1, emit: (args) => `bf->string(${args[0]})` },
|
|
@@ -89,22 +96,6 @@ function resolveJsxChildrenProp(props) {
|
|
|
89
96
|
return [];
|
|
90
97
|
return prop.value.children;
|
|
91
98
|
}
|
|
92
|
-
function parsePureStringLiteral(source) {
|
|
93
|
-
const sf = ts.createSourceFile("__const.ts", `const __x = (${source});`, ts.ScriptTarget.Latest, false);
|
|
94
|
-
const stmt = sf.statements[0];
|
|
95
|
-
if (!stmt || !ts.isVariableStatement(stmt))
|
|
96
|
-
return null;
|
|
97
|
-
const decl = stmt.declarationList.declarations[0];
|
|
98
|
-
let init = decl?.initializer;
|
|
99
|
-
while (init && ts.isParenthesizedExpression(init))
|
|
100
|
-
init = init.expression;
|
|
101
|
-
if (!init)
|
|
102
|
-
return null;
|
|
103
|
-
if (ts.isStringLiteral(init) || ts.isNoSubstitutionTemplateLiteral(init)) {
|
|
104
|
-
return init.text;
|
|
105
|
-
}
|
|
106
|
-
return evalStringArrayJoin(source);
|
|
107
|
-
}
|
|
108
99
|
function perlHashKey(name) {
|
|
109
100
|
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(name) ? name : `'${name.replace(/'/g, "\\'")}'`;
|
|
110
101
|
}
|
|
@@ -152,7 +143,9 @@ class MojoAdapter extends BaseAdapter {
|
|
|
152
143
|
inLoop = false;
|
|
153
144
|
propsObjectName = null;
|
|
154
145
|
propsParams = [];
|
|
146
|
+
booleanTypedProps = new Set;
|
|
155
147
|
stringValueNames = new Set;
|
|
148
|
+
_searchParamsLocals = new Set;
|
|
156
149
|
moduleStringConsts = new Map;
|
|
157
150
|
localConstants = [];
|
|
158
151
|
loopBoundNames = new Map;
|
|
@@ -169,6 +162,7 @@ class MojoAdapter extends BaseAdapter {
|
|
|
169
162
|
this.propsObjectName = ir.metadata.propsObjectName ?? null;
|
|
170
163
|
augmentInheritedPropAccesses(ir);
|
|
171
164
|
this.propsParams = ir.metadata.propsParams.map((p) => ({ name: p.name }));
|
|
165
|
+
this.booleanTypedProps = new Set(ir.metadata.propsParams.filter((prop) => prop.type?.primitive === "boolean" || prop.type?.raw === "boolean").map((prop) => prop.name));
|
|
172
166
|
this.nullableOptionalProps = new Set(ir.metadata.propsParams.filter((p) => p.defaultValue === undefined && !p.isRest && p.type?.kind !== "primitive").map((p) => p.name));
|
|
173
167
|
this.stringValueNames = new Set;
|
|
174
168
|
for (const s of ir.metadata.signals) {
|
|
@@ -180,7 +174,8 @@ class MojoAdapter extends BaseAdapter {
|
|
|
180
174
|
if (isStringTypeInfo(p.type))
|
|
181
175
|
this.stringValueNames.add(p.name);
|
|
182
176
|
}
|
|
183
|
-
this.moduleStringConsts =
|
|
177
|
+
this.moduleStringConsts = collectModuleStringConsts(ir.metadata.localConstants);
|
|
178
|
+
this._searchParamsLocals = searchParamsLocalNames(ir.metadata);
|
|
184
179
|
this.localConstants = ir.metadata.localConstants ?? [];
|
|
185
180
|
this.loopBoundNames.clear();
|
|
186
181
|
this.errors = [];
|
|
@@ -210,18 +205,49 @@ class MojoAdapter extends BaseAdapter {
|
|
|
210
205
|
extension: this.extension
|
|
211
206
|
};
|
|
212
207
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
continue;
|
|
218
|
-
if (c.value === undefined)
|
|
219
|
-
continue;
|
|
220
|
-
const literal = parsePureStringLiteral(c.value);
|
|
221
|
-
if (literal !== null)
|
|
222
|
-
map.set(c.name, literal);
|
|
208
|
+
isBooleanTypedPropRef(expr) {
|
|
209
|
+
let bare = expr.trim();
|
|
210
|
+
if (this.propsObjectName && bare.startsWith(`${this.propsObjectName}.`)) {
|
|
211
|
+
bare = bare.slice(this.propsObjectName.length + 1);
|
|
223
212
|
}
|
|
224
|
-
|
|
213
|
+
if (!/^[A-Za-z_$][\w$]*$/.test(bare))
|
|
214
|
+
return false;
|
|
215
|
+
return this.booleanTypedProps.has(bare);
|
|
216
|
+
}
|
|
217
|
+
parseUndefinedAlternateTernary(expr) {
|
|
218
|
+
const parsed = parseExpression2(expr.trim());
|
|
219
|
+
if (parsed?.kind !== "conditional")
|
|
220
|
+
return null;
|
|
221
|
+
const alt = parsed.alternate;
|
|
222
|
+
const isUndef = alt.kind === "identifier" && (alt.name === "undefined" || alt.name === "null") || alt.kind === "literal" && (alt.value === null || alt.value === undefined);
|
|
223
|
+
if (!isUndef)
|
|
224
|
+
return null;
|
|
225
|
+
return {
|
|
226
|
+
condition: exprToString(parsed.test),
|
|
227
|
+
consequent: exprToString(parsed.consequent)
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
resolveLiteralConst(name) {
|
|
231
|
+
if (this.loopBoundNames?.has?.(name))
|
|
232
|
+
return null;
|
|
233
|
+
const c = (this.localConstants ?? []).find((lc) => lc.name === name);
|
|
234
|
+
if (c?.value === undefined)
|
|
235
|
+
return null;
|
|
236
|
+
const v = c.value.trim();
|
|
237
|
+
if (/^-?\d+(\.\d+)?$/.test(v))
|
|
238
|
+
return v;
|
|
239
|
+
const strLit = /^'([^'\\]*)'$/.exec(v) ?? /^"([^"\\]*)"$/.exec(v);
|
|
240
|
+
if (strLit)
|
|
241
|
+
return `'${strLit[1].replace(/[\\']/g, (m) => `\\${m}`)}'`;
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
resolveStaticRecordLiteral(objectName, key) {
|
|
245
|
+
if (this.loopBoundNames?.has?.(objectName))
|
|
246
|
+
return null;
|
|
247
|
+
const hit = lookupStaticRecordLiteral(objectName, key, this.localConstants);
|
|
248
|
+
if (!hit)
|
|
249
|
+
return null;
|
|
250
|
+
return hit.kind === "number" ? hit.text : `'${hit.text.replace(/[\\']/g, (m) => `\\${m}`)}'`;
|
|
225
251
|
}
|
|
226
252
|
resolveModuleStringConst(name) {
|
|
227
253
|
if (this.loopBoundNames.has(name))
|
|
@@ -289,12 +315,29 @@ class MojoAdapter extends BaseAdapter {
|
|
|
289
315
|
if (v.kind === "literal") {
|
|
290
316
|
return typeof v.value === "string" ? `'${v.value.replace(/[\\']/g, (m) => `\\${m}`)}'` : String(v.value);
|
|
291
317
|
}
|
|
292
|
-
if (v.kind === "expression")
|
|
318
|
+
if (v.kind === "expression") {
|
|
319
|
+
const hashref = this.providerObjectLiteralPerl(v.expr);
|
|
320
|
+
if (hashref !== null)
|
|
321
|
+
return hashref;
|
|
293
322
|
return this.convertExpressionToPerl(v.expr);
|
|
323
|
+
}
|
|
294
324
|
if (v.kind === "template")
|
|
295
325
|
return this.convertTemplateLiteralPartsToPerl(v.parts);
|
|
296
326
|
return "undef";
|
|
297
327
|
}
|
|
328
|
+
providerObjectLiteralPerl(expr) {
|
|
329
|
+
const members = parseProviderObjectLiteral(expr.trim());
|
|
330
|
+
if (members === null)
|
|
331
|
+
return null;
|
|
332
|
+
const entries = members.map((m) => {
|
|
333
|
+
const key = `'${m.name.replace(/[\\']/g, (c) => `\\${c}`)}'`;
|
|
334
|
+
if (m.kind === "function" || /^on[A-Z]/.test(m.name))
|
|
335
|
+
return `${key} => undef`;
|
|
336
|
+
const src = m.kind === "getter" ? m.body : m.expr;
|
|
337
|
+
return `${key} => ${this.convertExpressionToPerl(src)}`;
|
|
338
|
+
});
|
|
339
|
+
return `{ ${entries.join(", ")} }`;
|
|
340
|
+
}
|
|
298
341
|
contextDefaultPerl(c) {
|
|
299
342
|
const d = c.defaultValue;
|
|
300
343
|
if (d === null || d === undefined)
|
|
@@ -318,7 +361,6 @@ class MojoAdapter extends BaseAdapter {
|
|
|
318
361
|
const signals = ir.metadata.signals ?? [];
|
|
319
362
|
if (memos.length === 0 && signals.length === 0)
|
|
320
363
|
return "";
|
|
321
|
-
const ssrDefaults = extractSsrDefaults(ir.metadata) ?? {};
|
|
322
364
|
const available = new Set(ir.metadata.propsParams.map((p) => p.name));
|
|
323
365
|
const lines = [];
|
|
324
366
|
for (const signal of signals) {
|
|
@@ -328,18 +370,12 @@ class MojoAdapter extends BaseAdapter {
|
|
|
328
370
|
available.add(signal.getter);
|
|
329
371
|
}
|
|
330
372
|
for (const memo of memos) {
|
|
331
|
-
const def = ssrDefaults[memo.name];
|
|
332
|
-
const isNull = !def || typeof def === "object" && "value" in def && def.value === null;
|
|
333
|
-
if (!isNull) {
|
|
334
|
-
available.add(memo.name);
|
|
335
|
-
continue;
|
|
336
|
-
}
|
|
337
373
|
const body = extractArrowBodyExpression(memo.computation);
|
|
338
|
-
if (body
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
374
|
+
if (body !== null) {
|
|
375
|
+
const perl = this.tryLowerToPerl(body, available);
|
|
376
|
+
if (perl !== null)
|
|
377
|
+
lines.push(`% my $${memo.name} = ${perl};`);
|
|
378
|
+
}
|
|
343
379
|
available.add(memo.name);
|
|
344
380
|
}
|
|
345
381
|
return lines.length > 0 ? lines.join(`
|
|
@@ -374,6 +410,9 @@ class MojoAdapter extends BaseAdapter {
|
|
|
374
410
|
if (element.slotId) {
|
|
375
411
|
hydrationAttrs += ` ${this.renderSlotMarker(element.slotId)}`;
|
|
376
412
|
}
|
|
413
|
+
if (element.regionId) {
|
|
414
|
+
hydrationAttrs += ` ${BF_REGION}="${element.regionId}"`;
|
|
415
|
+
}
|
|
377
416
|
const voidElements = [
|
|
378
417
|
"area",
|
|
379
418
|
"base",
|
|
@@ -654,7 +693,7 @@ ${renderedChildren}` : renderedChildren;
|
|
|
654
693
|
renderComponent(comp) {
|
|
655
694
|
const propParts = [];
|
|
656
695
|
for (const p of comp.props) {
|
|
657
|
-
if (p.name.match(/^on[A-Z]/) && p.value.kind === "expression")
|
|
696
|
+
if ((p.name.match(/^on[A-Z]/) || p.name === "ref") && p.value.kind === "expression")
|
|
658
697
|
continue;
|
|
659
698
|
const lowered = emitAttrValue(p.value, this.componentPropEmitter, p.name);
|
|
660
699
|
if (lowered)
|
|
@@ -667,13 +706,17 @@ ${renderedChildren}` : renderedChildren;
|
|
|
667
706
|
const tplName = this.toTemplateName(comp.name);
|
|
668
707
|
const effectiveChildren = comp.children.length > 0 ? comp.children : resolveJsxChildrenProp(comp.props);
|
|
669
708
|
if (effectiveChildren.length > 0) {
|
|
709
|
+
const prevInLoop = this.inLoop;
|
|
710
|
+
this.inLoop = false;
|
|
670
711
|
const childrenBody = this.renderChildren(effectiveChildren);
|
|
712
|
+
this.inLoop = prevInLoop;
|
|
671
713
|
const varName = `$bf_children_${comp.slotId ?? "c" + this.childrenCaptureCounter++}`;
|
|
672
714
|
return `<% my ${varName} = begin %>${childrenBody}<% end %><%== bf->render_child('${tplName}'${propsStr}, children => ${varName}) %>`;
|
|
673
715
|
}
|
|
674
716
|
return `<%== bf->render_child('${tplName}'${propsStr}) %>`;
|
|
675
717
|
}
|
|
676
718
|
childrenCaptureCounter = 0;
|
|
719
|
+
presenceVarCounter = 0;
|
|
677
720
|
toTemplateName(componentName) {
|
|
678
721
|
return componentName.replace(/([A-Z])/g, "_$1").toLowerCase().replace(/^_/, "");
|
|
679
722
|
}
|
|
@@ -729,14 +772,28 @@ ${children}`;
|
|
|
729
772
|
const normalizedBareId = this.propsObjectName && bareId.startsWith(`${this.propsObjectName}.`) ? bareId.slice(this.propsObjectName.length + 1) : bareId;
|
|
730
773
|
if (!isBooleanAttr(name) && !value.presenceOrUndefined && /^[A-Za-z_$][\w$]*$/.test(normalizedBareId) && this.nullableOptionalProps.has(normalizedBareId)) {
|
|
731
774
|
const perl2 = this.convertExpressionToPerl(value.expr);
|
|
732
|
-
const body = isBooleanResultExpr(value.expr) || isAriaBooleanAttr(name) ? `${name}="<%= bf->bool_str(${perl2}) %>"` : `${name}="<%= ${perl2} %>"`;
|
|
775
|
+
const body = isBooleanResultExpr(value.expr) || isAriaBooleanAttr(name) || this.isBooleanTypedPropRef(value.expr) ? `${name}="<%= bf->bool_str(${perl2}) %>"` : `${name}="<%= ${perl2} %>"`;
|
|
733
776
|
return `<% if (defined ${perl2}) { %>${body}<% } %>`;
|
|
734
777
|
}
|
|
735
|
-
if (isBooleanAttr(name)
|
|
778
|
+
if (isBooleanAttr(name)) {
|
|
736
779
|
return `<%= ${this.convertExpressionToPerl(value.expr)} ? '${name}' : '' %>`;
|
|
737
780
|
}
|
|
781
|
+
if (value.presenceOrUndefined) {
|
|
782
|
+
const perl2 = this.convertExpressionToPerl(value.expr);
|
|
783
|
+
const tmp = `$bf_pu${this.presenceVarCounter++}`;
|
|
784
|
+
const body = isBooleanResultExpr(value.expr) || isAriaBooleanAttr(name) || this.isBooleanTypedPropRef(value.expr) ? `${name}="<%= bf->bool_str(${tmp}) %>"` : `${name}="<%= ${tmp} %>"`;
|
|
785
|
+
return `<% my ${tmp} = ${perl2}; if (${tmp}) { %>${body}<% } %>`;
|
|
786
|
+
}
|
|
787
|
+
{
|
|
788
|
+
const m = this.parseUndefinedAlternateTernary(value.expr);
|
|
789
|
+
if (m) {
|
|
790
|
+
const cond = this.convertExpressionToPerl(m.condition);
|
|
791
|
+
const val = this.convertExpressionToPerl(m.consequent);
|
|
792
|
+
return `<% if (${cond}) { %>${name}="<%= ${val} %>"<% } %>`;
|
|
793
|
+
}
|
|
794
|
+
}
|
|
738
795
|
const perl = this.convertExpressionToPerl(value.expr);
|
|
739
|
-
if (isBooleanResultExpr(value.expr) || isAriaBooleanAttr(name)) {
|
|
796
|
+
if (isBooleanResultExpr(value.expr) || isAriaBooleanAttr(name) || this.isBooleanTypedPropRef(value.expr)) {
|
|
740
797
|
return `${name}="<%= bf->bool_str(${perl}) %>"`;
|
|
741
798
|
}
|
|
742
799
|
return `${name}="<%= ${perl} %>"`;
|
|
@@ -995,6 +1052,18 @@ ${reason}` : "";
|
|
|
995
1052
|
return n;
|
|
996
1053
|
})();
|
|
997
1054
|
const indexed = this.recordIndexAccessToPerl(initNode);
|
|
1055
|
+
if (indexed === null && ts.isElementAccessExpression(initNode) && initNode.argumentExpression && !ts.isNumericLiteral(initNode.argumentExpression) && !ts.isStringLiteral(initNode.argumentExpression)) {
|
|
1056
|
+
this.errors.push({
|
|
1057
|
+
code: "BF101",
|
|
1058
|
+
severity: "error",
|
|
1059
|
+
message: `Spread object value '${initNode.getText(sf)}' indexes a record map whose values aren't scalar literals — it can't lower to an inline Perl hashref.`,
|
|
1060
|
+
loc: { file: this.componentName + ".tsx", start: { line: 1, column: 0 }, end: { line: 1, column: 0 } },
|
|
1061
|
+
suggestion: {
|
|
1062
|
+
message: "Index a record whose values are number/string literals, or move the spread into a `'use client'` component so hydration computes it."
|
|
1063
|
+
}
|
|
1064
|
+
});
|
|
1065
|
+
return null;
|
|
1066
|
+
}
|
|
998
1067
|
const valPerl = indexed !== null ? indexed : this.convertExpressionToPerl(prop.initializer.getText(sf));
|
|
999
1068
|
entries.push(`'${key.replace(/'/g, "\\'")}' => ${valPerl}`);
|
|
1000
1069
|
}
|
|
@@ -1118,6 +1187,11 @@ function renderArrayMethod(method, object, args, emit) {
|
|
|
1118
1187
|
const recv = emit(object);
|
|
1119
1188
|
return `bf->trim(${recv})`;
|
|
1120
1189
|
}
|
|
1190
|
+
case "toFixed": {
|
|
1191
|
+
const recv = emit(object);
|
|
1192
|
+
const digits = args.length >= 1 ? emit(args[0]) : "0";
|
|
1193
|
+
return `bf->to_fixed(${recv}, ${digits})`;
|
|
1194
|
+
}
|
|
1121
1195
|
case "split": {
|
|
1122
1196
|
const recv = emit(object);
|
|
1123
1197
|
if (args.length === 0) {
|
|
@@ -1220,6 +1294,10 @@ function isStringTypedOperand(expr, isStringName) {
|
|
|
1220
1294
|
}
|
|
1221
1295
|
return false;
|
|
1222
1296
|
}
|
|
1297
|
+
function emitIndexAccessPerl(object, index, emit, isStringName) {
|
|
1298
|
+
const i = emit(index);
|
|
1299
|
+
return isStringTypedOperand(index, isStringName) ? `${emit(object)}->{${i}}` : `${emit(object)}->[${i}]`;
|
|
1300
|
+
}
|
|
1223
1301
|
|
|
1224
1302
|
class MojoFilterEmitter {
|
|
1225
1303
|
param;
|
|
@@ -1253,6 +1331,9 @@ class MojoFilterEmitter {
|
|
|
1253
1331
|
}
|
|
1254
1332
|
return `${emit(object)}->{${property}}`;
|
|
1255
1333
|
}
|
|
1334
|
+
indexAccess(object, index, emit) {
|
|
1335
|
+
return emitIndexAccessPerl(object, index, emit, this.isStringName);
|
|
1336
|
+
}
|
|
1256
1337
|
call(callee, args, emit) {
|
|
1257
1338
|
if (callee.kind === "identifier" && args.length === 0) {
|
|
1258
1339
|
return `$${callee.name}`;
|
|
@@ -1353,9 +1434,14 @@ class MojoTopLevelEmitter {
|
|
|
1353
1434
|
this.adapter = adapter;
|
|
1354
1435
|
}
|
|
1355
1436
|
identifier(name) {
|
|
1437
|
+
if (name === "undefined" || name === "null")
|
|
1438
|
+
return "undef";
|
|
1356
1439
|
const inlined = this.adapter.resolveModuleStringConst(name);
|
|
1357
1440
|
if (inlined !== null)
|
|
1358
1441
|
return inlined;
|
|
1442
|
+
const literalConst = this.adapter.resolveLiteralConst(name);
|
|
1443
|
+
if (literalConst !== null)
|
|
1444
|
+
return literalConst;
|
|
1359
1445
|
return `$${name}`;
|
|
1360
1446
|
}
|
|
1361
1447
|
literal(value, literalType) {
|
|
@@ -1371,15 +1457,29 @@ class MojoTopLevelEmitter {
|
|
|
1371
1457
|
if (object.kind === "identifier" && object.name === "props") {
|
|
1372
1458
|
return `$${property}`;
|
|
1373
1459
|
}
|
|
1460
|
+
if (object.kind === "identifier") {
|
|
1461
|
+
const staticValue = this.adapter.resolveStaticRecordLiteral(object.name, property);
|
|
1462
|
+
if (staticValue !== null)
|
|
1463
|
+
return staticValue;
|
|
1464
|
+
}
|
|
1374
1465
|
const obj = emit(object);
|
|
1375
1466
|
if (property === "length")
|
|
1376
1467
|
return `scalar(@{${obj}})`;
|
|
1377
1468
|
return `${obj}->{${property}}`;
|
|
1378
1469
|
}
|
|
1470
|
+
indexAccess(object, index, emit) {
|
|
1471
|
+
return emitIndexAccessPerl(object, index, emit, (n) => this.adapter._isStringValueName(n));
|
|
1472
|
+
}
|
|
1379
1473
|
call(callee, args, emit) {
|
|
1380
1474
|
if (callee.kind === "identifier" && args.length === 0) {
|
|
1381
1475
|
return `$${callee.name}`;
|
|
1382
1476
|
}
|
|
1477
|
+
if (this.adapter._searchParamsLocals.size > 0) {
|
|
1478
|
+
const sp = matchSearchParamsMethodCall(callee, args, this.adapter._searchParamsLocals);
|
|
1479
|
+
if (sp) {
|
|
1480
|
+
return `$searchParams->${sp.method}(${sp.args.map(emit).join(", ")})`;
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1383
1483
|
const path = identifierPath(callee);
|
|
1384
1484
|
const spec = path ? MOJO_TEMPLATE_PRIMITIVES[path] : undefined;
|
|
1385
1485
|
if (path && spec) {
|
|
@@ -57,6 +57,7 @@ export declare class MojoAdapter extends BaseAdapter implements IRNodeEmitter<Mo
|
|
|
57
57
|
*/
|
|
58
58
|
private propsObjectName;
|
|
59
59
|
private propsParams;
|
|
60
|
+
private booleanTypedProps;
|
|
60
61
|
/**
|
|
61
62
|
* Names (signal getters + props) whose value is a string, so `===`/`!==`
|
|
62
63
|
* against them lowers to Perl `eq`/`ne` rather than numeric `==`/`!=`.
|
|
@@ -64,6 +65,15 @@ export declare class MojoAdapter extends BaseAdapter implements IRNodeEmitter<Mo
|
|
|
64
65
|
* true — selecting the string operator from the operand's type avoids that.
|
|
65
66
|
*/
|
|
66
67
|
private stringValueNames;
|
|
68
|
+
/**
|
|
69
|
+
* (#1922) Local binding names the request-scoped `searchParams()` env signal
|
|
70
|
+
* is imported under (handles `import { searchParams as sp }`). When non-empty
|
|
71
|
+
* the emitter lowers a `<binding>().get(k)` call to a real method call on the
|
|
72
|
+
* per-request `$searchParams` reader (`$searchParams->get('sort')`) instead of
|
|
73
|
+
* the generic hash deref. Set at `generate()` entry from `ir.metadata.imports`;
|
|
74
|
+
* read by the top-level ParsedExpr emitter.
|
|
75
|
+
*/
|
|
76
|
+
_searchParamsLocals: Set<string>;
|
|
67
77
|
/**
|
|
68
78
|
* Module-scope pure string-literal constants (`const X = 'literal'` at
|
|
69
79
|
* file top-level), keyed by name → resolved literal value. Populated at
|
|
@@ -111,22 +121,42 @@ export declare class MojoAdapter extends BaseAdapter implements IRNodeEmitter<Mo
|
|
|
111
121
|
private nullableOptionalProps;
|
|
112
122
|
constructor(options?: MojoAdapterOptions);
|
|
113
123
|
generate(ir: ComponentIR, options?: AdapterGenerateOptions): AdapterOutput;
|
|
114
|
-
/**
|
|
115
|
-
* Build the module pure-string-const map from the IR's localConstants.
|
|
116
|
-
* A const qualifies only when it is module-scope (`isModule`) and its
|
|
117
|
-
* initializer parses to a single string literal (`ts.StringLiteral` or
|
|
118
|
-
* `ts.NoSubstitutionTemplateLiteral`). Template literals with `${}`,
|
|
119
|
-
* numeric/object initializers, `Record<T,string>` maps, memos, and
|
|
120
|
-
* signals are all excluded — only a pure compile-time string can be
|
|
121
|
-
* inlined byte-for-byte.
|
|
122
|
-
*/
|
|
123
|
-
private collectModuleStringConsts;
|
|
124
124
|
/**
|
|
125
125
|
* Resolve an identifier to its inlined Perl single-quoted string literal
|
|
126
126
|
* when it names a module pure-string const, else `null` (the caller then
|
|
127
127
|
* falls back to its normal `$name` stash lowering). Returns the Perl
|
|
128
128
|
* literal form `'<escaped>'` ready to drop into an expression.
|
|
129
129
|
*/
|
|
130
|
+
/**
|
|
131
|
+
* Resolve `IDENT.key` over a module object-literal const to its Perl
|
|
132
|
+
* literal (`variantClasses.ghost` in a class template literal —
|
|
133
|
+
* #1897). Same compile-time inlining family as
|
|
134
|
+
* `resolveModuleStringConst`; returns `null` for any non-static shape.
|
|
135
|
+
*/
|
|
136
|
+
/**
|
|
137
|
+
* Whether `expr` is a bare reference to a boolean-TYPED prop
|
|
138
|
+
* (`props.isActive` / destructured `isActive`) — used to route the
|
|
139
|
+
* binding through `bool_str` even though the expression itself is
|
|
140
|
+
* structurally opaque (#1897).
|
|
141
|
+
*/
|
|
142
|
+
isBooleanTypedPropRef(expr: string): boolean;
|
|
143
|
+
/**
|
|
144
|
+
* Parse `cond ? value : undefined` (or `: null`), returning the
|
|
145
|
+
* condition/consequent source spans, else `null`. Used for the
|
|
146
|
+
* attribute-omission rule (#1897); mirrors the Xslate adapter.
|
|
147
|
+
*/
|
|
148
|
+
parseUndefinedAlternateTernary(expr: string): {
|
|
149
|
+
condition: string;
|
|
150
|
+
consequent: string;
|
|
151
|
+
} | null;
|
|
152
|
+
/**
|
|
153
|
+
* Inline a const (any scope) whose initializer is a pure numeric or
|
|
154
|
+
* quoted string literal (`const totalPages = 5`, #1897 pagination) —
|
|
155
|
+
* function-scope consts never reach the per-render stash, so a bare
|
|
156
|
+
* `$totalPages` faults under strict mode.
|
|
157
|
+
*/
|
|
158
|
+
resolveLiteralConst(name: string): string | null;
|
|
159
|
+
resolveStaticRecordLiteral(objectName: string, key: string): string | null;
|
|
130
160
|
resolveModuleStringConst(name: string): string | null;
|
|
131
161
|
private generateScriptRegistrations;
|
|
132
162
|
private hasClientInteractivity;
|
|
@@ -148,6 +178,25 @@ export declare class MojoAdapter extends BaseAdapter implements IRNodeEmitter<Mo
|
|
|
148
178
|
emitProvider(node: IRProvider, _ctx: MojoRenderCtx, _emit: EmitIRNode<MojoRenderCtx>): string;
|
|
149
179
|
/** Lower a `<Ctx.Provider value>` value prop to a Perl expression. */
|
|
150
180
|
private providerValuePerl;
|
|
181
|
+
/**
|
|
182
|
+
* Lower an object-literal provider value (`value={{ open: () => props.open
|
|
183
|
+
* ?? false, onOpenChange: … }}`) to a Perl hashref (#1897). The SSR
|
|
184
|
+
* lowering is a per-member snapshot of what a consumer would READ during
|
|
185
|
+
* the same render:
|
|
186
|
+
*
|
|
187
|
+
* - zero-param expression-body arrows are getters — lower the body (the
|
|
188
|
+
* value is fixed for the render, so the call-time indirection drops out)
|
|
189
|
+
* - `on[A-Z]`-named members and function-shaped values are client-only
|
|
190
|
+
* behavior SSR never invokes — lower to `undef`
|
|
191
|
+
* - anything else lowers through the normal expression pipeline (so an
|
|
192
|
+
* unsupported getter body still refuses loudly with BF101)
|
|
193
|
+
*
|
|
194
|
+
* Keys keep their JS names verbatim so a consumer-side `ctx.open` access
|
|
195
|
+
* maps onto the same key. Returns `null` when the expression is not a
|
|
196
|
+
* plain object literal (spread / computed key) — the caller falls back to
|
|
197
|
+
* the whole-expression path, which refuses those shapes with BF101.
|
|
198
|
+
*/
|
|
199
|
+
private providerObjectLiteralPerl;
|
|
151
200
|
/** Perl literal for a context-consumer's `createContext` default. */
|
|
152
201
|
private contextDefaultPerl;
|
|
153
202
|
/**
|
|
@@ -208,6 +257,9 @@ export declare class MojoAdapter extends BaseAdapter implements IRNodeEmitter<Mo
|
|
|
208
257
|
private readonly componentPropEmitter;
|
|
209
258
|
renderComponent(comp: IRComponent): string;
|
|
210
259
|
private childrenCaptureCounter;
|
|
260
|
+
/** Uniquifies the `presenceOrUndefined` temp binding (`$bf_puN`) so two
|
|
261
|
+
* presence-folded attrs in one template don't collide. */
|
|
262
|
+
private presenceVarCounter;
|
|
211
263
|
private toTemplateName;
|
|
212
264
|
private renderIfStatement;
|
|
213
265
|
private renderFragment;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mojo-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/mojo-adapter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EACV,WAAW,EAEX,MAAM,EACN,SAAS,EACT,MAAM,EACN,YAAY,EACZ,aAAa,EACb,MAAM,EACN,WAAW,EACX,UAAU,EACV,MAAM,EACN,aAAa,EACb,UAAU,EACV,OAAO,EAMP,yBAAyB,EAC1B,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,WAAW,EACX,KAAK,aAAa,EAClB,KAAK,sBAAsB,EAM3B,KAAK,aAAa,EAClB,KAAK,UAAU,
|
|
1
|
+
{"version":3,"file":"mojo-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/mojo-adapter.ts"],"names":[],"mappings":"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"}
|