zustand: Importing in a `type=module` project fails

First: thanks for the simplified version of state management. So glad I found it, so sad it took me so long 😉.

I created a small project, where the package.json sets it to be a ESM project, by type=module. See https://codeberg.org/wolframkriesing/bug-exploration-zustand-and-esm/src/branch/main/package.json#L4

I ran into two issues, that are somehow related to #201 but I am not sure exactly what’s the root cause.

The Problem(s)

I wanted to get zustand to be testable with mocha in an ESM setup. But I ran into:

  1. When using type=module and importing zustand/vanilla mocha says there is no such file. No idea what that might be. This is the JS file and the error it throws: https://codeberg.org/wolframkriesing/bug-exploration-zustand-and-esm/src/branch/main/src/vanilla.test.js
  2. When importing zustand/vanilla.js the default export is somehow broken. A change of exports.default = create;
    to
    module.exports = create;
    in zustand/vanilla.js would fix it. This sounds like a packager problem in zustand? https://codeberg.org/wolframkriesing/bug-exploration-zustand-and-esm/src/branch/main/src/vanilla-js.test.js

Is the ESM setup just not there yet, and not recommended? I had hoped to go there gradually. How do others solve this issue?

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 33 (15 by maintainers)

Most upvoted comments

Sounds like a tricky one, not fun. What about a (challenging) suggestion: Why not ship ESM only? One file, only import/export, pure ESM. Why?

  1. this is where JS is going
  2. let the user transpiler if they need it (anyways)
  3. much simpler to maintain

Sounds more like a major version thing, and it might prevent more compat work?

I don’t know of a pattern that would allow you to get rid of the preceding require. Node’s the limiting factor there in the keys it has implemented.

As for the dual hazard, I suppose the only way to tell is to test. I’ll try a few things out, see if I can get something to break.

#357 adds “node” only. So, without “require” node with require() breaks. Hmmm.

Why doesn’t it work with pointing at esm/index.js? I see it doesn’t know if it’s mjs or cjs.

do any other packages depend on zustand/valtio/jotai?

Yeah, there are some 3rd party zustand packages and a few jotai packages (the library use is a big selling point in jotai.)

Another way around this can be to use both extensions, i.e., .cjs and .mjs.

I originally thought that too and tried, but it breaks old bundlers which don’t understand exports or something like that. We should keep .js and do things in exports.

The “workaround” requires using the default key on the exported object, i.e., import create from 'zustand/vanilla'; returns create: { default: <create function> }, so create.default() is needed.

Edit: Might just be something that needs to be hashed out next major. Altering exports in any way really causes breaking changes, so it’d be easier to figure this out going forward on a v4

@wolframkriesing Yeah, like @rschristian mentioned, the current v3.4.1 config only uses cjs even if you have type=module. This is regretful, but intentional for some reasons mentioned above.

We would like to have better ideas in the future.

My question for now is if the original issue with type-module is solved (as workaround) with v3.4.1 with CJS, that was not solved in v3.3.3.

@rschristian I forgot to mention that specifying import incurs dual package hazard. https://nodejs.org/api/packages.html#packages_dual_package_hazard

This is a real issue with valtio. For zustand, it might not be important because right now we don’t have such exports. (but we plan to add a new feature which impacts on it.) We also want to keep the configs consistent as much as possible among zustand/jotai/valtio for maintainability.

So far, I don’t find any good configs to allow mjs in node. Let’s keep research on it.

What am I doing wrong that it works for “everyone” else?

It is very strange, that the zustand/esm/vanilla.js does not get used, even though my project is pure ESM.

As mentioned in the above comments, the current setup only supports Webpack v5. The key used in exports (module) is not implemented by anything else as far as I know. Definitely not standard, though I suppose something out there could also support it.

As far as I can tell, webpack esm and nodejs mjs is not 100% equal.

@wolframkriesing We releaseed v3.4.1 lately and it ended up with something like what you suggested in #336 originally. Would you see how it works or not in your use case?