Skip to content

4.x: Bun: require: ["tsx/esm"] is mandatory even though tsx isn't used under Bun #5697

Description

@mirao

What are you trying to achieve?

Bun is a supported runtime for CodeceptJS 4 — the 4.0 release post says under Native ESM: "Your project needs "type": "module" and NodeJS 20 or Bun."

Run a TypeScript suite with Bun as the runtime (bunx --bun codeceptjs run).

Bun transpiles TypeScript natively, so no tsx / ts-node loader is involved. I expected to be able to leave require out of the config and not install tsx at all.

What do you get instead?

require: ['tsx/esm'] is mandatory even under Bun. Without it the run aborts with process.exit(1) and the "TypeScript Test Files Detected but No Loader Configured" banner — although the tests run perfectly fine once the check is bypassed.

The cause is checkTypeScriptLoader() in lib/utils/loaderCheck.js (called from validateTypeScriptSetup() in lib/codecept.js). It only string-matches the require array against a hardcoded list of Node loaders; there is no check of whether the runtime already handles TypeScript:

https://github.com/codeceptjs/CodeceptJS/blob/main/lib/utils/loaderCheck.js#L12-L22

So under Bun the require entry exists purely to satisfy that string match, and tsx sits in devDependencies and is imported on every run only to do nothing. Bun's module.register() is a no-op stub, so tsx's resolve/load hooks are never invoked. Verified with a hooks module that logs on every call:

// hooks.mjs
export function resolve(spec, ctx, next) { console.error('HOOK resolve:', spec); return next(spec, ctx) }
export function load(url, ctx, next) { console.error('HOOK load:', url); return next(url, ctx) }

// reg.mjs
import module from 'node:module'
module.register('./hooks.mjs', import.meta.url)
await import('./target.ts')       // contains `enum E { A = 1 }`
  • bun --bun reg.mjs → no HOOK line at all, and the enum evaluates fine (Bun transpiled it itself).
  • node --experimental-strip-types reg.mjsHOOK lines fire, i.e. registration works on Node.

Minimal reproduction

package.json      { "name": "bun-tsx-repro", "type": "module", "private": true }
codecept.conf.ts  (below)
basic_test.ts     (below)
// codecept.conf.ts
export const config = {
    tests: './*_test.ts',
    helpers: {},
    name: 'bun-tsx-repro',
}
// basic_test.ts
enum Color { Red = 'red' }

Feature('bun repro')

Scenario('plain TS scenario with an enum', () => {
    if (Color.Red !== 'red') throw new Error('enum broken')
})
bun add --dev codeceptjs@4.1.0      # note: tsx is NOT installed
bunx --bun codeceptjs run

Result — exit 1, the loader banner. With checkTypeScriptLoader() patched to return true under Bun and nothing else changed (still no require, still no tsx in node_modules):

CodeceptJS v4.1.0 #StandWithUkraine
bun repro --
  ✔ plain TS scenario with an enum in 1ms
  OK  | 1 passed   // 4ms

I also confirmed this on a real 14-suite Playwright project (~1900 tests): removing the require line, deleting tsx from node_modules entirely (import('tsx/esm')Cannot find package 'tsx') and applying only the one-line patch, a full browser test passed unchanged — page objects, enums and extensionless relative imports included. Restoring the stock loaderCheck.js alone brings the banner back.

Suggested fix

export function checkTypeScriptLoader(requiredModules = []) {
  // Bun transpiles TypeScript natively; no loader is needed
  if (process.versions.bun) return true

  // Check if a loader is configured in the require array
  return (
    requiredModules.includes('tsx/esm') ||
    // ...
  )
}

This is deliberately Bun-specific and not "skip the check when the runtime can do TypeScript". Node cannot replace tsx here: its native type stripping rejects enums (ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX) and does not resolve extensionless relative imports.

Details

  • CodeceptJS version: 4.1.0 (main is unchanged in this area)
  • Runtime: Bun 1.4.2 — bunx --bun codeceptjs
  • NodeJS Version: n/a, Bun is the runtime (Node 24.18.0 only used for the hooks comparison above)
  • Operating System: Ubuntu 24.04
  • Configuration file: see the minimal reproduction above

Created by AI agent (Claude Code)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions