-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Error with zustand exports in parcel production builds #1563
Comments
Can @barelyhuman help please? |
I’ll check this out 👍 |
;tldr build with longer version so, this is actually a problem with Parcel where during optimization and variable hoisting it ends up creating invalid assigns to the original export. The related issue also, you can fix this for now by using As for if something can be done by zustand? Here's a screenshot of how you could fix it manually in the generated build, you disable the overlapping assignments on the a.k.a disable them for both the exported and used functions which in practicality would be impossible to do manually for a project with a lot of these kinds of libraries. |
This commit adds `--no-scope-hoist` to `parcel build` to work around an issue with zustand and parcel. See pmndrs/zustand#1563.
This commit adds `--no-scope-hoist` to `parcel build` to work around an issue with zustand and parcel. See pmndrs/zustand#1563.
--no-scope-hoist inflates our build as far as i can see. Is there a different way around? i have this error with zustand 4.3.3 and parcel 2.8.3 |
I understand, sadly that's the current solution. We could add in additional patchwork in the builds but if the final user bundler changes it, then it's very hard for us to control what the output would be. I'll link an experimental zustand build in a bit that you can try and let me know if that solves your problem |
I'm on it. Plz send! I'll check it at once! We happen to use parcel (I think this is a mistake to use it). But we stuck with it so need to resolve this somehow for a production build that should be reasonably sized... |
Scope hoisting is mostly useful, so I wouldn't say so but yeah, this isn't something that's a problem with just parcel or zustand. It's more because of how we have mixed exports (both default and named) from the same file and is hence causing these issues. It's not a straightforward task for parcel to handle this and so I wouldn't blame parcel for it, it's more a mistake from our end. This is why we've been recommending people to move into using named exports instead. Anyway, to the problem at hand here's the experimental fix https://ci.codesandbox.io/status/barelyhuman/zustand/pr/13/builds/350389 You can go through the Once done, please do confirm that it works for you Edit: |
Works fine now. Thank you very much! I've heard several opinions that export default is bad. But can you explain a little bit why it is an antipattern in your opinion?.. |
TLDR; The point is to not do both default and named exports from the same module. And it's considered bad because you end up polluting the default export's prototype , which can cause Longer Explanation There's quite a few explanantions for this but our main reason was to keep the imports aesthetic and that backfired later due to introduction of ESM and we already had an API that was solidified by then. The ES spec has Now, on the other hand the node's CommonJS require pattern works with // module1.js
export const a = () => {};
export const b = () => {};
export default a;
// ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
const a = () => {};
const b = () => {};
exports.a = a;
exports.b = b;
exports.default = a;
// module2.js
export const a = () => {};
export const b = () => {};
export const c = () => {};
// ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
const a = () => {};
const b = () => {};
const c = () => {};
exports.a = a;
exports.b = b;
exports.c = c; Each of these can now be imported in the following manner import aliasedA, { a, b } from "module1.js";
import * as pkg from "module2.js";
const { a, b, c } = pkg;
// or in commonjs
const aliasedA = require("module1.js").default;
const { a, b } = require("module1.js");
const { a, b, c } = require("module2.js"); This isn't that harmful since all that's changed is the added Though, most people are used to having a bundler or compiler of some kind and some of them replace As a user, you'd want that consistency across environments, since you wouldn't wanna spend time figuring out // TS with moduleInterop off
import * as pkgOne from "module1.js";
const { a, b } = pkgOne;
// and pkgOne itself is also `a`
import * as pkg from "module2.js";
const { a, b, c } = pkg;
// TS with moduleInterop on
import pkgOne, { a, b } from "module2.js";
import { a, b, c } from "module2.js";
// esm
import pkgOne, { a, b } from "module2.js";
import { a, b, c } from "module2.js";
// cjs
const pkgOne = require("module2.js").default;
const { a, b } = pkgOne;
const { a, b, c } = require("module2.js"); Though, you'd see zustand,jotai,valtio have tried to maintain a very specific import pattern no matter CJS, ESM, JEST Sandbox, it doesn't matter who handles the dependency resolution, we want people to maintain the same code and this is where all the problems show up. This is what the current export for exports.create = create;
exports.default = react;
exports.useStore = useStore; and here's how you'd have to write it in different envs if we didn't patch the exports // ESM
import create from "zustand";
// CJS
const create = require("zustand").default;
// JEST
const create = require("zustand").default; but if you've seen the docs, you don't have to, because we patch the exports on build time to also exports.create = create;
exports.default = react;
exports.useStore = useStore;
(module.exports = (exports && exports.default) || {}),
Object.assign(module.exports, exports); Which tries to go through all exports and if there's a // ESM
import create from "zustand";
// CJS
const create = require("zustand");
// JEST
const create = require("zustand"); Aesthetic but we still have a problem, we're basically polluting the function scope of the Which is better avoided. Now if I had only named exports , then I don't have to worry about bundlers // ESM / TS with or without moduleInterop
import { create, useStore } from "zustand";
// CJS
const { create, useStore } = require("zustand"); No more patching required, so I won't be polluting another function/export prototype, and neither do I have worry about that accidentally changing my execution context. The build fix I just made is actually adding the pollution manually as code instead of a programmatic patch which parcel is able to understand and hoist properly, it's not good but we'll have to wait till we can get rid of the default export altogether. |
Thank you! |
Summary
A new error occurred in production builds using
[email protected]
andparcel
. This error does not occur in[email protected]
and is probably related to #1531.The following error is displayed in the console when serving a production build:
Development server works fine.
This issue is similar to #1475.
Link to reproduction
https://github.com/nicobohne/zustand-parcel-reproduction
https://codesandbox.io/p/github/nicobohne/zustand-parcel-reproduction/main
Check List
Please do not ask questions in issues.
Please include a minimal reproduction.
The text was updated successfully, but these errors were encountered: