/** * Dynamic import() / require() detection and constant-folding for JS/TS. * * The JS/TS extractors only recognized STATIC `import ... from '...'` * (importTypes: ['import_statement']) — dynamic `import()` and * CommonJS `require()` were invisible, producing false-negative * `imports` edges (ADR 0048, Lattice sensor correctness fix b). The oracle * shape this exists to solve: * * import { dirname, join, resolve } from "node:path"; * export const ROOT = resolve(import.meta.dirname, "..", ".."); * export const CONTROL_LIB = join(ROOT, "lib", "orchestrate", "control-record.mjs"); * export const loadControl = () => import(CONTROL_LIB); * * which requires 3 folding steps: `import.meta.dirname` -> a project-relative * dirname, `resolve`/`join` -> path arithmetic, and identifier -> a same-file * module-level `const` binding (recursively, since CONTROL_LIB itself refers * to ROOT). * * This module is PURE detection/folding logic — no side effects, no * ExtractionContext dependency — so it's usable from (and unit-testable * independent of) the core extractor's `extractCall` dispatch, which is the * single choke point both AST walkers (the declaration-level `visitNode` * ladder AND the function-body-only `visitForCallsAndStructure`) funnel * every `call_expression` through. See the call site in tree-sitter.ts for * why: a `visitNode`-only hook (the Lua/Ruby `require()` precedent) never * fires for a call nested inside a function body — which is exactly where * `() => import(CONTROL_LIB)` lives. */ import type { Node as SyntaxNode } from 'web-tree-sitter'; /** * Sentinel `referenceName` for a dynamic import/require whose argument could * NOT be statically folded (a variable, a non-`join`/`resolve` function call, * a template literal with substitutions, etc.). Deliberately illegal as both * a JS identifier and a file basename (angle brackets, colon) so it can NEVER * coincidentally equal a real symbol/import-local name — `hasAnyPossibleMatch` * and `matchesAnyImport` in the resolver are both name-equality checks, so an * unmatchable sentinel guarantees this never resolves via name-matching into * an accidental wrong edge. It is pushed as a normal unresolved reference so * it goes through the standard resolve-attempt -> mark `status='failed'` * lifecycle (ADR 0048's "no silent fallback" requirement) — a `lattice sensor * status`/`unresolved_refs` query surfaces it exactly like any other * unresolvable ref, grouped under this one name. */ export declare const DYNAMIC_IMPORT_UNRESOLVED_MARKER = ""; /** True when `node` is a dynamic `import(...)` call (`function` field is the bare `import` keyword node, not an identifier). */ export declare function isDynamicImportCall(node: SyntaxNode): boolean; /** * True when `node` is a bare `require(...)` call. The callee must be a plain * `identifier` reading "require" — `x.require(...)` / `foo.require(...)` * (a `member_expression` callee) is excluded, matching the Lua precedent's * dotted-callee exclusion. */ export declare function isRequireCall(node: SyntaxNode, source: string): boolean; /** * Attempt to fold the single argument of a dynamic import()/require() call * to a constant specifier string. Returns null (folding failure, NOT an * error) when the argument isn't statically determinable — a bare variable, * a function call other than `join`/`resolve`, a template literal with * substitutions, multiple/zero arguments, etc. * * The returned string is used AS-IS as the `imports` unresolved reference's * `referenceName` — a relative literal (`./x.mjs`) resolves through the same * relative-specifier path static `import` statements use (Strategy 2, * resolveViaImport); a project-relative path produced by `join`/`resolve` * arithmetic (`lib/orchestrate/control-record.mjs`) instead lands via * Strategy 3's `matchByFilePath` exact `filePath` match. Both are legitimate, * pre-existing resolution paths — this function doesn't need to know which * one a given result will take. */ export declare function foldDynamicImportArg(callNode: SyntaxNode, source: string, filePath: string): string | null; /** * Public entry point into the same constant-folding logic `foldDynamicImportArg` * uses, for a single already-selected argument node (no argument-count gating). * Reused by spawn-invokes.ts (Lattice sensor correctness fix c/1, ADR 0048) to * fold `child_process` spawn/fork target arguments — a `fork(mod, args, opts)` * call legitimately takes more than one argument, unlike `import()`/`require()`, * so the caller must select the argument node itself rather than going through * `foldDynamicImportArg`'s exactly-one-argument gate. */ export declare function foldConstantExpr(node: SyntaxNode, source: string, filePath: string): string | null; //# sourceMappingURL=dynamic-import.d.ts.map