microsoft / BotFramework-WebChat

A highly-customizable web-based client for Azure Bot Services.
https://www.botframework.com/
MIT License
1.58k stars 1.53k forks source link

ReactWebChat not working on Next.JS 14 #5024

Closed Chosey98 closed 6 months ago

Chosey98 commented 6 months ago

Is it an issue related to Adaptive Cards?

No

Is this an accessibility issue?

No

What version of Web Chat are you using?

Latest production

Which distribution are you using Web Chat from?

NPM

Which hosting environment does this issue primarily affect?

Web apps

Which browsers and platforms do the issue happened?

No response

Which area does this issue affect?

Others or unrelated

What is the public URL for the website?

No response

Please describe the bug

I'm trying to use the Component in a page.tsx (I'm using 'use client' for client side rendering) but it always returns this error:

 ⨯ ./node_modules/errlop/edition-browsers/index.js
Module parse failed: 'import' and 'export' may appear only with 'sourceType: module' (13:0)
|     /* eslint no-use-before-define:0 */ return value instanceof Error || Errlop.isErrlop(value);
| }
> export default class Errlop extends Error {
|     /**
|        * Create an instance of an error, using a message, as well as an optional parent.

Import trace for requested module:
./node_modules/errlop/edition-browsers/index.js
./node_modules/editions/edition-es5/index.js
./node_modules/istextorbinary/index.js
./node_modules/async-disk-cache/index.js
./node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.browser/CertChecks.js
./node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.browser/WebsocketMessageAdapter.js
./node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.browser/Exports.js
./node_modules/botframework-webchat/lib/speech/createMicrophoneAudioConfigAndAudioContext.js
./node_modules/botframework-webchat/lib/createCognitiveServicesSpeechServicesPonyfillFactory.js
./node_modules/botframework-webchat/lib/index.js
./app/digital-assistant/page.tsx

Here is the code for the page.tsx

'use client';

import { DirectLine } from 'botframework-directlinejs';
import { useEffect, useState } from 'react';
import axios from 'axios';
import dynamic from 'next/dynamic';

const ReactWebChat = dynamic(
    () => {
        return import('botframework-webchat');
    },
    {
        ssr: false,
    }
);
export default function DigitalAssistantPage() {
    const [directLineToken, setDirectLineToken] = useState<DirectLine | string>(
        ''
    );
    useEffect(() => {
        if (directLineToken != '') {
            axios
                .post(
                    'https://directline.botframework.com/api/tokens/conversation',
                    {},
                    {
                        headers: {
                            Authorization:
                                'Bearer REDACTED',
                        },
                    }
                )
                .then((res: any) => {
                    setDirectLineToken(new DirectLine({ token: res.data }));
                });
        }
    }, [directLineToken]);

    if (directLineToken == '') {
        return <>Fetching Token...</>;
    }
    return <ReactWebChat directLine={directLineToken} />;
}

I think the problem is mostly generating from the createDirectLineSpeechServices but I'm not using it in the first place, I tried even commenting them in the index.ts but that also didn't work

Do you see any errors in console log?

⨯ ./node_modules/errlop/edition-browsers/index.js
Module parse failed: 'import' and 'export' may appear only with 'sourceType: module' (13:0)
|     /* eslint no-use-before-define:0 */ return value instanceof Error || Errlop.isErrlop(value);
| }
> export default class Errlop extends Error {
|     /**
|        * Create an instance of an error, using a message, as well as an optional parent.

Import trace for requested module:
./node_modules/errlop/edition-browsers/index.js
./node_modules/editions/edition-es5/index.js
./node_modules/istextorbinary/index.js
./node_modules/async-disk-cache/index.js
./node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.browser/CertChecks.js
./node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.browser/WebsocketMessageAdapter.js
./node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.browser/Exports.js
./node_modules/botframework-webchat/lib/speech/createMicrophoneAudioConfigAndAudioContext.js
./node_modules/botframework-webchat/lib/createCognitiveServicesSpeechServicesPonyfillFactory.js
./node_modules/botframework-webchat/lib/index.js
./app/digital-assistant/page.tsx

How to reproduce the issue?

  1. Install BotFramework-WebChat using yarn
  2. Add ReactWebChat component to a page.tsx file and make it a client component using 'use client'

What do you expect?

The ReactWebChat component should render

What actually happened?

It's not rendering and produces the error above

Do you have any screenshots or recordings to repro the issue?

No response

Adaptive Card JSON

No response

Additional context

No response

OEvgeny commented 6 months ago

I'm looking into this and seems the issue is that WebChat doesn't currently support ESM environments properly

OEvgeny commented 6 months ago

Here is the working example of WebChat + NextJS: https://stackblitz.com/edit/stackblitz-starters-e38mut?file=app%2Fpage.tsx

Chosey98 commented 6 months ago

Oh it worked, Could you please elaborate more on why did it work like that? @OEvgeny

OEvgeny commented 6 months ago

@Chosey98 if using regular top-level imports, despite use-client directive it looks like NextJS imports WebChat on the server, which doesn't work as WebChat is designed to work in the browser.

In browser, in order to include WebChat into the same component tree, we need to use the same React and ReactDOM distributions. This means not only React and ReactDOM versions should match, but the actual imported modules should be the same.

For such cases WebChat provides a way to use exact React and ReactDOM versions. All you need is to expose them to window as window.React and window.ReactDOM.

At this point, this seems the best way possible to handle dependencies in browser. In future we may be able to use ES modules and import maps to handle this.

iahmediibrahim commented 6 months ago

@OEvgeny for the build to succeed, you need to change the return of the provider to this:

return function WebChatProvider(props) { return <WebChatContext.Provider value={WebChat} {...props} />; };