privatenumber/tsx

Unable to import default exports with ESM

teddybee opened this issue · 6 comments

Precheck

  • I searched existing issues before opening this one to avoid duplicates
  • I'm able to reproduce this issue and prove it with a minimal reproduction
  • I understand this is not a place to ask for free debugging support

Problem

I would like to import a default export of a lib.
Unfortunately with tsx I couldn't do this:

import zennv from 'zennv';

export const env = zennv({...})

I got this error:
Error: _zennv.default is not a function

Expected behavior

Working with default imports.

Minimal reproduction URL

https://stackblitz.com/edit/stackblitz-starters-wkqcy7?file=package.json,src%2Findex.ts,src%2Fenv.ts

Version

v4.6.0

Node.js version

v20.10.0

Package manager

pnpm

Operating system

Windows

Contributions

  • I plan to open a pull request for this issue
  • I plan to make a financial contribution to this project

zod or the extra index file doesn't seem necessary for the reproduction. Please keep it minimal as isolating the bug creates more work to vet your reproduction.

Yes it does, as it is needed for the param of the constructor.

Not necessary. Here's a minimal reproduction: https://stackblitz.com/edit/stackblitz-starters-sykrfb

I'm having the same issue with NextAuth providers.

Tried to tweak the TS config without any success.

In the repro, changing zenv to zenv.default fixes the issue (same in my case, but it's not a workaround I can use)

I think I'm hitting this issue with some older libraries, too. What is the recommended workaround or do I need to stick to ts-node-dev?

I've started investigating this, and so far found that the import happens fine if I specify type: "module" in package.json or use a .mts file as a target, but otherwise an extra default key indirection gets added to the returned object.

Edit № 1: The format returned here becomes module when the specifications from above are applied and commonjs otherwise:

const loaded = await nextLoad(url, context);

Edit № 2: Ah, I see, it happens here:

export const getFormatFromFileUrl = (fileUrl: string) => {
const format = getFormatFromExtension(fileUrl);
if (format) {
return format;
}
// ts, tsx, jsx
if (tsExtensionsPattern.test(fileUrl)) {
return getPackageType(fileUrl);
}
};

Edit № 3: I'm starting to think that this might not actually be an issue with tsx itself, but with certain libraries presenting their formats incorrectly. The same "double default" behavior can be observed with Node itself as well when using CommonJS modules:

// example.js
module.exports = { default: 'example' }

// node
> await import('./example.js')
[Module: null prototype] { default: { default: 'example' } }