Repository navigation
Permalink
Choose a base ref
{{ refName }}
default
Choose a head ref
{{ refName }}
default
Comparing changes
Choose two branches to see what’s changed or to start a new pull request.
If you need to, you can also or
learn more about diff comparisons.
Open a pull request
Create a new pull request by comparing changes across two branches. If you need to, you can also .
Learn more about diff comparisons here.
base repository: privatenumber/tsx
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v4.22.4
Could not load branches
Nothing to show
Loading
Could not load tags
Nothing to show
{{ refName }}
default
Loading
...
head repository: privatenumber/tsx
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v4.22.5
Could not load branches
Nothing to show
Loading
Could not load tags
Nothing to show
{{ refName }}
default
Loading
- 5 commits
- 9 files changed
- 1 contributor
Commits on May 31, 2026
-
test: cover ESM-syntax dependency with omitted "type" field
## Problem Issue [#761](#761) reported that importing a dependency that ships ESM via `exports.import` but omits a `"type"` field surfaced as `SyntaxError: ... does not provide an export named '...'`. The bug landed in 4.21.0 and was fixed implicitly by the 4.21.1 sync-hook refactor: tsx still classifies the dependency as CJS, but Node's default-on syntax detection (>= 20.19.0 / >= 22.7.0) re-classifies the output through CJS<->ESM interop and recovers the named export. There is no existing test that pins this behavior, so a future refactor of the classifier or hook layout could silently regress it. ## Changes - Adds one version-sensitive regression test gated on `requireEsm`, mirroring the reporter's shape: an ESM-typed entry that named-imports from `module-a`, where `module-a/package.json` has `exports.import` but no `"type"`, and `index.js` uses ESM syntax. - Asserts `exitCode === 0` and `stdout === 'hello world from esm'`, so a reintroduction (the dependency classified as CJS with named exports dropped) fails loudly on the supported Node matrix.
Configuration menu - View commit details
-
Copy full SHA for 1472f3e - Browse repository at this point
Copy the full SHA 1472f3eView commit details -
test: lock in lenient ESM for ambiguous and CJS-typed packages
## Problem tsx is intentionally more lenient than Node when classifying ambiguous packages: a `.js`/`.ts` file in a `package.json` without `"type"` (or with `"type": "commonjs"`) that mixes ESM `import`/`export` syntax with explicit `require()` calls runs successfully. tsx classifies the file as CJS and esbuild rewrites the ESM imports while passing `require` through. Plain Node detection refuses these files (`ReferenceError: require is not defined in ES module scope`); tsx's existing lenient behavior is what lets a decade of pre-ESM TypeScript projects keep running. This contract was not exercised by any test. A speculative "match Node's syntax detection" refactor against an earlier head of this branch passed the full suite while silently breaking every hybrid file. These tests close that gap. ## Changes New `commonjs-mode-contracts.ts` block `lenient ESM in CommonJS-classified files`, exercising four shapes through the existing `commonJsModes` loop (`omitted type` and `explicit commonjs` shapes per test): - hybrid `import` + bare `require()` in `.ts` - TypeScript `import x = require()` interop combined with bare `require()` - hybrid `import` + bare `require()` in `.js` - `export` paired with bare `require()` of a `.cjs` sibling Each test asserts exit 0, expected stdout, and empty stderr, with the existing `onTestFail(() => console.log(label, result))` pattern so the failing shape is visible.
Configuration menu - View commit details
-
Copy full SHA for 75d9bf0 - Browse repository at this point
Copy the full SHA 75d9bf0View commit details -
test: cover __dirname, __filename, & require.cache in CJS TS file
## Problem Two closed bugs left the CJS-scope-globals contract untested: - [#694](#694) — Node 23.6+ began classifying `.ts` entrypoints as ESM in `type: "commonjs"` packages, breaking `__dirname` and `__filename` with `ReferenceError: __dirname is not defined in ES module scope`. Implicit fix landed in the 4.20/4.21 era; no paired regression test. - [#726](#726) — `require.cache` returned `undefined` in v4.20 after the loader refactor; the maintainer reverted in v4.20.3 and explicitly noted it should land as a test for the next release. A future refactor of the CJS-classification path could silently reintroduce either bug. ## Changes One new `commonjs-mode-contracts.ts` test, iterated through both `omitted type` and `explicit commonjs` package shapes (two process spawns total), asserts that a `.ts` entrypoint sees: - `__dirname` → fixture path - `__filename` → fixture entry path - `require.cache` → populated object after a sibling `require('./dep.cjs')`
1Configuration menu - View commit details
-
Copy full SHA for 596cd1f - Browse repository at this point
Copy the full SHA 596cd1fView commit details
Commits on Jun 18, 2026
-
Configuration menu - View commit details
-
Copy full SHA for ca501a9 - Browse repository at this point
Copy the full SHA ca501a9View commit details
Commits on Jul 2, 2026
-
fix: isolate hook state per async module.register() registration
## Problem `tsImport()` stops settling after ~5 calls on Node versions that use the async `module.register()` path (below v24.11.1 / v25.1.0): resolution work multiplies ~10x per call, so the 6th call appears to hang forever. Regression in 4.22.0. The async path registers a cache-busted copy of the hook entry (`./esm/index.mjs?<timestamp>`) per registration so that each copy gets its own state. Since 4.22.0, `esm/api/register.ts` imports the hook factories for the sync `module.registerHooks()` path, which moved the hook modules — including the mutable `data` singleton — into a bundler chunk shared by both entries. Chunks evaluate once per thread, so every registration's `initialize()` mutated the same object: all chained registrations claimed the latest namespace, and each chain entry re-applied TypeScript extension guessing to the failed candidates of the hook below it. Fixes #806 ## Changes - Hook state is now created in the hook entry module itself (`src/esm/index.ts`) — the unit the async path cache-busts — so each registration gets its own state regardless of how the bundler chunks shared modules. - `initialize` / `globalPreload` / `load` / `resolve` are constructed per entry evaluation from factories; the module-level `data` singleton is removed (`createDefaultData()` + `createInitialize()` / `createGlobalPreload()`). - Regression test: repeated `tsImport()` of a failing import must reject every call. Runs across the CI Node matrix, so Node 18/20/22 exercise the async path.
Configuration menu - View commit details
-
Copy full SHA for a305f36 - Browse repository at this point
Copy the full SHA a305f36View commit details
Loading
This comparison is taking too long to generate.
Unfortunately it looks like we can’t render this comparison for you right now. It might be too big, or there might be something weird with your repository.
You can try running this command locally to see the comparison on your machine:
git diff v4.22.4...v4.22.5