Skip to content
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

[BUG] react-dropzone cannot be imported in a Node.js ESM environment #1259

Open
cdauth opened this issue Dec 14, 2022 · 13 comments · May be fixed by #1293
Open

[BUG] react-dropzone cannot be imported in a Node.js ESM environment #1259

cdauth opened this issue Dec 14, 2022 · 13 comments · May be fixed by #1293
Labels

Comments

@cdauth
Copy link

cdauth commented Dec 14, 2022

I am experimenting with React SSR in a project that uses ESM ("type": "module" is set in package.json). The project uses react-dropzone, and although it will not be part of the server-side render, the app still imports it.

Importing react-dropzone in such an environment fails with the following error:

import { useDropzone } from "../node_modules/react-dropzone/dist/index.js";
         ^^^^^^^^^^^
SyntaxError: Named export 'useDropzone' not found. The requested module '../node_modules/react-dropzone/dist/index.js' is a CommonJS module, which may not support all module.exports as named exports.

The combination of two problems leads to this error:

  • react-dropzone's package.json specifies a main and module field. Node.js only interprets the main field, which leads to the CJS build. To fix this problem, an exports field should be defined whose import property points to the ESM build.
  • The ESM build (under dist/es/index.js) is interpreted by Node.js as a CJS file, since no type is set in package.json and the file has a .js extension. To fix this problem, an ESM build with .mjs file extensions should be provided.

To fix the problem in a backwards-incompatible way (making a major release necessary):

  • Rename the files in dist/es from .js to .mjs
  • Add an exports field to package.json: "exports": { "import": "dist/es/index.mjs", "default": "dist/index.js" }

