@barefootjs/test 0.25.0 → 0.26.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/index.js
CHANGED
|
@@ -188803,6 +188803,15 @@ var EVAL_MATH_METHODS = new Set([
|
|
|
188803
188803
|
"ceil",
|
|
188804
188804
|
"round"
|
|
188805
188805
|
]);
|
|
188806
|
+
function identifierPath(callee) {
|
|
188807
|
+
if (callee.kind === "identifier")
|
|
188808
|
+
return callee.name;
|
|
188809
|
+
if (callee.kind === "member" && !callee.computed) {
|
|
188810
|
+
const head = identifierPath(callee.object);
|
|
188811
|
+
return head ? `${head}.${callee.property}` : null;
|
|
188812
|
+
}
|
|
188813
|
+
return null;
|
|
188814
|
+
}
|
|
188806
188815
|
|
|
188807
188816
|
// ../jsx/src/prop-rewrite.ts
|
|
188808
188817
|
var import_typescript5 = __toESM(require_typescript(), 1);
|
|
@@ -191421,6 +191430,37 @@ function extractFreeIdentifiersFromNode(node) {
|
|
|
191421
191430
|
visit2(node);
|
|
191422
191431
|
return ids;
|
|
191423
191432
|
}
|
|
191433
|
+
function extractFreeTypeIdentifiersFromNode(node) {
|
|
191434
|
+
const ids = new Set;
|
|
191435
|
+
const boundTypeParams = new Set;
|
|
191436
|
+
function rootName(name) {
|
|
191437
|
+
return import_typescript8.default.isQualifiedName(name) ? rootName(name.left) : name;
|
|
191438
|
+
}
|
|
191439
|
+
function visit2(n) {
|
|
191440
|
+
if (import_typescript8.default.isTypeReferenceNode(n)) {
|
|
191441
|
+
const name = rootName(n.typeName).text;
|
|
191442
|
+
if (!boundTypeParams.has(name))
|
|
191443
|
+
ids.add(name);
|
|
191444
|
+
}
|
|
191445
|
+
if (import_typescript8.default.isTypeQueryNode(n)) {
|
|
191446
|
+
const name = rootName(n.exprName).text;
|
|
191447
|
+
if (!boundTypeParams.has(name))
|
|
191448
|
+
ids.add(name);
|
|
191449
|
+
}
|
|
191450
|
+
if (import_typescript8.default.isFunctionLike(n) && n.typeParameters && n.typeParameters.length > 0) {
|
|
191451
|
+
const names = n.typeParameters.map((p) => p.name.text);
|
|
191452
|
+
for (const p of names)
|
|
191453
|
+
boundTypeParams.add(p);
|
|
191454
|
+
import_typescript8.default.forEachChild(n, visit2);
|
|
191455
|
+
for (const p of names)
|
|
191456
|
+
boundTypeParams.delete(p);
|
|
191457
|
+
return;
|
|
191458
|
+
}
|
|
191459
|
+
import_typescript8.default.forEachChild(n, visit2);
|
|
191460
|
+
}
|
|
191461
|
+
visit2(node);
|
|
191462
|
+
return ids;
|
|
191463
|
+
}
|
|
191424
191464
|
function initializerShapeContainsJsx(node) {
|
|
191425
191465
|
let found = false;
|
|
191426
191466
|
function visit2(n) {
|
|
@@ -192243,16 +192283,17 @@ function buildEntryImportIndex(sf, filePath) {
|
|
|
192243
192283
|
continue;
|
|
192244
192284
|
if (!import_typescript8.default.isStringLiteral(stmt.moduleSpecifier))
|
|
192245
192285
|
continue;
|
|
192246
|
-
if (stmt.importClause?.isTypeOnly)
|
|
192247
|
-
continue;
|
|
192248
192286
|
const src = stmt.moduleSpecifier.text;
|
|
192249
192287
|
const targetKey = src.startsWith("./") || src.startsWith("../") ? resolveRelativeImportToFile(src, filePath) ?? "unresolved:" + src : src;
|
|
192288
|
+
const wholeTypeOnly = stmt.importClause?.isTypeOnly === true;
|
|
192250
192289
|
const namedBindings = stmt.importClause?.namedBindings;
|
|
192251
192290
|
if (namedBindings && import_typescript8.default.isNamedImports(namedBindings)) {
|
|
192252
192291
|
for (const el of namedBindings.elements) {
|
|
192253
|
-
|
|
192254
|
-
|
|
192255
|
-
|
|
192292
|
+
index.set(el.name.text, {
|
|
192293
|
+
targetKey,
|
|
192294
|
+
exportedName: (el.propertyName ?? el.name).text,
|
|
192295
|
+
isTypeOnly: wholeTypeOnly || el.isTypeOnly
|
|
192296
|
+
});
|
|
192256
192297
|
}
|
|
192257
192298
|
}
|
|
192258
192299
|
}
|
|
@@ -192282,6 +192323,9 @@ function collectEntryBindingNames(sf) {
|
|
|
192282
192323
|
if ((import_typescript8.default.isFunctionDeclaration(node) || import_typescript8.default.isClassDeclaration(node) || import_typescript8.default.isEnumDeclaration(node)) && node.name) {
|
|
192283
192324
|
names.add(node.name.text);
|
|
192284
192325
|
}
|
|
192326
|
+
if ((import_typescript8.default.isTypeAliasDeclaration(node) || import_typescript8.default.isInterfaceDeclaration(node)) && node.name) {
|
|
192327
|
+
names.add(node.name.text);
|
|
192328
|
+
}
|
|
192285
192329
|
if (import_typescript8.default.isFunctionLike(node)) {
|
|
192286
192330
|
for (const p of node.parameters) {
|
|
192287
192331
|
const out = [];
|
|
@@ -192477,7 +192521,11 @@ function prescanImportedReactiveFactories(entrySourceFile, filePath, result) {
|
|
|
192477
192521
|
const required = [];
|
|
192478
192522
|
const pending = [];
|
|
192479
192523
|
let declinedEntry = null;
|
|
192480
|
-
|
|
192524
|
+
const allRefs = [
|
|
192525
|
+
...capture.importedRefs.map((r) => ({ ...r, isTypeOnly: false })),
|
|
192526
|
+
...capture.importedTypeRefs.map((r) => ({ ...r, isTypeOnly: true }))
|
|
192527
|
+
];
|
|
192528
|
+
for (const ref of allRefs) {
|
|
192481
192529
|
let specifier;
|
|
192482
192530
|
let targetKey;
|
|
192483
192531
|
if (ref.source.startsWith("./") || ref.source.startsWith("../")) {
|
|
@@ -192497,7 +192545,7 @@ function prescanImportedReactiveFactories(entrySourceFile, filePath, result) {
|
|
|
192497
192545
|
targetKey = ref.source;
|
|
192498
192546
|
}
|
|
192499
192547
|
const existing = entryImportIndex.get(ref.localName);
|
|
192500
|
-
if (existing && existing.targetKey === targetKey && existing.exportedName === ref.exportedName) {
|
|
192548
|
+
if (existing && existing.targetKey === targetKey && existing.exportedName === ref.exportedName && (ref.isTypeOnly || !existing.isTypeOnly)) {
|
|
192501
192549
|
continue;
|
|
192502
192550
|
}
|
|
192503
192551
|
const planned = plannedInjections.get(ref.localName);
|
|
@@ -192511,7 +192559,7 @@ function prescanImportedReactiveFactories(entrySourceFile, filePath, result) {
|
|
|
192511
192559
|
break;
|
|
192512
192560
|
}
|
|
192513
192561
|
pending.push([ref.localName, { targetKey, exportedName: ref.exportedName }]);
|
|
192514
|
-
required.push({ localName: ref.localName, exportedName: ref.exportedName, specifier });
|
|
192562
|
+
required.push({ localName: ref.localName, exportedName: ref.exportedName, specifier, isTypeOnly: ref.isTypeOnly || undefined });
|
|
192515
192563
|
}
|
|
192516
192564
|
if (declinedEntry) {
|
|
192517
192565
|
result.declined.set(spec.local, declinedEntry);
|
|
@@ -192531,6 +192579,8 @@ function prescanImportedReactiveFactories(entrySourceFile, filePath, result) {
|
|
|
192531
192579
|
}
|
|
192532
192580
|
function collectHelperModuleValueBindings(sf) {
|
|
192533
192581
|
const local = new Set;
|
|
192582
|
+
const localTypes = new Set;
|
|
192583
|
+
const importedTypes = new Map;
|
|
192534
192584
|
const imported = new Map;
|
|
192535
192585
|
for (const stmt of sf.statements) {
|
|
192536
192586
|
if (import_typescript8.default.isVariableStatement(stmt)) {
|
|
@@ -192546,35 +192596,41 @@ function collectHelperModuleValueBindings(sf) {
|
|
|
192546
192596
|
local.add(stmt.name.text);
|
|
192547
192597
|
continue;
|
|
192548
192598
|
}
|
|
192599
|
+
if ((import_typescript8.default.isTypeAliasDeclaration(stmt) || import_typescript8.default.isInterfaceDeclaration(stmt)) && stmt.name) {
|
|
192600
|
+
localTypes.add(stmt.name.text);
|
|
192601
|
+
continue;
|
|
192602
|
+
}
|
|
192549
192603
|
if (import_typescript8.default.isImportDeclaration(stmt)) {
|
|
192550
|
-
if (stmt.importClause?.isTypeOnly)
|
|
192551
|
-
continue;
|
|
192552
192604
|
if (!import_typescript8.default.isStringLiteral(stmt.moduleSpecifier))
|
|
192553
192605
|
continue;
|
|
192554
192606
|
const src = stmt.moduleSpecifier.text;
|
|
192555
192607
|
if (src === "@barefootjs/client" || src === "@barefootjs/client/runtime")
|
|
192556
192608
|
continue;
|
|
192609
|
+
const wholeTypeOnly = stmt.importClause?.isTypeOnly === true;
|
|
192557
192610
|
if (stmt.importClause?.name)
|
|
192558
|
-
local.add(stmt.importClause.name.text);
|
|
192611
|
+
(wholeTypeOnly ? localTypes : local).add(stmt.importClause.name.text);
|
|
192559
192612
|
const namedBindings = stmt.importClause?.namedBindings;
|
|
192560
192613
|
if (namedBindings && import_typescript8.default.isNamedImports(namedBindings)) {
|
|
192561
192614
|
for (const el of namedBindings.elements) {
|
|
192562
|
-
|
|
192563
|
-
|
|
192564
|
-
|
|
192615
|
+
const entry = { source: src, exportedName: (el.propertyName ?? el.name).text };
|
|
192616
|
+
if (wholeTypeOnly || el.isTypeOnly)
|
|
192617
|
+
importedTypes.set(el.name.text, entry);
|
|
192618
|
+
else
|
|
192619
|
+
imported.set(el.name.text, entry);
|
|
192565
192620
|
}
|
|
192566
192621
|
}
|
|
192567
192622
|
if (namedBindings && import_typescript8.default.isNamespaceImport(namedBindings)) {
|
|
192568
|
-
local.add(namedBindings.name.text);
|
|
192623
|
+
(wholeTypeOnly ? localTypes : local).add(namedBindings.name.text);
|
|
192569
192624
|
}
|
|
192570
192625
|
}
|
|
192571
192626
|
}
|
|
192572
|
-
return { local, imported };
|
|
192627
|
+
return { local, imported, localTypes, importedTypes };
|
|
192573
192628
|
}
|
|
192574
192629
|
function moduleCaptureCheck(fn, info, moduleBindings, selfName) {
|
|
192575
192630
|
if (!fn.body)
|
|
192576
|
-
return { captured: [], importedRefs: [] };
|
|
192631
|
+
return { captured: [], importedRefs: [], importedTypeRefs: [] };
|
|
192577
192632
|
const free = extractFreeIdentifiersFromNode(fn.body);
|
|
192633
|
+
const freeTypes = extractFreeTypeIdentifiersFromNode(fn.body);
|
|
192578
192634
|
const exclude = new Set(info.params);
|
|
192579
192635
|
for (const b of info.localBindings)
|
|
192580
192636
|
exclude.add(b);
|
|
@@ -192583,8 +192639,12 @@ function moduleCaptureCheck(fn, info, moduleBindings, selfName) {
|
|
|
192583
192639
|
for (const p of REACTIVE_PRIMITIVES)
|
|
192584
192640
|
exclude.add(p);
|
|
192585
192641
|
exclude.add(selfName);
|
|
192642
|
+
if (fn.typeParameters)
|
|
192643
|
+
for (const p of fn.typeParameters)
|
|
192644
|
+
exclude.add(p.name.text);
|
|
192586
192645
|
const captured = [];
|
|
192587
192646
|
const importedRefs = [];
|
|
192647
|
+
const importedTypeRefs = [];
|
|
192588
192648
|
for (const id of free) {
|
|
192589
192649
|
if (exclude.has(id))
|
|
192590
192650
|
continue;
|
|
@@ -192596,9 +192656,28 @@ function moduleCaptureCheck(fn, info, moduleBindings, selfName) {
|
|
|
192596
192656
|
if (imp)
|
|
192597
192657
|
importedRefs.push({ localName: id, source: imp.source, exportedName: imp.exportedName });
|
|
192598
192658
|
}
|
|
192659
|
+
for (const id of freeTypes) {
|
|
192660
|
+
if (exclude.has(id))
|
|
192661
|
+
continue;
|
|
192662
|
+
if (free.has(id))
|
|
192663
|
+
continue;
|
|
192664
|
+
if (moduleBindings.localTypes.has(id) || moduleBindings.local.has(id)) {
|
|
192665
|
+
captured.push(id);
|
|
192666
|
+
continue;
|
|
192667
|
+
}
|
|
192668
|
+
const typeImp = moduleBindings.importedTypes.get(id);
|
|
192669
|
+
if (typeImp) {
|
|
192670
|
+
importedTypeRefs.push({ localName: id, source: typeImp.source, exportedName: typeImp.exportedName });
|
|
192671
|
+
continue;
|
|
192672
|
+
}
|
|
192673
|
+
const valueImp = moduleBindings.imported.get(id);
|
|
192674
|
+
if (valueImp)
|
|
192675
|
+
importedRefs.push({ localName: id, source: valueImp.source, exportedName: valueImp.exportedName });
|
|
192676
|
+
}
|
|
192599
192677
|
captured.sort();
|
|
192600
192678
|
importedRefs.sort((a, b) => a.localName < b.localName ? -1 : 1);
|
|
192601
|
-
|
|
192679
|
+
importedTypeRefs.sort((a, b) => a.localName < b.localName ? -1 : 1);
|
|
192680
|
+
return { captured, importedRefs, importedTypeRefs };
|
|
192602
192681
|
}
|
|
192603
192682
|
function detectReactiveFactory(node, sourceFile, filePath) {
|
|
192604
192683
|
if (!node.body || !node.name)
|
|
@@ -192937,20 +193016,44 @@ function rewriteFactoryCallsInSource(source, prescan) {
|
|
|
192937
193016
|
if (edits.length === 0)
|
|
192938
193017
|
return source;
|
|
192939
193018
|
const importsBySpecifier = new Map;
|
|
193019
|
+
const typeImportsBySpecifier = new Map;
|
|
192940
193020
|
for (const f of inlinedFactories) {
|
|
192941
193021
|
for (const r of f.requiredImports ?? []) {
|
|
192942
|
-
|
|
193022
|
+
const bySpecifier = r.isTypeOnly ? typeImportsBySpecifier : importsBySpecifier;
|
|
193023
|
+
let names = bySpecifier.get(r.specifier);
|
|
192943
193024
|
if (!names) {
|
|
192944
193025
|
names = new Map;
|
|
192945
|
-
|
|
193026
|
+
bySpecifier.set(r.specifier, names);
|
|
192946
193027
|
}
|
|
192947
193028
|
names.set(r.localName, r.exportedName);
|
|
192948
193029
|
}
|
|
192949
193030
|
}
|
|
192950
|
-
|
|
192951
|
-
const
|
|
193031
|
+
for (const [spec, typeNames] of typeImportsBySpecifier) {
|
|
193032
|
+
const valueNames = importsBySpecifier.get(spec);
|
|
193033
|
+
if (!valueNames)
|
|
193034
|
+
continue;
|
|
193035
|
+
for (const local of [...typeNames.keys()]) {
|
|
193036
|
+
if (valueNames.has(local))
|
|
193037
|
+
typeNames.delete(local);
|
|
193038
|
+
}
|
|
193039
|
+
if (typeNames.size === 0)
|
|
193040
|
+
typeImportsBySpecifier.delete(spec);
|
|
193041
|
+
}
|
|
193042
|
+
if (importsBySpecifier.size > 0 || typeImportsBySpecifier.size > 0) {
|
|
193043
|
+
const buildLine = (names, keyword, spec) => {
|
|
192952
193044
|
const specifiers = [...names].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([local, exported]) => exported === local ? local : `${exported} as ${local}`);
|
|
192953
|
-
return `import { ${specifiers.join(", ")} } from '${spec}'`;
|
|
193045
|
+
return `import ${keyword}{ ${specifiers.join(", ")} } from '${spec}'`;
|
|
193046
|
+
};
|
|
193047
|
+
const allSpecifiers = new Set([...importsBySpecifier.keys(), ...typeImportsBySpecifier.keys()]);
|
|
193048
|
+
const lines = [...allSpecifiers].sort().flatMap((spec) => {
|
|
193049
|
+
const out2 = [];
|
|
193050
|
+
const valueNames = importsBySpecifier.get(spec);
|
|
193051
|
+
if (valueNames)
|
|
193052
|
+
out2.push(buildLine(valueNames, "", spec));
|
|
193053
|
+
const typeNames = typeImportsBySpecifier.get(spec);
|
|
193054
|
+
if (typeNames)
|
|
193055
|
+
out2.push(buildLine(typeNames, "type ", spec));
|
|
193056
|
+
return out2;
|
|
192954
193057
|
});
|
|
192955
193058
|
const at = factoryImportInsertionOffset(sourceFile);
|
|
192956
193059
|
edits.push({ start: at, end: at, replacement: at === 0 ? lines.join(`
|
|
@@ -193530,6 +193633,20 @@ function resolveFreeRefs(node, env) {
|
|
|
193530
193633
|
|
|
193531
193634
|
// ../jsx/src/to-locale-date-lowering.ts
|
|
193532
193635
|
var TO_LOCALE_TZ_RE = /^(?:UTC|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/;
|
|
193636
|
+
var tzProbeCache = new Map;
|
|
193637
|
+
function isBuildResolvableTimeZone(value) {
|
|
193638
|
+
const cached = tzProbeCache.get(value);
|
|
193639
|
+
if (cached !== undefined)
|
|
193640
|
+
return cached;
|
|
193641
|
+
let verified;
|
|
193642
|
+
try {
|
|
193643
|
+
verified = new Intl.DateTimeFormat("en-US", { timeZone: value }).resolvedOptions().timeZone === value;
|
|
193644
|
+
} catch {
|
|
193645
|
+
verified = false;
|
|
193646
|
+
}
|
|
193647
|
+
tzProbeCache.set(value, verified);
|
|
193648
|
+
return verified;
|
|
193649
|
+
}
|
|
193533
193650
|
var PROBE_UTC = new Date(Date.UTC(2001, 1, 3));
|
|
193534
193651
|
var formatCache = new Map;
|
|
193535
193652
|
var namesCache = new Map;
|
|
@@ -193768,7 +193885,7 @@ function matchToLocaleDateStringCall(callee, args, metadata) {
|
|
|
193768
193885
|
return null;
|
|
193769
193886
|
const value = String(prop.value.value);
|
|
193770
193887
|
if (prop.key === "timeZone") {
|
|
193771
|
-
if (!TO_LOCALE_TZ_RE.test(value))
|
|
193888
|
+
if (!TO_LOCALE_TZ_RE.test(value) && !isBuildResolvableTimeZone(value))
|
|
193772
193889
|
return null;
|
|
193773
193890
|
tz = value;
|
|
193774
193891
|
} else {
|
|
@@ -198681,12 +198798,37 @@ function collectClassTokens(attrValue, resolved, ctx) {
|
|
|
198681
198798
|
return [];
|
|
198682
198799
|
const isDynamic = attrValue.kind === "expression" || attrValue.kind === "spread";
|
|
198683
198800
|
if (isDynamic) {
|
|
198801
|
+
if (attrValue.kind === "expression" && attrValue.parsed?.kind === "conditional") {
|
|
198802
|
+
const value2 = resolveParsedClass(attrValue.parsed, ctx);
|
|
198803
|
+
return value2 === null ? [] : splitClassTokens(value2);
|
|
198804
|
+
}
|
|
198684
198805
|
const value = resolveClassValue(resolved, ctx);
|
|
198685
198806
|
if (value !== null)
|
|
198686
198807
|
return splitClassTokens(value);
|
|
198687
198808
|
}
|
|
198688
198809
|
return splitClassTokens(resolved);
|
|
198689
198810
|
}
|
|
198811
|
+
function resolveParsedClass(expr, ctx) {
|
|
198812
|
+
switch (expr.kind) {
|
|
198813
|
+
case "literal":
|
|
198814
|
+
return expr.literalType === "string" ? String(expr.value) : null;
|
|
198815
|
+
case "identifier":
|
|
198816
|
+
return ctx.cmap.get(expr.name) ?? ctx.defaults.get(expr.name) ?? null;
|
|
198817
|
+
case "member": {
|
|
198818
|
+
const path = identifierPath(expr);
|
|
198819
|
+
return path === null ? null : ctx.cmap.get(path) ?? null;
|
|
198820
|
+
}
|
|
198821
|
+
case "conditional": {
|
|
198822
|
+
const whenTrue = resolveParsedClass(expr.consequent, ctx);
|
|
198823
|
+
const whenFalse = resolveParsedClass(expr.alternate, ctx);
|
|
198824
|
+
if (whenTrue === null && whenFalse === null)
|
|
198825
|
+
return null;
|
|
198826
|
+
return [whenTrue, whenFalse].filter((v) => v !== null).join(" ");
|
|
198827
|
+
}
|
|
198828
|
+
default:
|
|
198829
|
+
return null;
|
|
198830
|
+
}
|
|
198831
|
+
}
|
|
198690
198832
|
function substituteInterpolations(value, ctx) {
|
|
198691
198833
|
return value.replace(/\$\{([^}]+)\}/g, (raw, expr) => {
|
|
198692
198834
|
const trimmed = expr.trim();
|
|
@@ -198736,6 +198878,14 @@ function resolveTemplateAttr(tl, ctx) {
|
|
|
198736
198878
|
function resolveConstants(constants3) {
|
|
198737
198879
|
const resolved = new Map;
|
|
198738
198880
|
for (const c of constants3) {
|
|
198881
|
+
if (c.parsed?.kind === "object-literal") {
|
|
198882
|
+
for (const prop of c.parsed.properties) {
|
|
198883
|
+
if (prop.value.kind === "literal" && prop.value.literalType === "string") {
|
|
198884
|
+
resolved.set(`${c.name}.${prop.key}`, String(prop.value.value));
|
|
198885
|
+
}
|
|
198886
|
+
}
|
|
198887
|
+
continue;
|
|
198888
|
+
}
|
|
198739
198889
|
if (!c.value)
|
|
198740
198890
|
continue;
|
|
198741
198891
|
if (c.valueBranches) {
|
|
@@ -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,
|
|
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,EAEX,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"}
|
|
@@ -7,17 +7,23 @@
|
|
|
7
7
|
* (from ternary initializers), those are used directly instead of
|
|
8
8
|
* re-parsing from the string representation.
|
|
9
9
|
*/
|
|
10
|
+
import type { ParsedExpr } from '@barefootjs/jsx';
|
|
10
11
|
/**
|
|
11
12
|
* Build a map of constant name → resolved string value.
|
|
12
13
|
* Resolves string literals, template literals, array.join() patterns,
|
|
13
14
|
* and plain identifier references. When `valueBranches` is present
|
|
14
15
|
* (from ternary initializers), each branch is resolved and merged
|
|
15
|
-
* with union semantics.
|
|
16
|
-
*
|
|
16
|
+
* with union semantics. An object-literal const with string-valued
|
|
17
|
+
* properties (`const rowClass = { active: 'row row-active', plain: 'row' }`)
|
|
18
|
+
* additionally seeds member-path keys (`rowClass.active` → `'row row-active'`)
|
|
19
|
+
* so a member-access className (`className={rowClass.active}`, or a
|
|
20
|
+
* ternary arm) resolves through the same lookup (#2354). Function
|
|
21
|
+
* expressions and other complex values are skipped.
|
|
17
22
|
*/
|
|
18
23
|
export declare function resolveConstants(constants: Array<{
|
|
19
24
|
name: string;
|
|
20
25
|
value?: string;
|
|
21
26
|
valueBranches?: string[];
|
|
27
|
+
parsed?: ParsedExpr;
|
|
22
28
|
}>): Map<string, string>;
|
|
23
29
|
//# sourceMappingURL=resolve-constants.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve-constants.d.ts","sourceRoot":"","sources":["../src/resolve-constants.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH
|
|
1
|
+
{"version":3,"file":"resolve-constants.d.ts","sourceRoot":"","sources":["../src/resolve-constants.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAEjD;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,CAAC,EAAE,UAAU,CAAA;CAAE,CAAC,GAChG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAuCrB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barefootjs/test",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.1",
|
|
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.26.1"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"typescript": "^5.0.0"
|
package/src/ir-to-test-node.ts
CHANGED
|
@@ -18,8 +18,9 @@ import type {
|
|
|
18
18
|
IRProvider,
|
|
19
19
|
IRAsync,
|
|
20
20
|
IRMetadata,
|
|
21
|
+
ParsedExpr,
|
|
21
22
|
} from '@barefootjs/jsx'
|
|
22
|
-
import { resolveSetters, buildLocalFunctionSetterMap, type SetterRef, type FnSetterResolution } from '@barefootjs/jsx'
|
|
23
|
+
import { resolveSetters, buildLocalFunctionSetterMap, identifierPath, type SetterRef, type FnSetterResolution } from '@barefootjs/jsx'
|
|
23
24
|
|
|
24
25
|
type IRAttribute = IRElement['attrs'][number]
|
|
25
26
|
type AttrValue = IRAttribute['value']
|
|
@@ -500,8 +501,17 @@ function splitClassTokens(value: string): string[] {
|
|
|
500
501
|
* - `string` spans → literal text, with `${ident}` interpolations
|
|
501
502
|
* substituted from resolved consts / literal prop defaults
|
|
502
503
|
* For expression attrs the value resolves through local consts (cmap)
|
|
503
|
-
* and literal prop defaults.
|
|
504
|
-
*
|
|
504
|
+
* and literal prop defaults. A bare `className={cond ? a : b}` ternary
|
|
505
|
+
* (no backticks) is walked structurally through the attr's `parsed`
|
|
506
|
+
* tree — the `expression`-kind analogue of the `template` branch's
|
|
507
|
+
* `ternary` part handling — so both arms union with the same semantics,
|
|
508
|
+
* and an unresolvable ternary yields `[]` rather than leaking its own
|
|
509
|
+
* operator tokens (`?`/`:`) and member fragments as class names (#2354).
|
|
510
|
+
* A non-ternary expression keeps the historical fallback: a resolved
|
|
511
|
+
* value splits into tokens, otherwise the raw source passes through
|
|
512
|
+
* (an unresolvable bare identifier surfaces as its name — a proxy some
|
|
513
|
+
* tests assert on). Anything still carrying a `${...}` interpolation is
|
|
514
|
+
* dropped by `splitClassTokens`.
|
|
505
515
|
*/
|
|
506
516
|
function collectClassTokens(attrValue: AttrValue, resolved: string | boolean | null, ctx: ConvertContext): string[] {
|
|
507
517
|
if (attrValue.kind === 'template') {
|
|
@@ -519,12 +529,53 @@ function collectClassTokens(attrValue: AttrValue, resolved: string | boolean | n
|
|
|
519
529
|
|
|
520
530
|
const isDynamic = attrValue.kind === 'expression' || attrValue.kind === 'spread'
|
|
521
531
|
if (isDynamic) {
|
|
532
|
+
// Bare `className={cond ? a : b}`: no backticks, so it never becomes a
|
|
533
|
+
// structured `template` attr. Walk the parsed ternary and union both
|
|
534
|
+
// arms — resolving identifier / member-access branches through the
|
|
535
|
+
// same cmap as everything else. A CSS class can never contain the
|
|
536
|
+
// `?`/`:` operator tokens the old raw-source fallback leaked here, so
|
|
537
|
+
// an unresolvable ternary suppresses to `[]` instead (#2354).
|
|
538
|
+
if (attrValue.kind === 'expression' && attrValue.parsed?.kind === 'conditional') {
|
|
539
|
+
const value = resolveParsedClass(attrValue.parsed, ctx)
|
|
540
|
+
return value === null ? [] : splitClassTokens(value)
|
|
541
|
+
}
|
|
522
542
|
const value = resolveClassValue(resolved, ctx)
|
|
523
543
|
if (value !== null) return splitClassTokens(value)
|
|
524
544
|
}
|
|
525
545
|
return splitClassTokens(resolved)
|
|
526
546
|
}
|
|
527
547
|
|
|
548
|
+
/**
|
|
549
|
+
* Reduce a parsed className expression to its resolved class string, or
|
|
550
|
+
* `null` when no branch resolves. Handles the shapes a className ternary
|
|
551
|
+
* reaches for: string literals, local-const / prop-default identifiers,
|
|
552
|
+
* object-property member access (`rowClass.active`, resolved through the
|
|
553
|
+
* member-path keys `resolveConstants` seeds), and nested ternaries. Both
|
|
554
|
+
* arms of a ternary union (matching the `template` ternary part and the
|
|
555
|
+
* intermediate-const `valueBranches` handling), since the IR can't pick a
|
|
556
|
+
* concrete arm without the runtime condition. (#2354)
|
|
557
|
+
*/
|
|
558
|
+
function resolveParsedClass(expr: ParsedExpr, ctx: ConvertContext): string | null {
|
|
559
|
+
switch (expr.kind) {
|
|
560
|
+
case 'literal':
|
|
561
|
+
return expr.literalType === 'string' ? String(expr.value) : null
|
|
562
|
+
case 'identifier':
|
|
563
|
+
return ctx.cmap.get(expr.name) ?? ctx.defaults.get(expr.name) ?? null
|
|
564
|
+
case 'member': {
|
|
565
|
+
const path = identifierPath(expr)
|
|
566
|
+
return path === null ? null : ctx.cmap.get(path) ?? null
|
|
567
|
+
}
|
|
568
|
+
case 'conditional': {
|
|
569
|
+
const whenTrue = resolveParsedClass(expr.consequent, ctx)
|
|
570
|
+
const whenFalse = resolveParsedClass(expr.alternate, ctx)
|
|
571
|
+
if (whenTrue === null && whenFalse === null) return null
|
|
572
|
+
return [whenTrue, whenFalse].filter((v): v is string => v !== null).join(' ')
|
|
573
|
+
}
|
|
574
|
+
default:
|
|
575
|
+
return null
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
528
579
|
/**
|
|
529
580
|
* Replace `${ident}` interpolations with a resolved const or a literal
|
|
530
581
|
* prop default; unresolvable spans are kept verbatim so the caller's
|
package/src/resolve-constants.ts
CHANGED
|
@@ -8,20 +8,38 @@
|
|
|
8
8
|
* re-parsing from the string representation.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import type { ParsedExpr } from '@barefootjs/jsx'
|
|
12
|
+
|
|
11
13
|
/**
|
|
12
14
|
* Build a map of constant name → resolved string value.
|
|
13
15
|
* Resolves string literals, template literals, array.join() patterns,
|
|
14
16
|
* and plain identifier references. When `valueBranches` is present
|
|
15
17
|
* (from ternary initializers), each branch is resolved and merged
|
|
16
|
-
* with union semantics.
|
|
17
|
-
*
|
|
18
|
+
* with union semantics. An object-literal const with string-valued
|
|
19
|
+
* properties (`const rowClass = { active: 'row row-active', plain: 'row' }`)
|
|
20
|
+
* additionally seeds member-path keys (`rowClass.active` → `'row row-active'`)
|
|
21
|
+
* so a member-access className (`className={rowClass.active}`, or a
|
|
22
|
+
* ternary arm) resolves through the same lookup (#2354). Function
|
|
23
|
+
* expressions and other complex values are skipped.
|
|
18
24
|
*/
|
|
19
25
|
export function resolveConstants(
|
|
20
|
-
constants: Array<{ name: string; value?: string; valueBranches?: string[] }>
|
|
26
|
+
constants: Array<{ name: string; value?: string; valueBranches?: string[]; parsed?: ParsedExpr }>
|
|
21
27
|
): Map<string, string> {
|
|
22
28
|
const resolved = new Map<string, string>()
|
|
23
29
|
|
|
24
30
|
for (const c of constants) {
|
|
31
|
+
// Object-literal const: expose each string-valued property as a
|
|
32
|
+
// `name.key` member-path key. The bare identifier stays unresolved
|
|
33
|
+
// (an object is not a class string), matching prior behavior.
|
|
34
|
+
if (c.parsed?.kind === 'object-literal') {
|
|
35
|
+
for (const prop of c.parsed.properties) {
|
|
36
|
+
if (prop.value.kind === 'literal' && prop.value.literalType === 'string') {
|
|
37
|
+
resolved.set(`${c.name}.${prop.key}`, String(prop.value.value))
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
continue
|
|
41
|
+
}
|
|
42
|
+
|
|
25
43
|
if (!c.value) continue
|
|
26
44
|
|
|
27
45
|
// When the analyzer provides structured branch info, resolve each
|