2323// `onToggle(todo)` inside `TodoItem({ onToggle })` resolve to handleToggle.
2424// Brace children are the `children` prop (a render prop is a function).
2525//
26- // Not modelled: a component reached only through a library wrapper
27- // (`memo (C)`, `connect(…)(C)`, `forwardRef(fn) `) whose library is not staged — the
28- // tag's value is the wrapper's return, which is unknown, and the element stays an
29- // ambiguous_unknown row .
26+ // A tag bound to a component WRAPPER (`memo(C)`, `forwardRef(fn)`, `observer(C)`,
27+ // `withRouter (C)`) or a LOADER (`lazy(() => import("./x")) `) renders what the wrapper
28+ // was handed or what the loader settles to — see "wrapped and lazy components" below.
29+ // Not modelled: `connect(…)(C)` and other curried wrappers .
3030// ============================================================================
3131
3232// ── the element and what its tag holds ──────────────────────────────────────
@@ -35,6 +35,68 @@ jsx_tag_value(je, k, i) :- jsx_element_tag(je, tag), expr_value(tag, k, i).
3535// A CommonJS module value renders through its default export, as a call of it does.
3636jsx_tag_value(je, k, i) :- jsx_element_tag(je, tag), expr_value(tag, "module", mod),
3737 module_export_value(mod, "default", k, i), k != "module".
38+ // A tag that holds a wrapper or loader call renders what that call renders.
39+ jsx_tag_value(je, k, i) :- jsx_element_tag(je, tag), jsx_holds(tag, w), jsx_call_renders(w, k, i).
40+
41+ // ── wrapped and lazy components ─────────────────────────────────────────────
42+ // `const Card = memo(Inner)` binds Card to what memo RETURNED, and memo's library is
43+ // usually not staged, so the tag has no value and the element was an ambiguous_unknown
44+ // row. What renders is argument 0. JSX only, and NOT an expr_value of the call: at an
45+ // ordinary call site `wrap(f)` is a factory whose argument says nothing about what
46+ // runs, and valuing the call would turn every `Card()` blind spot into a claimed
47+ // no_target. The wrappers are named by the libraries that define them — `memo` and
48+ // `forwardRef` (React, Preact), `observer` (MobX) — and by the higher-order-component
49+ // convention, a `with` prefix and a capital. Read off the callee name, so
50+ // `React.memo(…)` counts as `memo(…)`.
51+ jsx_wrapper_name("memo").
52+ jsx_wrapper_name("forwardRef").
53+ jsx_wrapper_name("observer").
54+ jsx_component_wrapper(w) :- expr_kind(_, "CALL", _, w), call_site("client", _, n, _, _, _, w, _, _), jsx_wrapper_name(n).
55+ jsx_component_wrapper(w) :- expr_kind(_, "CALL", _, w), call_site("client", _, n, _, _, _, w, _, _), match("with[A-Z].*", n).
56+ // A LOADER — `lazy(() => import("./Settings"))`, Next.js `dynamic(…)`, `loadable(…)`:
57+ // argument 0 returns a module or a promise of one, and what renders settles out of it.
58+ jsx_loader_name("lazy").
59+ jsx_loader_name("dynamic").
60+ jsx_loader_name("loadable").
61+ jsx_component_loader(w) :- expr_kind(_, "CALL", _, w), call_site("client", _, n, _, _, _, w, _, _), jsx_loader_name(n).
62+
63+ // jsx_holds(Expr, Call) — the wrapper or loader call an expression holds, followed the
64+ // ways a component reaches a tag: the call itself, a const initialised with it, a named
65+ // or default import of an export that holds it, and a re-export of one.
66+ jsx_holds(w, w) :- jsx_component_wrapper(w).
67+ jsx_holds(w, w) :- jsx_component_loader(w).
68+ jsx_holds(e, w) :- expr_binding(_, v, e), jsx_var_holds(v, w).
69+ jsx_var_holds(v, w) :- var_init(_, _, e, v), !var_binding_form(_, "OBJECT_PATTERN", v), !var_binding_form(_, "ARRAY_PATTERN", v),
70+ jsx_holds(e, w).
71+ jsx_var_holds(v, w) :- var_import(_, imp, v), jsx_import_holds(imp, w).
72+ jsx_import_holds(imp, w) :- import_decl(_, _, _, "DEFAULT", _, _, _, _, imp), import_module(imp, mod),
73+ jsx_export_holds(mod, "default", w).
74+ jsx_import_holds(imp, w) :- import_decl(_, _, _, bf, n, _, _, _, imp), import_binding_is_named(bf), n != "",
75+ import_module(imp, mod), jsx_export_holds(mod, n, w).
76+ jsx_export_holds(mod, n, w) :- export_decl(_, n, _, _, _, "VARIABLE", v, _, mod, x), n != "", v != "",
77+ !export_overwritten(_, x), jsx_var_holds(v, w).
78+ // `export default memo(Inner)` / `export default Card` — the source expression.
79+ jsx_export_holds(mod, n, w) :- export_decl(_, n, _, _, _, _, _, se, mod, x), n != "", se != "",
80+ !expr_kind(_, "ASSIGNMENT", _, se), !export_overwritten(_, x), jsx_holds(se, w).
81+ jsx_export_holds(mod, en, w) :- export_decl(_, en, ln, form, _, _, _, _, mod, x), form != "EXPORT_ALL", ln != "",
82+ export_reexport(_, _, imp, x), import_module(imp, src), jsx_export_holds(src, ln, w).
83+
84+ // jsx_call_renders(Call, K, I) — the value a wrapper or loader call renders.
85+ // A wrapper: its argument 0, or what argument 0 renders when it is itself a wrapped
86+ // component — `memo(forwardRef(function Inner() {…}))`, `memo(Card)`.
87+ jsx_call_renders(w, k, i) :- jsx_component_wrapper(w), call_arg(w, 0, a), expr_value(a, k, i), k != "ambient".
88+ jsx_call_renders(w, k, i) :- jsx_component_wrapper(w), call_arg(w, 0, a), jsx_holds(a, w2), jsx_call_renders(w2, k, i).
89+ // A loader: what argument 0 returns — the module, a promise of it, or through
90+ // `.then(cb)` what cb returns (promises.dl carries the module into cb's parameter).
91+ jsx_loader_settles(w, k, i) :- jsx_component_loader(w), call_arg(w, 0, a), expr_value(a, "func", f), return_value(f, k, i).
92+ // …the module itself: its default export renders.
93+ jsx_call_renders(w, k, i) :- jsx_loader_settles(w, "module", mod), module_export_value(mod, "default", k, i), k != "module".
94+ // …re-wrapped for `lazy`, `{ default: m.Named }`: the object's `default` property.
95+ jsx_call_renders(w, k, i) :- jsx_loader_settles(w, "obj", o), prop_value("obj", o, "default", k, i).
96+ // …or a component projected out of it, `.then((m) => m.Named)` (the `dynamic` form).
97+ jsx_call_renders(w, k, i) :- jsx_loader_settles(w, k, i), jsx_component_kind(k).
98+ jsx_component_kind("func").
99+ jsx_component_kind("ctor").
38100
39101// ── jsx_renders(Element, Component) — the callables the element runs ───────
40102// A function component (a constructor FUNCTION is callable_value's "ctor" half).
0 commit comments