notes/node/README.md
Maintainer notes tracking how Node module-loading internals affect tsx. These are not user docs (files: ["dist"] keeps them out of the npm package).
tsx fills gaps in Node's TypeScript and module-loading story. As Node adds native support — type stripping, require(esm), sync module hooks — those gaps get smaller, which is the preferred direction. These notes keep tsx aligned with Node behavior and identify the remaining gaps where tsx still helps users today.
| File | Covers |
|---|---|
| module-hooks.md | Async module.register() vs sync module.registerHooks(), loader-worker cost, CJS-reload safety |
| cjs-loader.md | CJS resolution, cache identity, require.extensions, eager ESM error decoration |
| cjs-esm-interop.md | ESM importing CJS, CJS requiring ESM, named-export preparsing, module.exports interop |
| type-stripping.md | Node's native TypeScript pipeline, limitations, current issues, tsconfig contract |
| File | Covers |
|---|---|
| gate-reference.md | Smaller gates: import attributes, import.meta props, wasm, package main, test runner flags |
| node-integration-points.md | Node loader integration points to re-verify across releases |
src/utils/node-features.ts gate | Context |
|---|---|
moduleRegister | module-hooks.md |
moduleRegisterHooksCjsReload | module-hooks.md |
importAttributes | gate-reference.md |
testRunnerGlob | gate-reference.md |
cliTestFlag | gate-reference.md |
esmLoadReadFile | cjs-esm-interop.md |
importMetaPathProperties | gate-reference.md |
requireEsm | cjs-esm-interop.md |
requireEsmNoWarning | cjs-esm-interop.md |
cjsNamespaceModuleExports | cjs-esm-interop.md |
nativeTypeScript | type-stripping.md |
wasmModules | gate-reference.md |
cjsNamespaceFromLoadHook | cjs-esm-interop.md |
requireEsmExtensionlessMjs | cjs-esm-interop.md |
modulePackageMainResolution | gate-reference.md |
Each documented behavior should record:
github.com/nodejs/node/blob/<tag>/...#L...), ideally last-without and first-with for boundaries;Optional sections:
## Decisions for untestable tradeoffs and rejected alternatives;## Implementation history in tsx for old commits/PRs that explain why the current approach exists.Testable behavior belongs in tests/; these notes explain context and maintenance intent.
Version boundaries are pinned from Node's git history, because backports often land after the main-line commit.
cd /path/to/nodejs/node
git fetch --tags
# Every commit carrying a PR-URL trailer = main-line commit + all backport
# cherry-picks. Collect all release tags containing them, first per major line:
pr=59929
{ for sha in $(git log --grep "PR-URL: https://github.com/nodejs/node/pull/${pr}\$" --format=%H --all); do
git tag --contains "$sha" | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$'
done; } | sort -u -V | awk -F. '{maj=$1} maj!=prev{print; prev=maj}'
After finding the boundary, verify the exact source shape with git show <tag>:<path> and link the public GitHub URL. Do not link to moving branches for source claims.
Verification caveats:
[from, before) — opened by one PR, closed by another.