minop1205 / react-dnd-treeview

A draggable / droppable React-based treeview component. You can use render props to create each node freely.
MIT License
530 stars 71 forks source link

Issue: error using `require` to import from react-dnd and react-dnd-html5-backend #118

Closed michaeldawson closed 2 years ago

michaeldawson commented 2 years ago

Report

Current behavior

This library outputs a main dist/index.js file that attempts to use require to import from the react-dnd-html5-backend and react-dnd packages. However, these packages use ES modules, and this results in an error:

Error [ERR_REQUIRE_ESM]: require() of ES Module /Users/me/Code/my-project/node_modules/react-dnd-html5-backend/dist/index.js from /Users/me/Code/my-project/node_modules/@minoru/react-dnd-treeview/dist/index.js not supported.
Instead change the require of /Users/me/Code/my-project/node_modules/react-dnd-html5-backend/dist/index.js in /Users/me/Code/my-project/node_modules/@minoru/react-dnd-treeview/dist/index.js to a dynamic import() which is available in all CommonJS modules.
    at Object.<anonymous> (/Users/me/Code/my-project/node_modules/@minoru/react-dnd-treeview/dist/index.js:6:28)
    at Object.2099 (/Users/me/Code/my-project/.next/server/pages/_app.js:487:18)
    at __webpack_require__ (/Users/me/Code/my-project/.next/server/webpack-runtime.js:25:42)
    at /Users/me/Code/my-project/.next/server/pages/_app.js:365:84
    at Function.__webpack_require__.a (/Users/me/Code/my-project/.next/server/webpack-runtime.js:103:13)
    at Object.3847 (/Users/me/Code/my-project/.next/server/pages/_app.js:358:21)
    at __webpack_require__ (/Users/me/Code/my-project/.next/server/webpack-runtime.js:25:42)
    at __webpack_exec__ (/Users/me/Code/my-project/.next/server/pages/_app.js:770:39)
    at /Users/me/Code/my-project/.next/server/pages/_app.js:771:74
    at Function.__webpack_require__.X (/Users/me/Code/my-project/.next/server/webpack-runtime.js:190:21)
    at /Users/me/Code/my-project/.next/server/pages/_app.js:771:47
    at Object.<anonymous> (/Users/me/Code/my-project/.next/server/pages/_app.js:774:3)
    at Object.requirePage (/Users/me/Code/my-project/node_modules/next/dist/server/require.js:48:12)
    at Object.loadComponents (/Users/me/Code/my-project/node_modules/next/dist/server/load-components.js:57:23)
    at Object.hasCustomGetInitialProps (/Users/me/Code/my-project/node_modules/next/dist/build/utils.js:657:51)
    at execFunction (/Users/me/Code/my-project/node_modules/next/dist/compiled/jest-worker/processChild.js:1:2826)
    at execHelper (/Users/me/Code/my-project/node_modules/next/dist/compiled/jest-worker/processChild.js:1:2484)
    at execMethod (/Users/me/Code/my-project/node_modules/next/dist/compiled/jest-worker/processChild.js:1:2572)
    at process.messageListener (/Users/me/Code/my-project/node_modules/next/dist/compiled/jest-worker/processChild.js:1:1282)
    at process.emit (node:events:390:28) {
  type: 'Error',
  code: 'ERR_REQUIRE_ESM'

I'm currently using Next.JS, and I understand that next should be preferring ES modules where possible. However, in this case it is choosing the CJS export of react-dnd-treeview, which isn't working with the latest versions of react-dnd and react-dnd-html5-backend.

Usage:

import { Tree } from "@minoru/react-dnd-treeview";

<Tree
  tree={treeData}
  rootId={0}
  render={(node, { depth, isOpen, onToggle }) => (
    <Task
      node={node}
      depth={depth}
      isOpen={isOpen}
      onToggle={onToggle}
      addTask={addTask}
    />
  )}
  dragPreviewRender={(monitorProps) => (
    <CustomDragPreview monitorProps={monitorProps} />
  )}
  onDrop={handleDrop}
  classes={{
    root: styles.treeRoot,
    draggingSource: styles.draggingSource,
    placeholder: styles.placeholderContainer,
  }}
  sort={false} //(node) => node.data?.ordinal || 0}
  insertDroppableFirst={false}
  canDrop={(_tree, { dragSource, dropTargetId, dropTarget }) => {
    if (dragSource?.parent === dropTargetId) {
      return true;
    }
  }}
  dropTargetOffset={5}
  placeholderRender={(node, { depth }) => (
    <Placeholder node={node} depth={depth} />
  )}
/>

Expected behaviour

I expect the dist/index.js file not to raise an error.

Reproduction method

Example: After logging out, log in again with another account.

Reference information

package.json:

{
  "scripts": {
    "dev": "next dev",
  },
  "dependencies": {
    ...
    "@minoru/react-dnd-treeview": "^2.0.0",
    "next": "12.1.6",
    "react": "18.1.0",
    "react-dnd": "^16.0.1",
    "react-dom": "18.1.0",
  },
  "type": "module",
  "devDependencies": {
    "@types/node": "17.0.35",
    "@types/react": "18.0.9",
    "@types/react-dom": "18.0.5",
    "typescript": "4.7.2"
  }
}
tomitrescak commented 2 years ago

I have exactly the same issue, and I'm pulling my hair out. It's this idiotic decision of react-dnd to only ship ES Next code.

I see you are also using NEXT.js. The issue is that next for some reason is importing the common.js file:

@minoru/react-dnd-treeview/dist/index.js

which is then importing the ES next file

react-dnd-html5-backend/dist/index.js

I do not understand, why NEXT starts starts to import the Common.js version ,albeit this package clearly specified that it has ESNext versions of the files.

I spent several hours on this now but no solution ....

BlakePro commented 2 years ago

Hello guys a quick fix will be nextjs/dynamic

First install...

npm i react-dnd-html5-backend
import dynamic from 'next/dynamic'

const { Tree } = {
  Tree: dynamic(() => import('@minoru/react-dnd-treeview').then((module) => module.Tree) , { ssr: false }),
}
const { DndProvider } = {
  DndProvider: dynamic(() => import('react-dnd').then((module) => module.DndProvider) , { ssr: false }),
}
import { HTML5Backend } from 'react-dnd-html5-backend'

After that use...

export default function Page() {
  const initialData = [
      {
        "id": 1,
        "parent": 0,
        "droppable": true,
        "text": "Folder 1"
      },
      {
        "id": 2,
        "parent": 1,
        "text": "File 1-1"
      },
      {
        "id": 3,
        "parent": 1,
        "text": "File 1-2"
      },
      {
        "id": 4,
        "parent": 0,
        "droppable": true,
        "text": "Folder 2"
      },
      {
        "id": 5,
        "parent": 4,
        "droppable": true,
        "text": "Folder 2-1"
      },
      {
        "id": 6,
        "parent": 5,
        "text": "File 2-1-1"
      }
    ]
  const [treeData, setTreeData] = useState(initialData)
  const handleDrop = (newTreeData) => setTreeData(newTreeData)

  return (
  <DndProvider backend={HTML5Backend} >
    <Tree
     tree={treeData}
     rootId={0}
     onDrop={handleDrop}
     render={(node, { depth, isOpen, onToggle }) => (
       <div style={{ marginLeft: depth * 10 }}>
         {node.droppable && (
           <span onClick={onToggle}>{isOpen ? "[-]" : "[+]"}</span>
         )}
         {node.text}
       </div>
     )}
   />
  </DndProvider>
  )
}
minop1205 commented 2 years ago

Thank you all for your reports.

I don't know why next reads the CommonJS files, but I have found that there is a problem with this package as well.

This package depends on react-dnd, react-dnd-html5-backend, etc., but these only support ESM format, so it makes no sense for this package to support CommonJS format.

Therefore, I have removed the CommonJS files, leaving only the ESM files and partially modified package.json. The alpha version can be installed from

npm i @minoru/react-dnd-treeview@alpha

This worked for the my Next project. How about your environment?

However, this fix has prevented the Storybook from building properly, so I will officially release it as v2.0.1 as soon as the problem is resolved.

minop1205 commented 2 years ago

v2.0.1 is released today. In this version, the package type was changed to "module" and distributed only in ESM format. This package with Next.js can be built without any problem.

tomitrescak commented 2 years ago

Thank you very much, works like a charm.