Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tsx loader is not compatible on Bun #15

Open
6 of 7 tasks
guyutongxue opened this issue Jan 19, 2025 · 0 comments
Open
6 of 7 tasks

tsx loader is not compatible on Bun #15

guyutongxue opened this issue Jan 19, 2025 · 0 comments

Comments

@guyutongxue
Copy link

Describe the bug

Assume that we have following files:

  • mod.ts
import "./lib/a.js";
export {};
  • lib/a.js
import { createRequire } from "node:module";
const require = createRequire(import.meta.url);
const patch = require("./data.json");
export {};
  • lib/data.json
{}

And the script tries to import mod.ts with importx will failed under Bun:

import * as importx from "importx";
const mod = await importx.import("./mod.ts", {
  parentURL: import.meta.url,
  cache: false,
});
console.log(mod);
> 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 support Module.register API that tsx'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 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 bun
const mod = await importx.import("./mod.ts", {
  parentURL: import.meta.url,
  loader: "jiti", // ...while specifying jiti loader explicitly here
  cache: 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.ts
const tsImport = ( specifier: string, options: string | Options) => {
	const parentURL = /* [...] */;
	const namespace = Date.now().toString();
	// Keep registered for hanging require() calls
	const cjs = 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
	 */
	const api = register({         // !!!! (2)
		namespace,
		options,
	});
	return api.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:

  1. Let @unocss/config calls importx with cache: null, which enables Bun's native TypeScript loader. But maybe disabling cache is required in that scenario.
  2. Let importx to do clean stuff after tsImport fulfilled. This is also hard to implement because tsImport API doesn't provide an unregister method.
  3. Let importx detect Bun by checking globalThis.Bun. Then disable tsx loader if using Bun.
  4. Let tsx change tsImport's implementation to do clean stuff (i.e. unregister) finally.
  5. Let tsx detect Bun and failed fast before register, preventing breaking further module resolutions.
  6. Let Bun implements node:module in a compliant behavior.

Workaround

Here is also a workaround by preventing touching Bun's buggy node:module.

import Module from "node:module";
// @ts-expect-error
Module.register = undefined;
Object.defineProperty(Module, "_resolveFilename", {
  value: null,
  writable: false,
  configurable: false,
  enumerable: false,
});

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

  • Follow our Code of Conduct
  • Read the Contributing Guide.
  • Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
  • Check that this is a concrete bug. For Q&A, please open a GitHub Discussion instead.
  • The provided reproduction is a minimal reproducible of the bug.

Contributions

  • I am willing to submit a PR to fix this issue
  • I am willing to submit a PR with failing tests (actually just go ahead and do it, thanks!)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant