fix(coverage): use dynamic import for ast-v8-to-istanbul to fix tsx#159
Conversation
ast-v8-to-istanbul is ESM-only and pulls in estree-walker, whose
package.json `exports` field has no `require` condition. Under tsx's
ESM loader, `require("ast-v8-to-istanbul")` resolves transitive imports
with the require condition and dies on estree-walker with
ERR_PACKAGE_PATH_NOT_EXPORTED — every coverage snapshot fails and no
data is uploaded.
Switching to dynamic `await import()` resolves with the import
condition throughout and works under both plain Node and tsx.
…cast Tighten V8FunctionCoverage to match Node's Profiler.FunctionCoverage contract (functionName and isBlockCoverage are always emitted by V8), which lets script.functions pass through to ast-v8-to-istanbul.convert without a cast. Update the test fixture accordingly. Source map still needs a cast since loadSourceMap returns Record<string, unknown>.
Code ReviewTusk Review: No issues foundUnit TestsGenerated 7 tests - 7 passedTest Summary
ResultsTusk's tests all pass. The critical test here is the dynamic import validation — it confirms that switching from Avg +35% line coverage gain across 1 file
Coverage is calculated by running tests directly associated with each source file, learn more here. Last run on fa0fad8. Comment Was Tusk helpful? Give feedback by reacting with 👍 or 👎 |


Switch
require("ast-v8-to-istanbul")toawait import("ast-v8-to-istanbul")incoverageProcessor.tsso coverage works for SDK consumers running their service viatsx.The bug
ast-v8-to-istanbulis ESM-only and pulls inestree-walker, whosepackage.jsonexportsfield declares only animportcondition (norequire). Under plain Node,require()of an ESM module loaded via require-esm resolves transitive imports with the import condition and the chain works. Under tsx's ESM loader, the samerequireresolves transitive imports with the require condition, falls offestree-walker's exports map, and throwsERR_PACKAGE_PATH_NOT_EXPORTED— every coverage snapshot fails and no per-test coverage data is sent back to the CLI.The error caught from CI logs:
The
tsx@4.21.0/.../register-D46fvsV_.cjsframe is what flips resolution into the require condition.Why dynamic import vs other approaches
createRequire(import.meta.url)("ast-v8-to-istanbul")has the same failure mode — it's still going through Node'srequirepath.estree-walkerto a version with arequirecondition isn't possible — it doesn't have one in any version.Dynamic
import()is the right primitive here: it resolves with theimportcondition unconditionally, regardless of which loader is active. The function is alreadyasyncso there's no structural change.