vitejs / vite

Next generation frontend tooling. It's fast!
http://vite.dev
MIT License
68.58k stars 6.19k forks source link

requires to access `.default` after build while it's not required in dev (CJS dependency) #2139

Closed songquanpeng closed 1 year ago

songquanpeng commented 3 years ago

Describe the bug

Encountered “Minified React error” in the production environment, the dev environment is fine.

error: Minified React error #130; visit https://reactjs.org/docs/error-decoder.html?invariant=130&args[]=object&args[]= for the full message or use the non-minified dev environment for full errors and additional helpful warnings.
    at vc (vendor.93c27df6.js:23)
    at d (vendor.93c27df6.js:23)
    at vendor.93c27df6.js:23
    at vendor.93c27df6.js:23
    at Ts (vendor.93c27df6.js:23)
    at ul (vendor.93c27df6.js:23)
    at ic (vendor.93c27df6.js:23)
    at tc (vendor.93c27df6.js:23)
    at ql (vendor.93c27df6.js:23)
    at zl (vendor.93c27df6.js:23)

Reproduction

Repo: https://github.com/songquanpeng/snippet-manager/tree/aa02a582c676bd3dcd5254f18d7087bb8a1a7220

My React application is on the web folder.

System Info

Summarization of the root problem

https://github.com/vitejs/vite/issues/2139#issuecomment-1024852072

underfin commented 3 years ago

Not encountered this error. Can you have a try after upgrade vite?

songquanpeng commented 3 years ago
PS D:\Project\Go\snippet-manager\web> vite --version
vite/2.0.1 win32-x64 node-v14.15.3

Dev: image Production: image

songquanpeng commented 3 years ago

And you can check out the deployed version here, built with Github Action: https://snippet.justsong.cn/

haikyuu commented 3 years ago

I ran into the same issue using latest Vite version.

Note

The title of the issue is very vague as this error is displayed by minified React instead of ANY React error. So it can be anything really. But in both our cases, this is the actual error Uncaught Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.

The first thing to do in this case is to debug the app in order to have more details: Use minify and dev mode in your vite.config.js in order to get the real issue with the possibility to inspect in the browser dev tools.

export default defineConfig({
  plugins: [reactRefresh()],
  mode: "development",
  build: {
    minify: false,
  }
});

Hacky solution in my case

