You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
> bun test.js
error: Cannot find module './data.json' from 'undefined'
Dive Deep
The bug occurs because the importx options are chosen with cache to false and loader to "auto". The first config disallow "native" loader (although Bun natively supports TypeScript but we can't import module disabling cache), and the second config will produce following loader candidates:
tsx
jiti
Firstly tsx will eventually failed with a weird error:
Cannot find module 'tsx://{"specifier":"./mod.ts","parentURL":"file:///home/my/workspace/test.js","namespace":"1737304325265"}' from '/home/my/workspace/node_modules/tsx/dist/register-RyGUjI6j.mjs'
That's because Bun doesn't supportModule.register API that tsx's ESM API is using:
(Above code register tsx:// protocol in module specifier for tsx's internal use. And absolutely, Bun do not recognize that.)
So we are falling through to the next candidate loader jiti. Notice that jiti loader should works under Bun if tsx has never been called:
// This script runs correctly under bunconstmod=awaitimportx.import("./mod.ts",{parentURL: import.meta.url,loader: "jiti",// ...while specifying jiti loader explicitly herecache: false,});console.log(mod);
But it won't run correctly after previous failing tsx loader's run. The output message is Cannot find module './data.json' from 'undefined' as we had seen on the very first example.
The reason of this is tsImport API of tsx/esm/api is not pure. It will call "register" functions that change how interpreter resolves CJS and/or ESM module. Here is part of that code:
// https://github.com/privatenumber/tsx/blob/28a3e7d2b8fd72b683aab8a98dd1fcee4624e4cb/src/esm/api/ts-import.tsconsttsImport=(specifier: string,options: string|Options)=>{constparentURL=/* [...] */;constnamespace=Date.now().toString();// Keep registered for hanging require() callsconstcjs=cjsRegister({// !!!! (1)
namespace,});/** * We don't want to unregister this after load since there can be child import() calls * that need TS support * * This is not accessible to others because of the namespace */constapi=register({// !!!! (2)
namespace,
options,});returnapi.import(specifier,parentURL);};
Here before doing actual import stuff, the library register (or inject, fairly speaking) some CJS resolve logic and some ESM resolve logic. And after import is fulfilled, those registered hook won't be unregistered. In tsx's opinion, these hooks are "restricted" under a so-called "namespace" and keeping them will be a better approach. But with a buggy Bun's node:module implementation, they will break subsequent require calls -- in the first example, the require("./data.json" will failed.
And that is why the issue happens.
Impact
@unocss/config, dependency of @unocss/postcss, use importx to load unocss.config.ts, with an option of auto loader and cache: false. Also a dependency of unocss, css-tree, calls require("./data.json") on module imported. So this issue will make running PostCSS that configured to UnoCSS errored under Bun.
Possible fixes
Because this issue is related to too many library (Bun, tsx, UnoCSS, and importx here), I cannot make sure where is the best place to fix it. Here is some possible apporach:
Let @unocss/config calls importx with cache: null, which enables Bun's native TypeScript loader. But maybe disabling cache is required in that scenario.
Let importx to do clean stuff after tsImport fulfilled. This is also hard to implement because tsImport API doesn't provide an unregister method.
Let importx detect Bun by checking globalThis.Bun. Then disable tsx loader if using Bun.
Let tsx change tsImport's implementation to do clean stuff (i.e. unregister) finally.
Let tsx detect Bun and failed fast before register, preventing breaking further module resolutions.
Let Bun implements node:module in a compliant behavior.
Workaround
Here is also a workaround by preventing touching Bun's buggy node:module.
Describe the bug
Assume that we have following files:
mod.ts
lib/a.js
lib/data.json
And the script tries to import
mod.ts
withimportx
will failed under Bun:Dive Deep
The bug occurs because the
importx
options are chosen withcache
tofalse
andloader
to"auto"
. The first config disallow"native"
loader (although Bun natively supports TypeScript but we can't import module disabling cache), and the second config will produce following loader candidates:tsx
jiti
Firstly
tsx
will eventually failed with a weird error:That's because Bun doesn't support
Module.register
API thattsx
's ESM API is using:https://github.com/privatenumber/tsx/blob/28a3e7d2b8fd72b683aab8a98dd1fcee4624e4cb/src/esm/api/register.ts#L59
(Above code register
tsx://
protocol in module specifier fortsx
's internal use. And absolutely, Bun do not recognize that.)So we are falling through to the next candidate loader
jiti
. Notice thatjiti
loader should works under Bun iftsx
has never been called:But it won't run correctly after previous failing
tsx
loader's run. The output message isCannot find module './data.json' from 'undefined'
as we had seen on the very first example.The reason of this is
tsImport
API oftsx/esm/api
is not pure. It will call "register" functions that change how interpreter resolves CJS and/or ESM module. Here is part of that code:Here before doing actual
import
stuff, the library register (or inject, fairly speaking) some CJS resolve logic and some ESM resolve logic. And after import is fulfilled, those registered hook won't be unregistered. Intsx
's opinion, these hooks are "restricted" under a so-called "namespace" and keeping them will be a better approach. But with a buggy Bun'snode:module
implementation, they will break subsequentrequire
calls -- in the first example, therequire("./data.json"
will failed.And that is why the issue happens.
Impact
@unocss/config
, dependency of@unocss/postcss
, useimportx
to loadunocss.config.ts
, with an option ofauto
loader andcache: false
. Also a dependency ofunocss
,css-tree
, callsrequire("./data.json")
on module imported. So this issue will make running PostCSS that configured to UnoCSS errored under Bun.Possible fixes
Because this issue is related to too many library (Bun,
tsx
, UnoCSS, andimportx
here), I cannot make sure where is the best place to fix it. Here is some possible apporach:@unocss/config
callsimportx
withcache: null
, which enables Bun's native TypeScript loader. But maybe disabling cache is required in that scenario.importx
to do clean stuff aftertsImport
fulfilled. This is also hard to implement becausetsImport
API doesn't provide anunregister
method.importx
detect Bun by checkingglobalThis.Bun
. Then disabletsx
loader if using Bun.tsx
changetsImport
's implementation to do clean stuff (i.e.unregister
) finally.tsx
detect Bun and failed fast beforeregister
, preventing breaking further module resolutions.node:module
in a compliant behavior.Workaround
Here is also a workaround by preventing touching
Bun
's buggynode:module
.Above snippet can be inserted to anywhere that before
tsImport
called.Reproduction
See above.
System Info
System: OS: Linux 6.1 Debian GNU/Linux 12 (bookworm) 12 (bookworm) CPU: (32) x64 13th Gen Intel(R) Core(TM) i9-13900KF Memory: 107.49 GB / 125.60 GB Container: Yes Shell: 5.9 - /usr/bin/zsh Binaries: Node: 22.10.0 - /run/user/1000/fnm_multishells/3410142_1737299607603/bin/node Yarn: 1.22.22 - /run/user/1000/fnm_multishells/3410142_1737299607603/bin/yarn npm: 10.9.0 - /run/user/1000/fnm_multishells/3410142_1737299607603/bin/npm pnpm: 10.0.0 - /run/user/1000/fnm_multishells/3410142_1737299607603/bin/pnpm bun: 1.1.45 - ~/.bun/bin/bun
Used Package Manager
bun
Validations
Contributions
The text was updated successfully, but these errors were encountered: