Expand description
The JS/TS static scan — a real parse on oxc.
oxc is pure Rust, so keel init still needs no Node toolchain (the design
constraint the old line-oriented scan existed to satisfy — this replaces
that documented simplification with an actual AST walk, dx-spec §2). Per
file, [ast] extracts:
- HTTP in use: a
fetch(…)call, or an import/require/dynamic-import of a known outbound client (undici,node:http/https,axios,got,node-fetch,superagent, DB clients likepg/redis) — including multi-line and aliased forms the old regex missed. - provider SDKs:
openai,@anthropic-ai/sdk(also bareanthropic), AI-SDK provider packages →llm:*targets. TSimport typeis excluded: type-only imports are erased at runtime and are not evidence. - URL literals: hosts from string literals and template-literal quasis
(
`https://api.x.com/${id}`resolves;`${scheme}://x`no longer false-positives). - effect call sites with enclosing-function attribution
(
super::CallSite) forkeel flows suggest, and a relative-import module graph (JsScan::imports).
The tradeoff (accepted here): a URL built by concatenation, or an exotic import form, is missed — exactly the ~20% the import-time and observed-run evidence sources exist to catch (dx-spec §2). Line numbers are exact.
§Function attribution (for keel flows suggest)
[ast::ScanVisitor] tracks a real enclosing-scope stack, so attribution
is exact scope containment, not a line heuristic. A super::FunctionFacts
entry opens for a function bound directly at module top level: a
function declaration (function f(…) {, export async function f(…) {),
a named function expression, or an arrow assigned directly to a top-level
const/let/var (const f = () => {…}, const f = function () {…}).
Everything lexically nested inside it — inner arrows, callbacks, helper
closures — attributes to that top-level entry, exactly like the Python
pass’s real ast containment (scan::python).
Class methods and object-literal methods do not open their own entry
— even though the scope walk names them (Class.method shows up in
super::CallSite::function for call-site evidence), a class body is not
a flow entrypoint any more than a Python class body is. A fetch inside
class Api { async load() {…} } is evidenced in the file-level scan (host
literals, call sites) but not credited to a function. The same holds for
anonymous top-level values (export default function () {…}): still
evidenced, never a flow candidate. This is strictly more precise than the
old line-heuristic scan it replaces, which additionally missed Allman-brace
functions and desynced on template-literal interpolation.
A file that fails to parse is warned about on stderr and skipped — never a
crash, and never silent narrowing (mirrors the Python pass’s
parse-or-skip). files_scanned counts parsed files only.
Structs§
- JsScan
- The JS pass result.
Functions§
- scan
- Scan
projectfor JS/TS effect seams.