egoist / tsup

The simplest and fastest way to bundle your TypeScript libraries.
https://tsup.egoist.dev
MIT License
9k stars 217 forks source link

Possible to have `export default` emit as `module.exports` #572

Closed alii closed 1 year ago

alii commented 2 years ago

I know it's not the preferred way of having a default export, but it would save a lot of time if this would be possible. I'm writing an ESLint plugin and currently cannot import the plugin into a CJS file because the module is exported as module.exports.default rather than simply module.exports.

Have only come across this issue which I wasn't able to find a way to implement it properly: https://github.com/egoist/tsup/issues/283 and also #255 but I got the warnings below:

image

Thanks!

Upvote & Fund

Fund with Polar

paambaati commented 2 years ago

+1. It isn't obvious what the fix is when trying to write modules with export default ... with CJS output.

egoist commented 2 years ago

You can re-export it in another file:

module.exports = require('./dist/index').default

And use this file in the main field in package.json instead

paambaati commented 2 years ago

FWIW, I solved this by adding this to my tsup config –

import { defineConfig } from 'tsup'

export default defineConfig({
  splitting: false,
  sourcemap: false,
  minify: true,
  clean: true,
+  esbuildOptions: (options) => {
+   options.footer = {
+    // This will ensure we can continue writing this plugin
+      // as a modern ECMA module, while still publishing this as a CommonJS
+      // library with a default export, as that's how ESLint expects plugins to look.
+      // @see https://github.com/evanw/esbuild/issues/1182#issuecomment-1011414271
+      js: 'module.exports = module.exports.default;',
+    }
  },
  entry: ['src/index.ts'],
})

See https://github.com/getoslash/eslint-plugin-tap/pull/36 for reference.

lvjiaxuan commented 2 years ago

You can re-export it in another file:

module.exports = require('./dist/index').default

And use this file in the main field in package.json instead

We can manually do that in our personal file, but maybe in some runtime env like ESlint couldn't do that.

lvjiaxuan commented 2 years ago

I know it's not the preferred way of having a default export, but it would save a lot of time if this would be possible. I'm writing an ESLint plugin and currently cannot import the plugin into a CJS file because the module is exported as module.exports.default rather than simply module.exports.

Have only come across this issue which I wasn't able to find a way to implement it properly: #283 and also #255 but I got the warnings below: image

Thanks!

I am wondering why it's not the preferred way of having a default export. Could you explain any arguments for me, thanks~~

u3u commented 1 year ago

Everyone, I created a library of fix-tsup-cjs to solve this problem. It can also fix d.ts (export =) Just modify your build script to tsup && npx fix-tsup-cjs 🎉

Related issues: #255 #283 #338 #572 #710 /cc @pushkine @privatenumber @tbnritzdoge @yaquawa

github-actions[bot] commented 1 year ago

:tada: This issue has been resolved in version 7.2.0 :tada:

The release is available on:

Your semantic-release bot :package::rocket:

psirenny commented 1 year ago

Should this issue be closed? I find that I still need the module.exports = module.exports.default workaround. The --cjsInterop flag doesn't set module exports to the default value even in modules with just a default export.

sxzz commented 1 year ago

Could you please provide a minimum reproduction repo?

psirenny commented 1 year ago

Here's a GitHub repo that reproduces the issue: https://github.com/psirenny/tsup-cjs-default-export

The source directory has a module exporting a default string. There's a test.cjs file that imports from the built module. It expects to import a string but instead imports a { default: [Getter] }.

sxzz commented 1 year ago

@psirenny Because of issue https://github.com/evanw/esbuild/issues/3281, we have to also enable --splitting flag as a workaround.

psirenny commented 1 year ago

we have to also enable --splitting flag as a workaround.

Aha, that does indeed do the trick. Thanks for explaining that!

Mintnoii commented 7 months ago

@sxzz --splitting does work indeed, but I'm not quite sure why enabling code splitting resolves this issue. Additionally, the documentation section on --cjsinterop doesn't explicitly mention the need to use --splitting concurrently. Could you briefly explain its underlying mechanism? Thank you very much!

sxzz commented 7 months ago

See https://github.com/evanw/esbuild/issues/3281. In CJS mode, esbuild didn't generate valid export metadata. I think it's a bug of esbuild. So it blocked using cjsinterop in CJS. But with --splitting flag, tsup will treat CJS as ESM at first then convert ESM back into CJS. So this tricky way can solve the problem.

For docs, maybe someone can submit a PR for mentioned that.