Skip to content
Permalink

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
Choose a base ref
...
head repository: privatenumber/tsx
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v4.22.5
Choose a head ref
  • 5 commits
  • 9 files changed
  • 1 contributor

Commits on May 31, 2026

  1. 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.
    privatenumber committed May 31, 2026
    Configuration menu
    Copy the full SHA
    1472f3e View commit details
    Browse the repository at this point in the history
  2. 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.
    privatenumber committed May 31, 2026
    Configuration menu
    Copy the full SHA
    75d9bf0 View commit details
    Browse the repository at this point in the history
  3. 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')`
    privatenumber committed May 31, 2026
    1 Configuration menu
    Copy the full SHA
    596cd1f View commit details
    Browse the repository at this point in the history

Commits on Jun 18, 2026

  1. Configuration menu
    Copy the full SHA
    ca501a9 View commit details
    Browse the repository at this point in the history

Commits on Jul 2, 2026

  1. 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.
    privatenumber authored Jul 2, 2026
    Configuration menu
    Copy the full SHA
    a305f36 View commit details
    Browse the repository at this point in the history
Loading