Singly-linked stack entry that also exposes a Set-like API
(has, size, iteration). Each doResolve call prepends a new
StackEntry that points at the previous tip via .parent, so pushing
is O(1) in time and memory. Recursion detection walks the linked list
(O(n)) but the stack is typically shallow, so this is cheaper overall
than cloning a Set per call.
new StackEntry(): StackEntryStackEntrybooleanstringbooleanstringStackEntrystring | falsestack: new Set([...])
.
Propagated through the chain so deeper
doResolve
calls still see
them during recursion checks.
undefined
in the common case so
there is no extra work on the hot path.stringstringnumberIterableIterator<string>Iterate entries from oldest (root) to newest (tip), matching how a
Set that was populated in insertion order would iterate. Pre-seeded
legacy Set<string> entries come first so error-message output stays
ordered oldest-to-newest.
Yields each entry as its formatted toString() form. Plugins written
against the pre-5.21 Set<string> shape — e.g.
[...resolveContext.stack].find(a => a.includes("module:")) — keep
working unchanged because each yielded value is a plain string with
all of String.prototype available natively. Resolves that never
iterate the stack pay nothing; iteration costs one toString()
allocation per stack frame.
has
has(query): booleanStackEntrybooleanWalk the linked list looking for an entry with the same request shape.
Set-compatible: callers that used stack.has(entry) keep working.
NOTE: kept monomorphic on purpose. An earlier draft accepted a string
query too (so pre-5.21 plugins keeping their own Set<string> of
seen entries could probe the live stack with the formatted form),
but adding the second shape regressed doResolve's heap profile by
~1 MiB / 200 resolves on stack-churn — V8 keeps a polymorphic
call-site state for parent.has(stackEntry) once has has two
argument shapes. Plugins that need string membership can reach for
[...stack].find(e => e.includes(formattedString)) via the
String-method proxies on StackEntry instead.
toString(): stringstringHuman-readable form used in recursion error messages, logs, and the
iterator above. Not memoized: caching would require an extra slot on
every StackEntry, which costs heap even on resolves that never look
at the formatted form.