Build Microsoft-Webchat Locally For Performance Improvement
Published: 2021-08-10 21:43:22

Microsoft Bot framework is a popular framework used for creating bots.It provides a webchat interface to use as a user interface for web and mobile users.
It is developed in react,it comes with packages to be used directly in a webpage.But if you want to do some real customization,you have to use it in a react application.
This makes the package size of the deployed application bigger than usual.Which can be an issue if you are looking for better performance.

In this blog i will show you how we can build the webchat locally and do some modifications,so it can give us better performance

**Assumptions**
- I did not need the webchat speech ,so i have removed it,to make the package smaller
- I am using next js for my custom webchat application

## First we will see with the default setup
to run webchat with react setup,we need to install botframework-webchat

```unix
npm install botframework-webchat
```
```js
//import as a dynamic module so this will load only when it is needed
const ReactWebChat = dynamic(
    () => {
        return import('botframework-webchat');
    },
    { ssr: false }
);

//the directline
 let directLine = useMemo(() => createDirectLine({
        secret: 'Mytoken',
        domain: 'http://directline endpoint',
        webSocket: true,
        conversationId: 'conversationId',
        pollingInterval: 2000
    }), []);
//redux store
let store=useMemo(() => createStore({}, ({ dispatch }) => next => action => {
     if (action.type === 'DIRECT_LINE/POST_ACTIVITY') {
         //your code here
     }
}), []);
<ReactWebChat directLine={directLine}
                    userID={userId}
                    username={userName}
                    locale='en-us'
                    sendTypingIndicator={true}
                    resize="detect"
                    bot={bot}
                    styleOptions={styleOptionsState}
                    store={store}
                />

With this setup we can run the chat app,for details you can check an example from Microsoft-Webchat 

```

## Next configuration

### Install Dev Dependencies
- @next/bundle-analyzer - this we will use to analyse the bundles 
- duplicate-package-checker-webpack-plugin - this we use to find out duplicate packages
- next-transpile-modules - transpile modules for IE11 support
- cross-env - to be used in windows


## Analyse the app
to analyse the package footprint,we will use following in scripts of package.json

```json
"analyse": "cross-env ANALYZE=true next build"
```

inside next.config

```js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
})

const config={
     future: {
        webpack5: true,
     },
}
```
then run
```unix
npm run-script analyse
```

For my setup this is what i get

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vd2zn0uzk51wn5d1c7rw.png)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dzt51koli6uv79r172at.png)

Check how botframework-webchat-api/lib is taking a lot of space for localisations.
and also how big the microsoft-cognitiveservices-speech-sdk distribution is taking,if we don't need speech then we can remove it too,so lets clean them up

## Lets Make it small
### Minimize botframework-webchat-api

- First we will copy the botframework-webchat-api folder from node_modules to our local directory 
- Remove the locale references we dont need

from botframework-webchat-api\src\localization\getAllLocalizedStrings.ts ,remove all languages except what you need ,in my case only i need en_US

```ts
// Strings commented out are pending official translations


import enUS from './en-US.json';

import bundledOverrides from './overrides.json';
import LocalizedStrings from '../types/LocalizedStrings';
import mergeLocalizedStrings from './mergeLocalizedStrings';

let localizedStrings;

function getAllLocalizedStrings(): { [language: string]: LocalizedStrings } {
  return (
    localizedStrings ||
    (localizedStrings = mergeLocalizedStrings(
      {
        'en-US': enUS,
      },
      bundledOverrides
    ))
  );
}

export default getAllLocalizedStrings;

```

-  Then from botframework-webchat-api\src\localization\overrides.json,just keep what you need,in my case it is en_US

```json
{
  "en-US": {
    "COGNITIVE_SERVICES_SPEECH_TO_TEXT": true,
    "COGNITIVE_SERVICES_TEXT_TO_SPEECH": "neural",
    "GLOBALIZE_LANGUAGE": "en",
    "SPEECH_LANGUAGE": "en-US"
  }
}

```

