reaflow icon indicating copy to clipboard operation
reaflow copied to clipboard

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

Open dylanjt opened this issue 2 years ago • 15 comments

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

dylanjt avatar Jan 09 '22 13:01 dylanjt

@dylanjt have you found a workaround?

wereHamster avatar Feb 03 '22 10:02 wereHamster

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;

subodhpareek18 avatar Feb 06 '22 08:02 subodhpareek18

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?

tunesmith avatar May 16 '22 06:05 tunesmith

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.

amcdnl avatar May 16 '22 11:05 amcdnl

@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.

tunesmith avatar May 16 '22 20:05 tunesmith

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

thiscantbeserious avatar Jun 02 '22 15:06 thiscantbeserious

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!

amcdnl avatar Jun 02 '22 17:06 amcdnl

@amcdnl my two cents…

  • A lot of packages (including p-cancellable) are adopting a «ESM-only» strategy. They don't bother with CJS, they simply set type:module in the package.json and include .js files which are valid ES modules in the package.
  • That is not really a problem anymore, all LTS Node.js versions and browsers support ESM, and packages with type:module are well understood by modern bundlers.
  • This approach is «infectious»: If you have a dependency that's ESM-only, you have to be ESM-only too
    • Well, not strictly. You can import CJS from ESM, but only via import() expression. But the import() expression is async, it returns a Promise, so you can't use it at the top-level.

My suggestion would be to:

  • Set type:module in package.json
  • Drop CJS and provide only ESM (~ if you set type:module then the files can keep the .js extension)
  • Adopt package.json exports
{
  "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

wereHamster avatar Jun 02 '22 20:06 wereHamster

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?

tunesmith avatar Jul 10 '22 21:07 tunesmith

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

amcdnl avatar Jul 11 '22 11:07 amcdnl

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

freyandhy avatar Nov 10 '22 10:11 freyandhy

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,
    },
);

nemopear avatar Nov 30 '22 11:11 nemopear

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

yyaskriloff avatar Jan 15 '23 20:01 yyaskriloff

@amcdnl will there be a fix for that?

AykutSarac avatar Feb 10 '23 13:02 AykutSarac

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

amcdnl avatar Feb 10 '23 13:02 amcdnl