reaviz / reaflow

🎯 React library for building workflow editors, flow charts and diagrams. Maintained by @goodcodeus.
https://reaflow.dev
Apache License 2.0
2.15k stars 123 forks source link

`require() of ES Module` error in Next.js #137

Open dylanjt opened 2 years ago

dylanjt commented 2 years ago

I'm submitting a...

[x] Bug report

Current behavior

Unable to use reaflow with the current version of Next.js. I have started a Next.js project using npx create-next-app. After adding reaflow as a dependency and importing it in /pages/index.js, I get the following error in my browser:

Error: require() of ES Module /Users/.../node_modules/p-cancelable/index.js from /Users/.../node_modules/reaflow/dist/index.cjs.js not supported.
Instead change the require of index.js in /Users/.../node_modules/reaflow/dist/index.cjs.js to a dynamic import() which is available in all CommonJS modules.

I tried patching this by overriding the p-cancelable library to it's previous 2.x version -- before its switch to ES modules only in v3 -- but this caused another, similar error from another dependency.

Expected behavior

Ideally the library will work as expected on import.

Minimal reproduction of the problem with instructions

Start a new Next.js project (12.0.7) and install reaflow as a dep. Try to import and use { Canvas } in /pages/index.js.

Environment


Libs:
- react version: 17.0.2
- next version: 12.0.7
- reaflow version: ^4.2.15

Browser:
- [x] Chrome (desktop) version XX
- [ ] Chrome (Android) version XX
- [ ] Chrome (iOS) version XX
- [ ] Firefox version XX
- [ ] Safari (desktop) version XX
- [ ] Safari (iOS) version XX
- [ ] IE version XX
- [ ] Edge version XX

For Tooling issues:
- Node version: 16.12.0, 14.18.1
- Platform:  macOS

wereHamster commented 2 years ago

@dylanjt have you found a workaround?

subodhpareek18 commented 2 years ago

I was able to run this package by installing a plugin and making two changes in the next.config.js file.

npm i -s next-transpile-modules
// next.config.js
const withTM = require('next-transpile-modules')(['reaflow']);  // first import this plugin with reaflow as a target

module.exports = withTM({
   ...previousConfig,
   experimental: {
    esmExternals: 'loose' // second add this experimental flag to the config
  }
})

I should note though this is my first time using reaflow and I've just about run the simplest two node example. So I don't know if it'll continue to work with more things added. Plus I don't mind using experimental flags in the current repository.

This is the actual component I'm running

import { Canvas } from 'reaflow';

const Page = () => (
  <div style={{ position: 'relative', width: '100vw', height: '100vh' }}>
    <div
      style={{
        position: 'absolute',
        top: 0,
        bottom: 0,
        left: 0,
        right: 0,
        backgroundColor: '#F5F5F5'
      }}
    >
      {
        // Don't render the Canvas on the server
        typeof window !== 'undefined' && (
          <Canvas
            maxWidth={800} // Use small canvas dimensions to make sure the nodes appear on screen immediately
            maxHeight={600}
            nodes={[
              {
                id: '1',
                text: '1'
              },
              {
                id: '2',
                text: '2'
              }
            ]}
            edges={[
              {
                id: '1-2',
                from: '1',
                to: '2'
              }
            ]}
          />
        )
      }
    </div>
  </div>
);

export default Page;
tunesmith commented 2 years ago

Just ran into this problem while trying out the library with NextJS. I'm not familiar with this type of issue, is it a problem with the rollup config or the package.json in the reaflow repo?

amcdnl commented 2 years ago

This is also an issue with reaviz - I'm not sure the best way to handle - if anyone has any suggestions happy to try them out.

tunesmith commented 2 years ago

@amcdnl do you have a recommended way to test out the package locally in other projects? For instance, git clone reaflow, and then being able to "publish" it locally somehow to refer to the altered version in another local project. Having not done this before, it looks like yalc is a popular option but I'm not sure what is standard.

thiscantbeserious commented 2 years ago

Downgrading to 4.2.0 does solve that issue, if that's an option.

amcdnl commented 2 years ago

If anyone has any suggestions, I'm happy to try them out. I don't know the best approach for this - build systems suck lately!

wereHamster commented 2 years ago

@amcdnl my two cents…

My suggestion would be to:

{
  "name": "reaflow",
  "type": "module",
  "exports": {
    ".": {
      "import": {
        "types": "./dist/index.d.ts",
        "default": "./dist/index.esm.js"
      }
    }
  }
}

(plus the other stuff, but drop main and module)

TypeScript 4.7 has added support for package.json exports: https://devblogs.microsoft.com/typescript/announcing-typescript-4-7-beta/#package-json-exports-imports-and-self-referencing

tunesmith commented 2 years ago

Just confirming this issue still exists with nextjs 12.2.2 and reaflow 5.0.6.

@wereHamster I created a fresh project with NextJS and tried editing node_modules/reaflow/package.json directly using your suggestions above. I think it made headway, but now the error I'm getting (from the nextJS app that imports reaflow) is

Error: No "exports" main defined in [...]/node_modules/reaflow/package.json

@amcdnl are you able to get any further?

amcdnl commented 2 years ago

@wereHamster - Any chance you could make a PR w/ your suggestions?

freyandhy commented 2 years ago

I use Dynamic Import to import the modules. See the example at codesandbox here

nemopear commented 1 year ago

This lib not support SSR, so I used dynamic import same @freyandhy but for functions need to import with other way

example import upsertNode
const { upsertNode } = await import("reaflow");

<Canvas
  nodes={nodes}
  edges={edges}
  edge={
    <Edge
      add={<Add hidden={false} />}
      onAdd={async (event, edge) => {
        const id = `node-${Math.random()}`;
        const newNode = {
          id,
          text: id,
        };

        const { upsertNode } = await import("reaflow");

        const results = upsertNode(nodes, edges, edge, newNode);

        setNodes(results.nodes);
        setEdges(results.edges);
      }}
    />
  }
  onLayoutChange={(layout) => console.log("Layout", layout)}
/>;

If your build fails, try to add types to dynamic import like the code below

const Canvas: React.ComponentType<CanvasContainerProps> = dynamic(
    () =>
        import("reaflow").then((mod) => {
            return mod.Canvas as any;
        }),
    { loading: () => <p>loading</p>, ssr: false },
);

const Node: React.ComponentType<NodeProps> = dynamic(
    () => import("reaflow").then((mod) => mod.Node as any),
    {
        ssr: false,
    },
);

import type { Edge as EdgeType } from "reaflow";

type EdgePropType = JSX.LibraryManagedAttributes<
    typeof EdgeType,
    React.ComponentProps<typeof EdgeType>
>;

const EdgeReaflow: React.ComponentType<EdgePropType> = dynamic(
    () => import("reaflow").then((mod) => mod.Edge as any),
    {
        ssr: false,
    },
);

const Add: React.ComponentType<Partial<AddProps>> = dynamic(
    () => import("reaflow").then((mod) => mod.Add as any),
    {
        ssr: false,
    },
);
yyaskriloff commented 1 year ago

I encountered this issue, I fixed it by just adding "type": "module", to the package.json file.

AykutSarac commented 1 year ago

@amcdnl will there be a fix for that?

amcdnl commented 1 year ago

If someone has a fix, I'm happy to accept it. I believe we have tried this but hit other issues during testing.