-  Build
```unix
npm i
npm run-script build
```

### Remove microsoft-cognitiveservices-speech-sdk 

-  Copy the botframework-webchat folder from node_modules
-  from webpack config remove 

```js
react: resolve(__dirname, '../isomorphic-react/dist/react.js'),  
   react-dom': resolve(__dirname, '../isomorphic-react-dom/dist/react-dom.js')
```

-  from package.json remove
   ```js
   "isomorphic-react": "4.13.0",  
    "isomorphic-react-dom": "4.13.0",  
    "microsoft-cognitiveservices-speech-sdk": "1.15.1",  
    "botframework-directlinespeech-sdk": "4.13.0",

   ```
    
-  from src/index.ts  
   comment reference for createCognitiveServicesSpeechServicesPonyfillFactory,createDirectLineSpeechAdapters


-  Build
```unix
npm i
npm run-script build
```

### remove node_modules from botframework-webchat-api,botframework-webchat

### in next config

```js

config.resolve.alias['microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.browser/Exports']= resolve(
      __dirname,
      'node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.browser/Exports.js'
    ),
    config.resolve.alias['microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.speech/Exports']= resolve(
      __dirname,
      'node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.speech/Exports.js'
    ),
    config.resolve.alias['microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common/Exports']= resolve(
      __dirname,
      'node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common/Exports.js'
    ),
    config.resolve.alias['microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/Audio/AudioStreamFormat']= resolve(
      __dirname,
      'node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/Audio/AudioStreamFormat.js'
    ),
    config.resolve.alias['microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/Exports']= resolve(
      __dirname,
      'node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/Exports.js'
    ),
    config.resolve.alias['microsoft-cognitiveservices-speech-sdk/distrib/lib/microsoft.cognitiveservices.speech.sdk']= resolve(
      __dirname,
      'node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/microsoft.cognitiveservices.speech.sdk.js'
    ),

    // This line must be placed after other specific imports.
    config.resolve.alias['microsoft-cognitiveservices-speech-sdk']= resolve(
      __dirname,
      'node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/microsoft.cognitiveservices.speech.sdk.js'
    ),

    config.resolve.alias['botframework-webchat-api'] = resolve(
      __dirname,
      'botframework-webchat-api'
    )

    config.resolve.alias['botframework-webchat'] = resolve(
      __dirname,
      'botframework-webchat'
    )

```

### Now lets run again

```unix
npm run-script analyse
```

This is what i get

![Image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zqo4l0o7zp4i6y3udm75.png)

![Image1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r1eq3wa9f4bbvc7rodbn.png)

Thats'it

We have decreased the size of our packages,

## Bonus
For decreasing packages for lodash or moment you can use following

### for moment inside next config

```js
 config.plugins.push(new webpack.IgnorePlugin({
      resourceRegExp: /^\.\/locale$/,
      contextRegExp: /moment$/,
    }))
```

### for lodash,use in your code like

```js
import cloneDeep from 'lodash/cloneDeep';
import isEmpty from 'lodash/isEmpty';

```

### To transpile for IE11

```js

const withTM = require('next-transpile-modules')(['react-markdown', 'react-markdown/node_modules/is-plain-obj',
 'postcss','sanitize-html','sanitize-html/node_modules/escape-string-regexp']);
let withAnalyzer = withBundleAnalyzer(config)
let final = withAnalyzer
if (!process.env.NODE_ENV || process.env.NODE_ENV === 'production') {
  final = withTM(withAnalyzer)
}

```

### To find duplicate packages
```js
 const DuplicatePackageCheckerPlugin = require('duplicate-package-checker-webpack-plugin')
 config.plugins.push(new DuplicatePackageCheckerPlugin())

//  then you can use for duplicate packages,to pickup only from default path
 config.resolve.alias['core-js'] = resolve(
      __dirname,
      'node_modules',
      'core-js'
    )
```

### To see errors in chrome console with proper source code use
```js
config.optimization.minimize = false
```