Repository navigation
Check that esbuild, Vite and webpack 4/5 apps can bundle and render Web Chat #5977
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
f8fb9fc
Check that webpack 4 and 5 apps can bundle Web Chat
4e427d4
Document the webpack 4 workarounds and check them in CI
57273ca
Transpile only the dependencies that need it for webpack 4, not Web Chat
8660475
Add the changelog fragment
cadcd12
Add the webpack 4 docs to the changelog fragment
9f5e517
Build the instrumented directlinespeech with the release browser targets
78dad1a
Ship a root schema.js so webpack 4 resolves botframework-webchat/sche…
aee0785
Read the Redux DevTools extension global directly and drop @redux-dev…
5ae2fda
Bundle with esbuild, Vite and webpack 4/5 on React 16, 18 and 19, and…
eff6b86
Exclude webpack 5 with React 19 until react-film is removed
2f91c03
Describe the Redux DevTools change without implying a breaking change
eceed01
Merge remote-tracking branch 'origin/main' into OEvgeny/fix-ci-webpac…
aa4bc07
Note that webpack 5 with React 19 needs exportsPresence until react-f…
3cfbed8
Time out Chrome start and DevTools calls in browser.mjs, and always c…
3bb3c22
Check that createStore uses the enhancer returned by the Redux DevToo…
bd8d545
Type the Redux DevTools global in createStore instead of globally, so…
7dc9d97
Bundle the consumer apps in production mode, also when NODE_ENV is test
a8d5455
Shorten the bundler-test workflow comments
4acc9c6
Bundler test: one app per bundler as in its getting-started docs, che…
5277f3b
Bundler test: React scripts install the tarballs in the same npm inst…
9588d4c
Bundler test: React 16 apps render with ReactDOM.render from their ow…
14e2b32
Bundler test: add bump scripts, and install React and Web Chat only f…
e3ced06
Merge remote-tracking branch 'origin/main' into OEvgeny/fix-ci-webpac…
a986de8
Bundler test: run webpack 5 with React 19 again, react-film no longer…
58dc7b8
Run the bundler apps' bump scripts from the root bump
97cb4d4
Bundler test: render in Chrome 141 in Docker over WebDriver instead o…
0dc701c
Bundler test: time out WebDriver calls, keep the first error, bind th…
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| ### Added | ||
|
|
||
| - 👷🏻 Pull request validation: added rendering Web Chat bundled by esbuild, Vite, webpack 4 and 5 with React 16, 18 and 19, in PR [#5977](https://github.com/microsoft/BotFramework-WebChat/pull/5977), by [@OEvgeny](https://github.com/OEvgeny) | ||
| - Docs: added [workarounds for webpack 4](./docs/HOSTING.md#bundling-with-webpack-4), in PR [#5977](https://github.com/microsoft/BotFramework-WebChat/pull/5977), by [@OEvgeny](https://github.com/OEvgeny) | ||
|
|
||
| ### Changed | ||
|
|
||
| - `botframework-webchat-core` connects to Redux DevTools without the `@redux-devtools/extension` package, in PR [#5977](https://github.com/microsoft/BotFramework-WebChat/pull/5977), by [@OEvgeny](https://github.com/OEvgeny) | ||
|
|
||
| ### Fixed | ||
|
|
||
| - Fixed webpack 4 failing to resolve `botframework-webchat/schema.js` and `@redux-devtools/extension`, in PR [#5977](https://github.com/microsoft/BotFramework-WebChat/pull/5977), by [@OEvgeny](https://github.com/OEvgeny) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| /node_modules | ||
| /package-lock.json |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| # Chrome 141 with the app in <APP>/dist/ served on port 8000, see README.md. Same Chrome as the HTML tests' grid | ||
| # (selenium/node-chrome:141.0), in the single-container "standalone" variant: WebDriver on port 4444, noVNC on port 7900. | ||
| # | ||
| # Usage: docker build --build-arg APP=<esbuild | vite | webpack4 | webpack5> . | ||
|
|
||
| FROM selenium/standalone-chrome:141.0@sha256:2315640266fa3a4a28c3023fa8edd190ca7f6b827810c2f9bd3b75cb88198cff | ||
|
|
||
| ARG APP | ||
|
|
||
| COPY --chown=seluser ${APP}/dist/ /home/seluser/app/ | ||
| COPY report.js /home/seluser/app/__runner/ | ||
|
|
||
| # Injects report.js before the app's scripts. | ||
| RUN sed -i 's|<head>|&<script src="/__runner/report.js"></script>|i' /home/seluser/app/index.html | ||
|
|
||
| COPY <<EOF /etc/supervisor/conf.d/app.conf | ||
| [program:app] | ||
| command=python3 -m http.server --directory /home/seluser/app 8000 | ||
| EOF |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| # Bundler tests | ||
|
|
||
| Each folder is a Web Chat app, set up the way the getting-started docs of its bundler do it: `esbuild/`, `vite/`, `webpack4/` (with the workarounds from [`docs/HOSTING.md`](../../docs/HOSTING.md#bundling-with-webpack-4)) and `webpack5/`. `src/` is the same in every folder: CI checks it. | ||
|
|
||
| CI builds every app with React 16, 18 and 19 and the tarballs of the pull request, then `run.mjs` opens it in Chrome 141 in Docker: the app must show the bot message, without errors in the console, uncaught errors or failed loads. | ||
|
|
||
| `run.mjs` builds the [`Dockerfile`](Dockerfile) for the app (the [Selenium standalone Chrome image](https://github.com/SeleniumHQ/docker-selenium) with the app's `dist/` served on port 8000 and [`report.js`](report.js) injected), runs it, and talks WebDriver to it: opens `http://localhost:8000/` in the container, reads the result that `report.js` stores in `window.bundlerTestResult`, then removes the container. | ||
|
|
||
| ## Debugging an app | ||
|
|
||
| Build the tarballs first (`npm run build`, then `npm exec --workspaces -- npm pack` in the repository root), then: | ||
|
|
||
| ```sh | ||
| cd __tests__/bundlers/vite | ||
| npm install | ||
| npm run react16 -- ../../../packages/{api,bundle,component,core,directlinespeech,fluent-theme}/*.tgz # Or react18, react19 | ||
| npm run build | ||
| ``` | ||
|
|
||
| Then open `dist/index.html` in a browser (Vite: `npm run preview`), or run the check like CI does (needs Docker): | ||
|
|
||
| ```sh | ||
| node ../run.mjs vite | ||
| ``` | ||
|
|
||
| To look at the app as served to that Chrome, run the image yourself and browse `http://localhost:8000/`: | ||
|
|
||
| ```sh | ||
| docker build --build-arg APP=vite --tag webchat-bundler-vite .. | ||
| docker run --rm --publish 127.0.0.1:8000:8000 webchat-bundler-vite | ||
| ``` | ||
|
|
||
| The image has a copy of `dist/`: build it again after `npm run build`. | ||
|
|
||
| The React scripts install the tarballs in the same `npm install`: a separate `npm install` would remove React and Web Chat, which are not in `package.json`. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| /dist/* | ||
| !/dist/index.html | ||
| /node_modules | ||
| /package-lock.json |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| // esbuild's build script (https://esbuild.github.io/getting-started/#build-scripts): the CLI cannot pick the entry by the installed React. | ||
|
|
||
| import * as esbuild from 'esbuild'; | ||
| import { createRequire } from 'node:module'; | ||
|
|
||
| const { version: reactDOMVersion } = createRequire(import.meta.url)('react-dom/package.json'); | ||
|
|
||
| await esbuild.build({ | ||
| bundle: true, | ||
| entryPoints: [reactDOMVersion.startsWith('16.') ? 'src/index.react16.js' : 'src/index.js'], | ||
| minify: true, | ||
| outfile: 'dist/main.js' | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| <!doctype html> | ||
| <html lang="en-US"> | ||
| <head> | ||
| <meta charset="utf-8" /> | ||
| <title>Web Chat bundled with esbuild</title> | ||
| </head> | ||
| <body> | ||
| <main id="webchat" style="height: 100vh"></main> | ||
| <script src="main.js"></script> | ||
| </body> | ||
| </html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| { | ||
| "name": "@msinternal/botframework-webchat-bundlers-esbuild", | ||
| "version": "0.0.0-0", | ||
| "description": "A Web Chat app bundled with esbuild, see ../README.md", | ||
| "private": true, | ||
| "license": "MIT", | ||
| "scripts": { | ||
| "build": "node build.mjs", | ||
| "bump": "vg bump prod && vg bump dev && (npm audit fix || exit 0)", | ||
| "react16": "npm install --no-save react@16.8.6 react-dom@16.8.6", | ||
| "react18": "npm install --no-save react@18.3.1 react-dom@18.3.1", | ||
| "react19": "npm install --no-save react@19.3.1 react-dom@19.3.1" | ||
| }, | ||
| "devDependencies": { | ||
| "esbuild": "0.28.2" | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| // Same in every app folder (CI diffs them): a Web Chat app, rendering a message from a fake chat adapter. | ||
| // index.js (React 18 and later) and index.react16.js render it. | ||
|
|
||
| import ReactWebChat, { createDirectLine, renderWebChat } from 'botframework-webchat'; | ||
| import { FluentThemeProvider } from 'botframework-webchat-fluent-theme'; | ||
| import { Composer } from 'botframework-webchat/component.js'; | ||
| import { WebChatDecorator } from 'botframework-webchat/decorator.js'; | ||
| import { useActivities } from 'botframework-webchat/hook.js'; | ||
| import { createActivityPolymiddleware } from 'botframework-webchat/middleware.js'; | ||
| import { createElement } from 'react'; | ||
|
|
||
| // Every import must be defined: a bundler can resolve a subpath to an empty module without an error. | ||
| // "botframework-webchat/schema.js" is imported by the Fluent theme. | ||
| Object.entries({ | ||
| Composer, | ||
| createActivityPolymiddleware, | ||
| createDirectLine, | ||
| FluentThemeProvider, | ||
| ReactWebChat, | ||
| renderWebChat, | ||
| useActivities, | ||
| WebChatDecorator | ||
| }).forEach(([name, value]) => { | ||
| if (!value) { | ||
| throw new Error(`Import "${name}" is ${value}.`); | ||
| } | ||
| }); | ||
|
|
||
| // Emits the values on every subscription, asynchronously like DirectLineJS. | ||
| function observableOf(...values) { | ||
| return Object.freeze({ | ||
| subscribe: ({ next }) => { | ||
| const timeout = setTimeout(() => values.forEach(value => next(value))); | ||
|
|
||
| return Object.freeze({ unsubscribe: () => clearTimeout(timeout) }); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| // A chat adapter that connects at once and receives one message from the bot. | ||
| const directLine = Object.freeze({ | ||
| activity$: observableOf({ | ||
| from: { id: 'bot', role: 'bot' }, | ||
| id: 'a-00001', | ||
| text: 'Hello from the bot.', | ||
| timestamp: new Date().toISOString(), | ||
| type: 'message' | ||
| }), | ||
| connectionStatus$: observableOf(0, 1, 2), // Uninitialized, connecting, online. | ||
| end: () => undefined, | ||
| postActivity: () => observableOf('a-00002') | ||
| }); | ||
|
|
||
| export default createElement(FluentThemeProvider, {}, createElement(ReactWebChat, { directLine })); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| // The entry for React 18 and later. | ||
|
|
||
| import { createRoot } from 'react-dom/client'; | ||
|
|
||
| import app from './app.js'; | ||
|
|
||
| createRoot(document.getElementById('webchat')).render(app); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| // The entry for React 16, as apps were written then: the bundler config picks it when React 16 is installed. | ||
|
|
||
| import ReactDOM from 'react-dom'; | ||
|
|
||
| import app from './app.js'; | ||
|
|
||
| ReactDOM.render(app, document.getElementById('webchat')); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| // Injected before the app's scripts (see Dockerfile): records errors, and reports in `window.bundlerTestResult` once Web | ||
| // Chat shows the bot message, for run.mjs to read over WebDriver. | ||
|
|
||
| (() => { | ||
| const errors = []; | ||
| const { error } = console; | ||
|
|
||
| console.error = (...args) => { | ||
| errors.push(args.map(arg => arg?.stack || String(arg)).join(' ')); | ||
| error.apply(console, args); | ||
| }; | ||
|
|
||
| // Capture phase: a script or style sheet that fails to load fires a non-bubbling "error" on its element. | ||
| addEventListener( | ||
| 'error', | ||
| event => | ||
| errors.push( | ||
| event.target === window | ||
| ? event.error?.stack || event.message | ||
| : `Failed to load ${event.target.src || event.target.href}.` | ||
| ), | ||
| true | ||
| ); | ||
|
|
||
| addEventListener('unhandledrejection', ({ reason }) => errors.push(reason?.stack || String(reason))); | ||
|
|
||
| const interval = setInterval(() => { | ||
| const rendered = | ||
| document.readyState === 'complete' && | ||
| !!document.querySelector('[role="feed"]')?.textContent.includes('Hello from the bot.') && | ||
| !!document.querySelector('textarea'); | ||
|
|
||
| if (rendered || errors.length) { | ||
| clearInterval(interval); | ||
| window.bundlerTestResult = Object.freeze({ errors: Object.freeze([...errors]), ok: rendered && !errors.length }); | ||
| } | ||
| }, 100); | ||
| })(); |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.