uuidjs/uuid

No default export provided

guybedford opened this issue · 9 comments

Describe the bug

Many packages import uuid as:

const uuid = require('uuid');

When converted into ES modules, this would correspond to:

import uuid from 'uuid';

When running the browser though, the browser ES module build does not support a "default" export and as a result packages on jspm using UUID are currently breaking.

How to reproduce

Try, for example, import('https://jspm.dev/@google-cloud/bigquery')

Expected behavior

UUID should be configured such that the shape of the module between browsers and Node.js remains the same - the default export should exist for both. Node.js CommonJS modules are always represented by a default export, see https://nodejs.org/dist/latest-v15.x/docs/api/esm.html#esm_commonjs_namespaces for more info.

Additional information

The suggested fix would be to add an export default uuid to the ES module namespace.

Oh gosh. So neither default nor named exports work with commonjs at all 🤦 .

@guybedford as far as I understand the problem is not default export itself but importing esm from commonjs, right?

@TrySound the problem is that the "ES Module Representation" of a CommonJS module should be based on importing the default export:

import uuid from 'uuid.cjs';

So if the browser branch is now going to turn that CJS module into an ES module, then it should have the same "shape".

Will expanding browser branch like node version solve the problem?

- "default": "./dist/esm-browser/index.js"
+ "default": {
+   "module": "./dist/esm-browser/index.js",
+   "require": "./dist/browser/index.js",
+   "import": "./dist/browser/wrapper.mjs"
+ },

Yes exactly, fixing the require + browser case to have a default export would work. It would also work and be a lot simpler just as well to add a default export to the browser ESM build.

It would also work and be a lot simpler

Not really. It will lead to api fragmentation and make ts users life miserable.

ts users life miserable.

Thought this was already the case :P

Seriously though, the fix looks good, and just requires the single require wrapper doing:

export * from './index-browser.mjs';
import * as m from './index-browser.mjs';
export default m;

then the exports in order as you have on the default with the others pointing to the current browser index.

I wanted to follow up on this issue - I've come around to the view of @sokra and @lukastaegert that allowing ES module imports into CommonJS should support different shapes than just { default }.

I've since implemented something similar to the requireReturnsDefault: 'preferred' option in RollupJS for JSPM (https://github.com/rollup/plugins/tree/master/packages/commonjs#requirereturnsdefault). And cases like import('https://jspm.dev/@google-cloud/bigquery') are working now with uuid being required as an ES module as require('uuid') being rewritten into:

import * as _uuid from 'uuid';
var uuid = 'default' in _uuid ? _uuid.default : _uuid.

I think there is still some clear incompatibility on these paths between tools as Webpack has one way, RollupJS has a default and lots of options, and I'm not sure what esbuild does. A clear investigation into these cases may be found in Tobias's compat table where the risks can be assessed but I haven't been able to fully check that.

Hopefully we can work towards this case being very clearly supported between tools, or at least have a very clear subset for it.

As to whether the PR should be merged, it would likely be a safer compatibility to still merge it, but could be fine to leave as well now.

I'm closing this issue as resolved since I believe it seems tools will ensure library authors shouldn't have to be too concerned about this, but let's continue discussion if anyone is interested too.