/** * JS/TS `child_process` process-spawn detection and target folding (ADR 0048, * Lattice sensor correctness fix c/1 — first "call graph非可視の結合" index). * * `spawnSync(process.execPath, [BIN, ...args])` reaches `BIN` by starting a * brand-new OS process, not by calling a JS function — no `calls` edge can * ever represent it, so a spawn-driven test harness (the oracle shape: * `tests/orchestrate/helpers.mjs` spawning `bin/orchestrate-run.mjs`) showed * zero `affected` dependents even though changing the spawned file plainly * breaks the test. This module detects the five `child_process` launch * functions (`spawn`, `spawnSync`, `execFile`, `execFileSync`, `fork`) — * `exec`/`execSync` (shell-string parsing) are OUT OF SCOPE, see * `resolveChildProcessCallee`'s doc — and folds their target argument(s) to a * project-relative path using the same constant-folding engine as dynamic * `import()`/`require()` (dynamic-import.ts's `foldConstantExpr`). * * Detection REQUIRES the callee to be verified bound to `child_process` (a * static `import`/aliased/namespace member, or a simple * `const { spawnSync } = require('node:child_process')`) — a bare name match * on a user-defined `spawn()` must NOT produce an edge (mirrors fix (a)'s * no-name-match-fallback principle). */ import type { Node as SyntaxNode } from 'web-tree-sitter'; /** * Sentinel `referenceName` for a `fork(...)` call whose module argument could * NOT be statically folded. Mirrors `DYNAMIC_IMPORT_UNRESOLVED_MARKER` * (dynamic-import.ts): illegal as both a JS identifier and a file path * (angle brackets, colon), so it can never coincidentally name-match a real * file, and is pushed as a normal unresolved reference so it surfaces as * `status='failed'` rather than being silently dropped. `fork`'s target is * ALWAYS a JS module (unlike `spawn`/`exec*`, whose target is usually an * external binary) — so an unfoldable `fork` argument stays visible, while * an unfoldable `spawn`/`execFile*` argument is a silent skip (see * `resolveInvokesTargets`'s case (iv) — spawn's target domain is mostly * external commands, so "couldn't fold" carries no useful signal there). */ export declare const SPAWN_INVOKES_UNRESOLVED_MARKER = ""; /** Per-file table of local names verified bound to `child_process`'s launch functions. */ export interface ChildProcessBindings { /** local identifier -> canonical child_process function name (`spawn`, `fork`, ...) */ readonly localFns: ReadonlyMap; /** local identifiers bound as a `child_process` namespace (`import * as cp from 'child_process'`) */ readonly namespaces: ReadonlySet; } /** * Scan the file's top-level (Program direct-child) statements for bindings of * `child_process`'s spawn family. Two binding shapes, per spec: * - static `import` — named (`import { spawnSync } from 'child_process'`, * alias-aware), or namespace (`import * as cp from 'node:child_process'`, * later matched via `cp.spawnSync(...)`). * - simple CommonJS destructure — `const { spawnSync } = require('node:child_process')` * (module-level only; `let`/`var` excluded like dynamic-import.ts's * module-level-const fold, since a reassignable binding's declaration-site * value isn't necessarily the call-site value). * A default import isn't a meaningful binding shape for this module (no * useful default export) and is intentionally not recognized. */ export declare function collectChildProcessBindings(anyNodeInTree: SyntaxNode, source: string): ChildProcessBindings; /** * Given a `call_expression` node, return the canonical `child_process` * function name (`spawn`/`spawnSync`/`execFile`/`execFileSync`/`fork`) it * invokes, or null when the callee isn't verified bound to one. Two callee * shapes: a bare identifier bound via `localFns` (static named import or the * `require` destructure), or a `.` member access where * `` is a verified `import * as cp from 'child_process'` binding. * * `exec`/`execSync` are deliberately excluded — their single argument is a * whole shell command line (`"git status && ls"`), which requires shell * grammar parsing (quoting, pipes, `&&`, env vars) to extract a target file, * not the argv-array shape this module folds. Out of scope for this fix. */ export declare function resolveChildProcessCallee(node: SyntaxNode, source: string, bindings: ChildProcessBindings): string | null; /** One candidate `invokes` target: a folded path (real or not — the resolver decides) or the fork sentinel. */ export interface InvokesTarget { readonly referenceName: string; } /** * Fold `callNode`'s target argument(s) into zero or more `invokes` reference * names, per `canonicalFn`: * * - `fork(modulePath, args?, options?)`: fold `modulePath` (argv[0] only — * `fork`'s target is always a JS module, so it is ALWAYS pushed, using the * unresolved sentinel on a folding failure rather than staying silent). * - `spawn`/`spawnSync`/`execFile`/`execFileSync`(cmd, args?, options?): * (i) `cmd` folds to a path — push it. The resolver's exact-file-path * match is the real filter: a project file resolves, an external * command (`git`) or arbitrary string simply never matches a file * node and silently stays unresolved (case iii from spec, "正しい * 挙動" — no special-casing needed here). * (ii) `cmd` is `process.execPath` (AST shape) or folds to the literal * string `'node'` — argv[0] is the current runtime, so the REAL * target is argv[1]'s array-literal elements. Each element is * folded independently; a `...spread` or an unfoldable element is * skipped WITHOUT abandoning the rest of the array. * (iv) `cmd` doesn't fold at all — skip entirely (no ref pushed). * Unlike `fork`, spawn's target domain is mostly external * commands, so an unfoldable argv[0] carries no signal that * visibility would improve on. */ export declare function resolveInvokesTargets(canonicalFn: string, callNode: SyntaxNode, source: string, filePath: string): InvokesTarget[]; //# sourceMappingURL=spawn-invokes.d.ts.map