In my case it was coming from using react-switch. And I hacked it to work using the following (don't tell anyone about this)

import S from "react-switch";
const Switch = S.default? S.default: S
console.log(S); // inspecting

Basically this is the content of the import {__esModule: true, default: ƒ}

The react-switch library is built as cjs so this is probably a cjs compatibility issue for Vite's Rollup build configuration ?

In the reproduction repo

If you follow the debugging steps, you will discover that the culprit in your case is react-highlight image

Notes for reproduction

In order to reproduce the issue, you need to:

ntkoopman commented 3 years ago

It's because esbuild is checking for __esModule at runtime and rollup is not. So it will happen to any library that has an entry point with

module.exports = require("./something");

Since this file does not include the 'magic string' __esModule as checked by https://github.com/rollup/plugins/blob/master/packages/commonjs/src/generate-exports.js#L82 it's not converted correctly.

vite dev vite build
import Switch from 'react-switch'; Switch ok
import Switch from 'react-switch'; Switch.default ok
import Switch from 'react-switch/dist/react-switch.dev.js'; Switch ok ok

So need to raise a bug in rollup for this.

ntkoopman commented 3 years ago

Unsurprisingly, it's a known issue mentioned in https://github.com/rollup/plugins/issues/481 but it doesn't seem like there will be a solution very soon.

One thing to note is that the node behaviour is mutually exclusive with supporting __esModule. When running natively from Node, Switch.default would be the only correct option. In the end the best way would probably to ask the library author to publish an esm version to avoid this problem completely.

haikyuu commented 3 years ago

It's because esbuild is checking for __esModule at runtime and rollup is not

Then we should issue a big warning at dev time about this behaviour. It's a critical bug (in the libraries) that affects the Vite experience drastically.

KannarFr commented 3 years ago

Yup, same problem here: https://github.com/vitejs/vite/issues/2692.

ntkoopman commented 3 years ago

After looking into this some more, I think vite should switch to behave the same as rollup. That way it would be consistent with how node works (and snowpack as well).

For now, importing a commonjs module should probably show a warning somewhere.

patak-dev commented 3 years ago

@ntkoopman would you expand a bit in what needs to be changed in Vite? A PR would be great so we can discuss what the implications are there (from anyone that is following this issue)

Agree about the warning in the meantime in case that change is not easy to land, PR welcome for that patch too

ntkoopman commented 3 years ago

So I already changed my mind again, since changing the dev behaviour will still break dependencies. So, the best case would still be that it gets fixed in rollup. I tested with a commonjs plugin I wrote and it seems to work for us for now:

import cjs from 'rollup-plugin-cjs';

export default defineConfig({
  build: {
    rollupOptions: {
      input: {
        index: resolve(__dirname, 'src/index.html'),
      },
      plugins: [cjs()],
    },
    commonjsOptions: {
      exclude: [/./],
    },
  },
});

I looked into returning a warning, but decided not to, because if it's possible to detect the issue it should be possible to fix it..

I'll continue on the commonjs plugin and see if I can fix it there (or maybe it's just a configuration option that's missing?) now that I have some unit tests.

barankyle commented 3 years ago

Hacky solution in my case

In my case it was coming from using react-switch. And I hacked it to work using the following (don't tell anyone about this)

import S from "react-switch";
const Switch = S.default? S.default: S
console.log(S); // inspecting

Basically this is the content of the import {__esModule: true, default: ƒ}

The react-switch library is built as cjs so this is probably a cjs compatibility issue for Vite's Rollup build configuration ?

I used this to fix this issue cropping up with react-no-ssr.

Kamyil commented 3 years ago

This issue is complete blocker for me from using Vite in my React production apps. In my case, the issue does not show up in the development mode (it works completely fine there) and it does only show up in production. The thing is... since a lot of external React components (like react-switch, react-notfification, react-select) uses export default (unfortunetly) - it forces developer to guess which one of it crashes the app. However, trying setting "development" mode for production build and disabling minifier option doesn't help either, because DevTools also doesn't show me any exact place in the stacktrace where the problem can exist. Hope this issue will be resolved soon, because I loved using Vite so far 😔

rememberlenny commented 3 years ago

Is there an ETA on at least catching this some way in development?

noorvir commented 3 years ago

I'm blocked by the same problem. How are people deploying their apps into production? Is there a workaround in the meantime?

barankyle commented 3 years ago

To track down which package was causing the problem, I had to do some grindy work of setting breakpoints in Chrome dev tools (on the error-throwing lines, and in some cases going up the call stack) and then iterating through until I found the culprit. Not an easily reproducible solution, especially since in most cases you're dealing with minified code.

vikbert commented 3 years ago

🔥 I'm blocked by the same issue. My team is thinking about stopping using vite, and going back to webpack. Please give more power and fix it as soon as possible.

flasco commented 3 years ago

React + vite doesn't work well in a production environment, need to wait.. :(

aleclarson commented 3 years ago

Note: This isn't a React-specific or Vite-specific issue, but a Rollup issue (specifically with their CommonJS plugin).

The Workaround

Example: https://github.com/alloc/vite-react-jsx/commit/2db2870f5ad888ed7d4a57e5ba10a82f8bdf80b2

Instead of importing the CJS package directly, use a middle man that unwraps the module.default property like so:

import S from 'react-switch'

export const Switch = interopDefault(S)

function interopDefault<T>(value: T): T {
  return (value as any).default
}

It's ugly, but it works. If 10 people sponsor me on Github for $1 a month, I'll make a plugin that handles this compatibility layer automatically (and maybe even work it into Vite core).

smajl commented 3 years ago

With some heavy trial-and-error I have identified the imports which were causing this and fixed them. A day (not so) well spent... Really a horrible experience for someone using Vite for the first time. The worst thing that can happen is when you get this by importing a componet from one of those "unsafe" libs, then the exception originates from react-dom and good luck finding why/where. Other imports (like hooks, etc.) will fortunately show up the exact line (with sourcemaps turned on). Plugin for some compat layer would be awesome. @aleclarson how big one-time donation would it require so you start working on this? 😄

aleclarson commented 3 years ago

@smajl My goal is to increase my sponsor count. I'm the only React-focused maintainer of Vite, so if any of you use React, please sponsor me, or I can't justify sharing my contributions. I recently made vite-react-jsx too.

chenqiangsuixin commented 3 years ago

@aleclarson uncaught at Error: Minified React error #130; visit https://reactjs.org/docs/error-decoder.html?invariant=130&args[]=object&args[]= for the full message or use the non-minified dev environment for full errors and additional helpful warnings. I had the same problem, I like vite but it is still a lot of problems with react production.

friday commented 3 years ago

@rollup/plugin-commonjs works for me with the commonjsOptions exclude pattern ntkoopman used (thank you). I don't understand how that works, since that should match any path longer than 0 characters. @rollup/plugin-commonjs is also used internally in vite, but not passed to rollup.

import { defineConfig } from 'vite'
import commonjs from '@rollup/plugin-commonjs';

// https://vitejs.dev/config/
export default defineConfig({
  build: {
    rollupOptions: {
      plugins: [commonjs()],
    },
    commonjsOptions: {
      exclude: [/./],
    },
  }
})

Someone should at least rename and pin this issue imo (if documenting or fixing the issue are not viable short term options). Like many others in here I had a terrible onboarding experience with Vite spending hours debugging and finding the solution for this, because some of my dependencies used commonjs-only libraries (example: https://github.com/plurals/pluralize). aleclarson's solution was not viable, as I can't wrap my dependencies import logic, and it didn't import an object with the default property. It imported an empty object ({}).

jrmyio commented 3 years ago

Same issue here with just this:

import React from 'react';
import ReactDOM from 'react-dom';

import JSONTree from 'react-json-tree';

ReactDOM.render(
    <React.StrictMode>
        <JSONTree data={{ test: 'Hello World' }} />
    </React.StrictMode>,
    document.getElementById('root')
);

Neither proposed @rollup/plugin-commonjs or rollup-plugin-cjs worked for me as I would run into 'x is not a function()'.

friday commented 3 years ago

@jrmyio Disable minification to see the real error https://github.com/vitejs/vite/issues/2139#issuecomment-802981228

jrmyio commented 3 years ago

@jrmyio Disable minification to see the real error #2139 (comment)

Thanks, the error is

Uncaught ReferenceError: Cannot access '__çjs$synthetic__' before initialization
    at vendor.0646077f.js:1
(anonymous) @ vendor.0646077f.js:1

when using rollup-plugin-cjs

Using @rollup/plugin-commonjs will keep the React error because cause my example (https://github.com/vitejs/vite/issues/2139#issuecomment-866857521) uses react-json-tree.

dsantoro commented 3 years ago

@jrmyio Disable minification to see the real error #2139 (comment)

I did the configuration to disable minification but seems not work for me. Still showing Minify 130 error.

adil-waqar commented 3 years ago

@jrmyio Disable minification to see the real error #2139 (comment)

I did the configuration to disable minification but seems not work for me. Still showing Minify 130 error.

Likewise. How do we get proper errors with stack traces in production? Preferably like CRA. It's very difficult to debug such errors.

Cubelrti commented 3 years ago

Same issue when using react-json-tree. Workaround in this issue like

import T from 'react-json-tree';
const JSONTree = T.default? T.default: T

didn't work for me. It seems this package have some ESModule import inside itself, but hacking the index is not sufficient.

// lib/JSONNode.js
// ...
var _default = JSONNode;
exports["default"] = _default;

// lib/index.js
var _JSONNode = _interopRequireDefault(require("./JSONNode"));

My workaround is import this module source directly. But it is really tricky and breaking type check.

import JSONTree from 'react-json-tree/src/index'
TrejoCode commented 3 years ago

Same error, using: I don't know, my project grow up, when I'm ready to deploy and publish: ERROR. 😢

BarneyRoos commented 3 years ago

Unfortunately, I can only continue to use "CRA + react app rewired" to build, and everything finally starts to work normally.

jugglingcats commented 3 years ago

It was a pain to identify the offending component (react-datetime in my case), but I found the workaround from @aleclarson and @barankyle worked for me.

It would be great if vite/rollup could identify the offending component at least. This is a real gotcha for people migrating apps from other frameworks (Snowpack and NX in my case), finding everything works beautifully in dev and then hitting unexpected errors in production.

n8sabes commented 2 years ago

This is a major issue that prevented migration from CRA to Vite. Unfortunately this problem was not discovered until a production build was attempted once everything was working in dev mode.

Related Discussion: https://github.com/vitejs/vite/discussions/5803

I agree with @jugglingcats that any insights to identify offending components would be helpful. A 'non-minified' 'development mode' build seems like a critical feature for debugging.

Rolling back to CRA. 😢

lidaof commented 2 years ago

encounter same issue, is there a solution? thanks!

bogdan-calapod commented 2 years ago

Any tips on identifying which module causes this when the stacktrace just points to ReactDOM ? Mocking ReactDOM as mentioned by @aleclarson doesn't seem to do anything (only causes a render is not a function error) so I guess that ain't the source of my issues.

Edit: For future visitors, adding the following to the Vite config file helped:

{
  mode: 'development', // < this is important
  build: {
    minify: false,
    sourcemap: true // < this allows the browser to point you to the correct file 
  }
}
bogdan-calapod commented 2 years ago

Alas, I've also given up. I've spent one week trying to migrate a CRAv4 project, but it seems like Vite ain't ready yet. Will upgrade to CRAv5 for now, looking forward to trying again in the future. :)

mquandalle commented 2 years ago

I'm also experiencing this issue with a package I've authored, and I don't understand how to build this package to avoid this issue.

haoqunjiang commented 2 years ago

A sum up:

The root problem is that many packages follow a bad practice that breaks Rollup ESM-CJS interop:

  1. Like react, they use a CJS entry with mere conditional require()s to redirect the request to different source modules based on NODE_ENV. (e.g. https://unpkg.com/browse/react-switch@6.0.0/index.js). The actual source modules are compiled ESM with an exports.__esModule flag; (e.g. https://unpkg.com/browse/react-switch@6.0.0/dist/react-switch.dev.js)
  2. Unlike react, they use default exports in the source;
  3. Tools with runtime helpers (esbuild, babel, etc.) can detect the __esModule flag from the entry module at runtime and only at runtime
  4. Rollup CJS plugin is compile-time only, it can't possibly know that a CJS module should be treated as ESM after the non-top-level require() call resolves at runtime.
  5. This issue doesn't matter when the exports are all named (like in react). But when there's a default export, it would become nested after Rollup bundling (import Switch from 'react-switch' returns { default: ... }).

It's easy to work around at the user side with the above-mentioned hack:

import S from "react-switch";
const Switch = S.default? S.default: S
console.log(S); // inspecting

But a more general solution might not be that easy, especially since there could be named exports along with default exports in the same module. Detecting such possible breakages seems not easy too because they are resolved at runtime… I'll see what I can do here to help mitigate the issue.


Ideally, these packages should:

damaon commented 2 years ago

You should just make these fail in development as well.

Why you think it's good idea to have these modules working only in development and then fail in prod build?

mquandalle commented 2 years ago

Thanks you @sodatea for the detailed write up. I've found that the PR that introduced this feature in esbuild provide some interesting additional context https://github.com/evanw/esbuild/pull/1849#issue-1079368644

I agree that the behavior should be the same in dev and in prod modes, which imply either:

Also related: https://github.com/evanw/bundler-esm-cjs-tests#results

rodrigojmr commented 2 years ago

Just wanna chime in I ran into this today and while I'm upset it's blocking, I want to encourage sponsoring maintainers like @aleclarson not only for their work so far but so hopefully someday we can get this fixed :)

fwouts commented 2 years ago

Question for maintainers: has anyone already tried implementing Evan's idea #2 of pre-bundling dependencies with esbuild in production as well? Do we know how much work that would involve? (I may be able to help)

haoqunjiang commented 2 years ago

@fwouts https://github.com/vitejs/vite/pull/4677

fwouts commented 2 years ago

For anyone looking at this issue, this should hopefully be solved by https://github.com/rollup/plugins/pull/1165, which depends on another big Rollup CJS PR, so I'm not exactly sure when we'll get it all released and updated in Vite, but a fix is in sight at least 👀

If it takes too long, we could also consider patching the Rollup CJS plugin in Vite (it's a small diff when you exclude tests).

abdallah-nour commented 2 years ago

:(

yeion7 commented 2 years ago

I'm about to finish the migration from CRA to Vite, but I found this same error with react-switch, thanks to this thread I could fix the issue, thanks

But, now I'm wondering if there is another library with the same problem in my project, there is a way to validate more libraries have the same issue?

Kylar13 commented 2 years ago

Hey!! I've just encountered this, and I've realized that rollup has already issued the fix for this in release v22.0.0 https://github.com/rollup/plugins/commit/3bc287cd6ad067a54e69ec17a1869cb12da2e952

Can we get the rollup plugin in vite bumped to v22?

patak-dev commented 2 years ago

@Kylar13 we're hitting some issues with the update to rollup plugin commonjs v22, in case someone wants to help check out the issue:

Kylar13 commented 2 years ago

I'm completely lost as to how Vite works internally so I am unable to help, but I will add that the library that was giving me issues is https://www.npmjs.com/package/react-phone-input-2, and the fix for now is the one suggested by @haikyuu:

import RPI from "react-phone-input-2";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const ReactPhoneInput = RPI.default ? RPI.default : RPI;
fwouts commented 2 years ago

@Kylar13 we're hitting some issues with the update to rollup plugin commonjs v22, in case someone wants to help check out the issue:

Until we manage to make it work, we could stay on v21 and patch it in Vite with https://github.com/rollup/plugins/pull/1165 (it's just a couple of lines of actual code). I believe this would at least allow us to close this issue.

Patching is not ideal, but I bet it would unlock the next level of growth for Vite as this issue is holding back larger codebases from migrating over from Webpack.