A named class expression, like register(class Widget extends Base {})
or module.exports = class Router {}, was not indexed as a class, as
issue #2499 found. Its methods became free functions, its fields and
supertypes were dropped, and calls in it had no class to start from,
so its this-calls, super calls and own static calls went unresolved or
to a by-name guess, and calls elsewhere guessed into its methods.
Both engines now index a named class expression as a class, with its
methods, properties, extends and implements. An anonymous one is still
passed over. Its head (class Widget extends Base) is its signature,
which marks it as an expression, and it is never exported by itself.
JavaScript binds the name only inside the expression, and the resolver
now scopes it the same way. A reference written outside every class
expression resolves in a view of the graph that hides class
expressions and their members, so new Widget() elsewhere links
nothing, and a same-named class, function or binding keeps its links.
A name only class expressions carry is unknown there. A reference
inside one sees the whole graph, unless it names another class
expression. A class a CommonJS module exports, module.exports = class
Router {} or exports.Store = class Store {}, is the module's value,
which require hands to every importer, so it is not scoped: it reads
as a declaration does, and a require of it reaches its static methods
and its instances' methods. The reader of CommonJS named exports takes
exports.X = class Y {} too. Super calls in a class expression now
resolve through the super-call matcher.
Validation: the new test fails 11 of 13 on main d5fc1fbb and passes 13
of 13 on the fix, and a mutation of each of 7 parts (the view, the
inside check, the name rule, the expression marker, the class node,
the CommonJS export rule, the named-export reader) fails a test.
A/B against main d5fc1fbb on 22 repos (20 TS/JS, flask and gin): 193
classes are added in 10 repos with their members, and 801 edge sites
change in 7 repos, each bucketed and sampled by hand. 563 are jest's
calls to its global test(), 0.4 guesses that move from one wrong
namesake to another: the old one, a class expression's test() in an
e2e fixture, is now a method. Of the rest, 85 are new extends,
implements and type references of class expressions; 74 calls, news
and references move to the member they are written in; 65 calls and
news gain or change a target, inside class expressions (this-calls,
own static calls) and through require (static calls, typed instance
calls, new); and 14 guesses are removed, among them 3 super calls
whose parent the index can't name. 12 repos' dumps, the controls among
them, are byte-identical. Kernel parity holds on 22 repos. Three sync
scenarios match a fresh index. Index time, median of 5 interleaved
runs: lit +0.8%, webpack +2.7%, jest +0.6%; insomnia +12.8%, though it
indexes byte-identically, holds no class expression, and its resolver
time is the same on both arms, so that gap is other jobs' load.
Fixes#2499
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A lazy React Router route whose module is a barrel, and whose page sits in a
folder below the barrel's, stayed unlinked after `codegraph sync` added the
page or gave it its export, though a fresh index linked it. In
DigiShop.Frontend, `@/pages` is `src/pages/index.ts`, which imports `./Home`,
`src/pages/Home/index.tsx`, and exports it again. While the page is missing,
the route's reference fails and waits under the key of the module it names,
`module:pages` (#2422). The retry looks failed references up by the keys of
the changed files, and a file in `pages/Home/` gives only `module:Home` and
its own stem. The limit, which the `export *` barrels of #2436 share, was
recorded with #2558.
After the failed-ref retry, which makes the barrel's import edge into an
added file, sync now walks up the import edges from each file it added, or
gave a definition, to the barrels that pass it on: `export … from`, or an
import exported again (`import { X } from './x'; export { X }`, `export
default X`), as deep as the resolver chases a name. It re-opens, for the
sweep, the failed references waiting under those barrels' keys.
Only keys a failed reference waits under are looked up, in one DISTINCT
over the failed rows parked under module keys, so a sync with none reads
nothing, and a body-only edit seeds nothing. The ImportReader that the
added-files reopen (#2512) uses is shared, and gains exportedBindings,
behind a regex pre-check, since most importers export nothing.
defaultExportBinding moves to alias-binding.ts for this walk and for
react.ts's exportsBinding.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Four gaps left CommonJS calls on the local alias variable - or on the wrong
function:
- `require('../')` up to the project root produced an empty relative path, so
its `/index.js` candidate was never found;
- a default import never followed `module.exports = X` /
`exports = module.exports = X` / `module.exports = require('./lib/x')`;
- only the first declarator of `var a = require('x'), b = require('y')` was a
binding, so `b()` - a package call - stayed on the local variable;
- `require('./utils').member` was read as the module's DEFAULT export: every
such alias in express resolved to utils' first exported function
(`normalizeType`). It is now the named member, and `exports.name =
function impl(...)`, `module.exports.name = impl` and `module.exports = { a,
b: c }` bind names the export index could not see.
express -1087 +727 (707 `express()` calls reach createApplication; supertest /
node:assert calls no longer edge to the local variable), eslint -750 +827
(`new RuleTester()` reaches the class, not another test's variable), mocha
-83 +90 (`run(...)` no longer lands in another spec file), koa 65 wrong
`request` edges -> `response`, body-parser +20. zod, trpc, hono, typeorm,
axios unchanged.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(resolution): resolve same-file calls through object-literal aliases (#1932)
`api.getUser()` where `api` is `const api = { getUser }` (or
`{ getUser: fetchUser }`) and the function is declared outside the literal
resolved to nothing in the literal's own file: the #1573 containment lookup
only finds members defined inside the literal, and the binding-following
alias path existed only on the import side, so same-file callers were
missing from callers / impact / blast radius.
- Extract the member-binding read into `objectLiteralMemberBinding`, which
splits the literal into its own top-level members on comment- and
string-blanked source, so a nested object, a member body, a comment or a
string never donates a binding, and `{ fn: 1 }` names nothing. The import
path now uses it too.
- In the same-file object-literal receiver strategy, fall back to that
binding: a symbol of the file, else one of its imports, skipped when a
parameter or nested declaration shadows the name where the literal is
written.
Resolution-only change; extraction output (and kernel parity) unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* test(resolution): a literal wrapped in Object.freeze still names its members (#1932)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(resolution): resolve object-literal function references safely (#1932)
Same-file object-member calls only found functions defined inside the literal.
Share lexical binding resolution across same-file, import, and alias-forwarding paths.
Respect exact source columns, top-level membership, shadowing, and property overwrites while preserving wrappers and inline members.
Add JavaScript and TypeScript regressions and verify kernel/WASM parity.
Co-authored-by: danusha2345 <ewidusoc498@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: danusha2345 <ewidusoc498@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Adapt upstream PR #1485 by @valkyriweb (cc791cfc51)
onto current main. Keep the upstream alias-binding module and six-test suite
verbatim, preserve target-kind gating and default-export bindings, and add
one credited Unreleased changelog entry.
Linux verification (x86_64, Node 22.19.0): TypeScript build and asset copy
pass; the fresh ./impl.js repro changes callers/impact of realImpl from
missing consumerFn to including it. All 6 upstream tests and 231 related
resolver regression tests pass.
Fixes#1482
The Forge PR will supersede upstream PR #1485.
Co-authored-by: Colby McHenry <colbymchenry@users.noreply.github.com>