Summary
#1759 ("a receiver-less JS/TS call never binds to a method") makes isLocallyBoundJsName treat a Zustand-style store binding as a local rebinding, so bare calls to store actions stop resolving. This regresses the repo's own test suite: main at cd4e65b fails one of its own tests.
Reproduction
Clean checkout of main at cd4e65b, npm ci, full build, then:
npx vitest run __tests__/object-literal-methods.test.ts
FAIL __tests__/object-literal-methods.test.ts > object-literal method resolution (end-to-end)
> resolves callers of store actions across files (destructured + chained getState())
AssertionError: expected [] to include 'loginFlow'
__tests__/ui-steps-api.test.ts fails the same way (2 tests):
buildSteps > walks a screen through its handler, the bridge, the event, the store and the request
screen regions > a screen names every step's region: the screen body for its own code, inherited down the walk
Cause
isLocallyBoundJsName carves out require(...) / import(...) on the right-hand side, because an imported name is a node elsewhere rather than a shadow. A store binding is the same situation and is not carved out:
const { fetchUser } = useStore.getState(); // action lives in the store file
const setZipUri = useStore((s) => s.setZipUri);
Both name the store's own action, so the bare call that follows means that action. matchStoreAccessorChain already recognises the accessor shapes; isLocallyBoundJsName now discards those candidates before it runs.
Fix
Extend the existing carve-out to the two accessor shapes plus the selector read. PR follows.
The selector pattern is deliberately narrow — one named parameter and a plain member read of the same name — so a genuine local binding such as const now = options.now || (() => Date.now()) still shadows. That case is covered by bare-call-no-method, which stays green.
Verification
Full suite on the same machine, same build, pristine vs patched:
|
pristine cd4e65b |
with the fix |
| Test files |
13 failed |
7 failed |
| Unique failing tests |
29 |
24 |
object-literal-methods and both ui-steps-api tests go green, and no test that passed before fails after. The remaining failures are pre-existing on this machine (Windows, native kernel not built) and byte-identical across both runs.
Summary
#1759("a receiver-less JS/TS call never binds to a method") makesisLocallyBoundJsNametreat a Zustand-style store binding as a local rebinding, so bare calls to store actions stop resolving. This regresses the repo's own test suite:mainatcd4e65bfails one of its own tests.Reproduction
Clean checkout of
mainatcd4e65b,npm ci, full build, then:__tests__/ui-steps-api.test.tsfails the same way (2 tests):buildSteps > walks a screen through its handler, the bridge, the event, the store and the requestscreen regions > a screen names every step's region: the screen body for its own code, inherited down the walkCause
isLocallyBoundJsNamecarves outrequire(...)/import(...)on the right-hand side, because an imported name is a node elsewhere rather than a shadow. A store binding is the same situation and is not carved out:Both name the store's own action, so the bare call that follows means that action.
matchStoreAccessorChainalready recognises the accessor shapes;isLocallyBoundJsNamenow discards those candidates before it runs.Fix
Extend the existing carve-out to the two accessor shapes plus the selector read. PR follows.
The selector pattern is deliberately narrow — one named parameter and a plain member read of the same name — so a genuine local binding such as
const now = options.now || (() => Date.now())still shadows. That case is covered bybare-call-no-method, which stays green.Verification
Full suite on the same machine, same build, pristine vs patched:
cd4e65bobject-literal-methodsand bothui-steps-apitests go green, and no test that passed before fails after. The remaining failures are pre-existing on this machine (Windows, native kernel not built) and byte-identical across both runs.