To fix the problem in a backwards-compatible way:

  • Create an additional (identical) ESM build using .mjs extensions, for example in dist/mjs
  • Add an exports field to package.json: "exports": { ".": { "import": "dist/mjs/index.mjs", "default": "dist/index.js" }, "./": "./" }`
@cdauth cdauth added the bug label Dec 14, 2022
@rxmarbles
Copy link
Collaborator

@cdauth thanks for the report. We will look into it. Also if you feel up to it we are also open to a contribution PR to add this 😁. Otherwise I'll take a look later after the holidays.

@hlehmann
Copy link

I am also having the same issue.

@stalet
Copy link

stalet commented Jan 20, 2023

I am also having this issue!

@jacobclarke92
Copy link

jacobclarke92 commented Jan 23, 2023

Yep this is going to continue being an issue with the prevalence of Next.
I don't think you should have to rename to .mjs, you should be able to just specify different export types in package.json
Something like this should do the trick:

{
  [...]
  "type": "module",
  "main": "./dist/index.js",
  "module": "./dist/es/index.js",
  "types": "./typings/react-dropzone.d.ts",
  "exports": {
    ".": {
      "import": "./dist/es/index.js",
      "require": "./dist/index.js"
    }
  },
  [...]
}

EDIT: After the below discourse I admit I gave bad info here, it really should just be what OP suggested so an edit like this:

{
  [...]
  "main": "./dist/index.js",
  "module": "./dist/es/index.mjs",
  "types": "./typings/react-dropzone.d.ts",
  "exports": {
    ".": {
      "import": "./dist/es/index.jsm",
      "require": "./dist/index.js"
    }
  },
  [...]
}

@cdauth
Copy link
Author

cdauth commented Jan 23, 2023

I don't think you should have to rename to .mjs, you should be able to just specify different export types in package.json

This will not work in a Node.js environment. See https://nodejs.org/api/packages.html#determining-module-system.

@jacobclarke92
Copy link

@cdauth I agree .mjs is more clear but read the second point in that link - it can be .js when coupled with "type": "module" in package.json which I had included in my suggestion.

I'm by no means a veteran at this stuff though and I don't know the full implications of setting type:module but so far the above approach has worked for me with modules i've made that get consumed by node and bundled via commonjs (also being used in both frontend/backend parts of a Next app).

Honestly keen to hear more insight on the topic though because it really is confounding stuff..

@cdauth
Copy link
Author

cdauth commented Jan 24, 2023

@jacobclarke92 It's true that .js will work if "type": "module" is set. However it comes with the following implications:

  • All .js files in the project will be interpreted as ESM. This means that everything that runs under Node.js (which in this case is probably just the build process) will have to work with ESM, including the build dependencies. This usually requires a bit of work to get it running.
  • The CommonJS bundle in dist/index.js will also be interpreted as ESM, so it has to be renamed to dist/index.cjs. This could be interpreted as a breaking change, requiring a new major release.

I think/hope the future will be all ESM, so "type": "module" will be standard practice and only an ESM bundle will be published. More and more projects are making this step, and I think it is a good decision for a major release. Until then, the way to support ESM setups without breaking any existing setups seems to be to add a additional .mjs ESM bundle (in addition to the existing .js ESM bundle, since some build tools don't support the .mjs extension yet).

@jacobclarke92
Copy link

@cdauth That's some really good, concise info. Thanks!
Don't know how I've not run into that issue of trying to use packages marked as type:module as CJS yet.. actually looks like next has been preferring MJS since version 12 now, I didn't realise - my project must have never even been importing CJS versions at all .. doh.

Hope I've not derailed this issue too much haha.. I'll show myself out now.
Still want to add a +1 to this issue and the suggestions you've made though!

@jacobclarke92
Copy link

Here's a patch-package snippet in the meantime in case it helps others.

react-dropzone+14.2.3.patch

diff --git a/node_modules/react-dropzone/dist/es/index.js b/node_modules/react-dropzone/dist/es/index.mjs
similarity index 99%
rename from node_modules/react-dropzone/dist/es/index.js
rename to node_modules/react-dropzone/dist/es/index.mjs
index 39d9345..5deaac3 100755
--- a/node_modules/react-dropzone/dist/es/index.js
+++ b/node_modules/react-dropzone/dist/es/index.mjs
@@ -37,7 +37,7 @@ function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) r
 import React, { forwardRef, Fragment, useCallback, useEffect, useImperativeHandle, useMemo, useReducer, useRef } from "react";
 import PropTypes from "prop-types";
 import { fromEvent } from "file-selector";
-import { acceptPropAsAcceptAttr, allFilesAccepted, composeEventHandlers, fileAccepted, fileMatchSize, canUseFileSystemAccessAPI, isAbort, isEvtWithFiles, isIeOrEdge, isPropagationStopped, isSecurityError, onDocumentDragOver, pickerOptionsFromAccept, TOO_MANY_FILES_REJECTION } from "./utils/index";
+import { acceptPropAsAcceptAttr, allFilesAccepted, composeEventHandlers, fileAccepted, fileMatchSize, canUseFileSystemAccessAPI, isAbort, isEvtWithFiles, isIeOrEdge, isPropagationStopped, isSecurityError, onDocumentDragOver, pickerOptionsFromAccept, TOO_MANY_FILES_REJECTION } from "./utils/index.mjs";
 /**
  * Convenience wrapper component for the `useDropzone` hook
  *
@@ -383,7 +383,7 @@ var initialState = {
  * @function useDropzone
  *
  * @param {object} props
- * @param {import("./utils").AcceptProp} [props.accept] Set accepted file types.
+ * @param {import("./utils/index.mjs").AcceptProp} [props.accept] Set accepted file types.
  * Checkout https://developer.mozilla.org/en-US/docs/Web/API/window/showOpenFilePicker types option for more information.
  * Keep in mind that mime type determination is not reliable across platforms. CSV files,
  * for example, are reported as text/plain under macOS but as application/vnd.ms-excel under
@@ -969,4 +969,4 @@ function reducer(state, action) {
 
 function noop() {}
 
-export { ErrorCode } from "./utils";
\ No newline at end of file
+export { ErrorCode } from "./utils/index.mjs";
\ No newline at end of file
diff --git a/node_modules/react-dropzone/dist/es/utils/index.js b/node_modules/react-dropzone/dist/es/utils/index.mjs
similarity index 99%
rename from node_modules/react-dropzone/dist/es/utils/index.js
rename to node_modules/react-dropzone/dist/es/utils/index.mjs
index 8e5abd5..f155743 100644
--- a/node_modules/react-dropzone/dist/es/utils/index.js
+++ b/node_modules/react-dropzone/dist/es/utils/index.mjs
@@ -26,7 +26,9 @@ function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Sy
 
 function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
 
-import accepts from "attr-accept"; // Error codes
+import _accepts from "attr-accept"; // Error codes
+
+const accepts = typeof _accepts === "function" ? _accepts : _accepts.default;
 
 export var FILE_INVALID_TYPE = "file-invalid-type";
 export var FILE_TOO_LARGE = "file-too-large";
diff --git a/node_modules/react-dropzone/package.json b/node_modules/react-dropzone/package.json
index 9409558..97b8175 100644
--- a/node_modules/react-dropzone/package.json
+++ b/node_modules/react-dropzone/package.json
@@ -2,7 +2,13 @@
   "name": "react-dropzone",
   "description": "Simple HTML5 drag-drop zone with React.js",
   "main": "dist/index.js",
-  "module": "dist/es/index.js",
+  "module": "dist/es/index.mjs",
+  "exports": {
+    ".": {
+      "import": "./dist/es/index.mjs",
+      "require": "./dist/index.js"
+    }
+  },
   "sideEffects": false,
   "scripts": {
     "cz": "git-cz",

@stalet
Copy link

stalet commented Mar 1, 2023

Is this bug fixed in the newer versions ?

@micah-redwood
Copy link

Unfortunately still broken as of 14.2.3

@cblauvelt
Copy link

I was able to use this work around based on a tip from pingdotgg/uploadthing#42.

import dropzone from "react-dropzone";
const { useDropzone } = dropzone;

It appears react-dropzone project hasn't been updated since Oct-2022. Is there anything we can do to help the authors maintain this project? It appears a PR #1293 is available to fix this problem.

@rtivital
Copy link

rtivital commented Oct 1, 2023

Hi everyone, I've created a temporary package react-dropzone-esm which works correctly with modern frameworks.
The package provides both esm and cjs bundles. Exports in package.json are properly configured (you can check the difference between react-dropzone and react-dropzone-esm).

The package can be used as a drop-in replacement for react-dropzone – the source code was not modified, I've changed only the build system.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants