@barefootjs/test 0.26.0 → 0.26.2
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);
|
|
@@ -198789,12 +198798,37 @@ function collectClassTokens(attrValue, resolved, ctx) {
|
|
|
198789
198798
|
return [];
|
|
198790
198799
|
const isDynamic = attrValue.kind === "expression" || attrValue.kind === "spread";
|
|
198791
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
|
+
}
|
|
198792
198805
|
const value = resolveClassValue(resolved, ctx);
|
|
198793
198806
|
if (value !== null)
|
|
198794
198807
|
return splitClassTokens(value);
|
|
198795
198808
|
}
|
|
198796
198809
|
return splitClassTokens(resolved);
|
|
198797
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
|
+
}
|
|
198798
198832
|
function substituteInterpolations(value, ctx) {
|
|
198799
198833
|
return value.replace(/\$\{([^}]+)\}/g, (raw, expr) => {
|
|
198800
198834
|
const trimmed = expr.trim();
|
|
@@ -198844,6 +198878,14 @@ function resolveTemplateAttr(tl, ctx) {
|
|
|
198844
198878
|
function resolveConstants(constants3) {
|
|
198845
198879
|
const resolved = new Map;
|
|
198846
198880
|
for (const c of constants3) {
|
|
198881
|
+
if (c.parsed?.kind === "object-literal") {
|
|
198882
|
+
for (const prop of c.parsed.properties) {
|
|
198883
|
+
const value2 = resolveObjectPropValue(prop.value, resolved);
|
|
198884
|
+
if (value2 !== null)
|
|
198885
|
+
resolved.set(`${c.name}.${prop.key}`, value2);
|
|
198886
|
+
}
|
|
198887
|
+
continue;
|
|
198888
|
+
}
|
|
198847
198889
|
if (!c.value)
|
|
198848
198890
|
continue;
|
|
198849
198891
|
if (c.valueBranches) {
|
|
@@ -198868,6 +198910,31 @@ function resolveConstants(constants3) {
|
|
|
198868
198910
|
}
|
|
198869
198911
|
return resolved;
|
|
198870
198912
|
}
|
|
198913
|
+
function resolveObjectPropValue(expr, resolved) {
|
|
198914
|
+
if (expr.kind === "literal" && expr.literalType === "string") {
|
|
198915
|
+
return String(expr.value);
|
|
198916
|
+
}
|
|
198917
|
+
if (expr.kind === "identifier") {
|
|
198918
|
+
return resolved.get(expr.name) ?? null;
|
|
198919
|
+
}
|
|
198920
|
+
if (expr.kind === "template-literal") {
|
|
198921
|
+
const strs = [];
|
|
198922
|
+
for (const part of expr.parts) {
|
|
198923
|
+
if (part.type === "string") {
|
|
198924
|
+
strs.push(part.value);
|
|
198925
|
+
continue;
|
|
198926
|
+
}
|
|
198927
|
+
if (part.expr.kind !== "identifier")
|
|
198928
|
+
return null;
|
|
198929
|
+
const value = resolved.get(part.expr.name);
|
|
198930
|
+
if (value === undefined)
|
|
198931
|
+
return null;
|
|
198932
|
+
strs.push(value);
|
|
198933
|
+
}
|
|
198934
|
+
return strs.join("");
|
|
198935
|
+
}
|
|
198936
|
+
return null;
|
|
198937
|
+
}
|
|
198871
198938
|
function tryResolve(raw, resolved) {
|
|
198872
198939
|
const value = raw.trim();
|
|
198873
198940
|
if (value.startsWith("'") && value.endsWith("'") && !value.slice(1, -1).includes("'")) {
|
|
@@ -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,25 @@
|
|
|
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-literal,
|
|
17
|
+
* template-literal, or bare-identifier-valued properties (`const rowClass
|
|
18
|
+
* = { active: \`${base} row-active\`, plain: base }`) additionally seeds
|
|
19
|
+
* member-path
|
|
20
|
+
* keys (`rowClass.active` → `'row row-active'`) so a member-access
|
|
21
|
+
* className (`className={rowClass.active}`, or a ternary arm) resolves
|
|
22
|
+
* through the same lookup (#2354, template-literal values #2360).
|
|
23
|
+
* Function expressions and other complex property values are skipped.
|
|
17
24
|
*/
|
|
18
25
|
export declare function resolveConstants(constants: Array<{
|
|
19
26
|
name: string;
|
|
20
27
|
value?: string;
|
|
21
28
|
valueBranches?: string[];
|
|
29
|
+
parsed?: ParsedExpr;
|
|
22
30
|
}>): Map<string, string>;
|
|
23
31
|
//# 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;;;;;;;;;;;;;GAaG;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,CA0CrB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@barefootjs/test",
|
|
3
|
-
"version": "0.26.
|
|
3
|
+
"version": "0.26.2",
|
|
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.26.
|
|
42
|
+
"@barefootjs/jsx": "0.26.2"
|
|
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,43 @@
|
|
|
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-literal,
|
|
19
|
+
* template-literal, or bare-identifier-valued properties (`const rowClass
|
|
20
|
+
* = { active: \`${base} row-active\`, plain: base }`) additionally seeds
|
|
21
|
+
* member-path
|
|
22
|
+
* keys (`rowClass.active` → `'row row-active'`) so a member-access
|
|
23
|
+
* className (`className={rowClass.active}`, or a ternary arm) resolves
|
|
24
|
+
* through the same lookup (#2354, template-literal values #2360).
|
|
25
|
+
* Function expressions and other complex property values are skipped.
|
|
18
26
|
*/
|
|
19
27
|
export function resolveConstants(
|
|
20
|
-
constants: Array<{ name: string; value?: string; valueBranches?: string[] }>
|
|
28
|
+
constants: Array<{ name: string; value?: string; valueBranches?: string[]; parsed?: ParsedExpr }>
|
|
21
29
|
): Map<string, string> {
|
|
22
30
|
const resolved = new Map<string, string>()
|
|
23
31
|
|
|
24
32
|
for (const c of constants) {
|
|
33
|
+
// Object-literal const: expose each resolvable property as a
|
|
34
|
+
// `name.key` member-path key. The object's OWN bare identifier
|
|
35
|
+
// (`c.name` referenced directly, e.g. `className={rowClass}`) stays
|
|
36
|
+
// unresolved — an object is not a class string — matching prior
|
|
37
|
+
// behavior; this is unrelated to a resolvable identifier-valued
|
|
38
|
+
// PROPERTY (`{ plain: base }`), handled by resolveObjectPropValue
|
|
39
|
+
// below (Copilot review, PR #2361).
|
|
40
|
+
if (c.parsed?.kind === 'object-literal') {
|
|
41
|
+
for (const prop of c.parsed.properties) {
|
|
42
|
+
const value = resolveObjectPropValue(prop.value, resolved)
|
|
43
|
+
if (value !== null) resolved.set(`${c.name}.${prop.key}`, value)
|
|
44
|
+
}
|
|
45
|
+
continue
|
|
46
|
+
}
|
|
47
|
+
|
|
25
48
|
if (!c.value) continue
|
|
26
49
|
|
|
27
50
|
// When the analyzer provides structured branch info, resolve each
|
|
@@ -47,6 +70,56 @@ export function resolveConstants(
|
|
|
47
70
|
return resolved
|
|
48
71
|
}
|
|
49
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Resolve one object-literal property's value to a string, for the
|
|
75
|
+
* `name.key` member-path seeding above. Supports the same shapes as
|
|
76
|
+
* `tryResolve` — string literal, template literal, plain identifier
|
|
77
|
+
* reference — but walks the already-structured `ParsedExpr` (available
|
|
78
|
+
* here, unlike `tryResolve`'s raw-string input) instead of re-parsing
|
|
79
|
+
* with a regex, and is stricter about "unresolvable": `tryResolve`'s
|
|
80
|
+
* template-literal branch folds an unresolved `${...}` to `''` (its
|
|
81
|
+
* caller only ever uses that for the whole-constant case, where a
|
|
82
|
+
* partial string is an acceptable degrade), but a property value here
|
|
83
|
+
* returns `null` for the whole property instead — seeding `name.key`
|
|
84
|
+
* with a misleadingly partial class string would be worse than not
|
|
85
|
+
* seeding it at all. A bare identifier property (`{ plain: base }`) and
|
|
86
|
+
* an interpolated identifier inside a template literal both resolve
|
|
87
|
+
* against `resolved` — safe because `resolveConstants` processes
|
|
88
|
+
* `constants` in declaration order, so a module-scope const referenced
|
|
89
|
+
* inside a later object literal's property is already in the map
|
|
90
|
+
* (#2360). Anything else (a nested object/array, a call, a binary
|
|
91
|
+
* expression, ...) is left unresolved, same posture as `tryResolve`
|
|
92
|
+
* skipping "complex values".
|
|
93
|
+
*/
|
|
94
|
+
function resolveObjectPropValue(expr: ParsedExpr, resolved: Map<string, string>): string | null {
|
|
95
|
+
if (expr.kind === 'literal' && expr.literalType === 'string') {
|
|
96
|
+
return String(expr.value)
|
|
97
|
+
}
|
|
98
|
+
if (expr.kind === 'identifier') {
|
|
99
|
+
return resolved.get(expr.name) ?? null
|
|
100
|
+
}
|
|
101
|
+
if (expr.kind === 'template-literal') {
|
|
102
|
+
const strs: string[] = []
|
|
103
|
+
for (const part of expr.parts) {
|
|
104
|
+
if (part.type === 'string') {
|
|
105
|
+
strs.push(part.value)
|
|
106
|
+
continue
|
|
107
|
+
}
|
|
108
|
+
// An interpolation that isn't a plain identifier reference (a
|
|
109
|
+
// conditional, a member access, ...), or one that IS an identifier
|
|
110
|
+
// but doesn't resolve, makes the whole template unresolved — don't
|
|
111
|
+
// fold it to '' and seed a partial, misleadingly "resolved" string
|
|
112
|
+
// (Copilot review, PR #2361).
|
|
113
|
+
if (part.expr.kind !== 'identifier') return null
|
|
114
|
+
const value = resolved.get(part.expr.name)
|
|
115
|
+
if (value === undefined) return null
|
|
116
|
+
strs.push(value)
|
|
117
|
+
}
|
|
118
|
+
return strs.join('')
|
|
119
|
+
}
|
|
120
|
+
return null
|
|
121
|
+
}
|
|
122
|
+
|
|
50
123
|
function tryResolve(raw: string, resolved: Map<string, string>): string | null {
|
|
51
124
|
const value = raw.trim()
|
|
52
125
|
|