@barefootjs/test 0.17.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +414 -107
- package/dist/ir-to-test-node.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/ir-to-test-node.ts +124 -32
package/dist/index.js
CHANGED
|
@@ -187348,6 +187348,7 @@ function tsNodeToParsedExpr(node) {
|
|
|
187348
187348
|
}
|
|
187349
187349
|
var CALLBACK_METHODS = new Set([
|
|
187350
187350
|
"filter",
|
|
187351
|
+
"map",
|
|
187351
187352
|
"every",
|
|
187352
187353
|
"some",
|
|
187353
187354
|
"find",
|
|
@@ -187436,6 +187437,7 @@ function convertNode(node, raw) {
|
|
|
187436
187437
|
if (callee.property === "flat") {
|
|
187437
187438
|
const depthNode = node.arguments[0];
|
|
187438
187439
|
let flatDepth;
|
|
187440
|
+
let depthExpr;
|
|
187439
187441
|
if (depthNode === undefined) {
|
|
187440
187442
|
flatDepth = 1;
|
|
187441
187443
|
} else if (import_typescript.default.isIdentifier(depthNode) && depthNode.text === "Infinity") {
|
|
@@ -187448,16 +187450,23 @@ function convertNode(node, raw) {
|
|
|
187448
187450
|
n = -Number(depthNode.operand.text);
|
|
187449
187451
|
}
|
|
187450
187452
|
if (n === undefined || Number.isNaN(n)) {
|
|
187451
|
-
|
|
187452
|
-
|
|
187453
|
-
|
|
187454
|
-
|
|
187455
|
-
}
|
|
187453
|
+
const parsedDepth = convertNode(depthNode, raw);
|
|
187454
|
+
if (checkSupport(parsedDepth).supported) {
|
|
187455
|
+
depthExpr = parsedDepth;
|
|
187456
|
+
flatDepth = 1;
|
|
187457
|
+
} else {
|
|
187458
|
+
return {
|
|
187459
|
+
kind: "unsupported",
|
|
187460
|
+
raw,
|
|
187461
|
+
reason: `\`.flat(depth)\` needs a literal integer, \`Infinity\`, or a supported dynamic depth expression — this depth can't be resolved. Use a literal depth, a supported expression (prop/signal/arithmetic), or pre-compute the value before the template.`
|
|
187462
|
+
};
|
|
187463
|
+
}
|
|
187464
|
+
} else {
|
|
187465
|
+
const truncated = Math.trunc(n);
|
|
187466
|
+
flatDepth = truncated < 0 ? 0 : truncated;
|
|
187456
187467
|
}
|
|
187457
|
-
const truncated = Math.trunc(n);
|
|
187458
|
-
flatDepth = truncated < 0 ? 0 : truncated;
|
|
187459
187468
|
}
|
|
187460
|
-
return { kind: "array-method", method: "flat", object: callee.object, args: [], flatDepth };
|
|
187469
|
+
return depthExpr !== undefined ? { kind: "array-method", method: "flat", object: callee.object, args: [], flatDepth, depthExpr } : { kind: "array-method", method: "flat", object: callee.object, args: [], flatDepth };
|
|
187461
187470
|
}
|
|
187462
187471
|
if (callee.property === "toLowerCase") {
|
|
187463
187472
|
return { kind: "array-method", method: "toLowerCase", object: callee.object, args };
|
|
@@ -188002,6 +188011,8 @@ function validateRestUsage(expr, restName, excludedTopKeys) {
|
|
|
188002
188011
|
walk(e.object);
|
|
188003
188012
|
for (const a of e.args)
|
|
188004
188013
|
walk(a);
|
|
188014
|
+
if (e.method === "flat" && e.depthExpr)
|
|
188015
|
+
walk(e.depthExpr);
|
|
188005
188016
|
return;
|
|
188006
188017
|
case "literal":
|
|
188007
188018
|
case "unsupported":
|
|
@@ -188087,6 +188098,8 @@ function collectIdentifiers(expr, out) {
|
|
|
188087
188098
|
case "array-method":
|
|
188088
188099
|
collectIdentifiers(expr.object, out);
|
|
188089
188100
|
expr.args.forEach((e) => collectIdentifiers(e, out));
|
|
188101
|
+
if (expr.method === "flat" && expr.depthExpr)
|
|
188102
|
+
collectIdentifiers(expr.depthExpr, out);
|
|
188090
188103
|
return;
|
|
188091
188104
|
case "literal":
|
|
188092
188105
|
case "regex":
|
|
@@ -188144,7 +188157,14 @@ function substituteDestructuredFields(expr, fieldMap, syntheticParam, restName)
|
|
|
188144
188157
|
return { kind: "array-literal", elements: e.elements.map(walk) };
|
|
188145
188158
|
case "array-method":
|
|
188146
188159
|
if (e.method === "flat") {
|
|
188147
|
-
return {
|
|
188160
|
+
return {
|
|
188161
|
+
kind: "array-method",
|
|
188162
|
+
method: "flat",
|
|
188163
|
+
object: walk(e.object),
|
|
188164
|
+
args: [],
|
|
188165
|
+
flatDepth: e.flatDepth,
|
|
188166
|
+
...e.depthExpr ? { depthExpr: walk(e.depthExpr) } : {}
|
|
188167
|
+
};
|
|
188148
188168
|
}
|
|
188149
188169
|
return { kind: "array-method", method: e.method, object: walk(e.object), args: e.args.map(walk) };
|
|
188150
188170
|
case "literal":
|
|
@@ -188241,6 +188261,11 @@ function checkSupport(expr) {
|
|
|
188241
188261
|
if (!argSupport.supported)
|
|
188242
188262
|
return argSupport;
|
|
188243
188263
|
}
|
|
188264
|
+
if (expr.method === "flat" && expr.depthExpr) {
|
|
188265
|
+
const depthSupport = checkSupport(expr.depthExpr);
|
|
188266
|
+
if (!depthSupport.supported)
|
|
188267
|
+
return depthSupport;
|
|
188268
|
+
}
|
|
188244
188269
|
return { supported: true, level: "L2" };
|
|
188245
188270
|
}
|
|
188246
188271
|
case "call": {
|
|
@@ -188340,6 +188365,9 @@ function checkSupport(expr) {
|
|
|
188340
188365
|
const leftSupport = checkSupport(expr.left);
|
|
188341
188366
|
if (!leftSupport.supported)
|
|
188342
188367
|
return leftSupport;
|
|
188368
|
+
if (expr.op === "??" && expr.right.kind === "object-literal" && expr.right.properties.length === 0) {
|
|
188369
|
+
return { supported: true, level: "L4" };
|
|
188370
|
+
}
|
|
188343
188371
|
const rightSupport = checkSupport(expr.right);
|
|
188344
188372
|
if (!rightSupport.supported)
|
|
188345
188373
|
return rightSupport;
|
|
@@ -188534,7 +188562,10 @@ function usesPerPath(name, expr) {
|
|
|
188534
188562
|
case "array-literal":
|
|
188535
188563
|
return sum(e.elements);
|
|
188536
188564
|
case "array-method":
|
|
188537
|
-
|
|
188565
|
+
if (e.method === "flat") {
|
|
188566
|
+
return add(walk(e.object), e.depthExpr ? walk(e.depthExpr) : { min: 0, max: 0 });
|
|
188567
|
+
}
|
|
188568
|
+
return add(walk(e.object), sum(e.args));
|
|
188538
188569
|
case "object-literal":
|
|
188539
188570
|
return sum(e.properties.map((p) => p.value));
|
|
188540
188571
|
case "arrow":
|
|
@@ -188589,7 +188620,14 @@ function inlineBinding(expr, name, value) {
|
|
|
188589
188620
|
return { kind: "array-literal", elements: e.elements.map((el) => walk(el, enclosing)) };
|
|
188590
188621
|
case "array-method":
|
|
188591
188622
|
if (e.method === "flat") {
|
|
188592
|
-
return {
|
|
188623
|
+
return {
|
|
188624
|
+
kind: "array-method",
|
|
188625
|
+
method: "flat",
|
|
188626
|
+
object: walk(e.object, enclosing),
|
|
188627
|
+
args: [],
|
|
188628
|
+
flatDepth: e.flatDepth,
|
|
188629
|
+
...e.depthExpr ? { depthExpr: walk(e.depthExpr, enclosing) } : {}
|
|
188630
|
+
};
|
|
188593
188631
|
}
|
|
188594
188632
|
return { kind: "array-method", method: e.method, object: walk(e.object, enclosing), args: e.args.map((a) => walk(a, enclosing)) };
|
|
188595
188633
|
case "object-literal":
|
|
@@ -188725,6 +188763,9 @@ function stringifyParsedExpr(expr) {
|
|
|
188725
188763
|
return `[${expr.elements.map(stringifyParsedExpr).join(", ")}]`;
|
|
188726
188764
|
case "array-method":
|
|
188727
188765
|
if (expr.method === "flat") {
|
|
188766
|
+
if (expr.depthExpr) {
|
|
188767
|
+
return `${stringifyParsedExpr(expr.object)}.flat(${stringifyParsedExpr(expr.depthExpr)})`;
|
|
188768
|
+
}
|
|
188728
188769
|
const d = expr.flatDepth;
|
|
188729
188770
|
const depthSrc = d === "infinity" ? "Infinity" : String(d);
|
|
188730
188771
|
return `${stringifyParsedExpr(expr.object)}.flat(${d === 1 ? "" : depthSrc})`;
|
|
@@ -189329,6 +189370,15 @@ function createAnalyzerContext(sourceFile, filePath) {
|
|
|
189329
189370
|
checker: null,
|
|
189330
189371
|
componentBodyBlock: null,
|
|
189331
189372
|
getJS(node) {
|
|
189373
|
+
let ownSourceFile;
|
|
189374
|
+
try {
|
|
189375
|
+
ownSourceFile = node.getSourceFile();
|
|
189376
|
+
} catch {
|
|
189377
|
+
ownSourceFile = undefined;
|
|
189378
|
+
}
|
|
189379
|
+
if (ownSourceFile && ownSourceFile !== sourceFile) {
|
|
189380
|
+
return node.getText(ownSourceFile);
|
|
189381
|
+
}
|
|
189332
189382
|
return reconstructWithoutTypes(node, sourceFile, this.typeExcludeRanges);
|
|
189333
189383
|
}
|
|
189334
189384
|
};
|
|
@@ -191305,6 +191355,7 @@ function extractProps(param, ctx) {
|
|
|
191305
191355
|
loc: getSourceLocation(param, ctx.sourceFile, ctx.filePath),
|
|
191306
191356
|
hasIgnoreDirective: ignored
|
|
191307
191357
|
};
|
|
191358
|
+
const memberTypes = param.type ? collectMemberTypes(param.type, ctx) : null;
|
|
191308
191359
|
for (const element of param.name.elements) {
|
|
191309
191360
|
if (import_typescript8.default.isBindingElement(element) && import_typescript8.default.isIdentifier(element.name)) {
|
|
191310
191361
|
const localName = element.name.text;
|
|
@@ -191313,10 +191364,12 @@ function extractProps(param, ctx) {
|
|
|
191313
191364
|
ctx.restPropsName = localName;
|
|
191314
191365
|
continue;
|
|
191315
191366
|
}
|
|
191367
|
+
const sourcePropName = element.propertyName && import_typescript8.default.isIdentifier(element.propertyName) ? element.propertyName.text : localName;
|
|
191368
|
+
const resolvedType = memberTypes?.get(sourcePropName) ?? { kind: "unknown", raw: "unknown" };
|
|
191316
191369
|
const defaultContainsArrow = element.initializer ? nodeContainsArrow(element.initializer) : false;
|
|
191317
191370
|
ctx.propsParams.push({
|
|
191318
191371
|
name: localName,
|
|
191319
|
-
type:
|
|
191372
|
+
type: resolvedType,
|
|
191320
191373
|
optional: !!element.initializer,
|
|
191321
191374
|
defaultValue,
|
|
191322
191375
|
defaultContainsArrow: defaultContainsArrow || undefined
|
|
@@ -191377,6 +191430,37 @@ function collectKeysFromMembers(members, ctx) {
|
|
|
191377
191430
|
}
|
|
191378
191431
|
return keys;
|
|
191379
191432
|
}
|
|
191433
|
+
function collectMemberTypes(typeNode, ctx) {
|
|
191434
|
+
const isResolvablePrimitive = (info) => info.kind === "primitive" && (info.primitive === "string" || info.primitive === "number" || info.primitive === "boolean");
|
|
191435
|
+
const fromMembers = (members) => {
|
|
191436
|
+
const map = new Map;
|
|
191437
|
+
for (const member of members) {
|
|
191438
|
+
if (import_typescript8.default.isPropertySignature(member) && member.name && member.type && !member.questionToken) {
|
|
191439
|
+
const info = typeNodeToTypeInfo(member.type, ctx.sourceFile);
|
|
191440
|
+
if (info && isResolvablePrimitive(info)) {
|
|
191441
|
+
map.set(member.name.getText(ctx.sourceFile), info);
|
|
191442
|
+
}
|
|
191443
|
+
}
|
|
191444
|
+
}
|
|
191445
|
+
return map;
|
|
191446
|
+
};
|
|
191447
|
+
if (import_typescript8.default.isTypeLiteralNode(typeNode)) {
|
|
191448
|
+
return fromMembers(typeNode.members);
|
|
191449
|
+
}
|
|
191450
|
+
if (import_typescript8.default.isTypeReferenceNode(typeNode)) {
|
|
191451
|
+
const typeName = typeNode.typeName.getText(ctx.sourceFile);
|
|
191452
|
+
const typeDecl = findTypeDeclaration(typeName, ctx.sourceFile);
|
|
191453
|
+
if (!typeDecl)
|
|
191454
|
+
return null;
|
|
191455
|
+
if (import_typescript8.default.isInterfaceDeclaration(typeDecl)) {
|
|
191456
|
+
return fromMembers(typeDecl.members);
|
|
191457
|
+
}
|
|
191458
|
+
if (import_typescript8.default.isTypeAliasDeclaration(typeDecl) && import_typescript8.default.isTypeLiteralNode(typeDecl.type)) {
|
|
191459
|
+
return fromMembers(typeDecl.type.members);
|
|
191460
|
+
}
|
|
191461
|
+
}
|
|
191462
|
+
return null;
|
|
191463
|
+
}
|
|
191380
191464
|
function extractPropsFromType(typeNode, ctx) {
|
|
191381
191465
|
if (import_typescript8.default.isTypeLiteralNode(typeNode)) {
|
|
191382
191466
|
extractPropsFromTypeMembers(typeNode.members, ctx);
|
|
@@ -192033,6 +192117,38 @@ var AttrValueOf = {
|
|
|
192033
192117
|
}
|
|
192034
192118
|
};
|
|
192035
192119
|
|
|
192120
|
+
// ../jsx/src/module-exports.ts
|
|
192121
|
+
function formatParamWithType(p) {
|
|
192122
|
+
const rest = p.isRest ? "..." : "";
|
|
192123
|
+
const optional = p.optional ? "?" : "";
|
|
192124
|
+
const typeAnnotation = p.type?.raw && p.type.raw !== "unknown" ? `: ${p.type.raw}` : "";
|
|
192125
|
+
const defaultPart = p.defaultValue !== undefined ? ` = ${p.defaultValue}` : "";
|
|
192126
|
+
return `${rest}${p.name}${optional}${typeAnnotation}${defaultPart}`;
|
|
192127
|
+
}
|
|
192128
|
+
function findReachableNames(primaryRefs, declarations) {
|
|
192129
|
+
const allNames = new Set(declarations.map((d) => d.name));
|
|
192130
|
+
const bodyMap = new Map(declarations.map((d) => [d.name, d.body]));
|
|
192131
|
+
const reachable = new Set;
|
|
192132
|
+
const queue = [];
|
|
192133
|
+
for (const name of allNames) {
|
|
192134
|
+
if (new RegExp(`\\b${name}\\b`).test(primaryRefs)) {
|
|
192135
|
+
reachable.add(name);
|
|
192136
|
+
queue.push(name);
|
|
192137
|
+
}
|
|
192138
|
+
}
|
|
192139
|
+
while (queue.length > 0) {
|
|
192140
|
+
const current = queue.shift();
|
|
192141
|
+
const body = bodyMap.get(current) || "";
|
|
192142
|
+
for (const name of allNames) {
|
|
192143
|
+
if (!reachable.has(name) && new RegExp(`\\b${name}\\b`).test(body)) {
|
|
192144
|
+
reachable.add(name);
|
|
192145
|
+
queue.push(name);
|
|
192146
|
+
}
|
|
192147
|
+
}
|
|
192148
|
+
}
|
|
192149
|
+
return reachable;
|
|
192150
|
+
}
|
|
192151
|
+
|
|
192036
192152
|
// ../jsx/src/builtins.ts
|
|
192037
192153
|
var CLIENT_BUILTIN_SOURCE = "@barefootjs/client";
|
|
192038
192154
|
function isClientBuiltinName(name) {
|
|
@@ -193140,6 +193256,7 @@ function transformExpression(node, ctx) {
|
|
|
193140
193256
|
return transformExpressionInner(expr, ctx, node, isClientOnly);
|
|
193141
193257
|
}
|
|
193142
193258
|
function transformExpressionInner(expr, ctx, node, isClientOnly) {
|
|
193259
|
+
expr = tryDesugarInterleaveTaggedTemplate(expr, ctx);
|
|
193143
193260
|
checkBareSignalOrMemoIdentifier(expr, ctx);
|
|
193144
193261
|
if (import_typescript11.default.isIdentifier(expr)) {
|
|
193145
193262
|
const jsxNode = ctx.analyzer.jsxConstants.get(expr.text);
|
|
@@ -193681,11 +193798,10 @@ function isIteratorShapeCall(node) {
|
|
|
193681
193798
|
return { array: node.expression.expression, shape: name };
|
|
193682
193799
|
}
|
|
193683
193800
|
function extractSortComparator(callback, _method, ctx) {
|
|
193684
|
-
const
|
|
193685
|
-
|
|
193686
|
-
|
|
193687
|
-
|
|
193688
|
-
unsupportedReason: `Sort comparator '${raw}' is not a supported shape. Accepted:
|
|
193801
|
+
const outerRaw = ctx.getJS(callback);
|
|
193802
|
+
const unsupported = () => ({
|
|
193803
|
+
result: null,
|
|
193804
|
+
unsupportedReason: `Sort comparator '${outerRaw}' is not a supported shape. Accepted:
|
|
193689
193805
|
` + ` (a, b) => a - b
|
|
193690
193806
|
` + ` (a, b) => a.field - b.field
|
|
193691
193807
|
` + ` (a, b) => a.localeCompare(b)
|
|
@@ -193693,15 +193809,25 @@ function extractSortComparator(callback, _method, ctx) {
|
|
|
193693
193809
|
` + ` (a, b) => a.field > b.field ? 1 : a.field < b.field ? -1 : 0
|
|
193694
193810
|
` + ` any of the above '||'-chained for multi-key tie-breaks
|
|
193695
193811
|
` + `(reverse the operands for descending order).`
|
|
193696
|
-
|
|
193697
|
-
|
|
193698
|
-
if (
|
|
193812
|
+
});
|
|
193813
|
+
let resolvedNode = callback;
|
|
193814
|
+
if (import_typescript11.default.isIdentifier(callback)) {
|
|
193815
|
+
const resolved = resolveSortComparatorIdentifier(callback.text, ctx);
|
|
193816
|
+
if (!resolved) {
|
|
193817
|
+
return {
|
|
193818
|
+
result: null,
|
|
193819
|
+
unsupportedReason: `Sort comparator '${outerRaw}' could not be resolved to a local function — ` + `declare it in the same file or inline it.`
|
|
193820
|
+
};
|
|
193821
|
+
}
|
|
193822
|
+
resolvedNode = resolved;
|
|
193823
|
+
}
|
|
193824
|
+
if (!import_typescript11.default.isArrowFunction(resolvedNode) && !import_typescript11.default.isFunctionExpression(resolvedNode)) {
|
|
193699
193825
|
return {
|
|
193700
193826
|
result: null,
|
|
193701
193827
|
unsupportedReason: "Sort comparator must be an arrow function or function expression"
|
|
193702
193828
|
};
|
|
193703
193829
|
}
|
|
193704
|
-
const arrow = tsNodeToParsedExpr(
|
|
193830
|
+
const arrow = tsNodeToParsedExpr(resolvedNode);
|
|
193705
193831
|
if (arrow.kind !== "arrow" || arrow.params.length !== 2)
|
|
193706
193832
|
return unsupported();
|
|
193707
193833
|
if (sortComparatorFromArrow(arrow) === null)
|
|
@@ -193715,6 +193841,21 @@ function extractSortComparator(callback, _method, ctx) {
|
|
|
193715
193841
|
}
|
|
193716
193842
|
};
|
|
193717
193843
|
}
|
|
193844
|
+
function resolveSortComparatorIdentifier(name, ctx) {
|
|
193845
|
+
const constInfo = findLocalConst(name, ctx);
|
|
193846
|
+
const fnInfo = findLocalFunction(name, ctx);
|
|
193847
|
+
if (constInfo && fnInfo)
|
|
193848
|
+
return null;
|
|
193849
|
+
if (constInfo) {
|
|
193850
|
+
const ast = parseConstInitializer(constInfo);
|
|
193851
|
+
return ast && (import_typescript11.default.isArrowFunction(ast) || import_typescript11.default.isFunctionExpression(ast)) ? ast : null;
|
|
193852
|
+
}
|
|
193853
|
+
if (fnInfo) {
|
|
193854
|
+
const ast = parseFunctionInfoAsExpr(fnInfo);
|
|
193855
|
+
return ast && (import_typescript11.default.isArrowFunction(ast) || import_typescript11.default.isFunctionExpression(ast)) ? ast : null;
|
|
193856
|
+
}
|
|
193857
|
+
return null;
|
|
193858
|
+
}
|
|
193718
193859
|
function extractFilterPredicate(callback, ctx) {
|
|
193719
193860
|
if (!import_typescript11.default.isArrowFunction(callback))
|
|
193720
193861
|
return { result: null };
|
|
@@ -193787,7 +193928,7 @@ function extractLoopParamBindings(pattern) {
|
|
|
193787
193928
|
const appendDotAccess = (prefix, key) => {
|
|
193788
193929
|
return isIdent(key) ? `${prefix}.${key}` : `${prefix}[${JSON.stringify(key)}]`;
|
|
193789
193930
|
};
|
|
193790
|
-
const walk = (p, prefix) => {
|
|
193931
|
+
const walk = (p, prefix, segments) => {
|
|
193791
193932
|
if (unsupported)
|
|
193792
193933
|
return;
|
|
193793
193934
|
if (import_typescript11.default.isArrayBindingPattern(p)) {
|
|
@@ -193804,15 +193945,17 @@ function extractLoopParamBindings(pattern) {
|
|
|
193804
193945
|
bindings.push({
|
|
193805
193946
|
name: el.name.text,
|
|
193806
193947
|
path: prefix,
|
|
193807
|
-
rest: { kind: "array", from: index }
|
|
193948
|
+
rest: { kind: "array", from: index },
|
|
193949
|
+
segments
|
|
193808
193950
|
});
|
|
193809
193951
|
return;
|
|
193810
193952
|
}
|
|
193811
193953
|
const path = `${prefix}[${index}]`;
|
|
193954
|
+
const nextSegments = [...segments, { kind: "index", index }];
|
|
193812
193955
|
if (import_typescript11.default.isIdentifier(el.name)) {
|
|
193813
|
-
bindings.push({ name: el.name.text, path });
|
|
193956
|
+
bindings.push({ name: el.name.text, path, segments: nextSegments });
|
|
193814
193957
|
} else {
|
|
193815
|
-
walk(el.name, path);
|
|
193958
|
+
walk(el.name, path, nextSegments);
|
|
193816
193959
|
}
|
|
193817
193960
|
}
|
|
193818
193961
|
return;
|
|
@@ -193829,7 +193972,8 @@ function extractLoopParamBindings(pattern) {
|
|
|
193829
193972
|
bindings.push({
|
|
193830
193973
|
name: el.name.text,
|
|
193831
193974
|
path: prefix,
|
|
193832
|
-
rest: { kind: "object", exclude: collectedKeys }
|
|
193975
|
+
rest: { kind: "object", exclude: collectedKeys },
|
|
193976
|
+
segments
|
|
193833
193977
|
});
|
|
193834
193978
|
return;
|
|
193835
193979
|
}
|
|
@@ -193852,17 +193996,19 @@ function extractLoopParamBindings(pattern) {
|
|
|
193852
193996
|
unsupported = true;
|
|
193853
193997
|
return;
|
|
193854
193998
|
}
|
|
193855
|
-
|
|
193999
|
+
const keyIsIdent = isIdent(keyText);
|
|
194000
|
+
collectedKeys.push({ key: keyText, isIdent: keyIsIdent });
|
|
193856
194001
|
const path = appendDotAccess(prefix, keyText);
|
|
194002
|
+
const nextSegments = [...segments, { kind: "field", key: keyText, isIdent: keyIsIdent }];
|
|
193857
194003
|
if (import_typescript11.default.isIdentifier(el.name)) {
|
|
193858
|
-
bindings.push({ name: el.name.text, path });
|
|
194004
|
+
bindings.push({ name: el.name.text, path, segments: nextSegments });
|
|
193859
194005
|
} else {
|
|
193860
|
-
walk(el.name, path);
|
|
194006
|
+
walk(el.name, path, nextSegments);
|
|
193861
194007
|
}
|
|
193862
194008
|
}
|
|
193863
194009
|
};
|
|
193864
194010
|
if (import_typescript11.default.isArrayBindingPattern(pattern) || import_typescript11.default.isObjectBindingPattern(pattern)) {
|
|
193865
|
-
walk(pattern, "");
|
|
194011
|
+
walk(pattern, "", []);
|
|
193866
194012
|
if (unsupported)
|
|
193867
194013
|
return { unsupported: true };
|
|
193868
194014
|
return bindings;
|
|
@@ -194631,6 +194777,7 @@ function getAttributeValue(attr, ctx) {
|
|
|
194631
194777
|
expr = branchInit;
|
|
194632
194778
|
}
|
|
194633
194779
|
}
|
|
194780
|
+
expr = tryDesugarInterleaveTaggedTemplate(expr, ctx);
|
|
194634
194781
|
if (import_typescript11.default.isAwaitExpression(expr)) {
|
|
194635
194782
|
ctx.analyzer.errors.push(createError(ErrorCodes.STAGE_AWAIT_IN_TEMPLATE, getSourceLocation(expr, ctx.sourceFile, ctx.filePath)));
|
|
194636
194783
|
return AttrValueOf.expression("undefined");
|
|
@@ -194766,6 +194913,14 @@ function findLocalConst(name, ctx) {
|
|
|
194766
194913
|
const pool = fnScoped.length > 0 ? fnScoped : matches;
|
|
194767
194914
|
return pool[pool.length - 1];
|
|
194768
194915
|
}
|
|
194916
|
+
function findLocalFunction(name, ctx) {
|
|
194917
|
+
const matches = ctx.analyzer.localFunctions.filter((f) => f.name === name);
|
|
194918
|
+
if (matches.length === 0)
|
|
194919
|
+
return;
|
|
194920
|
+
const fnScoped = matches.filter((f) => !f.isModule);
|
|
194921
|
+
const pool = fnScoped.length > 0 ? fnScoped : matches;
|
|
194922
|
+
return pool[pool.length - 1];
|
|
194923
|
+
}
|
|
194769
194924
|
function isDynamicTagLocal(name, ctx) {
|
|
194770
194925
|
if (!hasDynamicTagBinding(name, ctx.sourceFile))
|
|
194771
194926
|
return false;
|
|
@@ -194860,6 +195015,147 @@ function parseConstInitializerImpl(c) {
|
|
|
194860
195015
|
function astText(node) {
|
|
194861
195016
|
return node.getText(node.getSourceFile());
|
|
194862
195017
|
}
|
|
195018
|
+
var functionInfoExprCache = new WeakMap;
|
|
195019
|
+
function parseFunctionInfoAsExpr(fn) {
|
|
195020
|
+
const cached = functionInfoExprCache.get(fn);
|
|
195021
|
+
if (cached !== undefined)
|
|
195022
|
+
return cached;
|
|
195023
|
+
const result = parseFunctionInfoAsExprImpl(fn);
|
|
195024
|
+
functionInfoExprCache.set(fn, result);
|
|
195025
|
+
return result;
|
|
195026
|
+
}
|
|
195027
|
+
function parseFunctionInfoAsExprImpl(fn) {
|
|
195028
|
+
if (!fn.body)
|
|
195029
|
+
return null;
|
|
195030
|
+
const params = fn.typedParams !== undefined ? fn.typedParams : fn.params.map(formatParamWithType).join(", ");
|
|
195031
|
+
const body = fn.typedBody ?? fn.body;
|
|
195032
|
+
const wrapped = `const __bf_resolve_fn__ = function(${params}) ${body}`;
|
|
195033
|
+
const sf = import_typescript11.default.createSourceFile("__bf_resolve_fn.ts", wrapped, import_typescript11.default.ScriptTarget.Latest, true, import_typescript11.default.ScriptKind.TS);
|
|
195034
|
+
const stmt = sf.statements[0];
|
|
195035
|
+
if (!stmt || !import_typescript11.default.isVariableStatement(stmt))
|
|
195036
|
+
return null;
|
|
195037
|
+
const decl = stmt.declarationList.declarations[0];
|
|
195038
|
+
if (!decl?.initializer)
|
|
195039
|
+
return null;
|
|
195040
|
+
return decl.initializer;
|
|
195041
|
+
}
|
|
195042
|
+
function tryDesugarInterleaveTaggedTemplate(expr, ctx) {
|
|
195043
|
+
if (!import_typescript11.default.isTaggedTemplateExpression(expr))
|
|
195044
|
+
return expr;
|
|
195045
|
+
if (!import_typescript11.default.isIdentifier(expr.tag))
|
|
195046
|
+
return expr;
|
|
195047
|
+
const resolvedTag = resolveInterleaveTagIdentifier(expr.tag.text, ctx);
|
|
195048
|
+
if (!resolvedTag)
|
|
195049
|
+
return expr;
|
|
195050
|
+
if (!isInterleaveTagFunction(resolvedTag))
|
|
195051
|
+
return expr;
|
|
195052
|
+
const rewritten = buildUntaggedTemplateLiteral(expr, ctx);
|
|
195053
|
+
return rewritten ?? expr;
|
|
195054
|
+
}
|
|
195055
|
+
function resolveInterleaveTagIdentifier(name, ctx) {
|
|
195056
|
+
const constInfo = findLocalConst(name, ctx);
|
|
195057
|
+
const fnInfo = findLocalFunction(name, ctx);
|
|
195058
|
+
if (constInfo && fnInfo)
|
|
195059
|
+
return null;
|
|
195060
|
+
if (constInfo) {
|
|
195061
|
+
const ast = parseConstInitializer(constInfo);
|
|
195062
|
+
return ast && (import_typescript11.default.isArrowFunction(ast) || import_typescript11.default.isFunctionExpression(ast)) ? ast : null;
|
|
195063
|
+
}
|
|
195064
|
+
if (fnInfo) {
|
|
195065
|
+
const ast = parseFunctionInfoAsExpr(fnInfo);
|
|
195066
|
+
return ast && (import_typescript11.default.isArrowFunction(ast) || import_typescript11.default.isFunctionExpression(ast)) ? ast : null;
|
|
195067
|
+
}
|
|
195068
|
+
return null;
|
|
195069
|
+
}
|
|
195070
|
+
function isInterleaveTagFunction(fn) {
|
|
195071
|
+
if (!import_typescript11.default.isArrowFunction(fn) && !import_typescript11.default.isFunctionExpression(fn))
|
|
195072
|
+
return false;
|
|
195073
|
+
if (fn.parameters.length !== 2)
|
|
195074
|
+
return false;
|
|
195075
|
+
const [partsParam, argsParam] = fn.parameters;
|
|
195076
|
+
if (!import_typescript11.default.isIdentifier(partsParam.name) || partsParam.dotDotDotToken)
|
|
195077
|
+
return false;
|
|
195078
|
+
if (!import_typescript11.default.isIdentifier(argsParam.name) || !argsParam.dotDotDotToken)
|
|
195079
|
+
return false;
|
|
195080
|
+
const parsed = tsNodeToParsedExpr(fn);
|
|
195081
|
+
if (parsed.kind !== "arrow")
|
|
195082
|
+
return false;
|
|
195083
|
+
return isInterleaveReduceCall(parsed.body, partsParam.name.text, argsParam.name.text);
|
|
195084
|
+
}
|
|
195085
|
+
function isInterleaveReduceCall(body, partsName, argsName) {
|
|
195086
|
+
if (body.kind !== "call" || body.args.length !== 2)
|
|
195087
|
+
return false;
|
|
195088
|
+
const { callee, args } = body;
|
|
195089
|
+
if (callee.kind !== "member" || callee.computed || callee.property !== "reduce")
|
|
195090
|
+
return false;
|
|
195091
|
+
if (callee.object.kind !== "identifier" || callee.object.name !== partsName)
|
|
195092
|
+
return false;
|
|
195093
|
+
const [callback, init] = args;
|
|
195094
|
+
if (init.kind !== "literal" || init.literalType !== "string" || init.value !== "")
|
|
195095
|
+
return false;
|
|
195096
|
+
if (callback.kind !== "arrow" || callback.params.length !== 3)
|
|
195097
|
+
return false;
|
|
195098
|
+
const [acc, p, i2] = callback.params;
|
|
195099
|
+
return isInterleaveReduceCallbackBody(callback.body, acc, p, i2, argsName);
|
|
195100
|
+
}
|
|
195101
|
+
function isInterleaveReduceCallbackBody(body, acc, p, i2, argsName) {
|
|
195102
|
+
if (body.kind !== "binary" || body.op !== "+")
|
|
195103
|
+
return false;
|
|
195104
|
+
const { left, right } = body;
|
|
195105
|
+
if (left.kind !== "binary" || left.op !== "+")
|
|
195106
|
+
return false;
|
|
195107
|
+
if (left.left.kind !== "identifier" || left.left.name !== acc)
|
|
195108
|
+
return false;
|
|
195109
|
+
if (left.right.kind !== "identifier" || left.right.name !== p)
|
|
195110
|
+
return false;
|
|
195111
|
+
return isInterleaveSpanExpr(right, i2, argsName);
|
|
195112
|
+
}
|
|
195113
|
+
function isInterleaveSpanExpr(expr, i2, argsName) {
|
|
195114
|
+
let inner = expr;
|
|
195115
|
+
if (inner.kind === "call" && inner.args.length === 1 && inner.callee.kind === "identifier" && inner.callee.name === "String") {
|
|
195116
|
+
inner = inner.args[0];
|
|
195117
|
+
}
|
|
195118
|
+
if (inner.kind !== "logical" || inner.op !== "??")
|
|
195119
|
+
return false;
|
|
195120
|
+
if (inner.right.kind !== "literal" || inner.right.literalType !== "string" || inner.right.value !== "") {
|
|
195121
|
+
return false;
|
|
195122
|
+
}
|
|
195123
|
+
const idx = inner.left;
|
|
195124
|
+
if (idx.kind !== "index-access")
|
|
195125
|
+
return false;
|
|
195126
|
+
if (idx.object.kind !== "identifier" || idx.object.name !== argsName)
|
|
195127
|
+
return false;
|
|
195128
|
+
if (idx.index.kind !== "identifier" || idx.index.name !== i2)
|
|
195129
|
+
return false;
|
|
195130
|
+
return true;
|
|
195131
|
+
}
|
|
195132
|
+
function buildUntaggedTemplateLiteral(node, ctx) {
|
|
195133
|
+
const template = node.template;
|
|
195134
|
+
let text;
|
|
195135
|
+
if (import_typescript11.default.isNoSubstitutionTemplateLiteral(template)) {
|
|
195136
|
+
text = "`" + (template.rawText ?? template.text) + "`";
|
|
195137
|
+
} else {
|
|
195138
|
+
let body = template.head.rawText ?? template.head.text;
|
|
195139
|
+
for (const span of template.templateSpans) {
|
|
195140
|
+
const spanText = ctx.getJS(span.expression);
|
|
195141
|
+
body += "${(" + spanText + ") ?? ''}";
|
|
195142
|
+
body += span.literal.rawText ?? span.literal.text;
|
|
195143
|
+
}
|
|
195144
|
+
text = "`" + body + "`";
|
|
195145
|
+
}
|
|
195146
|
+
const wrapped = `const __bf_resolve_tagged__ = (${text})`;
|
|
195147
|
+
const sf = import_typescript11.default.createSourceFile("__bf_resolve_tagged.tsx", wrapped, import_typescript11.default.ScriptTarget.Latest, true, import_typescript11.default.ScriptKind.TSX);
|
|
195148
|
+
const stmt = sf.statements[0];
|
|
195149
|
+
if (!stmt || !import_typescript11.default.isVariableStatement(stmt))
|
|
195150
|
+
return null;
|
|
195151
|
+
const decl = stmt.declarationList.declarations[0];
|
|
195152
|
+
if (!decl?.initializer)
|
|
195153
|
+
return null;
|
|
195154
|
+
const result = import_typescript11.default.isParenthesizedExpression(decl.initializer) ? decl.initializer.expression : decl.initializer;
|
|
195155
|
+
if (!import_typescript11.default.isTemplateExpression(result) && !import_typescript11.default.isNoSubstitutionTemplateLiteral(result))
|
|
195156
|
+
return null;
|
|
195157
|
+
return result;
|
|
195158
|
+
}
|
|
194863
195159
|
function parseTernary(expr, ctx) {
|
|
194864
195160
|
const whenTrueValue = getStringValue(expr.whenTrue);
|
|
194865
195161
|
const whenFalseValue = getStringValue(expr.whenFalse);
|
|
@@ -195335,6 +195631,18 @@ var import_typescript12 = __toESM(require_typescript(), 1);
|
|
|
195335
195631
|
|
|
195336
195632
|
// ../jsx/src/relocate.ts
|
|
195337
195633
|
var import_typescript13 = __toESM(require_typescript(), 1);
|
|
195634
|
+
|
|
195635
|
+
// ../jsx/src/lowering-registry.ts
|
|
195636
|
+
var plugins = [];
|
|
195637
|
+
function registerLoweringPlugin(plugin) {
|
|
195638
|
+
const existing = plugins.findIndex((p) => p.name === plugin.name);
|
|
195639
|
+
if (existing >= 0)
|
|
195640
|
+
plugins[existing] = plugin;
|
|
195641
|
+
else
|
|
195642
|
+
plugins.push(plugin);
|
|
195643
|
+
}
|
|
195644
|
+
|
|
195645
|
+
// ../jsx/src/relocate.ts
|
|
195338
195646
|
var REGISTRY_SAFE_BINDING_KINDS = new Set([
|
|
195339
195647
|
"global",
|
|
195340
195648
|
"module-import",
|
|
@@ -195464,6 +195772,9 @@ var JS_BUILTINS = new Set([
|
|
|
195464
195772
|
var ENV_SIGNAL_CLIENT_FACTORY = {
|
|
195465
195773
|
search: "createSearchParams"
|
|
195466
195774
|
};
|
|
195775
|
+
var ENV_SIGNAL_READERS = new Map([
|
|
195776
|
+
["search", { key: "search", canonicalName: "searchParams", methods: new Set(["get"]) }]
|
|
195777
|
+
]);
|
|
195467
195778
|
function queryHrefLocalNames(metadata) {
|
|
195468
195779
|
const names = new Set;
|
|
195469
195780
|
for (const imp of metadata.imports) {
|
|
@@ -195629,38 +195940,6 @@ class SourceMapGenerator {
|
|
|
195629
195940
|
}
|
|
195630
195941
|
}
|
|
195631
195942
|
|
|
195632
|
-
// ../jsx/src/module-exports.ts
|
|
195633
|
-
function formatParamWithType(p) {
|
|
195634
|
-
const rest = p.isRest ? "..." : "";
|
|
195635
|
-
const optional = p.optional ? "?" : "";
|
|
195636
|
-
const typeAnnotation = p.type?.raw && p.type.raw !== "unknown" ? `: ${p.type.raw}` : "";
|
|
195637
|
-
const defaultPart = p.defaultValue !== undefined ? ` = ${p.defaultValue}` : "";
|
|
195638
|
-
return `${rest}${p.name}${optional}${typeAnnotation}${defaultPart}`;
|
|
195639
|
-
}
|
|
195640
|
-
function findReachableNames(primaryRefs, declarations) {
|
|
195641
|
-
const allNames = new Set(declarations.map((d) => d.name));
|
|
195642
|
-
const bodyMap = new Map(declarations.map((d) => [d.name, d.body]));
|
|
195643
|
-
const reachable = new Set;
|
|
195644
|
-
const queue = [];
|
|
195645
|
-
for (const name of allNames) {
|
|
195646
|
-
if (new RegExp(`\\b${name}\\b`).test(primaryRefs)) {
|
|
195647
|
-
reachable.add(name);
|
|
195648
|
-
queue.push(name);
|
|
195649
|
-
}
|
|
195650
|
-
}
|
|
195651
|
-
while (queue.length > 0) {
|
|
195652
|
-
const current = queue.shift();
|
|
195653
|
-
const body = bodyMap.get(current) || "";
|
|
195654
|
-
for (const name of allNames) {
|
|
195655
|
-
if (!reachable.has(name) && new RegExp(`\\b${name}\\b`).test(body)) {
|
|
195656
|
-
reachable.add(name);
|
|
195657
|
-
queue.push(name);
|
|
195658
|
-
}
|
|
195659
|
-
}
|
|
195660
|
-
}
|
|
195661
|
-
return reachable;
|
|
195662
|
-
}
|
|
195663
|
-
|
|
195664
195943
|
// ../jsx/src/preprocess-inline-jsx-callbacks.ts
|
|
195665
195944
|
var import_typescript15 = __toESM(require_typescript(), 1);
|
|
195666
195945
|
|
|
@@ -195668,9 +195947,12 @@ var import_typescript15 = __toESM(require_typescript(), 1);
|
|
|
195668
195947
|
var import_typescript16 = __toESM(require_typescript(), 1);
|
|
195669
195948
|
var UNRESOLVED = Symbol("unresolved");
|
|
195670
195949
|
var NO_RETURN = Symbol("no-return");
|
|
195950
|
+
|
|
195951
|
+
// ../jsx/src/augment-inherited-props.ts
|
|
195952
|
+
var import_typescript17 = __toESM(require_typescript(), 1);
|
|
195671
195953
|
// ../jsx/src/shared-program.ts
|
|
195672
195954
|
init_path();
|
|
195673
|
-
var
|
|
195955
|
+
var import_typescript18 = __toESM(require_typescript(), 1);
|
|
195674
195956
|
// ../jsx/src/adapters/interface.ts
|
|
195675
195957
|
class BaseAdapter {
|
|
195676
195958
|
renderChildren(children) {
|
|
@@ -196156,15 +196438,6 @@ function isOmitBranch(node) {
|
|
|
196156
196438
|
}
|
|
196157
196439
|
return false;
|
|
196158
196440
|
}
|
|
196159
|
-
// ../jsx/src/lowering-registry.ts
|
|
196160
|
-
var plugins = [];
|
|
196161
|
-
function registerLoweringPlugin(plugin) {
|
|
196162
|
-
const existing = plugins.findIndex((p) => p.name === plugin.name);
|
|
196163
|
-
if (existing >= 0)
|
|
196164
|
-
plugins[existing] = plugin;
|
|
196165
|
-
else
|
|
196166
|
-
plugins.push(plugin);
|
|
196167
|
-
}
|
|
196168
196441
|
// ../jsx/src/builtin-lowering-plugins.ts
|
|
196169
196442
|
var queryHrefPlugin = {
|
|
196170
196443
|
name: "queryHref",
|
|
@@ -196184,9 +196457,9 @@ function registerBuiltinLoweringPlugins() {
|
|
|
196184
196457
|
registerLoweringPlugin(plugin);
|
|
196185
196458
|
}
|
|
196186
196459
|
// ../jsx/src/combine-client-js.ts
|
|
196187
|
-
var import_typescript18 = __toESM(require_typescript(), 1);
|
|
196188
|
-
// ../jsx/src/debug.ts
|
|
196189
196460
|
var import_typescript19 = __toESM(require_typescript(), 1);
|
|
196461
|
+
// ../jsx/src/debug.ts
|
|
196462
|
+
var import_typescript20 = __toESM(require_typescript(), 1);
|
|
196190
196463
|
function escapeForIdBoundary(name) {
|
|
196191
196464
|
return name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
196192
196465
|
}
|
|
@@ -196278,8 +196551,6 @@ function resolveSetters(handler, setterToSignal, fnSetters) {
|
|
|
196278
196551
|
return refs;
|
|
196279
196552
|
}
|
|
196280
196553
|
// ../jsx/src/profiler.ts
|
|
196281
|
-
var import_typescript20 = __toESM(require_typescript(), 1);
|
|
196282
|
-
// ../jsx/src/augment-inherited-props.ts
|
|
196283
196554
|
var import_typescript21 = __toESM(require_typescript(), 1);
|
|
196284
196555
|
|
|
196285
196556
|
// ../jsx/src/index.ts
|
|
@@ -196378,6 +196649,7 @@ function irNodeToTestNode(node, constantMap, metadata) {
|
|
|
196378
196649
|
const cmap = constantMap ?? new Map;
|
|
196379
196650
|
const setterToSignal = new Map;
|
|
196380
196651
|
const fnSetters = new Map;
|
|
196652
|
+
const defaults = new Map;
|
|
196381
196653
|
if (metadata) {
|
|
196382
196654
|
for (const s of metadata.signals) {
|
|
196383
196655
|
if (s.setter)
|
|
@@ -196386,8 +196658,24 @@ function irNodeToTestNode(node, constantMap, metadata) {
|
|
|
196386
196658
|
for (const [k, v] of buildLocalFunctionSetterMap(metadata, setterToSignal)) {
|
|
196387
196659
|
fnSetters.set(k, v);
|
|
196388
196660
|
}
|
|
196661
|
+
for (const p of metadata.propsParams) {
|
|
196662
|
+
const lit = p.defaultValue !== undefined ? parseLiteralDefault(p.defaultValue) : null;
|
|
196663
|
+
if (lit !== null)
|
|
196664
|
+
defaults.set(p.name, lit);
|
|
196665
|
+
}
|
|
196389
196666
|
}
|
|
196390
|
-
return convert(node, { cmap, setterToSignal, fnSetters });
|
|
196667
|
+
return convert(node, { cmap, setterToSignal, fnSetters, defaults });
|
|
196668
|
+
}
|
|
196669
|
+
function parseLiteralDefault(js) {
|
|
196670
|
+
const v = js.trim();
|
|
196671
|
+
if (v.startsWith("'") && v.endsWith("'") && !v.slice(1, -1).includes("'") || v.startsWith('"') && v.endsWith('"') && !v.slice(1, -1).includes('"')) {
|
|
196672
|
+
return v.slice(1, -1);
|
|
196673
|
+
}
|
|
196674
|
+
if (/^-?\d+(\.\d+)?$/.test(v))
|
|
196675
|
+
return v;
|
|
196676
|
+
if (v === "true" || v === "false")
|
|
196677
|
+
return v;
|
|
196678
|
+
return null;
|
|
196391
196679
|
}
|
|
196392
196680
|
function convert(node, ctx) {
|
|
196393
196681
|
switch (node.type) {
|
|
@@ -196396,7 +196684,7 @@ function convert(node, ctx) {
|
|
|
196396
196684
|
case "text":
|
|
196397
196685
|
return convertText(node);
|
|
196398
196686
|
case "expression":
|
|
196399
|
-
return convertExpression(node);
|
|
196687
|
+
return convertExpression(node, ctx);
|
|
196400
196688
|
case "conditional":
|
|
196401
196689
|
return convertConditional(node, ctx);
|
|
196402
196690
|
case "loop":
|
|
@@ -196426,19 +196714,9 @@ function convertElement(node, ctx) {
|
|
|
196426
196714
|
let dataState = null;
|
|
196427
196715
|
let classes = [];
|
|
196428
196716
|
for (const attr of node.attrs) {
|
|
196429
|
-
const value = resolveAttrValue(attr);
|
|
196717
|
+
const value = resolveAttrValue(attr, ctx);
|
|
196430
196718
|
if (attr.name === "className" || attr.name === "class") {
|
|
196431
|
-
|
|
196432
|
-
const isDynamic = attr.value.kind === "expression" || attr.value.kind === "template" || attr.value.kind === "spread";
|
|
196433
|
-
if (isDynamic && ctx.cmap.size > 0) {
|
|
196434
|
-
const resolved = resolveClassValue(value, ctx.cmap);
|
|
196435
|
-
if (resolved !== null) {
|
|
196436
|
-
classes = resolved.split(/\s+/).filter(Boolean);
|
|
196437
|
-
continue;
|
|
196438
|
-
}
|
|
196439
|
-
}
|
|
196440
|
-
classes = value.split(/\s+/).filter(Boolean);
|
|
196441
|
-
}
|
|
196719
|
+
classes = collectClassTokens(attr.value, value, ctx);
|
|
196442
196720
|
continue;
|
|
196443
196721
|
}
|
|
196444
196722
|
if (attr.name === "role") {
|
|
@@ -196501,12 +196779,13 @@ function convertText(node) {
|
|
|
196501
196779
|
componentName: null
|
|
196502
196780
|
});
|
|
196503
196781
|
}
|
|
196504
|
-
function convertExpression(node) {
|
|
196782
|
+
function convertExpression(node, ctx) {
|
|
196783
|
+
const text = ctx.defaults.get(node.expr.trim()) ?? node.expr;
|
|
196505
196784
|
return new TestNode({
|
|
196506
196785
|
tag: null,
|
|
196507
196786
|
type: "expression",
|
|
196508
196787
|
children: [],
|
|
196509
|
-
text
|
|
196788
|
+
text,
|
|
196510
196789
|
props: {},
|
|
196511
196790
|
classes: [],
|
|
196512
196791
|
role: null,
|
|
@@ -196571,7 +196850,7 @@ function convertComponent(node, ctx) {
|
|
|
196571
196850
|
props[prop.name] = prop.value.expr;
|
|
196572
196851
|
break;
|
|
196573
196852
|
case "template":
|
|
196574
|
-
props[prop.name] = resolveTemplateAttr(prop.value);
|
|
196853
|
+
props[prop.name] = resolveTemplateAttr(prop.value, ctx);
|
|
196575
196854
|
break;
|
|
196576
196855
|
case "boolean-shorthand":
|
|
196577
196856
|
case "boolean-attr":
|
|
@@ -196711,41 +196990,69 @@ function refsToHandler(refs) {
|
|
|
196711
196990
|
}
|
|
196712
196991
|
return { setters, via };
|
|
196713
196992
|
}
|
|
196714
|
-
function
|
|
196715
|
-
|
|
196716
|
-
|
|
196993
|
+
function splitClassTokens(value) {
|
|
196994
|
+
return value.split(/\s+/).filter((t) => t && !t.includes("${"));
|
|
196995
|
+
}
|
|
196996
|
+
function collectClassTokens(attrValue, resolved, ctx) {
|
|
196997
|
+
if (attrValue.kind === "template") {
|
|
196998
|
+
const joined = attrValue.parts.map((part) => {
|
|
196999
|
+
if (part.type === "string")
|
|
197000
|
+
return substituteInterpolations(part.value, ctx);
|
|
197001
|
+
if (part.type === "ternary")
|
|
197002
|
+
return `${part.whenTrue} ${part.whenFalse}`;
|
|
197003
|
+
return Object.values(part.cases).join(" ");
|
|
197004
|
+
}).join("");
|
|
197005
|
+
return splitClassTokens(joined);
|
|
197006
|
+
}
|
|
197007
|
+
if (typeof resolved !== "string")
|
|
197008
|
+
return [];
|
|
197009
|
+
const isDynamic = attrValue.kind === "expression" || attrValue.kind === "spread";
|
|
197010
|
+
if (isDynamic) {
|
|
197011
|
+
const value = resolveClassValue(resolved, ctx);
|
|
197012
|
+
if (value !== null)
|
|
197013
|
+
return splitClassTokens(value);
|
|
197014
|
+
}
|
|
197015
|
+
return splitClassTokens(resolved);
|
|
197016
|
+
}
|
|
197017
|
+
function substituteInterpolations(value, ctx) {
|
|
197018
|
+
return value.replace(/\$\{([^}]+)\}/g, (raw, expr) => {
|
|
197019
|
+
const trimmed = expr.trim();
|
|
197020
|
+
return ctx.cmap.get(trimmed) ?? ctx.defaults.get(trimmed) ?? raw;
|
|
197021
|
+
});
|
|
197022
|
+
}
|
|
197023
|
+
function resolveClassValue(value, ctx) {
|
|
197024
|
+
if (ctx.cmap.has(value)) {
|
|
197025
|
+
return ctx.cmap.get(value);
|
|
197026
|
+
}
|
|
197027
|
+
if (ctx.defaults.has(value)) {
|
|
197028
|
+
return ctx.defaults.get(value);
|
|
196717
197029
|
}
|
|
196718
197030
|
if (value.startsWith("`") && value.endsWith("`")) {
|
|
196719
|
-
|
|
196720
|
-
return inner.replace(/\$\{([^}]+)\}/g, (_, expr) => {
|
|
196721
|
-
const trimmed = expr.trim();
|
|
196722
|
-
return cmap.get(trimmed) ?? "";
|
|
196723
|
-
});
|
|
197031
|
+
return substituteInterpolations(value.slice(1, -1), ctx);
|
|
196724
197032
|
}
|
|
196725
197033
|
return null;
|
|
196726
197034
|
}
|
|
196727
|
-
function resolveAttrValue(attr) {
|
|
197035
|
+
function resolveAttrValue(attr, ctx) {
|
|
196728
197036
|
switch (attr.value.kind) {
|
|
196729
197037
|
case "boolean-attr":
|
|
196730
197038
|
return true;
|
|
196731
197039
|
case "literal":
|
|
196732
197040
|
return attr.value.value;
|
|
196733
197041
|
case "expression":
|
|
196734
|
-
return attr.value.expr;
|
|
196735
197042
|
case "spread":
|
|
196736
|
-
return attr.value.expr;
|
|
197043
|
+
return ctx.defaults.get(attr.value.expr.trim()) ?? attr.value.expr;
|
|
196737
197044
|
case "template":
|
|
196738
|
-
return resolveTemplateAttr(attr.value);
|
|
197045
|
+
return resolveTemplateAttr(attr.value, ctx);
|
|
196739
197046
|
case "boolean-shorthand":
|
|
196740
197047
|
return true;
|
|
196741
197048
|
case "jsx-children":
|
|
196742
197049
|
return null;
|
|
196743
197050
|
}
|
|
196744
197051
|
}
|
|
196745
|
-
function resolveTemplateAttr(tl) {
|
|
197052
|
+
function resolveTemplateAttr(tl, ctx) {
|
|
196746
197053
|
return tl.parts.map((part) => {
|
|
196747
197054
|
if (part.type === "string")
|
|
196748
|
-
return part.value;
|
|
197055
|
+
return substituteInterpolations(part.value, ctx);
|
|
196749
197056
|
if (part.type === "ternary")
|
|
196750
197057
|
return `{${part.condition}}`;
|
|
196751
197058
|
return Object.values(part.cases).join(" ");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ir-to-test-node.d.ts","sourceRoot":"","sources":["../src/ir-to-test-node.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,MAAM,EAYN,UAAU,EACX,MAAM,iBAAiB,CAAA;AAMxB,OAAO,EAAE,QAAQ,EAAqB,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"ir-to-test-node.d.ts","sourceRoot":"","sources":["../src/ir-to-test-node.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,MAAM,EAYN,UAAU,EACX,MAAM,iBAAiB,CAAA;AAMxB,OAAO,EAAE,QAAQ,EAAqB,MAAM,gBAAgB,CAAA;AAU5D,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,QAAQ,CAAC,EAAE,UAAU,GAAG,QAAQ,CAuBjH"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barefootjs/test",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Test utilities for BarefootJS - IR-based component testing without a browser",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"directory": "packages/test"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@barefootjs/jsx": "0.
|
|
42
|
+
"@barefootjs/jsx": "0.18.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"typescript": "^5.0.0"
|
package/src/ir-to-test-node.ts
CHANGED
|
@@ -30,12 +30,15 @@ interface ConvertContext {
|
|
|
30
30
|
cmap: Map<string, string>
|
|
31
31
|
setterToSignal: Map<string, string>
|
|
32
32
|
fnSetters: Map<string, FnSetterResolution[]>
|
|
33
|
+
/** Prop name → literal destructure-default value (`{ size = 'md' }` → `size: 'md'`). */
|
|
34
|
+
defaults: Map<string, string>
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
export function irNodeToTestNode(node: IRNode, constantMap?: Map<string, string>, metadata?: IRMetadata): TestNode {
|
|
36
38
|
const cmap = constantMap ?? new Map<string, string>()
|
|
37
39
|
const setterToSignal = new Map<string, string>()
|
|
38
40
|
const fnSetters = new Map<string, FnSetterResolution[]>()
|
|
41
|
+
const defaults = new Map<string, string>()
|
|
39
42
|
if (metadata) {
|
|
40
43
|
for (const s of metadata.signals) {
|
|
41
44
|
if (s.setter) setterToSignal.set(s.setter, s.getter)
|
|
@@ -43,8 +46,35 @@ export function irNodeToTestNode(node: IRNode, constantMap?: Map<string, string>
|
|
|
43
46
|
for (const [k, v] of buildLocalFunctionSetterMap(metadata, setterToSignal)) {
|
|
44
47
|
fnSetters.set(k, v)
|
|
45
48
|
}
|
|
49
|
+
// renderToTest models the component compiled with NO incoming props,
|
|
50
|
+
// so a literal destructure default IS the statically-known value of
|
|
51
|
+
// that prop (#2069). Non-literal defaults (arrows, objects, computed
|
|
52
|
+
// expressions) stay unresolved — surfacing a stale expression string
|
|
53
|
+
// would be worse than the raw reference.
|
|
54
|
+
for (const p of metadata.propsParams) {
|
|
55
|
+
const lit = p.defaultValue !== undefined ? parseLiteralDefault(p.defaultValue) : null
|
|
56
|
+
if (lit !== null) defaults.set(p.name, lit)
|
|
57
|
+
}
|
|
46
58
|
}
|
|
47
|
-
return convert(node, { cmap, setterToSignal, fnSetters })
|
|
59
|
+
return convert(node, { cmap, setterToSignal, fnSetters, defaults })
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Parse a `ParamInfo.defaultValue` JS-text into a plain string when it is
|
|
64
|
+
* a self-contained literal: quoted string, number, or boolean. Returns
|
|
65
|
+
* null for anything else.
|
|
66
|
+
*/
|
|
67
|
+
function parseLiteralDefault(js: string): string | null {
|
|
68
|
+
const v = js.trim()
|
|
69
|
+
if (
|
|
70
|
+
(v.startsWith("'") && v.endsWith("'") && !v.slice(1, -1).includes("'")) ||
|
|
71
|
+
(v.startsWith('"') && v.endsWith('"') && !v.slice(1, -1).includes('"'))
|
|
72
|
+
) {
|
|
73
|
+
return v.slice(1, -1)
|
|
74
|
+
}
|
|
75
|
+
if (/^-?\d+(\.\d+)?$/.test(v)) return v
|
|
76
|
+
if (v === 'true' || v === 'false') return v
|
|
77
|
+
return null
|
|
48
78
|
}
|
|
49
79
|
|
|
50
80
|
function convert(node: IRNode, ctx: ConvertContext): TestNode {
|
|
@@ -54,7 +84,7 @@ function convert(node: IRNode, ctx: ConvertContext): TestNode {
|
|
|
54
84
|
case 'text':
|
|
55
85
|
return convertText(node)
|
|
56
86
|
case 'expression':
|
|
57
|
-
return convertExpression(node)
|
|
87
|
+
return convertExpression(node, ctx)
|
|
58
88
|
case 'conditional':
|
|
59
89
|
return convertConditional(node, ctx)
|
|
60
90
|
case 'loop':
|
|
@@ -90,20 +120,10 @@ function convertElement(node: IRElement, ctx: ConvertContext): TestNode {
|
|
|
90
120
|
let classes: string[] = []
|
|
91
121
|
|
|
92
122
|
for (const attr of node.attrs) {
|
|
93
|
-
const value = resolveAttrValue(attr)
|
|
123
|
+
const value = resolveAttrValue(attr, ctx)
|
|
94
124
|
|
|
95
125
|
if (attr.name === 'className' || attr.name === 'class') {
|
|
96
|
-
|
|
97
|
-
const isDynamic = attr.value.kind === 'expression' || attr.value.kind === 'template' || attr.value.kind === 'spread'
|
|
98
|
-
if (isDynamic && ctx.cmap.size > 0) {
|
|
99
|
-
const resolved = resolveClassValue(value, ctx.cmap)
|
|
100
|
-
if (resolved !== null) {
|
|
101
|
-
classes = resolved.split(/\s+/).filter(Boolean)
|
|
102
|
-
continue
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
classes = value.split(/\s+/).filter(Boolean)
|
|
106
|
-
}
|
|
126
|
+
classes = collectClassTokens(attr.value, value, ctx)
|
|
107
127
|
continue
|
|
108
128
|
}
|
|
109
129
|
|
|
@@ -178,12 +198,20 @@ function convertText(node: IRText): TestNode {
|
|
|
178
198
|
// Expression
|
|
179
199
|
// ---------------------------------------------------------------------------
|
|
180
200
|
|
|
181
|
-
function convertExpression(node: IRExpression): TestNode {
|
|
201
|
+
function convertExpression(node: IRExpression, ctx: ConvertContext): TestNode {
|
|
202
|
+
// A bare reference to a defaulted prop (`<div>{label}</div>` with
|
|
203
|
+
// `{ label = 'Hello' }`) resolves to its literal default, so
|
|
204
|
+
// `findByText('Hello')` sees the zero-props render (#2069). Prop refs
|
|
205
|
+
// are flagged reactive (they update on parent re-render), so this
|
|
206
|
+
// keys off defaults-map membership, not the reactive flag: signal /
|
|
207
|
+
// memo reads are call expressions (`count()`), never bare identifiers,
|
|
208
|
+
// and keep their source text — wiring is the assertion surface there.
|
|
209
|
+
const text = ctx.defaults.get(node.expr.trim()) ?? node.expr
|
|
182
210
|
return new TestNode({
|
|
183
211
|
tag: null,
|
|
184
212
|
type: 'expression',
|
|
185
213
|
children: [],
|
|
186
|
-
text
|
|
214
|
+
text,
|
|
187
215
|
props: {},
|
|
188
216
|
classes: [],
|
|
189
217
|
role: null,
|
|
@@ -265,7 +293,7 @@ function convertComponent(node: IRComponent, ctx: ConvertContext): TestNode {
|
|
|
265
293
|
props[prop.name] = prop.value.expr
|
|
266
294
|
break
|
|
267
295
|
case 'template':
|
|
268
|
-
props[prop.name] = resolveTemplateAttr(prop.value)
|
|
296
|
+
props[prop.name] = resolveTemplateAttr(prop.value, ctx)
|
|
269
297
|
break
|
|
270
298
|
case 'boolean-shorthand':
|
|
271
299
|
case 'boolean-attr':
|
|
@@ -450,19 +478,77 @@ function refsToHandler(refs: SetterRef[]): EventHandler {
|
|
|
450
478
|
return { setters, via }
|
|
451
479
|
}
|
|
452
480
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
481
|
+
/**
|
|
482
|
+
* Split a resolved className value into tokens, dropping any span that
|
|
483
|
+
* still carries an unresolved runtime interpolation (`${className}`,
|
|
484
|
+
* `foo-${x}`). Those are dynamic passthroughs the IR can't evaluate —
|
|
485
|
+
* they aren't real class tokens, and leaking them verbatim pollutes
|
|
486
|
+
* `.classes` for exact-match assertions.
|
|
487
|
+
*/
|
|
488
|
+
function splitClassTokens(value: string): string[] {
|
|
489
|
+
return value.split(/\s+/).filter(t => t && !t.includes('${'))
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* Resolve a className attribute value into its class tokens.
|
|
494
|
+
*
|
|
495
|
+
* For a structured template attr the parts are walked directly so class
|
|
496
|
+
* collection keeps union semantics per part kind:
|
|
497
|
+
* - `lookup` (`${MAP[KEY]}`) → every case's tokens (PR #2000)
|
|
498
|
+
* - `ternary` (`cond ? 'on' : 'off'`) → both branches' tokens,
|
|
499
|
+
* matching the intermediate-const `valueBranches` union (#525)
|
|
500
|
+
* - `string` spans → literal text, with `${ident}` interpolations
|
|
501
|
+
* substituted from resolved consts / literal prop defaults
|
|
502
|
+
* For expression attrs the value resolves through local consts (cmap)
|
|
503
|
+
* and literal prop defaults. Anything still carrying a `${...}`
|
|
504
|
+
* interpolation is dropped by `splitClassTokens`.
|
|
505
|
+
*/
|
|
506
|
+
function collectClassTokens(attrValue: AttrValue, resolved: string | boolean | null, ctx: ConvertContext): string[] {
|
|
507
|
+
if (attrValue.kind === 'template') {
|
|
508
|
+
const joined = attrValue.parts
|
|
509
|
+
.map(part => {
|
|
510
|
+
if (part.type === 'string') return substituteInterpolations(part.value, ctx)
|
|
511
|
+
if (part.type === 'ternary') return `${part.whenTrue} ${part.whenFalse}`
|
|
512
|
+
return Object.values(part.cases).join(' ')
|
|
513
|
+
})
|
|
514
|
+
.join('')
|
|
515
|
+
return splitClassTokens(joined)
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
if (typeof resolved !== 'string') return []
|
|
519
|
+
|
|
520
|
+
const isDynamic = attrValue.kind === 'expression' || attrValue.kind === 'spread'
|
|
521
|
+
if (isDynamic) {
|
|
522
|
+
const value = resolveClassValue(resolved, ctx)
|
|
523
|
+
if (value !== null) return splitClassTokens(value)
|
|
524
|
+
}
|
|
525
|
+
return splitClassTokens(resolved)
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Replace `${ident}` interpolations with a resolved const or a literal
|
|
530
|
+
* prop default; unresolvable spans are kept verbatim so the caller's
|
|
531
|
+
* token filter can drop them.
|
|
532
|
+
*/
|
|
533
|
+
function substituteInterpolations(value: string, ctx: ConvertContext): string {
|
|
534
|
+
return value.replace(/\$\{([^}]+)\}/g, (raw, expr) => {
|
|
535
|
+
const trimmed = expr.trim()
|
|
536
|
+
return ctx.cmap.get(trimmed) ?? ctx.defaults.get(trimmed) ?? raw
|
|
537
|
+
})
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function resolveClassValue(value: string, ctx: ConvertContext): string | null {
|
|
541
|
+
// Simple identifier lookup: a local const or a literal prop default.
|
|
542
|
+
if (ctx.cmap.has(value)) {
|
|
543
|
+
return ctx.cmap.get(value)!
|
|
544
|
+
}
|
|
545
|
+
if (ctx.defaults.has(value)) {
|
|
546
|
+
return ctx.defaults.get(value)!
|
|
457
547
|
}
|
|
458
548
|
|
|
459
549
|
// Template literal: `...${var}...`
|
|
460
550
|
if (value.startsWith('`') && value.endsWith('`')) {
|
|
461
|
-
|
|
462
|
-
return inner.replace(/\$\{([^}]+)\}/g, (_, expr) => {
|
|
463
|
-
const trimmed = expr.trim()
|
|
464
|
-
return cmap.get(trimmed) ?? ''
|
|
465
|
-
})
|
|
551
|
+
return substituteInterpolations(value.slice(1, -1), ctx)
|
|
466
552
|
}
|
|
467
553
|
|
|
468
554
|
return null
|
|
@@ -472,18 +558,22 @@ function resolveClassValue(value: string, cmap: Map<string, string>): string | n
|
|
|
472
558
|
// Attribute value resolution
|
|
473
559
|
// ---------------------------------------------------------------------------
|
|
474
560
|
|
|
475
|
-
function resolveAttrValue(attr: IRAttribute): string | boolean | null {
|
|
561
|
+
function resolveAttrValue(attr: IRAttribute, ctx: ConvertContext): string | boolean | null {
|
|
476
562
|
switch (attr.value.kind) {
|
|
477
563
|
case 'boolean-attr':
|
|
478
564
|
return true
|
|
479
565
|
case 'literal':
|
|
480
566
|
return attr.value.value
|
|
481
567
|
case 'expression':
|
|
482
|
-
return attr.value.expr
|
|
483
568
|
case 'spread':
|
|
484
|
-
|
|
569
|
+
// A bare reference to a defaulted prop (`type={type}` with
|
|
570
|
+
// `{ type = 'button' }`) resolves to its literal default —
|
|
571
|
+
// renderToTest models the zero-props render, where the default
|
|
572
|
+
// IS the value (#2069). Anything non-bare keeps the expression
|
|
573
|
+
// text (the wiring-visible representation).
|
|
574
|
+
return ctx.defaults.get(attr.value.expr.trim()) ?? attr.value.expr
|
|
485
575
|
case 'template':
|
|
486
|
-
return resolveTemplateAttr(attr.value)
|
|
576
|
+
return resolveTemplateAttr(attr.value, ctx)
|
|
487
577
|
case 'boolean-shorthand':
|
|
488
578
|
return true
|
|
489
579
|
case 'jsx-children':
|
|
@@ -491,10 +581,12 @@ function resolveAttrValue(attr: IRAttribute): string | boolean | null {
|
|
|
491
581
|
}
|
|
492
582
|
}
|
|
493
583
|
|
|
494
|
-
function resolveTemplateAttr(tl: TemplateAttr): string {
|
|
584
|
+
function resolveTemplateAttr(tl: TemplateAttr, ctx: ConvertContext): string {
|
|
495
585
|
return tl.parts
|
|
496
586
|
.map(part => {
|
|
497
|
-
|
|
587
|
+
// `${ident}` interpolations against a literal prop default
|
|
588
|
+
// resolve like local consts do (#2069).
|
|
589
|
+
if (part.type === 'string') return substituteInterpolations(part.value, ctx)
|
|
498
590
|
if (part.type === 'ternary') return `{${part.condition}}`
|
|
499
591
|
// `lookup` (`Record<T, string>[key]`) — the test framework
|
|
500
592
|
// doesn't render against a specific key, so concatenate every
|