elastic / next-eui-starter

Start building Kibana protoypes quickly with the Next.js EUI Starter
https://elastic.github.io/next-eui-starter/
Apache License 2.0
93 stars 30 forks source link

Any Plans to Deprecate React Ace and Migrate to Next JS 11 ? (Webpack 5) #33

Closed dukesx closed 3 years ago

dukesx commented 3 years ago

I am struggling here to make it work with the latest from Next js. I just don't get how i can migrate next.config.js to webpack 5. Nothing works. tried all the syntax and functions to exclude React Ace but it just doesn't works. Any help would be greatly appreciated.

miukimiu commented 3 years ago

Hi @dukesx,

The react-ace is going to be deprecated soon: https://elastic.github.io/eui/#/editors-syntax/code-editor. I guess once we deprecate it will make it easier to update our starter to Next JS 11.

But recently, in a different project, I was also struggling to make EUI work in Next JS 11. This was my solution to exclude react-ace (next.config.js):

const path = require('path');

module.exports = {
  reactStrictMode: true,

  webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => {
    config.resolve.alias = {
      ...config.resolve.alias,
      // we don't want to load react-ace because it gives the error `window is not defined`
      [path.resolve(__dirname, './node_modules/react-ace')]: false,
    };

    config.resolve.mainFields = ['module', 'main'];

    return config;
  },
};

Let me know if that worked! 🙂

dukesx commented 3 years ago

Hi @dukesx,

The react-ace is going to be deprecated soon: https://elastic.github.io/eui/#/editors-syntax/code-editor. I guess once we deprecate it will make it easier to update our starter to Next JS 11.

But recently, in a different project, I was also struggling to make EUI work in Next JS 11. This was my solution to exclude react-ace (next.config.js):

const path = require('path');

module.exports = {
  reactStrictMode: true,

  webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => {
    config.resolve.alias = {
      ...config.resolve.alias,
      // we don't want to load react-ace because it gives the error `window is not defined`
      [path.resolve(__dirname, './node_modules/react-ace')]: false,
    };

    config.resolve.mainFields = ['module', 'main'];

    return config;
  },
};

Let me know if that worked! 🙂

Thank you, this resolves the React Ace issue, but now, another issue has popped up, please check the code below:

ReferenceError: HTMLElement is not defined at EuiPopover.render (C:\Users\Username\Desktop\my-eui-starter\.next\server\pages\_app.js:58710:30) at processChild (C:\Users\Username\Desktop\my-eui-starter\node_modules\react-dom\cjs\react-dom-server.node.development.js:3450:18) at resolve (C:\Users\Username\Desktop\my-eui-starter\node_modules\react-dom\cjs\react-dom-server.node.development.js:3270:5)

From what i have seen in debugging, the error is coming from "Chrome" named custom component, included in starter. It does not seem to inhabit "popover" element as stated in the log but the issue is gone if i remove the "Chrome" component. I suppose there is a EUI component in it that is causing this.

Update After Few Mins: My bad, Chrome houses a Theme Switcher that uses POPOVER as Select box to switch between themes. The error is coming from there.

Problem file: eui.d.ts it mentions HTMLELEMENT as ref type

dukesx commented 3 years ago

Help anyone??

miukimiu commented 3 years ago

EUI is meant to be used on client side and NextJS is used for server side rendering. So the HTMLElement doesn't exist on server side. That's the issue you're running. 🙂

I suggest you look into our next.config.js and try to translate what is done there into Webpack 5.

This part of the config might help you: https://github.com/elastic/next-eui-starter/blob/5e4414e2b46cac1277fe697e0c57cf9d71e2aeff/next.config.js#L87-L96

Or you can also turn off the Webpack 5 (in NextJS 11): https://nextjs.org/docs/messages/webpack5

I'm closing this issue because we're our starter doesn't support Webpack 5 and NextJS 11, so we can't give proper support for these types of questions.

wanjas commented 3 years ago

I haven't run next-eui-starter itself, but I took parts of webpack config. It was working with webpack 4 and not working with webpack 5 until I've found out that externals have different definitions.

Just fixing externals made it work with version 5 and nextjs 11.

    if (isServer) {
      config.externals = config.externals.map((fn) => (ctx, callback) => {
        if (typeof fn !== 'function') {
          return fn;
        }

        if (
          ctx.request.includes('@elastic/eui') ||
          ctx.request.includes('react-ace')
        ) {
          return callback();
        }

        return fn(ctx, callback);
      });

      // Mock react-ace server-side
      config.module.rules.push({
        test: /react-ace/,
        use: 'null-loader',
      });

      // Mock HTMLElement and window server-side
      const definePluginId = config.plugins.findIndex(
        (p) => p.constructor.name === 'DefinePlugin',
      );
      config.plugins[definePluginId].definitions = {
        ...config.plugins[definePluginId].definitions,
        // eslint-disable-next-line object-shorthand
        HTMLElement: function () {}, // I've no idea why but using shorthand leads to webpack error
        // eslint-disable-next-line object-shorthand
        window: function () {},
      };
    }

Or externals handler might look like this if you want to switch between v4 and v5 easily:

    config.externals = config.externals.map((fn) => (...args) => {
        if (typeof fn !== 'function') {
          return fn;
        }

        if (args.length === 2) {
          // webpack 5
          const [ctx, callback] = args;
          if (
            ctx.request.includes('@elastic/eui') ||
            ctx.request.includes('react-ace')
          ) {
            return callback();
          }
          return fn(ctx, callback);
        } else {
          // webpack 4
          const [context, request, callback] = args;

          if (
            request.includes('@elastic/eui') ||
            request.includes('react-ace')
          ) {
            return callback();
          }

          return fn(context, request, callback);
        }
      });