From 6d6f5ca5c922b4e6755ab3fd2b06359f4c2a328e Mon Sep 17 00:00:00 2001 From: William Wong Date: Tue, 14 Oct 2025 22:25:18 +0000 Subject: [PATCH 01/23] Initial commit --- .../experience/chatLauncher/simple.html | 45 ++++++++++++++ lint-staged.config.js | 1 + package-lock.json | 40 +++++++++++++ package.json | 4 ++ packages/bundle/esbuild.static.mjs | 23 +------ packages/bundle/experience/chatLauncher.js | 3 + packages/bundle/package.json | 11 +++- .../boot/actual/experience/chatLauncher.ts | 1 + .../boot/exports/experience/chatLauncher.ts | 1 + packages/bundle/tsup.config.ts | 1 + .../experience-chat-launcher/.eslintrc.yml | 14 +++++ packages/experience-chat-launcher/.gitignore | 5 ++ packages/experience-chat-launcher/README.md | 0 .../experience-chat-launcher/package.json | 60 +++++++++++++++++++ .../experience-chat-launcher/src/env.d.ts | 1 + .../experience-chat-launcher/src/index.ts | 1 + .../src/private/ChatLauncher.module.css | 3 + .../src/private/ChatLauncher.tsx | 34 +++++++++++ .../private/ChatLauncherButton.module.css | 6 ++ .../private/private/ChatLauncherButton.tsx | 18 ++++++ .../src/stylesheet/ChatLauncherStylesheet.tsx | 33 ++++++++++ .../createChatLauncherStyleElements.ts | 8 +++ .../src/tsconfig.json | 6 ++ .../experience-chat-launcher/tsup.config.ts | 57 ++++++++++++++++++ serve-test.json | 4 ++ 25 files changed, 357 insertions(+), 23 deletions(-) create mode 100644 __tests__/html2/simple/fatModule/experience/chatLauncher/simple.html create mode 100644 packages/bundle/experience/chatLauncher.js create mode 100644 packages/bundle/src/boot/actual/experience/chatLauncher.ts create mode 100644 packages/bundle/src/boot/exports/experience/chatLauncher.ts create mode 100644 packages/experience-chat-launcher/.eslintrc.yml create mode 100644 packages/experience-chat-launcher/.gitignore create mode 100644 packages/experience-chat-launcher/README.md create mode 100644 packages/experience-chat-launcher/package.json create mode 100644 packages/experience-chat-launcher/src/env.d.ts create mode 100644 packages/experience-chat-launcher/src/index.ts create mode 100644 packages/experience-chat-launcher/src/private/ChatLauncher.module.css create mode 100644 packages/experience-chat-launcher/src/private/ChatLauncher.tsx create mode 100644 packages/experience-chat-launcher/src/private/private/ChatLauncherButton.module.css create mode 100644 packages/experience-chat-launcher/src/private/private/ChatLauncherButton.tsx create mode 100644 packages/experience-chat-launcher/src/stylesheet/ChatLauncherStylesheet.tsx create mode 100644 packages/experience-chat-launcher/src/stylesheet/createChatLauncherStyleElements.ts create mode 100644 packages/experience-chat-launcher/src/tsconfig.json create mode 100644 packages/experience-chat-launcher/tsup.config.ts diff --git a/__tests__/html2/simple/fatModule/experience/chatLauncher/simple.html b/__tests__/html2/simple/fatModule/experience/chatLauncher/simple.html new file mode 100644 index 0000000000..cf006410e9 --- /dev/null +++ b/__tests__/html2/simple/fatModule/experience/chatLauncher/simple.html @@ -0,0 +1,45 @@ + + + + + + +
+ + + + diff --git a/lint-staged.config.js b/lint-staged.config.js index 1785a33a54..e1c64dca54 100644 --- a/lint-staged.config.js +++ b/lint-staged.config.js @@ -19,6 +19,7 @@ module.exports = { 'packages/core/src/**/*.{mjs,js,ts,tsx}': ['npm run precommit:eslint:core'], 'packages/debug-theme/src/**/*.{mjs,js,ts,tsx}': ['npm run precommit:eslint:debug-theme'], 'packages/directlinespeech/src/**/*.{mjs,js,ts,tsx}': ['npm run precommit:eslint:directlinespeech'], + 'packages/experience-chat-launcher/src/**/*.{mjs,js,ts,tsx}': ['npm run precommit:eslint:experience-chat-launcher'], 'packages/fluent-theme/src/**/*.{mjs,js,ts,tsx}': ['npm run precommit:eslint:fluent-theme'], 'packages/isomorphic-react/src/**/*.{mjs,js,ts,tsx}': ['npm run precommit:eslint:isomorphic-react'], 'packages/isomorphic-react-dom/src/**/*.{mjs,js,ts,tsx}': ['npm run precommit:eslint:isomorphic-react-dom'], diff --git a/package-lock.json b/package-lock.json index dbbab52530..e0899d6b1e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,7 @@ "./packages/isomorphic-react", "./packages/isomorphic-react-dom", "./packages/component", + "./packages/experience-chat-launcher", "./packages/repack/adaptivecards", "./packages/repack/base64-js", "./packages/repack/botframework-directlinejs", @@ -3051,6 +3052,10 @@ "resolved": "packages/debug-theme", "link": true }, + "node_modules/@msinternal/botframework-webchat-experience-chat-launcher": { + "resolved": "packages/experience-chat-launcher", + "link": true + }, "node_modules/@msinternal/botframework-webchat-react-hooks": { "resolved": "packages/react-hooks", "link": true @@ -19064,6 +19069,7 @@ "@msinternal/base64-js": "0.0.0-0", "@msinternal/botframework-directlinejs": "0.0.0-0", "@msinternal/botframework-webchat-base": "0.0.0-0", + "@msinternal/botframework-webchat-experience-chat-launcher": "0.0.0-0", "@msinternal/botframework-webchat-react-valibot": "0.0.0-0", "@msinternal/botframework-webchat-tsconfig": "0.0.0-0", "@msinternal/isomorphic-react": "0.0.0-0", @@ -19928,6 +19934,40 @@ "url": "https://github.com/sponsors/mysticatea" } }, + "packages/experience-chat-launcher": { + "name": "@msinternal/botframework-webchat-experience-chat-launcher", + "version": "0.0.0-0", + "license": "MIT", + "dependencies": { + "valibot": "1.1.0" + }, + "devDependencies": { + "@msinternal/botframework-webchat-react-valibot": "0.0.0-0", + "@msinternal/botframework-webchat-styles": "0.0.0-0", + "@types/node": "^22.13.4", + "typescript": "^5.7.3" + }, + "peerDependencies": { + "react": ">= 16.8.6" + } + }, + "packages/experience-chat-launcher/node_modules/@types/node": { + "version": "22.18.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.18.10.tgz", + "integrity": "sha512-anNG/V/Efn/YZY4pRzbACnKxNKoBng2VTFydVu8RRs5hQjikP8CQfaeAV59VFSCzKNp90mXiVXW2QzV56rwMrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "packages/experience-chat-launcher/node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, "packages/fluent-theme": { "name": "botframework-webchat-fluent-theme", "version": "0.0.0-0", diff --git a/package.json b/package.json index cd25e62695..0f12d62285 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "./packages/isomorphic-react", "./packages/isomorphic-react-dom", "./packages/component", + "./packages/experience-chat-launcher", "./packages/repack/adaptivecards", "./packages/repack/base64-js", "./packages/repack/botframework-directlinejs", @@ -79,6 +80,7 @@ "precommit:eslint:core": "cd packages && cd core && npm run precommit:eslint", "precommit:eslint:debug-theme": "cd packages && cd debug-theme && npm run precommit:eslint", "precommit:eslint:directlinespeech": "cd packages && cd directlinespeech && npm run precommit:eslint", + "precommit:eslint:experience-chat-launcher": "cd packages && cd experience-chat-launcher && npm run precommit:eslint", "precommit:eslint:fluent-theme": "cd packages && cd fluent-theme && npm run precommit:eslint", "precommit:eslint:isomorphic-react": "cd packages && cd isomorphic-react && npm run precommit:eslint", "precommit:eslint:isomorphic-react-dom": "cd packages && cd isomorphic-react-dom && npm run precommit:eslint", @@ -112,6 +114,7 @@ "precommit:typecheck:component": "cd packages && cd component && npm run precommit:typecheck", "precommit:typecheck:core": "cd packages && cd core && npm run precommit:typecheck", "precommit:typecheck:debug-theme": "cd packages && cd debug-theme && npm run precommit:typecheck", + "precommit:typecheck:experience-chat-launcher": "cd packages && cd experience-chat-launcher && npm run precommit:typecheck", "precommit:typecheck:fluent-theme": "cd packages && cd fluent-theme && npm run precommit:typecheck", "precommit:typecheck:react-hooks": "cd packages && cd react-hooks && npm run precommit:typecheck", "precommit:typecheck:react-valibot": "cd packages && cd react-valibot && npm run precommit:typecheck", @@ -139,6 +142,7 @@ "start:core": "cd packages && cd core && npm start", "start:debug-theme": "cd packages && cd debug-theme && npm start", "start:directlinespeech": "cd packages && cd directlinespeech && npm start", + "start:experience-chat-launcher": "cd packages && cd experience-chat-launcher && npm start", "start:fluent-theme": "cd packages && cd fluent-theme && npm start", "start:react-hooks": "cd packages && cd react-hooks && npm start", "start:react-valibot": "cd packages && cd react-valibot && npm start", diff --git a/packages/bundle/esbuild.static.mjs b/packages/bundle/esbuild.static.mjs index 439dafe42b..3030ae06e9 100644 --- a/packages/bundle/esbuild.static.mjs +++ b/packages/bundle/esbuild.static.mjs @@ -11,28 +11,6 @@ import { readPackage } from 'read-pkg'; import { fileURLToPath } from 'url'; import { cssPlugin } from '../../esbuildPlugins.mjs'; -// eslint-disable-next-line no-unused-vars -const isomorphicReactPlugin = { - name: 'isomorphic-react', - setup(build) { - // eslint-disable-next-line require-unicode-regexp - build.onResolve({ filter: /^(react|react-dom)$/, namespace: 'file' }, ({ path }) => ({ - namespace: 'isomorphic-react', - path - })); - - // eslint-disable-next-line require-unicode-regexp - build.onLoad({ filter: /^react$/, namespace: 'isomorphic-react' }, () => ({ - contents: "import React from 'react'; module.exports = globalThis.React || React;" - })); - - // eslint-disable-next-line require-unicode-regexp - build.onLoad({ filter: /^react-dom$/, namespace: 'isomorphic-react' }, () => ({ - contents: "import ReactDOM from 'react-dom'; module.exports = globalThis.ReactDOM || ReactDOM;" - })); - } -}; - function createWatcherPlugin(name) { /** @type { import('esbuild').Plugin } */ return { @@ -130,6 +108,7 @@ const IGNORED_OWN_PACKAGES = [ 'botframework-webchat': './src/boot/exports/index.ts', 'botframework-webchat/component': './src/boot/exports/component.ts', 'botframework-webchat/decorator': './src/boot/exports/decorator.ts', + 'botframework-webchat/experience/chatLauncher': './src/boot/exports/experience/chatLauncher.ts', 'botframework-webchat/hook': './src/boot/exports/hook.ts', 'botframework-webchat/internal': './src/boot/exports/internal.ts', 'botframework-webchat/middleware': './src/boot/exports/middleware.ts', diff --git a/packages/bundle/experience/chatLauncher.js b/packages/bundle/experience/chatLauncher.js new file mode 100644 index 0000000000..aeceb7f170 --- /dev/null +++ b/packages/bundle/experience/chatLauncher.js @@ -0,0 +1,3 @@ +// This is required for Webpack 4 which does not support named exports. +// eslint-disable-next-line no-undef +module.exports = require('./dist/botframework-webchat.experience.chat-launcher.js'); diff --git a/packages/bundle/package.json b/packages/bundle/package.json index 432a98e834..87a035aef6 100644 --- a/packages/bundle/package.json +++ b/packages/bundle/package.json @@ -35,6 +35,12 @@ "default": "./dist/botframework-webchat.decorator.js" } }, + "./experience/chatLauncher": { + "import": { + "types": "./dist/botframework-webchat.experience.chat-launcher.d.mts", + "default": "./dist/botframework-webchat.experience.chat-launcher.mjs" + } + }, "./hook": { "import": { "types": "./dist/botframework-webchat.hook.d.mts", @@ -82,6 +88,7 @@ "homepage": "https://github.com/microsoft/BotFramework-WebChat/#readme", "files": [ "./dist/**/*", + "./experience/**/*", "./src/**/*", "./static/**/*" ], @@ -121,8 +128,9 @@ "localDependencies": { "@msinternal/adaptivecards": "development", "@msinternal/base64-js": "development", - "@msinternal/botframework-webchat-base": "development", "@msinternal/botframework-directlinejs": "development", + "@msinternal/botframework-webchat-base": "development", + "@msinternal/botframework-webchat-experience-chat-launcher": "development", "@msinternal/botframework-webchat-react-valibot": "development", "@msinternal/botframework-webchat-tsconfig": "development", "@msinternal/isomorphic-react-baseline": "development", @@ -212,6 +220,7 @@ "@msinternal/base64-js": "0.0.0-0", "@msinternal/botframework-directlinejs": "0.0.0-0", "@msinternal/botframework-webchat-base": "0.0.0-0", + "@msinternal/botframework-webchat-experience-chat-launcher": "0.0.0-0", "@msinternal/botframework-webchat-react-valibot": "0.0.0-0", "@msinternal/botframework-webchat-tsconfig": "0.0.0-0", "@msinternal/isomorphic-react": "0.0.0-0", diff --git a/packages/bundle/src/boot/actual/experience/chatLauncher.ts b/packages/bundle/src/boot/actual/experience/chatLauncher.ts new file mode 100644 index 0000000000..b5a7a7a54a --- /dev/null +++ b/packages/bundle/src/boot/actual/experience/chatLauncher.ts @@ -0,0 +1 @@ +export { ChatLauncher } from '@msinternal/botframework-webchat-experience-chat-launcher'; diff --git a/packages/bundle/src/boot/exports/experience/chatLauncher.ts b/packages/bundle/src/boot/exports/experience/chatLauncher.ts new file mode 100644 index 0000000000..a7d1e88959 --- /dev/null +++ b/packages/bundle/src/boot/exports/experience/chatLauncher.ts @@ -0,0 +1 @@ +export * from '../../actual/experience/chatLauncher'; diff --git a/packages/bundle/tsup.config.ts b/packages/bundle/tsup.config.ts index c885f394f9..f95f847f72 100644 --- a/packages/bundle/tsup.config.ts +++ b/packages/bundle/tsup.config.ts @@ -21,6 +21,7 @@ const commonConfig = applyConfig(config => ({ 'botframework-webchat': './src/boot/exports/index.ts', 'botframework-webchat.component': './src/boot/exports/component.ts', 'botframework-webchat.decorator': './src/boot/exports/decorator.ts', + 'botframework-webchat.experience.chatLauncher': './src/boot/exports/experience/chatLauncher.ts', 'botframework-webchat.hook': './src/boot/exports/hook.ts', 'botframework-webchat.internal': './src/boot/exports/internal.ts', 'botframework-webchat.middleware': './src/boot/exports/middleware.ts' diff --git a/packages/experience-chat-launcher/.eslintrc.yml b/packages/experience-chat-launcher/.eslintrc.yml new file mode 100644 index 0000000000..6ba5244054 --- /dev/null +++ b/packages/experience-chat-launcher/.eslintrc.yml @@ -0,0 +1,14 @@ +extends: + - ../../.eslintrc.production.yml + - ../../.eslintrc.react.yml + +# This package is compatible with web browser. +env: + browser: true + +rules: + # React functional component is better in function style than arrow style. + prefer-arrow-callback: off + + # React already deprecated default props. + react/require-default-props: off diff --git a/packages/experience-chat-launcher/.gitignore b/packages/experience-chat-launcher/.gitignore new file mode 100644 index 0000000000..515a303349 --- /dev/null +++ b/packages/experience-chat-launcher/.gitignore @@ -0,0 +1,5 @@ +/*.tgz +/dist/ +/dist.tmp/ +/node_modules/ +/tsup.config.bundled_*.mjs diff --git a/packages/experience-chat-launcher/README.md b/packages/experience-chat-launcher/README.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/experience-chat-launcher/package.json b/packages/experience-chat-launcher/package.json new file mode 100644 index 0000000000..3bcd332c51 --- /dev/null +++ b/packages/experience-chat-launcher/package.json @@ -0,0 +1,60 @@ +{ + "name": "@msinternal/botframework-webchat-experience-chat-launcher", + "version": "0.0.0-0", + "description": "botframework-webchat-api/middleware package", + "main": "./dist/botframework-webchat-experience-chat-launcher.js", + "types": "./dist/botframework-webchat-experience-chat-launcher.d.ts", + "exports": { + ".": { + "import": { + "types": "./dist/botframework-webchat-experience-chat-launcher.d.mts", + "default": "./dist/botframework-webchat-experience-chat-launcher.mjs" + } + } + }, + "author": "Microsoft Corporation", + "license": "MIT", + "private": true, + "repository": { + "type": "git", + "url": "git+https://github.com/microsoft/BotFramework-WebChat.git" + }, + "bugs": { + "url": "https://github.com/microsoft/BotFramework-WebChat/issues" + }, + "files": [ + "./dist/**/*", + "./src/**/*", + "*.js" + ], + "localDependencies": { + "@msinternal/botframework-webchat-react-valibot": "development", + "@msinternal/botframework-webchat-styles": "development" + }, + "homepage": "https://github.com/microsoft/BotFramework-WebChat/tree/main/packages/experience-chat-launcher#readme", + "scripts": { + "build": "tsup --config ./tsup.config.ts", + "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", + "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", + "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", + "eslint": "npm run precommit", + "postversion": "cat package.json | jq '.version as $V | (.localDependencies // {} | with_entries(select(.value == \"production\") | { key: .key, value: $V })) as $L1 | (.localDependencies // {} | with_entries(select(.value == \"development\") | { key: .key, value: $V })) as $L2 | ((.dependencies // {}) + $L1 | to_entries | sort_by(.key) | from_entries) as $D1 | ((.devDependencies // {}) + $L2 | to_entries | sort_by(.key) | from_entries) as $D2 | . + { dependencies: $D1, devDependencies: $D2 }' > package-temp.json && mv package-temp.json package.json", + "precommit": "npm run precommit:eslint -- src && npm run precommit:typecheck", + "precommit:eslint": "../../node_modules/.bin/eslint --report-unused-disable-directives --max-warnings 0", + "precommit:typecheck": "tsc --project ./src --emitDeclarationOnly false --esModuleInterop true --noEmit --pretty false", + "preversion": "cat package.json | jq '(.localDependencies // {} | to_entries | map([if .value == \"production\" then \"dependencies\" else \"devDependencies\" end, .key])) as $P | delpaths($P)' > package-temp.json && mv package-temp.json package.json", + "start": "npm run build -- --watch" + }, + "devDependencies": { + "@msinternal/botframework-webchat-react-valibot": "0.0.0-0", + "@msinternal/botframework-webchat-styles": "0.0.0-0", + "@types/node": "^22.13.4", + "typescript": "^5.7.3" + }, + "peerDependencies": { + "react": ">= 16.8.6" + }, + "dependencies": { + "valibot": "1.1.0" + } +} diff --git a/packages/experience-chat-launcher/src/env.d.ts b/packages/experience-chat-launcher/src/env.d.ts new file mode 100644 index 0000000000..a8694ad586 --- /dev/null +++ b/packages/experience-chat-launcher/src/env.d.ts @@ -0,0 +1 @@ +/// diff --git a/packages/experience-chat-launcher/src/index.ts b/packages/experience-chat-launcher/src/index.ts new file mode 100644 index 0000000000..26aeff895f --- /dev/null +++ b/packages/experience-chat-launcher/src/index.ts @@ -0,0 +1 @@ +export { default as ChatLauncher } from './private/ChatLauncher'; diff --git a/packages/experience-chat-launcher/src/private/ChatLauncher.module.css b/packages/experience-chat-launcher/src/private/ChatLauncher.module.css new file mode 100644 index 0000000000..763c0e63ba --- /dev/null +++ b/packages/experience-chat-launcher/src/private/ChatLauncher.module.css @@ -0,0 +1,3 @@ +:global(.webchat).webchat-experience-chat-launcher { + display: contents; +} diff --git a/packages/experience-chat-launcher/src/private/ChatLauncher.tsx b/packages/experience-chat-launcher/src/private/ChatLauncher.tsx new file mode 100644 index 0000000000..7a934f70f5 --- /dev/null +++ b/packages/experience-chat-launcher/src/private/ChatLauncher.tsx @@ -0,0 +1,34 @@ +import { validateProps } from '@msinternal/botframework-webchat-react-valibot'; +import { useStyles } from '@msinternal/botframework-webchat-styles/react'; +import cx from 'classnames'; +import React, { memo } from 'react'; +import { object, optional, pipe, readonly, string, type InferInput } from 'valibot'; + +import ChatLauncherStylesheet from '../stylesheet/ChatLauncherStylesheet'; +import styles from './ChatLauncher.module.css'; +import ChatLauncherButton from './private/ChatLauncherButton'; + +const chatLauncherPropsSchema = pipe( + object({ + nonce: optional(string()) + }), + readonly() +); + +type ChatLauncherProps = InferInput; + +function ChatLauncher(props: ChatLauncherProps) { + const { nonce } = validateProps(chatLauncherPropsSchema, props); + const classNames = useStyles(styles); + + return ( +
+ + +
+ ); +} + +ChatLauncher.displayName = 'ChatLauncher'; + +export default memo(ChatLauncher); diff --git a/packages/experience-chat-launcher/src/private/private/ChatLauncherButton.module.css b/packages/experience-chat-launcher/src/private/private/ChatLauncherButton.module.css new file mode 100644 index 0000000000..45dc74b4e1 --- /dev/null +++ b/packages/experience-chat-launcher/src/private/private/ChatLauncherButton.module.css @@ -0,0 +1,6 @@ +:global(.webchat-experience-chat-launcher) .chat-launcher-button { + background-color: #e00; + color: White; + height: 40px; + width: 40px; +} diff --git a/packages/experience-chat-launcher/src/private/private/ChatLauncherButton.tsx b/packages/experience-chat-launcher/src/private/private/ChatLauncherButton.tsx new file mode 100644 index 0000000000..4cc6c3a03d --- /dev/null +++ b/packages/experience-chat-launcher/src/private/private/ChatLauncherButton.tsx @@ -0,0 +1,18 @@ +import { useStyles } from '@msinternal/botframework-webchat-styles/react'; +import React, { memo } from 'react'; + +import styles from './ChatLauncherButton.module.css'; + +function ChatLauncherButton() { + const classNames = useStyles(styles); + + return ( + + ); +} + +ChatLauncherButton.displayName = 'ChatLauncherButton'; + +export default memo(ChatLauncherButton); diff --git a/packages/experience-chat-launcher/src/stylesheet/ChatLauncherStylesheet.tsx b/packages/experience-chat-launcher/src/stylesheet/ChatLauncherStylesheet.tsx new file mode 100644 index 0000000000..a894609ac3 --- /dev/null +++ b/packages/experience-chat-launcher/src/stylesheet/ChatLauncherStylesheet.tsx @@ -0,0 +1,33 @@ +// TODO: [P2] This component can be replaced by `bindProps`. +import { validateProps } from '@msinternal/botframework-webchat-react-valibot'; +import { InjectStyleElements } from '@msinternal/botframework-webchat-styles/react'; +import { useStyleOptions } from 'botframework-webchat-api/hook'; +import React, { memo, type FunctionComponent } from 'react'; +import { never, object, optional, pipe, readonly, string, undefinedable, type InferInput } from 'valibot'; + +import createChatLauncherStyleElements from './createChatLauncherStyleElements'; + +const componentStylesheetPropsSchema = pipe( + object({ + children: optional(never()), + nonce: undefinedable(string()) + }), + readonly() +); + +type ComponentStylesheetProps = InferInput; + +const styleElements = createChatLauncherStyleElements('experience-chat-launcher'); + +function ChatLauncherStylesheet(props: ComponentStylesheetProps) { + const { nonce } = validateProps(componentStylesheetPropsSchema, props); + + const [{ stylesRoot }] = useStyleOptions(); + + return ; +} + +ChatLauncherStylesheet.displayName = 'ChatLauncherStylesheet'; + +export default memo(ChatLauncherStylesheet as FunctionComponent); +export { componentStylesheetPropsSchema, type ComponentStylesheetProps }; diff --git a/packages/experience-chat-launcher/src/stylesheet/createChatLauncherStyleElements.ts b/packages/experience-chat-launcher/src/stylesheet/createChatLauncherStyleElements.ts new file mode 100644 index 0000000000..6d1acf99f6 --- /dev/null +++ b/packages/experience-chat-launcher/src/stylesheet/createChatLauncherStyleElements.ts @@ -0,0 +1,8 @@ +import { makeCreateStyles } from '@msinternal/botframework-webchat-styles'; + +const chatLauncherStyleContent = '@--CHAT-LAUNCHER-STYLES-CONTENT--@'; + +const createChatLauncherStyleElements = makeCreateStyles(chatLauncherStyleContent); + +export default createChatLauncherStyleElements; +export { chatLauncherStyleContent }; diff --git a/packages/experience-chat-launcher/src/tsconfig.json b/packages/experience-chat-launcher/src/tsconfig.json new file mode 100644 index 0000000000..e6a6f30a92 --- /dev/null +++ b/packages/experience-chat-launcher/src/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": [ + "@msinternal/botframework-webchat-tsconfig/current", + "@msinternal/botframework-webchat-tsconfig/features/cssModules" + ] +} diff --git a/packages/experience-chat-launcher/tsup.config.ts b/packages/experience-chat-launcher/tsup.config.ts new file mode 100644 index 0000000000..712e998b72 --- /dev/null +++ b/packages/experience-chat-launcher/tsup.config.ts @@ -0,0 +1,57 @@ +import { injectCSSPlugin } from '@msinternal/botframework-webchat-styles/build'; +import { type Plugin } from 'esbuild'; +import { defineConfig } from 'tsup'; + +import { applyConfig } from '../../tsup.base.config'; +import { chatLauncherStyleContent as chatLauncherStyleContentPlaceholder } from './src/stylesheet/createChatLauncherStyleElements'; + +// TODO: [P1] Compute this automatically. +const DEPENDENT_PATHS = ['api/src/index.ts']; + +// eslint-disable-next-line no-unused-vars +const isomorphicReactPlugin: Plugin = { + name: 'isomorphic-react', + setup(build) { + // eslint-disable-next-line require-unicode-regexp + build.onResolve({ filter: /^(react|react-dom)$/, namespace: 'file' }, ({ path }) => ({ + namespace: 'isomorphic-react', + path + })); + + // eslint-disable-next-line require-unicode-regexp + build.onLoad({ filter: /^react$/, namespace: 'isomorphic-react' }, () => ({ + contents: "import React from 'react'; module.exports = globalThis.React || React;" + })); + + // eslint-disable-next-line require-unicode-regexp + build.onLoad({ filter: /^react-dom$/, namespace: 'isomorphic-react' }, () => ({ + contents: "import ReactDOM from 'react-dom'; module.exports = globalThis.ReactDOM || ReactDOM;" + })); + } +}; + +const commonConfig = applyConfig(config => ({ + ...config, + entry: { + 'botframework-webchat-experience-chat-launcher': './src/index.ts' + }, + external: ['react', 'react-dom'], + esbuildPlugins: [ + ...config.esbuildPlugins, + isomorphicReactPlugin, + injectCSSPlugin({ stylesPlaceholder: chatLauncherStyleContentPlaceholder }) + ], + onSuccess: `touch ${DEPENDENT_PATHS.map(path => `../${path}`).join(' ')}` +})); + +export default defineConfig([ + { + ...commonConfig, + format: 'esm' + }, + { + ...commonConfig, + format: 'cjs', + target: [...commonConfig.target, 'es2019'] + } +]); diff --git a/serve-test.json b/serve-test.json index 37386abc8a..771b0d2c71 100644 --- a/serve-test.json +++ b/serve-test.json @@ -87,6 +87,10 @@ "source": "/__dist__/packages/bundle/static/:filename", "destination": "packages/bundle/static/:filename" }, + { + "source": "/__dist__/packages/bundle/static/:dirname1/:dirname2/:filename", + "destination": "packages/bundle/static/:dirname1/:dirname2/:filename" + }, { "source": "/__dist__/packages/bundle/static/:dirname/:filename", "destination": "packages/bundle/static/:dirname/:filename" From 909aee0a01a6e5a726e663f6d771f285b75a1d8d Mon Sep 17 00:00:00 2001 From: William Wong Date: Wed, 15 Oct 2025 00:50:47 +0000 Subject: [PATCH 02/23] Add stylesRoot --- .../src/private/ChatLauncher.tsx | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/packages/experience-chat-launcher/src/private/ChatLauncher.tsx b/packages/experience-chat-launcher/src/private/ChatLauncher.tsx index 7a934f70f5..a15710722f 100644 --- a/packages/experience-chat-launcher/src/private/ChatLauncher.tsx +++ b/packages/experience-chat-launcher/src/private/ChatLauncher.tsx @@ -1,8 +1,9 @@ import { validateProps } from '@msinternal/botframework-webchat-react-valibot'; import { useStyles } from '@msinternal/botframework-webchat-styles/react'; +import { ThemeProvider } from 'botframework-webchat/component'; import cx from 'classnames'; -import React, { memo } from 'react'; -import { object, optional, pipe, readonly, string, type InferInput } from 'valibot'; +import React, { memo, useMemo } from 'react'; +import { custom, object, optional, pipe, readonly, string, union, type InferInput } from 'valibot'; import ChatLauncherStylesheet from '../stylesheet/ChatLauncherStylesheet'; import styles from './ChatLauncher.module.css'; @@ -10,7 +11,16 @@ import ChatLauncherButton from './private/ChatLauncherButton'; const chatLauncherPropsSchema = pipe( object({ - nonce: optional(string()) + nonce: optional(string()), + stylesRoot: optional( + union( + [ + custom(value => value instanceof HTMLLinkElement && value.rel === 'stylesheet'), + custom(value => value instanceof HTMLStyleElement) + ], + '"stylesRoot" must be either or
diff --git a/packages/experience-chat-launcher/src/private/private/ChatLauncherButton.module.css b/packages/experience-chat-launcher/src/private/private/ChatLauncherButton.module.css index 45dc74b4e1..d9d072bcaf 100644 --- a/packages/experience-chat-launcher/src/private/private/ChatLauncherButton.module.css +++ b/packages/experience-chat-launcher/src/private/private/ChatLauncherButton.module.css @@ -1,6 +1,32 @@ :global(.webchat-experience-chat-launcher) .chat-launcher-button { - background-color: #e00; - color: White; - height: 40px; - width: 40px; + /* TODO: [P0] Expose some CSS classes to white-label. */ + --webchat-colorNeutralBackground1: var(--colorNeutralBackground1, #ffffff); + --webchat-colorNeutralForeground1: var(--colorNeutralForeground1, #242424); + --webchat-colorNeutralStroke1: var(--colorNeutralStroke1, #d1d1d1); + --webchat-shadow8: var(--shadow8, 0 0 2px rgba(0, 0, 0, 0.12), 0 4px 8px rgba(0, 0, 0, 0.14)); + + /* TODO: [P0] Should we expose some customizable CSS variables? */ + /* TODO: [P0] How's the naming system? */ + /* TODO: [P0] Should it be customizable to anchor to bottom/left instead? */ + --webchat-chatLauncherButton-background: var(--webchat-colorNeutralBackground1); + --webchat-chatLauncherButton-borderColor: var(--webchat-colorNeutralStroke1); + --webchat-chatLauncherButton-borderWidth: 1px; + --webchat-chatLauncherButton-color: var(--webchat-colorNeutralForeground1); + --webchat-chatLauncherButton-margin: 40px; + --webchat-chatLauncherButton-shadow: var(--webchat-shadow8); + + appearance: none; + background: var(--webchat-chatLauncherButton-background); + border: 0; + border-radius: 30px; + bottom: 0; + box-shadow: var(--webchat-chatLauncherButton-shadow); + color: var(--webchat-chatLauncherButton-color); + outline: var(--webchat-chatLauncherButton-borderWidth) solid var(--webchat-chatLauncherButton-borderColor); + position: fixed; + height: 60px; + margin: var(--webchat-chatLauncherButton-margin); + padding: 0; + right: 0; + width: 60px; } diff --git a/packages/experience-chat-launcher/src/private/private/ChatLauncherButton.tsx b/packages/experience-chat-launcher/src/private/private/ChatLauncherButton.tsx index 7d3bf0a1f5..76465eaa79 100644 --- a/packages/experience-chat-launcher/src/private/private/ChatLauncherButton.tsx +++ b/packages/experience-chat-launcher/src/private/private/ChatLauncherButton.tsx @@ -4,6 +4,7 @@ import React, { memo } from 'react'; import { boolean, object, pipe, readonly, type InferInput } from 'valibot'; import styles from './ChatLauncherButton.module.css'; +import ChatLauncherIcon from './ChatLauncherIcon'; const chatLauncherButtonPropsSchema = pipe( object({ @@ -14,13 +15,14 @@ const chatLauncherButtonPropsSchema = pipe( type ChatLauncherButtonProps = InferInput; +// TODO: [P0] Should we make an IconButton polymiddleware and ChatLauncherButton is based from that? function ChatLauncherButton(props: ChatLauncherButtonProps) { const { hasMessage } = validateProps(chatLauncherButtonPropsSchema, props); const classNames = useStyles(styles); return ( ); } diff --git a/packages/experience-chat-launcher/src/private/private/ChatLauncherIcon.tsx b/packages/experience-chat-launcher/src/private/private/ChatLauncherIcon.tsx new file mode 100644 index 0000000000..ca0dcc7761 --- /dev/null +++ b/packages/experience-chat-launcher/src/private/private/ChatLauncherIcon.tsx @@ -0,0 +1,17 @@ +import React, { memo } from 'react'; + +// TODO: [P0] Use monochromatic icon, i.e. FluentIcon. +function ChatLauncherIcon() { + return ( + + + + ); +} + +ChatLauncherIcon.displayName = 'ChatLauncherIcon'; + +export default memo(ChatLauncherIcon); diff --git a/packages/fluent-theme/src/components/theme/Theme.module.css b/packages/fluent-theme/src/components/theme/Theme.module.css index 25f1979a5a..cb74ba6a80 100644 --- a/packages/fluent-theme/src/components/theme/Theme.module.css +++ b/packages/fluent-theme/src/components/theme/Theme.module.css @@ -81,6 +81,7 @@ /* https://github.com/microsoft/fluentui/blob/master/packages/tokens/src/utils/shadows.ts */ --webchat-shadow2: var(--shadow2, 0 0 2px rgba(0, 0, 0, 12%), 0 1px 2px rgba(0, 0, 0, 14%)); --webchat-shadow4: var(--shadow4, 0 0 2px rgba(0, 0, 0, 0.12), 0 2px 4px rgba(0, 0, 0, 0.14)); + --webchat-shadow8: var(--shadow8, 0 0 2px rgba(0, 0, 0, 0.12), 0 4px 8px rgba(0, 0, 0, 0.14)); --webchat-shadow16: var(--shadow16, 0 6.4px 14.4px 0 rgba(0, 0, 0, 0.132), 0 1.2px 3.6px 0 rgba(0, 0, 0, 0.108)); --webchat-shadow64: var(--shadow64, 0 0 8px rgba(0, 0, 0, 0.12), 0 32px 64px rgba(0, 0, 0, 0.14)); From 1290251f0e335fb9211ecef6eed3f1e87ba7bb9a Mon Sep 17 00:00:00 2001 From: William Wong Date: Thu, 16 Oct 2025 00:11:49 +0000 Subject: [PATCH 09/23] Add middleware --- package-lock.json | 3 + .../src/PolymiddlewareComposer.tsx | 25 ++++- .../src/chatLauncherButtonPolymiddleware.tsx | 2 +- .../src/iconButtonPolymiddleware.tsx | 106 ++++++++++++++++++ packages/api-middleware/src/index.ts | 14 +++ .../src/types/Polymiddleware.ts | 7 +- packages/api/src/boot/middleware.ts | 14 +++ .../experience-chat-launcher/package.json | 1 + .../src/private/ChatLauncher.tsx | 6 +- .../private/ChatLauncherButton.module.css | 24 ---- .../private/private/ChatLauncherButton.tsx | 5 +- .../src/private/private/IconButton.module.css | 29 +++++ .../src/private/private/IconButton.tsx | 40 +++++++ 13 files changed, 246 insertions(+), 30 deletions(-) create mode 100644 packages/api-middleware/src/iconButtonPolymiddleware.tsx create mode 100644 packages/experience-chat-launcher/src/private/private/IconButton.module.css create mode 100644 packages/experience-chat-launcher/src/private/private/IconButton.tsx diff --git a/package-lock.json b/package-lock.json index 488b7f438d..de4052fee5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17852,6 +17852,8 @@ }, "node_modules/use-ref-from": { "version": "0.1.0", + "resolved": "https://registry.npmjs.org/use-ref-from/-/use-ref-from-0.1.0.tgz", + "integrity": "sha512-PRjmfhUGUKghhOjKV1dBU66M7CASdb4NkMsaaWLdJA81yOZFlVL7Pi3O9aD+68pRh0VrRQjZfS6Ux3vPy1VhRg==", "license": "MIT", "dependencies": { "@babel/runtime-corejs3": "^7.24.1", @@ -19942,6 +19944,7 @@ "botframework-webchat-api": "0.0.0-0", "botframework-webchat-component": "0.0.0-0", "botframework-webchat-core": "0.0.0-0", + "use-ref-from": "0.1.0", "valibot": "1.1.0" }, "devDependencies": { diff --git a/packages/api-middleware/src/PolymiddlewareComposer.tsx b/packages/api-middleware/src/PolymiddlewareComposer.tsx index cd7342be28..2ef9414f66 100644 --- a/packages/api-middleware/src/PolymiddlewareComposer.tsx +++ b/packages/api-middleware/src/PolymiddlewareComposer.tsx @@ -20,6 +20,7 @@ import { extractChatLauncherButtonEnhancer } from './chatLauncherButtonPolymiddleware'; import { ErrorBoxPolymiddlewareProvider, extractErrorBoxEnhancer } from './errorBoxPolymiddleware'; +import { IconButtonPolymiddlewareProvider, extractIconButtonEnhancer } from './iconButtonPolymiddleware'; import { Polymiddleware } from './types/Polymiddleware'; const polymiddlewareComposerPropsSchema = pipe( @@ -88,6 +89,24 @@ function PolymiddlewareComposer(props: PolymiddlewareComposerProps) { const errorBoxPolymiddleware = useMemo(() => errorBoxEnhancers.map(enhancer => () => enhancer), [errorBoxEnhancers]); + const iconButtonEnhancers = useMemoWithPrevious>( + (prevIconButtonEnhancers = []) => { + const iconButtonEnhancers = extractIconButtonEnhancer(polymiddleware); + + // Checks for array equality, return previous version if nothing has changed. + return prevIconButtonEnhancers.length === iconButtonEnhancers.length && + iconButtonEnhancers.every((middleware, index) => Object.is(middleware, prevIconButtonEnhancers.at(index))) + ? prevIconButtonEnhancers + : iconButtonEnhancers; + }, + [polymiddleware] + ); + + const iconButtonPolymiddleware = useMemo( + () => iconButtonEnhancers.map(enhancer => () => enhancer), + [iconButtonEnhancers] + ); + // Didn't thoroughly think through this part yet, but I am using the first approach for now: // 1. for every type of middleware @@ -100,7 +119,11 @@ function PolymiddlewareComposer(props: PolymiddlewareComposerProps) { return ( - {children} + + + {children} + + ); diff --git a/packages/api-middleware/src/chatLauncherButtonPolymiddleware.tsx b/packages/api-middleware/src/chatLauncherButtonPolymiddleware.tsx index 57478973a0..7b0937674f 100644 --- a/packages/api-middleware/src/chatLauncherButtonPolymiddleware.tsx +++ b/packages/api-middleware/src/chatLauncherButtonPolymiddleware.tsx @@ -22,7 +22,7 @@ const { reactComponent: chatLauncherButtonComponent, useBuildRenderCallback: useBuildRenderChatLauncherButtonCallback } = templatePolymiddleware( - 'chatLauncherButton' + 'ChatLauncherButton' ); type ChatLauncherButtonPolymiddleware = InferMiddleware; diff --git a/packages/api-middleware/src/iconButtonPolymiddleware.tsx b/packages/api-middleware/src/iconButtonPolymiddleware.tsx new file mode 100644 index 0000000000..34fbc0c9be --- /dev/null +++ b/packages/api-middleware/src/iconButtonPolymiddleware.tsx @@ -0,0 +1,106 @@ +import { reactNode, validateProps } from '@msinternal/botframework-webchat-react-valibot'; +import React, { memo, useMemo, type ReactNode } from 'react'; +import { type EmptyObject } from 'type-fest'; +import { function_, object, optional, pipe, readonly, string, type InferInput } from 'valibot'; + +import createErrorBoundaryMiddleware from './private/createErrorBoundaryMiddleware'; +import templatePolymiddleware, { + type InferHandler, + type InferHandlerResult, + type InferMiddleware, + type InferProps, + type InferProviderProps, + type InferRenderer, + type InferRequest +} from './private/templatePolymiddleware'; + +const { + createMiddleware: createIconButtonPolymiddleware, + extractEnhancer: extractIconButtonEnhancer, + Provider, + Proxy, + reactComponent: iconButtonComponent, + useBuildRenderCallback: useBuildRenderIconButtonCallback +} = templatePolymiddleware< + EmptyObject, + { + readonly children?: ReactNode | undefined; + readonly className?: string | undefined; + readonly onClick?: (() => void) | undefined; + } +>('IconButton'); + +type IconButtonPolymiddleware = InferMiddleware; +type IconButtonPolymiddlewareHandler = InferHandler; +type IconButtonPolymiddlewareHandlerResult = InferHandlerResult; +type IconButtonPolymiddlewareProps = InferProps; +type IconButtonPolymiddlewareRenderer = InferRenderer; +type IconButtonPolymiddlewareRequest = InferRequest; +type IconButtonPolymiddlewareProviderProps = InferProviderProps; + +const iconButtonPolymiddlewareProxyPropsSchema = pipe( + object({ + children: optional(reactNode()), + className: optional(string()), + onClick: optional(function_()) + }), + readonly() +); + +type IconButtonPolymiddlewareProxyProps = Readonly>; + +const EMPTY_OBJECT = Object.freeze({}); + +// A friendlier version than the organic . +const IconButtonPolymiddlewareProxy = memo(function IconButtonPolymiddlewareProxy( + props: IconButtonPolymiddlewareProxyProps +) { + const { children, className, onClick } = validateProps(iconButtonPolymiddlewareProxyPropsSchema, props); + + return ( + + {children} + + ); +}); + +const IconButtonPolymiddlewareProvider = memo(function IconButtonPolymiddlewareProvider({ + children, + middleware +}: IconButtonPolymiddlewareProviderProps) { + // Decorates middleware with . + const middlewareWithErrorBoundary = useMemo( + () => + Object.freeze([ + // TODO: [P1] Should we simplify this middleware signature and allow error boundary middleware to run on every type of middleware? + // (init: any) => next => (request: undefined) => reactComponentForAll() + // We can't do it today because we have sanity check that prevent `reactComponent()` from different middleware cross-polluting each other. + createErrorBoundaryMiddleware({ + createMiddleware: createIconButtonPolymiddleware, + reactComponent: iconButtonComponent, + where: 'Icon button polymiddleware' + }), + ...middleware + ]), + [middleware] + ); + + return {children}; +}); + +export { + createIconButtonPolymiddleware, + extractIconButtonEnhancer, + iconButtonComponent, + IconButtonPolymiddlewareProvider, + IconButtonPolymiddlewareProxy, + useBuildRenderIconButtonCallback, + type IconButtonPolymiddleware, + type IconButtonPolymiddlewareHandler, + type IconButtonPolymiddlewareHandlerResult, + type IconButtonPolymiddlewareProps, + type IconButtonPolymiddlewareProviderProps, + type IconButtonPolymiddlewareProxyProps, + type IconButtonPolymiddlewareRenderer, + type IconButtonPolymiddlewareRequest +}; diff --git a/packages/api-middleware/src/index.ts b/packages/api-middleware/src/index.ts index 6e2c7f8369..bf8c80b9e8 100644 --- a/packages/api-middleware/src/index.ts +++ b/packages/api-middleware/src/index.ts @@ -40,6 +40,20 @@ export { type ErrorBoxPolymiddlewareRequest } from './errorBoxPolymiddleware'; +export { + createIconButtonPolymiddleware, + iconButtonComponent, + IconButtonPolymiddlewareProxy, + useBuildRenderIconButtonCallback, + type IconButtonPolymiddleware, + type IconButtonPolymiddlewareHandler, + type IconButtonPolymiddlewareHandlerResult, + type IconButtonPolymiddlewareProps, + type IconButtonPolymiddlewareProxyProps, + type IconButtonPolymiddlewareRenderer, + type IconButtonPolymiddlewareRequest +} from './iconButtonPolymiddleware'; + // TODO: [P0] Add tests for nesting `polymiddleware`. export { default as PolymiddlewareComposer } from './PolymiddlewareComposer'; export { type Polymiddleware } from './types/Polymiddleware'; diff --git a/packages/api-middleware/src/types/Polymiddleware.ts b/packages/api-middleware/src/types/Polymiddleware.ts index 5b6b8384ca..61a9141edf 100644 --- a/packages/api-middleware/src/types/Polymiddleware.ts +++ b/packages/api-middleware/src/types/Polymiddleware.ts @@ -1,5 +1,10 @@ import { type ActivityPolymiddleware } from '../activityPolymiddleware'; import { type ChatLauncherButtonPolymiddleware } from '../chatLauncherButtonPolymiddleware'; import { type ErrorBoxPolymiddleware } from '../errorBoxPolymiddleware'; +import { type IconButtonPolymiddleware } from '../iconButtonPolymiddleware'; -export type Polymiddleware = ActivityPolymiddleware | ChatLauncherButtonPolymiddleware | ErrorBoxPolymiddleware; +export type Polymiddleware = + | ActivityPolymiddleware + | ChatLauncherButtonPolymiddleware + | ErrorBoxPolymiddleware + | IconButtonPolymiddleware; diff --git a/packages/api/src/boot/middleware.ts b/packages/api/src/boot/middleware.ts index 801638a176..adad06fdf2 100644 --- a/packages/api/src/boot/middleware.ts +++ b/packages/api/src/boot/middleware.ts @@ -44,4 +44,18 @@ export { type ErrorBoxPolymiddlewareRequest } from '@msinternal/botframework-webchat-api-middleware'; +export { + createIconButtonPolymiddleware, + iconButtonComponent, + IconButtonPolymiddlewareProxy, + useBuildRenderIconButtonCallback, + type IconButtonPolymiddleware, + type IconButtonPolymiddlewareHandler, + type IconButtonPolymiddlewareHandlerResult, + type IconButtonPolymiddlewareProps, + type IconButtonPolymiddlewareProxyProps, + type IconButtonPolymiddlewareRenderer, + type IconButtonPolymiddlewareRequest +} from '@msinternal/botframework-webchat-api-middleware'; + export { default as createActivityPolymiddlewareFromLegacy } from '../legacy/createActivityPolymiddlewareFromLegacy'; diff --git a/packages/experience-chat-launcher/package.json b/packages/experience-chat-launcher/package.json index 1283aa3f79..3aaa5f0565 100644 --- a/packages/experience-chat-launcher/package.json +++ b/packages/experience-chat-launcher/package.json @@ -64,6 +64,7 @@ "botframework-webchat-api": "0.0.0-0", "botframework-webchat-component": "0.0.0-0", "botframework-webchat-core": "0.0.0-0", + "use-ref-from": "0.1.0", "valibot": "1.1.0" } } diff --git a/packages/experience-chat-launcher/src/private/ChatLauncher.tsx b/packages/experience-chat-launcher/src/private/ChatLauncher.tsx index fe68f50ea1..f65a7594ed 100644 --- a/packages/experience-chat-launcher/src/private/ChatLauncher.tsx +++ b/packages/experience-chat-launcher/src/private/ChatLauncher.tsx @@ -5,6 +5,8 @@ import { chatLauncherButtonComponent, ChatLauncherButtonPolymiddlewareProxy, createChatLauncherButtonPolymiddleware, + createIconButtonPolymiddleware, + iconButtonComponent, type Polymiddleware } from 'botframework-webchat-api/middleware'; import { Composer } from 'botframework-webchat-component/component'; @@ -27,6 +29,7 @@ import { import ChatLauncherStylesheet from '../stylesheet/ChatLauncherStylesheet'; import styles from './ChatLauncher.module.css'; import ChatLauncherButton from './private/ChatLauncherButton'; +import IconButton from './private/IconButton'; // Best-effort to check if it is an Observable. const observableSchema = object({ subscribe: function_() }); @@ -63,7 +66,8 @@ function ChatLauncher(props: ChatLauncherProps) { const polymiddleware = useMemo( () => Object.freeze([ - createChatLauncherButtonPolymiddleware(() => () => chatLauncherButtonComponent(ChatLauncherButton)) + createChatLauncherButtonPolymiddleware(() => () => chatLauncherButtonComponent(ChatLauncherButton)), + createIconButtonPolymiddleware(() => () => iconButtonComponent(IconButton)) ]), [] ); diff --git a/packages/experience-chat-launcher/src/private/private/ChatLauncherButton.module.css b/packages/experience-chat-launcher/src/private/private/ChatLauncherButton.module.css index d9d072bcaf..e781ec5710 100644 --- a/packages/experience-chat-launcher/src/private/private/ChatLauncherButton.module.css +++ b/packages/experience-chat-launcher/src/private/private/ChatLauncherButton.module.css @@ -1,32 +1,8 @@ :global(.webchat-experience-chat-launcher) .chat-launcher-button { - /* TODO: [P0] Expose some CSS classes to white-label. */ - --webchat-colorNeutralBackground1: var(--colorNeutralBackground1, #ffffff); - --webchat-colorNeutralForeground1: var(--colorNeutralForeground1, #242424); - --webchat-colorNeutralStroke1: var(--colorNeutralStroke1, #d1d1d1); - --webchat-shadow8: var(--shadow8, 0 0 2px rgba(0, 0, 0, 0.12), 0 4px 8px rgba(0, 0, 0, 0.14)); - - /* TODO: [P0] Should we expose some customizable CSS variables? */ - /* TODO: [P0] How's the naming system? */ - /* TODO: [P0] Should it be customizable to anchor to bottom/left instead? */ - --webchat-chatLauncherButton-background: var(--webchat-colorNeutralBackground1); - --webchat-chatLauncherButton-borderColor: var(--webchat-colorNeutralStroke1); - --webchat-chatLauncherButton-borderWidth: 1px; - --webchat-chatLauncherButton-color: var(--webchat-colorNeutralForeground1); --webchat-chatLauncherButton-margin: 40px; - --webchat-chatLauncherButton-shadow: var(--webchat-shadow8); - appearance: none; - background: var(--webchat-chatLauncherButton-background); - border: 0; - border-radius: 30px; bottom: 0; - box-shadow: var(--webchat-chatLauncherButton-shadow); - color: var(--webchat-chatLauncherButton-color); - outline: var(--webchat-chatLauncherButton-borderWidth) solid var(--webchat-chatLauncherButton-borderColor); position: fixed; - height: 60px; margin: var(--webchat-chatLauncherButton-margin); - padding: 0; right: 0; - width: 60px; } diff --git a/packages/experience-chat-launcher/src/private/private/ChatLauncherButton.tsx b/packages/experience-chat-launcher/src/private/private/ChatLauncherButton.tsx index 76465eaa79..9ce5e895b9 100644 --- a/packages/experience-chat-launcher/src/private/private/ChatLauncherButton.tsx +++ b/packages/experience-chat-launcher/src/private/private/ChatLauncherButton.tsx @@ -1,5 +1,6 @@ import { validateProps } from '@msinternal/botframework-webchat-react-valibot'; import { useStyles } from '@msinternal/botframework-webchat-styles/react'; +import { IconButtonPolymiddlewareProxy } from 'botframework-webchat-api/middleware'; import React, { memo } from 'react'; import { boolean, object, pipe, readonly, type InferInput } from 'valibot'; @@ -21,9 +22,9 @@ function ChatLauncherButton(props: ChatLauncherButtonProps) { const classNames = useStyles(styles); return ( - + ); } diff --git a/packages/experience-chat-launcher/src/private/private/IconButton.module.css b/packages/experience-chat-launcher/src/private/private/IconButton.module.css new file mode 100644 index 0000000000..f6d17493c3 --- /dev/null +++ b/packages/experience-chat-launcher/src/private/private/IconButton.module.css @@ -0,0 +1,29 @@ +/* TODO: [P0] Put this inside botframework-webchat-component as default implementation. */ +:global(.webchat-experience-chat-launcher) .icon-button { + /* TODO: [P0] Expose some CSS classes to white-label. */ + --webchat-colorNeutralBackground1: var(--colorNeutralBackground1, #ffffff); + --webchat-colorNeutralForeground1: var(--colorNeutralForeground1, #242424); + --webchat-colorNeutralStroke1: var(--colorNeutralStroke1, #d1d1d1); + --webchat-shadow8: var(--shadow8, 0 0 2px rgba(0, 0, 0, 0.12), 0 4px 8px rgba(0, 0, 0, 0.14)); + + /* TODO: [P0] Should we expose some customizable CSS variables? */ + /* TODO: [P0] How's the naming system? */ + /* TODO: [P0] Should it be customizable to anchor to bottom/left instead? */ + --webchat-iconButton-background: var(--webchat-colorNeutralBackground1); + --webchat-iconButton-borderColor: var(--webchat-colorNeutralStroke1); + --webchat-iconButton-borderWidth: 1px; + --webchat-iconButton-color: var(--webchat-colorNeutralForeground1); + --webchat-iconButton-shadow: var(--webchat-shadow8); + --webchat-iconButton-size: 60px; + + appearance: none; + background: var(--webchat-iconButton-background); + border: 0; + border-radius: var(--webchat-iconButton-size); + box-shadow: var(--webchat-iconButton-shadow); + color: var(--webchat-iconButton-color); + outline: var(--webchat-iconButton-borderWidth) solid var(--webchat-iconButton-borderColor); + height: var(--webchat-iconButton-size); + padding: 0; + width: var(--webchat-iconButton-size); +} diff --git a/packages/experience-chat-launcher/src/private/private/IconButton.tsx b/packages/experience-chat-launcher/src/private/private/IconButton.tsx new file mode 100644 index 0000000000..c2db7a023f --- /dev/null +++ b/packages/experience-chat-launcher/src/private/private/IconButton.tsx @@ -0,0 +1,40 @@ +import { reactNode, validateProps } from '@msinternal/botframework-webchat-react-valibot'; +import { useStyles } from '@msinternal/botframework-webchat-styles/react'; +import cx from 'classnames'; +import React, { memo, useCallback } from 'react'; +import { useRefFrom } from 'use-ref-from'; +import { function_, object, optional, pipe, readonly, string, type InferInput } from 'valibot'; + +import styles from './IconButton.module.css'; + +const iconButtonPropsSchema = pipe( + object({ + children: optional(reactNode()), + className: optional(string()), + onClick: optional(function_()) + }), + readonly() +); + +type IconButtonProps = InferInput; + +// TODO: [P0] Should we make an IconButton polymiddleware and IconButton is based from that? +function IconButton(props: IconButtonProps) { + const { children, className, onClick } = validateProps(iconButtonPropsSchema, props); + const classNames = useStyles(styles); + + const onClickRef = useRefFrom(onClick); + + const handleClick = useCallback(() => onClickRef.current?.(), [onClickRef]); + + return ( + + ); +} + +IconButton.displayName = 'IconButton'; + +export default memo(IconButton); +export { iconButtonPropsSchema, type IconButtonProps }; From 35ee76e16c387595b19613f5b91c3de6d7a8c52b Mon Sep 17 00:00:00 2001 From: William Wong Date: Thu, 16 Oct 2025 01:11:15 +0000 Subject: [PATCH 10/23] Add hover-active-focus --- .../src/private/ChatLauncher.tsx | 2 +- .../private/private/ChatLauncherButton.tsx | 4 +- .../src/private/private/ChatLauncherIcon.tsx | 17 ------ .../src/private/private/Icon.module.css | 57 +++++++++++++++++++ .../src/private/private/Icon.tsx | 41 +++++++++++++ .../src/private/private/IconButton.module.css | 27 ++++++++- 6 files changed, 126 insertions(+), 22 deletions(-) delete mode 100644 packages/experience-chat-launcher/src/private/private/ChatLauncherIcon.tsx create mode 100644 packages/experience-chat-launcher/src/private/private/Icon.module.css create mode 100644 packages/experience-chat-launcher/src/private/private/Icon.tsx diff --git a/packages/experience-chat-launcher/src/private/ChatLauncher.tsx b/packages/experience-chat-launcher/src/private/ChatLauncher.tsx index f65a7594ed..d526f3b495 100644 --- a/packages/experience-chat-launcher/src/private/ChatLauncher.tsx +++ b/packages/experience-chat-launcher/src/private/ChatLauncher.tsx @@ -76,7 +76,7 @@ function ChatLauncher(props: ChatLauncherProps) {
- +
); diff --git a/packages/experience-chat-launcher/src/private/private/ChatLauncherButton.tsx b/packages/experience-chat-launcher/src/private/private/ChatLauncherButton.tsx index 9ce5e895b9..fca41d79ee 100644 --- a/packages/experience-chat-launcher/src/private/private/ChatLauncherButton.tsx +++ b/packages/experience-chat-launcher/src/private/private/ChatLauncherButton.tsx @@ -5,7 +5,7 @@ import React, { memo } from 'react'; import { boolean, object, pipe, readonly, type InferInput } from 'valibot'; import styles from './ChatLauncherButton.module.css'; -import ChatLauncherIcon from './ChatLauncherIcon'; +import Icon from './Icon'; const chatLauncherButtonPropsSchema = pipe( object({ @@ -23,7 +23,7 @@ function ChatLauncherButton(props: ChatLauncherButtonProps) { return ( - {hasMessage ? : } + {hasMessage ? : } ); } diff --git a/packages/experience-chat-launcher/src/private/private/ChatLauncherIcon.tsx b/packages/experience-chat-launcher/src/private/private/ChatLauncherIcon.tsx deleted file mode 100644 index ca0dcc7761..0000000000 --- a/packages/experience-chat-launcher/src/private/private/ChatLauncherIcon.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import React, { memo } from 'react'; - -// TODO: [P0] Use monochromatic icon, i.e. FluentIcon. -function ChatLauncherIcon() { - return ( - - - - ); -} - -ChatLauncherIcon.displayName = 'ChatLauncherIcon'; - -export default memo(ChatLauncherIcon); diff --git a/packages/experience-chat-launcher/src/private/private/Icon.module.css b/packages/experience-chat-launcher/src/private/private/Icon.module.css new file mode 100644 index 0000000000..837cc299a3 --- /dev/null +++ b/packages/experience-chat-launcher/src/private/private/Icon.module.css @@ -0,0 +1,57 @@ +/* TODO: [P0] Dupe from packages/fluent-theme/src/components/icon/FluentIcon.module.css, should dedupe. */ +:global(.webchat-experience-chat-launcher) .icon { + min-width: var(--webchat__icon--size, 1em); + min-height: var(--webchat__icon--size, 1em); + place-self: center; + + /* Use the image as texture. */ + background-image: var(--webchat__icon--image, none); + background-position: center; + background-repeat: no-repeat; + background-size: var(--webchat__icon--size, 1em); + + /* If image is not set, fallback to solid color. */ + background-color: var(--webchat__icon--color, transparent); + + /* 3. Set the mask if any. */ + -webkit-mask-image: var(--webchat__icon--mask); + -webkit-mask-position: center; + -webkit-mask-repeat: no-repeat; + -webkit-mask-size: var(--webchat__icon--size, 1em); + mask-image: var(--webchat__icon--mask); + mask-position: center; + mask-repeat: no-repeat; + mask-size: var(--webchat__icon--size, 1em); +} + +/* #region: Appearance */ +:global(.webchat) .appearance--hero { + --webchat__icon--color: currentColor; + --webchat__icon--size: 32px; +} + +:global(.webchat) .appearance--text { + --webchat__icon--color: currentColor; +} +/* #endregion */ + +/* #region: Icons */ +:global(.webchat) .icon--chat-multiple { + --webchat__icon--mask: url('data:image/svg+xml;utf8,'); +} + +/* TODO: [P0] This is ugly way to show a different (filled) icon when its parent is hovered. */ +:global(.webchat button:is(:focus, :hover)) .icon--chat-multiple { + --webchat__icon--mask: url('data:image/svg+xml;utf8,'); +} + +:global(.webchat) .icon--chat-sparkle { + --webchat__icon--mask: url('data:image/svg+xml;utf8,'); +} + +/* TODO: [P0] This is ugly way to show a different (filled) icon when its parent is hovered. */ +:global(.webchat button:is(:focus, :hover)) .icon--chat-sparkle { + --webchat__icon--mask: url('data:image/svg+xml;utf8,'); +} + +/* #endregion */ diff --git a/packages/experience-chat-launcher/src/private/private/Icon.tsx b/packages/experience-chat-launcher/src/private/private/Icon.tsx new file mode 100644 index 0000000000..c85b52e854 --- /dev/null +++ b/packages/experience-chat-launcher/src/private/private/Icon.tsx @@ -0,0 +1,41 @@ +/* TODO: [P0] Dupe from packages/fluent-theme/src/components/icon/FluentIcon.tsx, should dedupe. */ +import { validateProps } from '@msinternal/botframework-webchat-react-valibot'; +import { useStyles } from '@msinternal/botframework-webchat-styles/react'; +import { createIconComponent } from 'botframework-webchat/internal'; +import cx from 'classnames'; +import React, { memo, useMemo, type CSSProperties } from 'react'; +import { object, optional, pipe, readonly, string, union, type InferInput } from 'valibot'; + +import styles from './Icon.module.css'; + +const baseIconPropsSchema = pipe( + object({ + className: optional(string()), + mask: optional(string()) + }), + readonly() +); + +function BaseIcon(props: InferInput) { + const { className, mask } = validateProps(baseIconPropsSchema, props); + + const classNames = useStyles(styles); + + const maskStyle = useMemo( + () => (mask ? ({ '--webchat__icon--mask': `url(${JSON.stringify(mask)})` } as CSSProperties) : {}), + [mask] + ); + + return
; +} + +const { component: Icon, modifierPropsSchema } = createIconComponent(styles, ['appearance', 'icon'], BaseIcon); + +Icon.displayName = 'Icon'; + +const iconPropsSchema = pipe(union([baseIconPropsSchema, modifierPropsSchema]), readonly()); + +type IconProps = InferInput; + +export default memo(Icon); +export { iconPropsSchema, type IconProps }; diff --git a/packages/experience-chat-launcher/src/private/private/IconButton.module.css b/packages/experience-chat-launcher/src/private/private/IconButton.module.css index f6d17493c3..78efae111c 100644 --- a/packages/experience-chat-launcher/src/private/private/IconButton.module.css +++ b/packages/experience-chat-launcher/src/private/private/IconButton.module.css @@ -4,6 +4,9 @@ --webchat-colorNeutralBackground1: var(--colorNeutralBackground1, #ffffff); --webchat-colorNeutralForeground1: var(--colorNeutralForeground1, #242424); --webchat-colorNeutralStroke1: var(--colorNeutralStroke1, #d1d1d1); + --webchat-colorNeutralStroke1Hover: var(--colorNeutralStroke1Hover, #c7c7c7); + --webchat-colorNeutralStroke1Pressed: var(--colorNeutralStroke1Pressed, #b3b3b3); + --webchat-colorNeutralStrokeDisabled: var(--colorNeutralStrokeDisabled, #e0e0e0); --webchat-shadow8: var(--shadow8, 0 0 2px rgba(0, 0, 0, 0.12), 0 4px 8px rgba(0, 0, 0, 0.14)); /* TODO: [P0] Should we expose some customizable CSS variables? */ @@ -11,6 +14,9 @@ /* TODO: [P0] Should it be customizable to anchor to bottom/left instead? */ --webchat-iconButton-background: var(--webchat-colorNeutralBackground1); --webchat-iconButton-borderColor: var(--webchat-colorNeutralStroke1); + --webchat-iconButton-borderColor--active: var(--webchat-colorNeutralStroke1Pressed); + --webchat-iconButton-borderColor--hover: var(--webchat-colorNeutralStroke1Hover); + --webchat-iconButton-borderColor--disabled: var(--webchat-colorNeutralStrokeDisabled); --webchat-iconButton-borderWidth: 1px; --webchat-iconButton-color: var(--webchat-colorNeutralForeground1); --webchat-iconButton-shadow: var(--webchat-shadow8); @@ -18,12 +24,29 @@ appearance: none; background: var(--webchat-iconButton-background); - border: 0; + border-color: var(--webchat-iconButton-borderColor); border-radius: var(--webchat-iconButton-size); + border-style: solid; + border-width: var(--webchat-iconButton-borderWidth); box-shadow: var(--webchat-iconButton-shadow); color: var(--webchat-iconButton-color); - outline: var(--webchat-iconButton-borderWidth) solid var(--webchat-iconButton-borderColor); + /* TODO: [P0] Should not use calc() to minus the border width. */ height: var(--webchat-iconButton-size); padding: 0; width: var(--webchat-iconButton-size); } + +:global(.webchat-experience-chat-launcher) .icon-button:hover { + border-color: var(--webchat-iconButton-borderColor--hover); +} + +:global(.webchat-experience-chat-launcher) .icon-button:active { + border-color: var(--webchat-iconButton-borderColor--active); +} + +/* :global(.webchat-experience-chat-launcher) .icon-button:focus { +} */ + +:global(.webchat-experience-chat-launcher) .icon-button:disabled { + border-color: var(--webchat-iconButton-borderColor--disabled); +} From e0721dcf5ef351936f454284cd9489881fe55f37 Mon Sep 17 00:00:00 2001 From: William Wong Date: Thu, 16 Oct 2025 09:45:09 +0000 Subject: [PATCH 11/23] Use inotifywait to watch and skip initial build --- package-lock.json | 47 ++++++- package.json | 2 + packages/api-middleware/package.json | 27 ++-- packages/api-middleware/tsup.config.ts | 9 +- packages/api/package.json | 37 ++--- packages/api/tsup.config.ts | 9 +- packages/base/package.json | 8 +- packages/base/tsup.config.ts | 17 +-- packages/bundle/package.json | 132 +++++++++--------- packages/bundle/tsup.config.ts | 3 +- packages/component/package.json | 35 ++--- packages/component/tsup.config.ts | 9 +- packages/core/package.json | 23 +-- packages/core/tsup.config.ts | 9 +- packages/debug-theme/package.json | 17 ++- packages/debug-theme/tsup.config.ts | 3 +- packages/directlinespeech/package.json | 30 ++-- packages/directlinespeech/tsup.config.ts | 3 +- .../experience-chat-launcher/package.json | 31 ++-- .../experience-chat-launcher/tsup.config.ts | 9 +- packages/fluent-theme/package.json | 34 +++-- packages/fluent-theme/tsup.config.ts | 3 +- packages/isomorphic-react-dom/package.json | 11 +- packages/isomorphic-react/package.json | 11 +- packages/react-hooks/package.json | 12 +- packages/react-hooks/tsup.config.ts | 9 +- packages/react-valibot/package.json | 14 +- packages/react-valibot/tsup.config.ts | 17 +-- packages/redux-store/package.json | 18 ++- packages/redux-store/tsup.config.ts | 9 +- packages/repack/adaptivecards/package.json | 16 ++- packages/repack/base64-js/package.json | 16 ++- .../botframework-directlinejs/package.json | 16 ++- .../repack/html-react-parser/package.json | 16 ++- .../package.json | 16 ++- packages/repack/object-assign/package.json | 12 +- packages/repack/react-dom/package.json | 16 ++- .../repack/react-dom@baseline/package.json | 18 ++- packages/repack/react-dom@umd/package.json | 16 +-- packages/repack/react-is/package.json | 16 ++- packages/repack/react/package.json | 18 +-- packages/repack/react@baseline/package.json | 18 ++- packages/repack/react@umd/package.json | 12 +- packages/styles/package.json | 16 ++- packages/styles/tsup.config.ts | 9 +- .../support/cldr-data-downloader/package.json | 9 +- packages/support/cldr-data/package.json | 15 +- packages/test/dev-server/package.json | 8 +- packages/test/harness/package.json | 38 ++--- packages/test/page-object/package.json | 26 ++-- packages/test/web-server/package.json | 18 ++- packages/test/web-server/tsup.config.ts | 3 +- packages/tsconfig/package.json | 10 +- scripts/buildWatch.mjs | 44 ++++++ scripts/npm/build-local-dependencies.sh | 31 ++++ scripts/npm/build-watch.sh | 12 ++ scripts/npm/bump-dev.sh | 2 + scripts/npm/bump-peer.sh | 2 + scripts/npm/bump-prod.sh | 2 + scripts/npm/notify-build.sh | 47 +++++++ scripts/npm/postversion.sh | 3 + scripts/npm/preversion.sh | 5 +- tsup.base.config.ts | 42 +----- 63 files changed, 695 insertions(+), 451 deletions(-) create mode 100644 scripts/buildWatch.mjs create mode 100755 scripts/npm/build-local-dependencies.sh create mode 100755 scripts/npm/build-watch.sh create mode 100755 scripts/npm/notify-build.sh diff --git a/package-lock.json b/package-lock.json index de4052fee5..defb8bdf63 100644 --- a/package-lock.json +++ b/package-lock.json @@ -94,6 +94,8 @@ "minimatch": "^10.0.3", "node-dev": "^8.0.0", "prettier": "^3.6.2", + "read-package-up": "^11.0.0", + "read-pkg": "^9.0.1", "selenium-webdriver": "^4.34.0", "serve": "^14.2.4", "serve-handler": "^6.1.6", @@ -6199,6 +6201,8 @@ }, "node_modules/concurrently": { "version": "9.2.0", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.0.tgz", + "integrity": "sha512-IsB/fiXTupmagMW4MNp2lx2cdSN2FfZq78vF90LBB+zZHArbIQZjQtzXCiXnvTxCZSvXanTqFLWBjw2UkLx1SQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8391,7 +8395,9 @@ } }, "node_modules/find-up-simple": { - "version": "1.0.0", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz", + "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==", "license": "MIT", "engines": { "node": ">=18" @@ -13694,6 +13700,8 @@ }, "node_modules/nodemon": { "version": "3.1.10", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz", + "integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==", "dev": true, "license": "MIT", "dependencies": { @@ -14837,10 +14845,42 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/read-package-up": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz", + "integrity": "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up-simple": "^1.0.0", + "read-pkg": "^9.0.0", + "type-fest": "^4.6.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-package-up/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/read-pkg": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", + "license": "MIT", "dependencies": { "@types/normalize-package-data": "^2.4.3", "normalize-package-data": "^6.0.0", @@ -14857,6 +14897,9 @@ }, "node_modules/read-pkg-up": { "version": "11.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-11.0.0.tgz", + "integrity": "sha512-LOVbvF1Q0SZdjClSefZ0Nz5z8u+tIE7mV5NibzmE9VYmDe9CaBbAVtz1veOSZbofrdsilxuDAYnFenukZVp8/Q==", + "deprecated": "Renamed to read-package-up", "license": "MIT", "dependencies": { "find-up-simple": "^1.0.0", @@ -19904,7 +19947,6 @@ "babel-jest": "^29.7.0", "babel-plugin-istanbul": "^7.0.0", "babel-plugin-transform-inline-environment-variables": "^0.4.4", - "concurrently": "^9.2.0", "cross-env": "^10.0.0", "dotenv": "^17.2.1", "eslint": "^8.57.1", @@ -19992,6 +20034,7 @@ "@msinternal/botframework-webchat-tsconfig": "0.0.0-0", "@types/math-random": "^1.0.2", "@types/node": "^24.1.0", + "concurrently": "^9.2.0", "tsup": "^8.5.0", "typescript": "~5.8.3" }, diff --git a/package.json b/package.json index 0f12d62285..7b8a32877f 100644 --- a/package.json +++ b/package.json @@ -260,6 +260,8 @@ "minimatch": "^10.0.3", "node-dev": "^8.0.0", "prettier": "^3.6.2", + "read-package-up": "^11.0.0", + "read-pkg": "^9.0.1", "selenium-webdriver": "^4.34.0", "serve": "^14.2.4", "serve-handler": "^6.1.6", diff --git a/packages/api-middleware/package.json b/packages/api-middleware/package.json index 9910432b41..b3c1700b98 100644 --- a/packages/api-middleware/package.json +++ b/packages/api-middleware/package.json @@ -55,14 +55,13 @@ "./src/**/*", "*.js" ], - "localDependencies": { - "@msinternal/botframework-webchat-base": "development", - "@msinternal/botframework-webchat-react-hooks": "development", - "@msinternal/botframework-webchat-react-valibot": "development" - }, "homepage": "https://github.com/microsoft/BotFramework-WebChat/tree/main/packages/api-middleware#readme", "scripts": { - "build": "tsup --config ./tsup.config.ts", + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch", + "build:pre:local-dependencies": "../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../scripts/npm/build-watch.sh", + "build:run": "tsup", "bump": "npm run bump:prod && npm run bump:dev && npm run bump:peer && (npm audit fix || exit 0)", "bump:dev": "../../scripts/npm/bump-dev.sh", "bump:peer": "../../scripts/npm/bump-peer.sh", @@ -73,7 +72,13 @@ "precommit:eslint": "../../node_modules/.bin/eslint --report-unused-disable-directives --max-warnings 0", "precommit:typecheck": "tsc --project ./src --emitDeclarationOnly false --esModuleInterop true --noEmit --pretty false", "preversion": "node ../../scripts/npm/preversion.sh", - "start": "npm run build -- --watch" + "start": "../../scripts/npm/notify-build.sh \"./src/\" \"../base/package.json\" \"../react-hooks/package.json\" \"../react-valibot/package.json\"" + }, + "pinDependencies": {}, + "localDependencies": { + "@msinternal/botframework-webchat-base": "development", + "@msinternal/botframework-webchat-react-hooks": "development", + "@msinternal/botframework-webchat-react-valibot": "development" }, "devDependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", @@ -85,13 +90,13 @@ "type-fest": "^4.41.0", "typescript": "^5.7.3" }, - "peerDependencies": { - "react": ">= 16.8.6", - "react-chain-of-responsibility": "0.4.0-main.c2f17da" - }, "dependencies": { "handler-chain": "^0.1.0", "react-wrap-with": "0.1.0", "valibot": "1.1.0" + }, + "peerDependencies": { + "react": ">= 16.8.6", + "react-chain-of-responsibility": "0.4.0-main.c2f17da" } } diff --git a/packages/api-middleware/tsup.config.ts b/packages/api-middleware/tsup.config.ts index d2bfd849cf..fbfa7926c2 100644 --- a/packages/api-middleware/tsup.config.ts +++ b/packages/api-middleware/tsup.config.ts @@ -2,22 +2,19 @@ import { defineConfig } from 'tsup'; import { applyConfig } from '../../tsup.base.config'; -// TODO: [P1] Compute this automatically. -const DEPENDENT_PATHS = ['api/src/index.ts']; - const commonConfig = applyConfig(config => ({ ...config, entry: { 'botframework-webchat-api-middleware': './src/index.ts', 'botframework-webchat-api-middleware.legacy': './src/legacy.ts' - }, - onSuccess: `touch ${DEPENDENT_PATHS.map(path => `../${path}`).join(' ')}` + } })); export default defineConfig([ { ...commonConfig, - format: 'esm' + format: 'esm', + onSuccess: 'touch ./package.json' }, { ...commonConfig, diff --git a/packages/api/package.json b/packages/api/package.json index 3cef62ebcd..0e42f533ea 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -75,12 +75,15 @@ ], "homepage": "https://github.com/microsoft/BotFramework-WebChat/tree/main/packages/component#readme", "scripts": { - "build": "npm run build:globalize && npm run build:tsup && npm run build:dtsroll && npm run build:validate", - "build:globalize": "node scripts/createPrecompiledGlobalize.mjs", - "build:tsup": "tsup", - "build:dtsroll": "dtsroll ./dist/*.d.*", - "build:validate": "npm run build:validate:dts", - "build:validate:dts": "if grep -q -P '@msinternal\\/' dist/*.d.* 2>/dev/null; then echo \"Error: dist/*.d.* is not compiled by dtsroll\" >&2; exit 1; fi", + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:post": "npm run build:post:dtsroll && npm run build:post:validate:dts", + "build:post:dtsroll": "dtsroll ./dist/*.d.*", + "build:post:validate:dts": "if grep -q -P '@msinternal\\/' dist/*.d.* 2>/dev/null; then echo \"Error: dist/*.d.* is not compiled by dtsroll\" >&2; exit 1; fi", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch && npm run build:pre:globalize", + "build:pre:globalize": "node scripts/createPrecompiledGlobalize.mjs", + "build:pre:local-dependencies": "../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../scripts/npm/build-watch.sh", + "build:run": "tsup", "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", @@ -90,17 +93,7 @@ "precommit:eslint": "../../node_modules/.bin/eslint --report-unused-disable-directives --max-warnings 0", "precommit:typecheck": "tsc --project ./src --emitDeclarationOnly false --esModuleInterop true --noEmit --pretty false", "preversion": "cat package.json | jq '(.localDependencies // {} | to_entries | map([if .value == \"production\" then \"dependencies\" else \"devDependencies\" end, .key])) as $P | delpaths($P)' > package-temp.json && mv package-temp.json package.json", - "start": "npm run build:tsup -- --watch" - }, - "localDependencies": { - "@msinternal/botframework-webchat-api-middleware": "development", - "@msinternal/botframework-webchat-base": "development", - "@msinternal/botframework-webchat-cldr-data": "development", - "@msinternal/botframework-webchat-react-hooks": "development", - "@msinternal/botframework-webchat-react-valibot": "development", - "@msinternal/botframework-webchat-redux-store": "development", - "@msinternal/botframework-webchat-tsconfig": "development", - "botframework-webchat-core": "production" + "start": "../../scripts/npm/notify-build.sh \"./src/\" \"../api-middleware/package.json\" \"../base/package.json\" \"../support/cldr-data/package.json\" \"../react-hooks/package.json\" \"../react-valibot/package.json\" \"../redux-store/package.json\" \"../tsconfig/package.json\" \"../core/package.json\"" }, "pinDependencies": { "@types/react": [ @@ -116,6 +109,16 @@ "@typescript-eslint/parser@8.38.0 does not support typescript@5.9.2 yet" ] }, + "localDependencies": { + "@msinternal/botframework-webchat-api-middleware": "development", + "@msinternal/botframework-webchat-base": "development", + "@msinternal/botframework-webchat-cldr-data": "development", + "@msinternal/botframework-webchat-react-hooks": "development", + "@msinternal/botframework-webchat-react-valibot": "development", + "@msinternal/botframework-webchat-redux-store": "development", + "@msinternal/botframework-webchat-tsconfig": "development", + "botframework-webchat-core": "production" + }, "devDependencies": { "@babel/core": "^7.28.0", "@babel/preset-env": "^7.28.0", diff --git a/packages/api/tsup.config.ts b/packages/api/tsup.config.ts index 5ad48a19c5..fb023e1519 100644 --- a/packages/api/tsup.config.ts +++ b/packages/api/tsup.config.ts @@ -2,9 +2,6 @@ import { defineConfig } from 'tsup'; import { applyConfig } from '../../tsup.base.config'; -// TODO: [P1] Compute this automatically. -const DEPENDENT_PATHS = ['component/src/index.ts']; - const commonConfig = applyConfig(config => ({ ...config, entry: { @@ -13,8 +10,7 @@ const commonConfig = applyConfig(config => ({ 'botframework-webchat-api.hook': './src/boot/hook.ts', 'botframework-webchat-api.internal': './src/boot/internal.ts', 'botframework-webchat-api.middleware': './src/boot/middleware.ts' - }, - onSuccess: `touch ${DEPENDENT_PATHS.map(path => `../${path}`).join(' ')}` + } })); export default defineConfig([ @@ -25,7 +21,8 @@ export default defineConfig([ 'globalThis.WEB_CHAT_BUILD_INFO_MODULE_FORMAT': '"esmodules"' }, format: 'esm', - noExternal: [...(commonConfig.noExternal ?? []), 'globalize'] + noExternal: [...(commonConfig.noExternal ?? []), 'globalize'], + onSuccess: 'touch ./package.json' }, { ...commonConfig, diff --git a/packages/base/package.json b/packages/base/package.json index eae50cbffe..51cf552aa8 100644 --- a/packages/base/package.json +++ b/packages/base/package.json @@ -43,7 +43,11 @@ "homepage": "https://github.com/microsoft/BotFramework-WebChat/tree/main/packages/component#readme", "private": true, "scripts": { - "build": "tsup", + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch", + "build:pre:local-dependencies": "../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../scripts/npm/build-watch.sh", + "build:run": "tsup", "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", @@ -53,7 +57,7 @@ "precommit:eslint": "../../node_modules/.bin/eslint --report-unused-disable-directives --max-warnings 0", "precommit:typecheck": "tsc --project ./src --emitDeclarationOnly false --esModuleInterop true --noEmit --pretty false", "preversion": "cat package.json | jq '(.localDependencies // {} | to_entries | map([if .value == \"production\" then \"dependencies\" else \"devDependencies\" end, .key])) as $P | delpaths($P)' > package-temp.json && mv package-temp.json package.json", - "start": "npm run build -- --watch" + "start": "../../scripts/npm/notify-build.sh \"./src/\" \"../tsconfig/package.json\"" }, "localDependencies": { "@msinternal/botframework-webchat-tsconfig": "development" diff --git a/packages/base/tsup.config.ts b/packages/base/tsup.config.ts index 12174d0851..86d8e13469 100644 --- a/packages/base/tsup.config.ts +++ b/packages/base/tsup.config.ts @@ -2,30 +2,19 @@ import { defineConfig } from 'tsup'; import { applyConfig } from '../../tsup.base.config'; -// TODO: [P1] Compute this automatically. -const DEPENDENT_PATHS = [ - 'api/src/index.ts', - 'api-middleware/src/index.ts', - 'bundle/src/boot/exports/index.ts', - 'component/src/index.ts', - 'core/src/index.ts', - 'debug-theme/src/index.ts', - 'fluent-theme/src/index.ts' -]; - const commonConfig = applyConfig(config => ({ ...config, entry: { 'botframework-webchat-base': './src/index.ts', 'botframework-webchat-base.utils': './src/utils/index.ts' - }, - onSuccess: `touch ${DEPENDENT_PATHS.map(path => `../${path}`).join(' ')}` + } })); export default defineConfig([ { ...commonConfig, - format: 'esm' + format: 'esm', + onSuccess: 'touch ./package.json' }, { ...commonConfig, diff --git a/packages/bundle/package.json b/packages/bundle/package.json index 87a035aef6..418f2c1bd3 100644 --- a/packages/bundle/package.json +++ b/packages/bundle/package.json @@ -105,12 +105,16 @@ } }, "scripts": { - "build": "npm run build:static && npm run build:tsup && npm run build:dtsroll && npm run build:validate", - "build:static": "node ./esbuild.static.mjs", - "build:tsup": "tsup", - "build:dtsroll": "dtsroll ./dist/*.d.*", - "build:validate": "npm run build:validate:dts", - "build:validate:dts": "if grep -q -P '@msinternal\\/' dist/*.d.* 2>/dev/null; then echo \"Error: dist/*.d.* is not compiled by dtsroll\" >&2; exit 1; fi", + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:post": "npm run build:post:dtsroll && npm run build:post:validate:dts", + "build:post:dtsroll": "dtsroll ./dist/*.d.*", + "build:post:validate:dts": "if grep -q -P '@msinternal\\/' dist/*.d.* 2>/dev/null; then echo \"Error: dist/*.d.* is not compiled by dtsroll\" >&2; exit 1; fi", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch", + "build:pre:local-dependencies": "../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../scripts/npm/build-watch.sh", + "build:run": "concurrently --prefix-colors \"auto\" \"npm:build:run:*\"", + "build:run:static": "node ./esbuild.static.mjs", + "build:run:tsup": "tsup", "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", @@ -120,39 +124,9 @@ "precommit:eslint": "../../node_modules/.bin/eslint --report-unused-disable-directives --max-warnings 0", "precommit:typecheck": "tsc --project ./src --emitDeclarationOnly false --esModuleInterop true --noEmit --pretty false", "preversion": "cat package.json | jq '(.localDependencies // {} | to_entries | map([if .value == \"production\" then \"dependencies\" else \"devDependencies\" end, .key])) as $P | delpaths($P)' > package-temp.json && mv package-temp.json package.json", - "start": "cross-env NODE_OPTIONS=--no-deprecation concurrently --kill-others --prefix-colors \"auto\" \"npm:start:*\"", - "start:static": "npm run build:static -- --watch", - "start:tsup": "npm run build:tsup -- --watch", + "start": "../../scripts/npm/notify-build.sh \"./src/\" \"../repack/adaptivecards/package.json\" \"../repack/base64-js/package.json\" \"../repack/botframework-directlinejs/package.json\" \"../base/package.json\" \"../experience-chat-launcher/package.json\" \"../react-valibot/package.json\" \"../tsconfig/package.json\" \"../isomorphic-react/package.json\" \"../isomorphic-react-dom/package.json\" \"../repack/microsoft-cognitiveservices-speech-sdk/package.json\" \"../repack/object-assign/package.json\" \"../repack/react/package.json\" \"../repack/react@baseline/package.json\" \"../repack/react-dom/package.json\" \"../repack/react-dom@baseline/package.json\" \"../repack/react-dom@umd/package.json\" \"../repack/react-is/package.json\" \"../repack/react@umd/package.json\" \"../directlinespeech/package.json\" \"../api/package.json\" \"../component/package.json\" \"../core/package.json\"", "test:tsd": "tsd" }, - "localDependencies": { - "@msinternal/adaptivecards": "development", - "@msinternal/base64-js": "development", - "@msinternal/botframework-directlinejs": "development", - "@msinternal/botframework-webchat-base": "development", - "@msinternal/botframework-webchat-experience-chat-launcher": "development", - "@msinternal/botframework-webchat-react-valibot": "development", - "@msinternal/botframework-webchat-tsconfig": "development", - "@msinternal/isomorphic-react-baseline": "development", - "@msinternal/isomorphic-react-dom-baseline": "development", - "@msinternal/isomorphic-react-dom-umd": "development", - "@msinternal/isomorphic-react-dom": "development", - "@msinternal/isomorphic-react-umd": "development", - "@msinternal/isomorphic-react": "development", - "@msinternal/microsoft-cognitiveservices-speech-sdk": "development", - "@msinternal/object-assign": "development", - "@msinternal/react-baseline": "development", - "@msinternal/react-dom-baseline": "development", - "@msinternal/react-dom-umd": "development", - "@msinternal/react-dom": "development", - "@msinternal/react-is": "development", - "@msinternal/react-umd": "development", - "@msinternal/react": "development", - "botframework-directlinespeech-sdk": "production", - "botframework-webchat-api": "production", - "botframework-webchat-component": "production", - "botframework-webchat-core": "production" - }, "pinDependencies": { "@types/react": [ "16", @@ -179,36 +153,29 @@ "uuid@9 emit non-ES5 build because of default parameters" ] }, - "dependencies": { - "@babel/runtime": "7.28.2", - "adaptivecards": "3.0.2", - "botframework-directlinejs": "0.15.6", - "botframework-directlinespeech-sdk": "0.0.0-0", - "botframework-webchat-api": "0.0.0-0", - "botframework-webchat-component": "0.0.0-0", - "botframework-webchat-core": "0.0.0-0", - "classnames": "2.5.1", - "core-js": "3.44.0", - "katex": "0.16.22", - "math-random": "2.0.1", - "mdast-util-from-markdown": "2.0.2", - "memoize-one": "6.0.0", - "micromark": "4.0.2", - "micromark-extension-gfm": "3.0.0", - "micromark-util-character": "2.1.1", - "micromark-util-sanitize-uri": "^2.0.1", - "microsoft-cognitiveservices-speech-sdk": "1.17.0", - "prop-types": "15.8.1", - "punycode": "2.3.1", - "sanitize-html": "2.17.0", - "shiki": "2.5.0", - "swiper": "8.4.7", - "url-search-params-polyfill": "8.2.5", - "use-ref-from": "0.1.0", - "uuid": "8.3.2", - "valibot": "1.1.0", - "web-speech-cognitive-services": "8.1.3", - "whatwg-fetch": "3.6.20" + "localDependencies": { + "@msinternal/adaptivecards": "development", + "@msinternal/base64-js": "development", + "@msinternal/botframework-directlinejs": "development", + "@msinternal/botframework-webchat-base": "development", + "@msinternal/botframework-webchat-experience-chat-launcher": "development", + "@msinternal/botframework-webchat-react-valibot": "development", + "@msinternal/botframework-webchat-tsconfig": "development", + "@msinternal/isomorphic-react": "development", + "@msinternal/isomorphic-react-dom": "development", + "@msinternal/microsoft-cognitiveservices-speech-sdk": "development", + "@msinternal/object-assign": "development", + "@msinternal/react": "development", + "@msinternal/react-baseline": "development", + "@msinternal/react-dom": "development", + "@msinternal/react-dom-baseline": "development", + "@msinternal/react-dom-umd": "development", + "@msinternal/react-is": "development", + "@msinternal/react-umd": "development", + "botframework-directlinespeech-sdk": "production", + "botframework-webchat-api": "production", + "botframework-webchat-component": "production", + "botframework-webchat-core": "production" }, "devDependencies": { "@babel/core": "^7.28.0", @@ -248,6 +215,37 @@ "type-fest": "^4.41.0", "typescript": "~5.8.3" }, + "dependencies": { + "@babel/runtime": "7.28.2", + "adaptivecards": "3.0.2", + "botframework-directlinejs": "0.15.6", + "botframework-directlinespeech-sdk": "0.0.0-0", + "botframework-webchat-api": "0.0.0-0", + "botframework-webchat-component": "0.0.0-0", + "botframework-webchat-core": "0.0.0-0", + "classnames": "2.5.1", + "core-js": "3.44.0", + "katex": "0.16.22", + "math-random": "2.0.1", + "mdast-util-from-markdown": "2.0.2", + "memoize-one": "6.0.0", + "micromark": "4.0.2", + "micromark-extension-gfm": "3.0.0", + "micromark-util-character": "2.1.1", + "micromark-util-sanitize-uri": "^2.0.1", + "microsoft-cognitiveservices-speech-sdk": "1.17.0", + "prop-types": "15.8.1", + "punycode": "2.3.1", + "sanitize-html": "2.17.0", + "shiki": "2.5.0", + "swiper": "8.4.7", + "url-search-params-polyfill": "8.2.5", + "use-ref-from": "0.1.0", + "uuid": "8.3.2", + "valibot": "1.1.0", + "web-speech-cognitive-services": "8.1.3", + "whatwg-fetch": "3.6.20" + }, "peerDependencies": { "@types/react": "^16.14.65", "react": ">= 16.8.6", diff --git a/packages/bundle/tsup.config.ts b/packages/bundle/tsup.config.ts index f95f847f72..f323c70d18 100644 --- a/packages/bundle/tsup.config.ts +++ b/packages/bundle/tsup.config.ts @@ -77,7 +77,8 @@ export default defineConfig([ ...commonConfig.define, 'globalThis.WEB_CHAT_BUILD_INFO_MODULE_FORMAT': '"esmodules"' }, - format: 'esm' + format: 'esm', + onSuccess: 'touch ./package.json' }, { ...commonConfig, diff --git a/packages/component/package.json b/packages/component/package.json index ca1f90a291..a339acd58f 100644 --- a/packages/component/package.json +++ b/packages/component/package.json @@ -75,12 +75,15 @@ ], "homepage": "https://github.com/microsoft/BotFramework-WebChat/tree/main/packages/component#readme", "scripts": { - "build": "npm run build:tsup && npm run build:dtsroll && npm run build:validate", - "build:tsup": "tsup", - "build:dtsroll": "dtsroll ./dist/*.d.*", - "build:validate": "npm run build:validate:css && npm run build:validate:dts", - "build:validate:css": "grep -q -P '\\.webchat \\.w([a-zA-Z-]){6}_' dist/*.css 2>/dev/null || { echo \"Error: dist/*.css is not compiled by Lightning CSS\" >&2; exit 1; }", - "build:validate:dts": "if grep -q -P '@msinternal\\/' dist/*.d.* 2>/dev/null; then echo \"Error: dist/*.d.* is not compiled by dtsroll\" >&2; exit 1; fi", + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:post": "npm run build:post:dtsroll && npm run build:post:validate:css && npm run build:post:validate:dts", + "build:post:dtsroll": "dtsroll ./dist/*.d.*", + "build:post:validate:css": "grep -q -P '\\.webchat \\.w([a-zA-Z-]){6}_' dist/*.css 2>/dev/null || { echo \"Error: dist/*.css is not compiled by Lightning CSS\" >&2; exit 1; }", + "build:post:validate:dts": "if grep -q -P '@msinternal\\/' dist/*.d.* 2>/dev/null; then echo \"Error: dist/*.d.* is not compiled by dtsroll\" >&2; exit 1; fi", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch", + "build:pre:local-dependencies": "../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../scripts/npm/build-watch.sh", + "build:run": "tsup", "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", @@ -90,16 +93,7 @@ "precommit:eslint": "../../node_modules/.bin/eslint --report-unused-disable-directives --max-warnings 0", "precommit:typecheck": "tsc --project ./src --emitDeclarationOnly false --esModuleInterop true --noEmit --pretty false", "preversion": "cat package.json | jq '(.localDependencies // {} | to_entries | map([if .value == \"production\" then \"dependencies\" else \"devDependencies\" end, .key])) as $P | delpaths($P)' > package-temp.json && mv package-temp.json package.json", - "start": "npm run build:tsup -- --watch" - }, - "localDependencies": { - "@msinternal/botframework-webchat-base": "development", - "@msinternal/botframework-webchat-react-hooks": "development", - "@msinternal/botframework-webchat-react-valibot": "development", - "@msinternal/botframework-webchat-styles": "development", - "@msinternal/botframework-webchat-tsconfig": "development", - "botframework-webchat-api": "production", - "botframework-webchat-core": "production" + "start": "../../scripts/npm/notify-build.sh \"./src/\" \"../base/package.json\" \"../react-hooks/package.json\" \"../react-valibot/package.json\" \"../styles/package.json\" \"../tsconfig/package.json\" \"../api/package.json\" \"../core/package.json\"" }, "pinDependencies": { "@types/react": [ @@ -125,6 +119,15 @@ "@typescript-eslint/parser@8.38.0 does not support typescript@5.9.2 yet" ] }, + "localDependencies": { + "@msinternal/botframework-webchat-base": "development", + "@msinternal/botframework-webchat-react-hooks": "development", + "@msinternal/botframework-webchat-react-valibot": "development", + "@msinternal/botframework-webchat-styles": "development", + "@msinternal/botframework-webchat-tsconfig": "development", + "botframework-webchat-api": "production", + "botframework-webchat-core": "production" + }, "devDependencies": { "@babel/core": "^7.28.0", "@babel/preset-env": "^7.28.0", diff --git a/packages/component/tsup.config.ts b/packages/component/tsup.config.ts index 186ce2fda5..9ff33e0e85 100644 --- a/packages/component/tsup.config.ts +++ b/packages/component/tsup.config.ts @@ -5,9 +5,6 @@ import { applyConfig } from '../../tsup.base.config'; import { decoratorStyleContent as decoratorStyleContentPlaceholder } from './src/decorator/stylesheet/createDecoratorStyleElements'; import { componentStyleContent as componentStyleContentPlaceholder } from './src/stylesheet/createComponentStyleElements'; -// TODO: [P1] Compute this automatically. -const DEPENDENT_PATHS = ['bundle/src/boot/exports/index.ts']; - const commonConfig = applyConfig(config => ({ ...config, entry: { @@ -27,8 +24,7 @@ const commonConfig = applyConfig(config => ({ stylesPlaceholder: componentStyleContentPlaceholder }), injectCSSPlugin({ stylesPlaceholder: decoratorStyleContentPlaceholder }) - ], - onSuccess: `touch ${DEPENDENT_PATHS.map(path => `../${path}`).join(' ')}` + ] })); export default defineConfig([ @@ -38,7 +34,8 @@ export default defineConfig([ ...commonConfig.define, 'globalThis.WEB_CHAT_BUILD_INFO_MODULE_FORMAT': '"esmodules"' }, - format: 'esm' + format: 'esm', + onSuccess: 'touch ./package.json' }, { ...commonConfig, diff --git a/packages/core/package.json b/packages/core/package.json index 647c86544a..ec64272676 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -57,11 +57,14 @@ } }, "scripts": { - "build": "npm run build:tsup && npm run build:dtsroll && npm run build:validate", - "build:dtsroll": "dtsroll ./dist/*.d.*", - "build:tsup": "tsup", - "build:validate": "npm run build:validate:dts", - "build:validate:dts": "if grep -q -P '@msinternal\\/' dist/*.d.* 2>/dev/null; then echo \"Error: dist/*.d.* is not compiled by dtsroll\" >&2; exit 1; fi", + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:post": "npm run build:post:dtsroll && npm run build:post:validate:dts", + "build:post:dtsroll": "dtsroll ./dist/*.d.*", + "build:post:validate:dts": "if grep -q -P '@msinternal\\/' dist/*.d.* 2>/dev/null; then echo \"Error: dist/*.d.* is not compiled by dtsroll\" >&2; exit 1; fi", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch", + "build:pre:local-dependencies": "../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../scripts/npm/build-watch.sh", + "build:run": "tsup", "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", @@ -71,22 +74,22 @@ "precommit:eslint": "../../node_modules/.bin/eslint --report-unused-disable-directives --max-warnings 0", "precommit:typecheck": "tsc --project ./src --emitDeclarationOnly false --esModuleInterop true --noEmit --pretty false", "preversion": "cat package.json | jq '(.localDependencies // {} | to_entries | map([if .value == \"production\" then \"dependencies\" else \"devDependencies\" end, .key])) as $P | delpaths($P)' > package-temp.json && mv package-temp.json package.json", - "start": "npm run build:tsup -- --watch", + "start": "../../scripts/npm/notify-build.sh \"./src/\" \"../base/package.json\" \"../tsconfig/package.json\"", "test:tsd": "tsd" }, "engines": { "node": ">=12.0.0" }, - "localDependences": { - "@msinternal/botframework-webchat-base": "development", - "@msinternal/botframework-webchat-tsconfig": "development" - }, "pinDependencies": { "typescript": [ "~5.8.3", "@typescript-eslint/parser@8.38.0 does not support typescript@5.9.2 yet" ] }, + "localDependencies": { + "@msinternal/botframework-webchat-base": "development", + "@msinternal/botframework-webchat-tsconfig": "development" + }, "devDependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-runtime": "^7.28.0", diff --git a/packages/core/tsup.config.ts b/packages/core/tsup.config.ts index 6b9b56867b..83f5c3b906 100644 --- a/packages/core/tsup.config.ts +++ b/packages/core/tsup.config.ts @@ -2,16 +2,12 @@ import { defineConfig } from 'tsup'; import { applyConfig } from '../../tsup.base.config'; -// TODO: [P1] Compute this automatically. -const DEPENDENT_PATHS = ['api/src/index.ts']; - const commonConfig = applyConfig(config => ({ ...config, entry: { 'botframework-webchat-core': './src/index.ts', 'botframework-webchat-core.internal': './src/internal/index.ts' - }, - onSuccess: `touch ${DEPENDENT_PATHS.map(path => `../${path}`).join(' ')}` + } })); export default defineConfig([ @@ -21,7 +17,8 @@ export default defineConfig([ ...commonConfig.define, 'globalThis.WEB_CHAT_BUILD_INFO_MODULE_FORMAT': '"esmodules"' }, - format: 'esm' + format: 'esm', + onSuccess: 'touch ./package.json' }, { ...commonConfig, diff --git a/packages/debug-theme/package.json b/packages/debug-theme/package.json index c2607c5747..e1070a2ff6 100644 --- a/packages/debug-theme/package.json +++ b/packages/debug-theme/package.json @@ -35,10 +35,13 @@ "./src/**/*", "*.js" ], - "localDependencies": {}, "homepage": "https://github.com/microsoft/BotFramework-WebChat/tree/main/packages/debug-theme#readme", "scripts": { - "build": "tsup --config ./tsup.config.ts", + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch", + "build:pre:local-dependencies": "../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../scripts/npm/build-watch.sh", + "build:run": "tsup", "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", @@ -48,13 +51,17 @@ "precommit:eslint": "../../node_modules/.bin/eslint --report-unused-disable-directives --max-warnings 0", "precommit:typecheck": "tsc --project ./src --emitDeclarationOnly false --esModuleInterop true --noEmit --pretty false", "preversion": "cat package.json | jq '(.localDependencies // {} | to_entries | map([if .value == \"production\" then \"dependencies\" else \"devDependencies\" end, .key])) as $P | delpaths($P)' > package-temp.json && mv package-temp.json package.json", - "start": "npm run build -- --watch" + "start": "../../scripts/npm/notify-build.sh \"./src/\" \"../api/package.json\" \"../component/package.json\"" }, - "peerDependencies": { - "react": ">= 16.8.6" + "localDependencies": { + "botframework-webchat-api": "production", + "botframework-webchat-component": "production" }, "dependencies": { "botframework-webchat-api": "^0.0.0-0", "botframework-webchat-component": "^0.0.0-0" + }, + "peerDependencies": { + "react": ">= 16.8.6" } } diff --git a/packages/debug-theme/tsup.config.ts b/packages/debug-theme/tsup.config.ts index 089a22e198..329296705e 100644 --- a/packages/debug-theme/tsup.config.ts +++ b/packages/debug-theme/tsup.config.ts @@ -49,7 +49,8 @@ export default defineConfig([ entry: { 'botframework-webchat-debug-theme': './src/index.ts' }, - format: 'esm' + format: 'esm', + onSuccess: 'touch ./package.json' })), applyConfig(config => ({ ...config, diff --git a/packages/directlinespeech/package.json b/packages/directlinespeech/package.json index 9a21bccfa4..057e119def 100644 --- a/packages/directlinespeech/package.json +++ b/packages/directlinespeech/package.json @@ -20,13 +20,20 @@ } } }, + "engines": { + "node": ">= 10.14.2" + }, "scripts": { - "build": "npm run build:tsup && npm run build:babel && npm run build:webpack", - "build:babel": "cross-env build_tool=babel module_format=commonjs babel src --ignore **/*.spec.js,**/*.test.js,__tests__/**/*.js --out-dir lib --verbose", - "build:tsup": "tsup", - "build:webpack": "npm run build:webpack:development && npm run build:webpack:production", - "build:webpack:development": "cross-env node_env=development webpack-cli", - "build:webpack:production": "cross-env node_env=production webpack-cli", + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch", + "build:pre:local-dependencies": "../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../scripts/npm/build-watch.sh", + "build:run": "npm run build:run:tsup && npm run build:run:babel && npm run build:run:webpack", + "build:run:babel": "cross-env build_tool=babel module_format=commonjs babel src --ignore **/*.spec.js,**/*.test.js,__tests__/**/*.js --out-dir lib --verbose", + "build:run:tsup": "tsup", + "build:run:webpack": "npm run build:run:webpack:development && npm run build:run:webpack:production", + "build:run:webpack:development": "cross-env node_env=development webpack-cli", + "build:run:webpack:production": "cross-env node_env=production webpack-cli", "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", @@ -36,16 +43,12 @@ "precommit:eslint": "eslint --report-unused-disable-directives --max-warnings 0", "prettier": "prettier --check src/**/*.{js,ts}", "preversion": "cat package.json | jq '(.localDependencies // {} | to_entries | map([if .value == \"production\" then \"dependencies\" else \"devDependencies\" end, .key])) as $P | delpaths($P)' > package-temp.json && mv package-temp.json package.json", - "start": "concurrently --kill-others --prefix-colors \"auto\" \"npm:start:*\"", - "start:babel": "npm run build:babel -- --skip-initial-build --watch", + "start": "../../scripts/npm/notify-build.sh \"./src/\"", "start:serve": "serve", - "start:tsup": "npm run build:tsup -- --watch", - "start:webpack": "npm run build:webpack:development -- --watch", "test": "jest --watch" }, "author": "Microsoft Corporation", "license": "MIT", - "localDependencies": {}, "pinDependencies": { "@types/jest": [ "29", @@ -68,6 +71,7 @@ "needs to bump manually" ] }, + "localDependencies": {}, "devDependencies": { "@babel/cli": "^7.28.0", "@babel/core": "^7.28.0", @@ -79,7 +83,6 @@ "babel-jest": "^29.7.0", "babel-plugin-istanbul": "^7.0.0", "babel-plugin-transform-inline-environment-variables": "^0.4.4", - "concurrently": "^9.2.0", "cross-env": "^10.0.0", "dotenv": "^17.2.1", "eslint": "^8.57.1", @@ -108,8 +111,5 @@ "math-random": "2.0.1", "microsoft-cognitiveservices-speech-sdk": "1.17.0", "web-speech-cognitive-services": "8.1.3" - }, - "engines": { - "node": ">= 10.14.2" } } diff --git a/packages/directlinespeech/tsup.config.ts b/packages/directlinespeech/tsup.config.ts index 1dd19e1744..d3ac54b07a 100644 --- a/packages/directlinespeech/tsup.config.ts +++ b/packages/directlinespeech/tsup.config.ts @@ -43,7 +43,8 @@ export default defineConfig([ ...config.env, module_format: 'esmodules' }, - format: 'esm' + format: 'esm', + onSuccess: 'touch ./package.json' }, { ...config, diff --git a/packages/experience-chat-launcher/package.json b/packages/experience-chat-launcher/package.json index 3aaa5f0565..5206817d4b 100644 --- a/packages/experience-chat-launcher/package.json +++ b/packages/experience-chat-launcher/package.json @@ -27,17 +27,13 @@ "./src/**/*", "*.js" ], - "localDependencies": { - "botframework-webchat-api": "production", - "botframework-webchat-component": "production", - "botframework-webchat-core": "production", - "@msinternal/botframework-webchat-redux-store": "development", - "@msinternal/botframework-webchat-react-valibot": "development", - "@msinternal/botframework-webchat-styles": "development" - }, "homepage": "https://github.com/microsoft/BotFramework-WebChat/tree/main/packages/experience-chat-launcher#readme", "scripts": { - "build": "tsup --config ./tsup.config.ts", + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch", + "build:pre:local-dependencies": "../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../scripts/npm/build-watch.sh", + "build:run": "tsup", "bump": "npm run bump:prod && npm run bump:dev && npm run bump:peer && (npm audit fix || exit 0)", "bump:dev": "../../scripts/npm/bump-dev.sh", "bump:peer": "../../scripts/npm/bump-peer.sh", @@ -48,7 +44,16 @@ "precommit:eslint": "../../node_modules/.bin/eslint --report-unused-disable-directives --max-warnings 0", "precommit:typecheck": "tsc --project ./src --emitDeclarationOnly false --esModuleInterop true --noEmit --pretty false", "preversion": "../../scripts/npm/preversion.sh", - "start": "npm run build -- --watch" + "start": "../../scripts/npm/notify-build.sh \"./src/\" \"../react-valibot/package.json\" \"../redux-store/package.json\" \"../styles/package.json\" \"../api/package.json\" \"../component/package.json\" \"../core/package.json\"" + }, + "pinDependencies": {}, + "localDependencies": { + "@msinternal/botframework-webchat-react-valibot": "development", + "@msinternal/botframework-webchat-redux-store": "development", + "@msinternal/botframework-webchat-styles": "development", + "botframework-webchat-api": "production", + "botframework-webchat-component": "production", + "botframework-webchat-core": "production" }, "devDependencies": { "@msinternal/botframework-webchat-react-valibot": "0.0.0-0", @@ -57,14 +62,14 @@ "@types/node": "^22.13.4", "typescript": "^5.7.3" }, - "peerDependencies": { - "react": ">= 16.8.6" - }, "dependencies": { "botframework-webchat-api": "0.0.0-0", "botframework-webchat-component": "0.0.0-0", "botframework-webchat-core": "0.0.0-0", "use-ref-from": "0.1.0", "valibot": "1.1.0" + }, + "peerDependencies": { + "react": ">= 16.8.6" } } diff --git a/packages/experience-chat-launcher/tsup.config.ts b/packages/experience-chat-launcher/tsup.config.ts index fc11ec2c69..7d12dc2106 100644 --- a/packages/experience-chat-launcher/tsup.config.ts +++ b/packages/experience-chat-launcher/tsup.config.ts @@ -5,9 +5,6 @@ import { defineConfig } from 'tsup'; import { applyConfig } from '../../tsup.base.config'; import { chatLauncherStyleContent as chatLauncherStyleContentPlaceholder } from './src/stylesheet/createChatLauncherStyleElements'; -// TODO: [P1] Compute this automatically. -const DEPENDENT_PATHS = ['api/src/index.ts']; - // eslint-disable-next-line no-unused-vars const isomorphicReactPlugin: Plugin = { name: 'isomorphic-react', @@ -40,14 +37,14 @@ const commonConfig = applyConfig(config => ({ ...config.esbuildPlugins, isomorphicReactPlugin, injectCSSPlugin({ stylesPlaceholder: chatLauncherStyleContentPlaceholder }) - ], - onSuccess: `touch ${DEPENDENT_PATHS.map(path => `../${path}`).join(' ')}` + ] })); export default defineConfig([ { ...commonConfig, - format: 'esm' + format: 'esm', + onSuccess: 'touch ./package.json' }, { ...commonConfig, diff --git a/packages/fluent-theme/package.json b/packages/fluent-theme/package.json index 056d1f4de3..41c24292a9 100644 --- a/packages/fluent-theme/package.json +++ b/packages/fluent-theme/package.json @@ -40,13 +40,18 @@ "static/**/*" ], "scripts": { - "build": "npm run build:static && npm run build:tsup && npm run build:dtsroll && npm run build:validate", - "build:dtsroll": "dtsroll ./dist/*.d.*", - "build:static": "node ./esbuild.static.mjs", - "build:tsup": "tsup", - "build:validate": "npm run build:validate:css && npm run build:validate:dts", - "build:validate:css": "grep -q -P '\\.webchat-fluent \\.w([a-zA-Z-]){6}_' dist/*.css 2>/dev/null || { echo \"Error: dist/*.css is not compiled by Lightning CSS\" >&2; exit 1; }", - "build:validate:dts": "if grep -q -P '@msinternal\\/' dist/*.d.* 2>/dev/null; then echo \"Error: dist/*.d.* is not compiled by dtsroll\" >&2; exit 1; fi", + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:post": "npm run build:post:dtsroll && npm run build:post:validate", + "build:post:dtsroll": "dtsroll ./dist/*.d.*", + "build:post:validate": "npm run build:post:validate:css && npm run build:post:validate:dts", + "build:post:validate:css": "grep -q -P '\\.webchat-fluent \\.w([a-zA-Z-]){6}_' dist/*.css 2>/dev/null || { echo \"Error: dist/*.css is not compiled by Lightning CSS\" >&2; exit 1; }", + "build:post:validate:dts": "if grep -q -P '@msinternal\\/' dist/*.d.* 2>/dev/null; then echo \"Error: dist/*.d.* is not compiled by dtsroll\" >&2; exit 1; fi", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch", + "build:pre:local-dependencies": "../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../scripts/npm/build-watch.sh", + "build:run": "concurrently --prefix-colors \"auto\" \"npm:build:run:*\"", + "build:run:static": "node ./esbuild.static.mjs", + "build:run:tsup": "tsup", "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", @@ -56,14 +61,7 @@ "precommit:eslint": "../../node_modules/.bin/eslint --report-unused-disable-directives --max-warnings 0", "precommit:typecheck": "tsc --project ./src --emitDeclarationOnly false --esModuleInterop true --noEmit --pretty false", "preversion": "cat package.json | jq '(.localDependencies // {} | to_entries | map([if .value == \"production\" then \"dependencies\" else \"devDependencies\" end, .key])) as $P | delpaths($P)' > package-temp.json && mv package-temp.json package.json", - "start": "cross-env NODE_OPTIONS=--no-deprecation concurrently --kill-others --prefix-colors \"auto\" \"npm:start:*\"", - "start:static": "npm run build:static -- --watch", - "start:tsup": "npm run build:tsup -- --watch" - }, - "localDependencies": { - "@msinternal/botframework-webchat-styles": "development", - "@msinternal/botframework-webchat-tsconfig": "development", - "botframework-webchat": "production" + "start": "../../scripts/npm/notify-build.sh \"./src/\" \"../styles/package.json\" \"../tsconfig/package.json\" \"../bundle/package.json\"" }, "pinDependencies": { "@types/react": [ @@ -75,11 +73,17 @@ "@typescript-eslint/parser@8.38.0 does not support typescript@5.9.2 yet" ] }, + "localDependencies": { + "@msinternal/botframework-webchat-styles": "development", + "@msinternal/botframework-webchat-tsconfig": "development", + "botframework-webchat": "production" + }, "devDependencies": { "@msinternal/botframework-webchat-styles": "0.0.0-0", "@msinternal/botframework-webchat-tsconfig": "0.0.0-0", "@types/math-random": "^1.0.2", "@types/node": "^24.1.0", + "concurrently": "^9.2.0", "tsup": "^8.5.0", "typescript": "~5.8.3" }, diff --git a/packages/fluent-theme/tsup.config.ts b/packages/fluent-theme/tsup.config.ts index 39ef2f430a..2786f66cee 100644 --- a/packages/fluent-theme/tsup.config.ts +++ b/packages/fluent-theme/tsup.config.ts @@ -73,7 +73,8 @@ export default defineConfig([ ...(config.esbuildPlugins ?? []), injectCSSPlugin({ stylesPlaceholder: fluentStyleContentPlaceholder }) ], - format: ['esm'] + format: ['esm'], + onSuccess: 'touch ./package.json' })), applyConfig(config => ({ ...config, diff --git a/packages/isomorphic-react-dom/package.json b/packages/isomorphic-react-dom/package.json index a6f23cffe0..defaa70823 100644 --- a/packages/isomorphic-react-dom/package.json +++ b/packages/isomorphic-react-dom/package.json @@ -5,9 +5,12 @@ "main": "dist/react-dom.js", "private": true, "scripts": { - "build": "npm run build:babel && npm run build:webpack", - "build:babel": "babel src --out-dir lib --verbose", - "build:webpack": "webpack-cli", + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:pre": "npm run build:pre:local-dependencies", + "build:pre:local-dependencies": "../../scripts/npm/build-local-dependencies.sh", + "build:run": "npm run build:run:babel && npm run build:run:webpack && touch ./package.json", + "build:run:babel": "babel src --out-dir lib --verbose", + "build:run:webpack": "webpack-cli", "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", @@ -19,8 +22,8 @@ }, "author": "Microsoft Corporation", "license": "MIT", - "localDependencies": {}, "pinDependencies": {}, + "localDependencies": {}, "devDependencies": { "@babel/cli": "^7.28.0", "@babel/core": "^7.28.0", diff --git a/packages/isomorphic-react/package.json b/packages/isomorphic-react/package.json index f7acce60d8..04b4562619 100644 --- a/packages/isomorphic-react/package.json +++ b/packages/isomorphic-react/package.json @@ -5,9 +5,12 @@ "main": "dist/react.js", "private": true, "scripts": { - "build": "npm run build:babel && npm run build:webpack", - "build:babel": "babel src --out-dir lib --verbose", - "build:webpack": "webpack-cli", + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:pre": "npm run build:pre:local-dependencies", + "build:pre:local-dependencies": "../../scripts/npm/build-local-dependencies.sh", + "build:run": "npm run build:run:babel && npm run build:run:webpack && touch ./package.json", + "build:run:babel": "babel src --out-dir lib --verbose", + "build:run:webpack": "webpack-cli", "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", @@ -19,8 +22,8 @@ }, "author": "Microsoft Corporation", "license": "MIT", - "localDependencies": {}, "pinDependencies": {}, + "localDependencies": {}, "devDependencies": { "@babel/cli": "^7.28.0", "@babel/core": "^7.28.0", diff --git a/packages/react-hooks/package.json b/packages/react-hooks/package.json index b3b4710f55..fcc3a915eb 100644 --- a/packages/react-hooks/package.json +++ b/packages/react-hooks/package.json @@ -35,10 +35,13 @@ "./src/**/*", "*.js" ], - "localDependencies": {}, "homepage": "https://github.com/microsoft/BotFramework-WebChat/tree/main/packages/react-hooks#readme", "scripts": { - "build": "tsup --config ./tsup.config.ts", + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch", + "build:pre:local-dependencies": "../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../scripts/npm/build-watch.sh", + "build:run": "tsup", "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", @@ -48,9 +51,10 @@ "precommit:eslint": "../../node_modules/.bin/eslint --report-unused-disable-directives --max-warnings 0", "precommit:typecheck": "tsc --project ./src --emitDeclarationOnly false --esModuleInterop true --noEmit --pretty false", "preversion": "cat package.json | jq '(.localDependencies // {} | to_entries | map([if .value == \"production\" then \"dependencies\" else \"devDependencies\" end, .key])) as $P | delpaths($P)' > package-temp.json && mv package-temp.json package.json", - "start": "npm run build -- --watch" + "start": "../../scripts/npm/notify-build.sh \"./src/\"" }, "peerDependencies": { "react": ">= 16.8.6" - } + }, + "localDependencies": {} } diff --git a/packages/react-hooks/tsup.config.ts b/packages/react-hooks/tsup.config.ts index cd7a9b44f8..f7b5e83bce 100644 --- a/packages/react-hooks/tsup.config.ts +++ b/packages/react-hooks/tsup.config.ts @@ -2,21 +2,18 @@ import { defineConfig } from 'tsup'; import { applyConfig } from '../../tsup.base.config'; -// TODO: [P1] Compute this automatically. -const DEPENDENT_PATHS = ['api/src/index.ts', 'api-middleware/src/index.ts']; - const commonConfig = applyConfig(config => ({ ...config, entry: { 'botframework-webchat-react-hooks': './src/index.ts' - }, - onSuccess: `touch ${DEPENDENT_PATHS.map(path => `../${path}`).join(' ')}` + } })); export default defineConfig([ { ...commonConfig, - format: 'esm' + format: 'esm', + onSuccess: 'touch ./package.json' }, { ...commonConfig, diff --git a/packages/react-valibot/package.json b/packages/react-valibot/package.json index 4d85aad21f..da2dfa3f52 100644 --- a/packages/react-valibot/package.json +++ b/packages/react-valibot/package.json @@ -32,7 +32,11 @@ "homepage": "https://github.com/microsoft/BotFramework-WebChat/tree/main/packages/react-valibot#readme", "private": true, "scripts": { - "build": "tsup", + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch", + "build:pre:local-dependencies": "../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../scripts/npm/build-watch.sh", + "build:run": "tsup", "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", @@ -42,10 +46,7 @@ "precommit:eslint": "../../node_modules/.bin/eslint --report-unused-disable-directives --max-warnings 0", "precommit:typecheck": "tsc --project ./src --emitDeclarationOnly false --esModuleInterop true --noEmit --pretty false", "preversion": "cat package.json | jq '(.localDependencies // {} | to_entries | map([if .value == \"production\" then \"dependencies\" else \"devDependencies\" end, .key])) as $P | delpaths($P)' > package-temp.json && mv package-temp.json package.json", - "start": "npm run build -- --watch" - }, - "localDependencies": { - "@msinternal/botframework-webchat-tsconfig": "development" + "start": "../../scripts/npm/notify-build.sh \"./src/\" \"../tsconfig/package.json\"" }, "pinDependencies": { "@types/react": [ @@ -56,6 +57,9 @@ "@typescript-eslint/parser@8.38.0 does not support typescript@5.9.2 yet" ] }, + "localDependencies": { + "@msinternal/botframework-webchat-tsconfig": "development" + }, "devDependencies": { "@msinternal/botframework-webchat-tsconfig": "^0.0.0-0", "@types/jest": "^30.0.0", diff --git a/packages/react-valibot/tsup.config.ts b/packages/react-valibot/tsup.config.ts index f832eff721..9526b6e770 100644 --- a/packages/react-valibot/tsup.config.ts +++ b/packages/react-valibot/tsup.config.ts @@ -2,29 +2,18 @@ import { defineConfig } from 'tsup'; import { applyConfig } from '../../tsup.base.config'; -// TODO: [P1] Compute this automatically. -const DEPENDENT_PATHS = [ - 'api/src/index.ts', - 'api-middleware/src/index.ts', - 'bundle/src/boot/exports/index.ts', - 'component/src/index.ts', - 'debug-theme/src/index.ts', - 'fluent-theme/src/index.ts', - 'redux-store/src/index.ts' -]; - const commonConfig = applyConfig(config => ({ ...config, entry: { 'botframework-webchat-react-valibot': './src/index.ts' - }, - onSuccess: `touch ${DEPENDENT_PATHS.map(path => `../${path}`).join(' ')}` + } })); export default defineConfig([ { ...commonConfig, - format: 'esm' + format: 'esm', + onSuccess: 'touch ./package.json' }, { ...commonConfig, diff --git a/packages/redux-store/package.json b/packages/redux-store/package.json index 349ccb1230..caffe32483 100644 --- a/packages/redux-store/package.json +++ b/packages/redux-store/package.json @@ -31,7 +31,11 @@ "homepage": "https://github.com/microsoft/BotFramework-WebChat/tree/main/packages/redux-store#readme", "private": true, "scripts": { - "build": "tsup", + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch", + "build:pre:local-dependencies": "../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../scripts/npm/build-watch.sh", + "build:run": "tsup", "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", @@ -41,12 +45,7 @@ "precommit:eslint": "../../node_modules/.bin/eslint --report-unused-disable-directives --max-warnings 0", "precommit:typecheck": "tsc --project ./src --emitDeclarationOnly false --esModuleInterop true --noEmit --pretty false", "preversion": "cat package.json | jq '(.localDependencies // {} | to_entries | map([if .value == \"production\" then \"dependencies\" else \"devDependencies\" end, .key])) as $P | delpaths($P)' > package-temp.json && mv package-temp.json package.json", - "start": "npm run build -- --watch" - }, - "localDependencies": { - "@msinternal/botframework-webchat-react-valibot": "development", - "@msinternal/botframework-webchat-tsconfig": "development", - "botframework-webchat-core": "production" + "start": "../../scripts/npm/notify-build.sh \"./src/\" \"../react-valibot/package.json\" \"../tsconfig/package.json\" \"../core/package.json\"" }, "pinDependencies": { "@types/react": [ @@ -57,6 +56,11 @@ "@typescript-eslint/parser@8.38.0 does not support typescript@5.9.2 yet" ] }, + "localDependencies": { + "@msinternal/botframework-webchat-react-valibot": "development", + "@msinternal/botframework-webchat-tsconfig": "development", + "botframework-webchat-core": "production" + }, "devDependencies": { "@msinternal/botframework-webchat-react-valibot": "^0.0.0-0", "@msinternal/botframework-webchat-tsconfig": "^0.0.0-0", diff --git a/packages/redux-store/tsup.config.ts b/packages/redux-store/tsup.config.ts index 78f7b8aca0..6901da8420 100644 --- a/packages/redux-store/tsup.config.ts +++ b/packages/redux-store/tsup.config.ts @@ -2,21 +2,18 @@ import { defineConfig } from 'tsup'; import { applyConfig } from '../../tsup.base.config'; -// TODO: [P1] Compute this automatically. -const DEPENDENT_PATHS = ['api/src/index.ts']; - const commonConfig = applyConfig(config => ({ ...config, entry: { 'botframework-webchat-redux-store': './src/index.ts' - }, - onSuccess: `touch ${DEPENDENT_PATHS.map(path => `../${path}`).join(' ')}` + } })); export default defineConfig([ { ...commonConfig, - format: 'esm' + format: 'esm', + onSuccess: 'touch ./package.json' }, { ...commonConfig, diff --git a/packages/repack/adaptivecards/package.json b/packages/repack/adaptivecards/package.json index 54416b0dbb..fe79193cb7 100644 --- a/packages/repack/adaptivecards/package.json +++ b/packages/repack/adaptivecards/package.json @@ -7,7 +7,11 @@ "type": "module", "main": "./dist/index.js", "scripts": { - "build": "esbuild --bundle=true --format=esm --outdir=dist --platform=browser --splitting --sourcemap ./src/index", + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch", + "build:pre:local-dependencies": "../../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../../scripts/npm/build-watch.sh", + "build:run": "esbuild --bundle=true --format=esm --outdir=dist --platform=browser --splitting --sourcemap ./src/index && touch ./package.json", "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", @@ -15,19 +19,19 @@ "precommit": "npm run precommit:eslint -- src && npm run precommit:typecheck", "precommit:eslint": "eslint --report-unused-disable-directives --max-warnings 0", "precommit:typecheck": "tsc --project ./src --emitDeclarationOnly false --esModuleInterop true --noEmit --pretty false", - "start": "npm run build -- --watch --watch-delay=200" + "start": "../../../scripts/npm/notify-build.sh \"./src/\"" }, - "localDependencies": {}, "pinDependencies": { "adaptivecards": [ "3.0.2", "Bump manually to ensure compatibility" ] }, - "dependencies": { - "adaptivecards": "3.0.2" - }, + "localDependencies": {}, "devDependencies": { "esbuild": "^0.25.10" + }, + "dependencies": { + "adaptivecards": "3.0.2" } } diff --git a/packages/repack/base64-js/package.json b/packages/repack/base64-js/package.json index fad25667f4..64ba943cf0 100644 --- a/packages/repack/base64-js/package.json +++ b/packages/repack/base64-js/package.json @@ -7,7 +7,11 @@ "type": "module", "main": "./dist/index.js", "scripts": { - "build": "esbuild --bundle=true --format=esm --outdir=dist --platform=browser --splitting --sourcemap ./src/index", + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch", + "build:pre:local-dependencies": "../../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../../scripts/npm/build-watch.sh", + "build:run": "esbuild --bundle=true --format=esm --outdir=dist --platform=browser --splitting --sourcemap ./src/index && touch ./package.json", "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", @@ -15,14 +19,14 @@ "precommit": "npm run precommit:eslint -- src && npm run precommit:typecheck", "precommit:eslint": "eslint --report-unused-disable-directives --max-warnings 0", "precommit:typecheck": "tsc --project ./src --emitDeclarationOnly false --esModuleInterop true --noEmit --pretty false", - "start": "npm run build -- --watch --watch-delay=200" + "start": "../../../scripts/npm/notify-build.sh \"./src/\"" }, - "localDependencies": {}, "pinDependencies": {}, - "dependencies": { - "base64-js": "1.5.1" - }, + "localDependencies": {}, "devDependencies": { "esbuild": "^0.25.10" + }, + "dependencies": { + "base64-js": "1.5.1" } } diff --git a/packages/repack/botframework-directlinejs/package.json b/packages/repack/botframework-directlinejs/package.json index c1620759cc..ba22a59c3b 100644 --- a/packages/repack/botframework-directlinejs/package.json +++ b/packages/repack/botframework-directlinejs/package.json @@ -7,7 +7,11 @@ "type": "module", "main": "./dist/index.js", "scripts": { - "build": "esbuild --bundle=true --format=esm --outdir=dist --platform=browser --splitting --sourcemap ./src/index", + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch", + "build:pre:local-dependencies": "../../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../../scripts/npm/build-watch.sh", + "build:run": "esbuild --bundle=true --format=esm --outdir=dist --platform=browser --splitting --sourcemap ./src/index && touch ./package.json", "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", @@ -15,14 +19,14 @@ "precommit": "npm run precommit:eslint -- src && npm run precommit:typecheck", "precommit:eslint": "eslint --report-unused-disable-directives --max-warnings 0", "precommit:typecheck": "tsc --project ./src --emitDeclarationOnly false --esModuleInterop true --noEmit --pretty false", - "start": "npm run build -- --watch --watch-delay=200" + "start": "../../../scripts/npm/notify-build.sh \"./src/\"" }, - "localDependencies": {}, "pinDependencies": {}, - "devDependencies": { - "esbuild": "^0.25.10" - }, + "localDependencies": {}, "dependencies": { "botframework-directlinejs": "0.15.6" + }, + "devDependencies": { + "esbuild": "^0.25.10" } } diff --git a/packages/repack/html-react-parser/package.json b/packages/repack/html-react-parser/package.json index d646be1588..c26be99926 100644 --- a/packages/repack/html-react-parser/package.json +++ b/packages/repack/html-react-parser/package.json @@ -9,7 +9,11 @@ ".": "./dist/index.js" }, "scripts": { - "build": "node ./build.js", + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch", + "build:pre:local-dependencies": "../../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../../scripts/npm/build-watch.sh", + "build:run": "node ./build.js && touch ./package.json", "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", @@ -17,14 +21,14 @@ "precommit": "npm run precommit:eslint -- src && npm run precommit:typecheck", "precommit:eslint": "eslint --report-unused-disable-directives --max-warnings 0", "precommit:typecheck": "tsc --project ./src --emitDeclarationOnly false --esModuleInterop true --noEmit --pretty false", - "start": "npm run build -- --watch" + "start": "../../../scripts/npm/notify-build.sh \"./src/\"" }, - "localDependencies": {}, "pinDependencies": {}, - "dependencies": { - "html-react-parser": "5.2.6" - }, + "localDependencies": {}, "devDependencies": { "esbuild": "^0.25.10" + }, + "dependencies": { + "html-react-parser": "5.2.6" } } diff --git a/packages/repack/microsoft-cognitiveservices-speech-sdk/package.json b/packages/repack/microsoft-cognitiveservices-speech-sdk/package.json index 9991042979..6b99d69057 100644 --- a/packages/repack/microsoft-cognitiveservices-speech-sdk/package.json +++ b/packages/repack/microsoft-cognitiveservices-speech-sdk/package.json @@ -16,7 +16,11 @@ "./distrib/lib/src/sdk/Exports.js": "./dist/sdk/Exports.js" }, "scripts": { - "build": "esbuild --bundle=true --format=esm --outdir=dist --platform=browser --splitting --sourcemap ./src/index.js ./src/common/AudioSourceEvents.js ./src/common/Exports.js ./src/common.browser/Exports.js ./src/common.speech/Exports.js ./src/sdk/Audio/AudioStreamFormat.js ./src/sdk/Exports.js", + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch", + "build:pre:local-dependencies": "../../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../../scripts/npm/build-watch.sh", + "build:run": "esbuild --bundle=true --format=esm --outdir=dist --platform=browser --splitting --sourcemap ./src/index.js ./src/common/AudioSourceEvents.js ./src/common/Exports.js ./src/common.browser/Exports.js ./src/common.speech/Exports.js ./src/sdk/Audio/AudioStreamFormat.js ./src/sdk/Exports.js && touch ./package.json", "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", @@ -24,19 +28,19 @@ "precommit": "npm run precommit:eslint -- src && npm run precommit:typecheck", "precommit:eslint": "eslint --report-unused-disable-directives --max-warnings 0", "precommit:typecheck": "tsc --project ./src --emitDeclarationOnly false --esModuleInterop true --noEmit --pretty false", - "start": "npm run build -- --watch --watch-delay=200" + "start": "../../../scripts/npm/notify-build.sh \"./src/\"" }, - "localDependencies": {}, "pinDependencies": { "microsoft-cognitiveservices-speech-sdk": [ "1.17.0", "<=1.45.0 is using new protocol that does not connect may need some tune." ] }, - "dependencies": { - "microsoft-cognitiveservices-speech-sdk": "1.17.0" - }, + "localDependencies": {}, "devDependencies": { "esbuild": "^0.25.10" + }, + "dependencies": { + "microsoft-cognitiveservices-speech-sdk": "1.17.0" } } diff --git a/packages/repack/object-assign/package.json b/packages/repack/object-assign/package.json index e9f319db07..bbc38b46a6 100644 --- a/packages/repack/object-assign/package.json +++ b/packages/repack/object-assign/package.json @@ -9,13 +9,19 @@ ".": "./src/index.js" }, "scripts": { + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch", + "build:pre:local-dependencies": "../../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../../scripts/npm/build-watch.sh", + "build:run": "echo This package does not need to be built.", "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", "eslint": "npm run precommit", "precommit": "npm run precommit:eslint -- src", - "precommit:eslint": "eslint --report-unused-disable-directives --max-warnings 0" + "precommit:eslint": "eslint --report-unused-disable-directives --max-warnings 0", + "start": "../../../scripts/npm/notify-build.sh \"./src/\"" }, - "localDependencies": {}, - "pinDependencies": {} + "pinDependencies": {}, + "localDependencies": {} } diff --git a/packages/repack/react-dom/package.json b/packages/repack/react-dom/package.json index da27fdd324..784ebd3f64 100644 --- a/packages/repack/react-dom/package.json +++ b/packages/repack/react-dom/package.json @@ -10,7 +10,11 @@ "./client": "./dist/react-dom/client.js" }, "scripts": { - "build": "node ./build.js", + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch", + "build:pre:local-dependencies": "../../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../../scripts/npm/build-watch.sh", + "build:run": "node ./build.js && touch ./package.json", "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", @@ -18,9 +22,8 @@ "precommit": "npm run precommit:eslint -- src && npm run precommit:typecheck", "precommit:eslint": "eslint --report-unused-disable-directives --max-warnings 0", "precommit:typecheck": "tsc --project ./src --emitDeclarationOnly false --esModuleInterop true --noEmit --pretty false", - "start": "npm run build -- --watch" + "start": "../../../scripts/npm/notify-build.sh \"./src/\"" }, - "localDependencies": {}, "pinDependencies": { "@types/react-dom": [ "~18.3", @@ -31,11 +34,12 @@ "Bump React manually" ] }, - "dependencies": { - "react-dom": "18.3.1" - }, + "localDependencies": {}, "devDependencies": { "@types/react-dom": "^18.3.7", "esbuild": "^0.25.10" + }, + "dependencies": { + "react-dom": "18.3.1" } } diff --git a/packages/repack/react-dom@baseline/package.json b/packages/repack/react-dom@baseline/package.json index bacfe14a6f..d3a2ed6704 100644 --- a/packages/repack/react-dom@baseline/package.json +++ b/packages/repack/react-dom@baseline/package.json @@ -9,7 +9,11 @@ ".": "./dist/react-dom.js" }, "scripts": { - "build": "node ./build.js", + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch", + "build:pre:local-dependencies": "../../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../../scripts/npm/build-watch.sh", + "build:run": "node ./build.js && touch ./package.json", "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", @@ -17,10 +21,7 @@ "precommit": "npm run precommit:eslint -- src && npm run precommit:typecheck", "precommit:eslint": "eslint --report-unused-disable-directives --max-warnings 0", "precommit:typecheck": "tsc --project ./src --emitDeclarationOnly false --esModuleInterop true --noEmit --pretty false", - "start": "npm run build -- --watch" - }, - "localDependencies": { - "@msinternal/object-assign": "development" + "start": "../../../scripts/npm/notify-build.sh \"./src/\" \"../object-assign/package.json\"" }, "pinDependencies": { "react-dom": [ @@ -28,12 +29,15 @@ "Bump React manually" ] }, - "dependencies": { - "react-dom": "16.8.6" + "localDependencies": { + "@msinternal/object-assign": "development" }, "devDependencies": { "@msinternal/object-assign": "0.0.0-0", "@types/react-dom": "^16.9.25", "esbuild": "^0.25.10" + }, + "dependencies": { + "react-dom": "16.8.6" } } diff --git a/packages/repack/react-dom@umd/package.json b/packages/repack/react-dom@umd/package.json index 02d93405fb..95ce9ab565 100644 --- a/packages/repack/react-dom@umd/package.json +++ b/packages/repack/react-dom@umd/package.json @@ -9,7 +9,11 @@ ".": "./dist/react-dom.js" }, "scripts": { - "build": "esbuild --bundle=true --chunk-names=react-dom/[name]-[hash] --entry-names=[dir]/[name] --format=esm --outdir=dist --platform=browser --splitting --sourcemap react-dom=./src/index.js", + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch", + "build:pre:local-dependencies": "../../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../../scripts/npm/build-watch.sh", + "build:run": "esbuild --bundle=true --chunk-names=react-dom/[name]-[hash] --entry-names=[dir]/[name] --format=esm --outdir=dist --platform=browser --splitting --sourcemap react-dom=./src/index.js && touch ./package.json", "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", @@ -17,15 +21,11 @@ "precommit": "npm run precommit:eslint -- src && npm run precommit:typecheck", "precommit:eslint": "eslint --report-unused-disable-directives --max-warnings 0", "precommit:typecheck": "tsc --project ./src --emitDeclarationOnly false --esModuleInterop true --noEmit --pretty false", - "start": "npm run build -- --watch --watch-delay=200" - }, - "localDependencies": { - "@msinternal/object-assign": "development" + "start": "../../../scripts/npm/notify-build.sh \"./src/\"" }, "pinDependencies": {}, + "localDependencies": {}, "devDependencies": { "esbuild": "^0.25.10" - }, - "main": "index.js", - "license": "ISC" + } } diff --git a/packages/repack/react-is/package.json b/packages/repack/react-is/package.json index d0b61e620b..648d4822dd 100644 --- a/packages/repack/react-is/package.json +++ b/packages/repack/react-is/package.json @@ -9,7 +9,11 @@ ".": "./dist/index.js" }, "scripts": { - "build": "esbuild --bundle=true --format=esm --outdir=dist --platform=browser --splitting --sourcemap ./src/index.js", + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch", + "build:pre:local-dependencies": "../../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../../scripts/npm/build-watch.sh", + "build:run": "esbuild --bundle=true --format=esm --outdir=dist --platform=browser --splitting --sourcemap ./src/index.js && touch ./package.json", "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", @@ -17,20 +21,20 @@ "precommit": "npm run precommit:eslint -- src && npm run precommit:typecheck", "precommit:eslint": "eslint --report-unused-disable-directives --max-warnings 0", "precommit:typecheck": "tsc --project ./src --emitDeclarationOnly false --esModuleInterop true --noEmit --pretty false", - "start": "npm run build -- --watch --watch-delay=200" + "start": "../../../scripts/npm/notify-build.sh \"./src/\"" }, - "localDependencies": {}, "pinDependencies": { "react-is": [ "16.13.1", "Bump with React" ] }, - "dependencies": { - "react-is": "16.13.1" - }, + "localDependencies": {}, "devDependencies": { "@types/react-is": "^16.7.5", "esbuild": "^0.25.10" + }, + "dependencies": { + "react-is": "16.13.1" } } diff --git a/packages/repack/react/package.json b/packages/repack/react/package.json index 121bc9f6fc..9de616e0e0 100644 --- a/packages/repack/react/package.json +++ b/packages/repack/react/package.json @@ -11,7 +11,11 @@ "./jsx-runtime": "./dist/react/jsx-runtime.js" }, "scripts": { - "build": "esbuild --bundle=true --chunk-names=react/[name]-[hash] --entry-names=[dir]/[name] --format=esm --outdir=dist --platform=browser --splitting --sourcemap react=./src/index.js react/jsx-dev-runtime=./src/jsx-dev-runtime.ts react/jsx-runtime=./src/jsx-runtime.ts", + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch", + "build:pre:local-dependencies": "../../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../../scripts/npm/build-watch.sh", + "build:run": "esbuild --bundle=true --chunk-names=react/[name]-[hash] --entry-names=[dir]/[name] --format=esm --outdir=dist --platform=browser --splitting --sourcemap react=./src/index.js react/jsx-dev-runtime=./src/jsx-dev-runtime.ts react/jsx-runtime=./src/jsx-runtime.ts && touch ./package.json", "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", @@ -19,10 +23,7 @@ "precommit": "npm run precommit:eslint -- src && npm run precommit:typecheck", "precommit:eslint": "eslint --report-unused-disable-directives --max-warnings 0", "precommit:typecheck": "tsc --project ./src --emitDeclarationOnly false --esModuleInterop true --noEmit --pretty false", - "start": "npm run build -- --watch --watch-delay=200" - }, - "localDependencies": { - "@msinternal/object-assign": "development" + "start": "../../../scripts/npm/notify-build.sh \"./src/\"" }, "pinDependencies": { "@types/react": [ @@ -34,11 +35,12 @@ "Bump React manually" ] }, - "dependencies": { - "react": "18.3.1" - }, + "localDependencies": {}, "devDependencies": { "@types/react": "^18.3.24", "esbuild": "^0.25.10" + }, + "dependencies": { + "react": "18.3.1" } } diff --git a/packages/repack/react@baseline/package.json b/packages/repack/react@baseline/package.json index 6f4e3b22c0..db586812b8 100644 --- a/packages/repack/react@baseline/package.json +++ b/packages/repack/react@baseline/package.json @@ -9,7 +9,11 @@ ".": "./dist/react.js" }, "scripts": { - "build": "esbuild --alias:object-assign=@msinternal/object-assign --bundle=true --chunk-names=react/[name]-[hash] --entry-names=[dir]/[name] --format=esm --outdir=dist --platform=browser --splitting --sourcemap react=./src/index.js", + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch", + "build:pre:local-dependencies": "../../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../../scripts/npm/build-watch.sh", + "build:run": "esbuild --alias:object-assign=@msinternal/object-assign --bundle=true --chunk-names=react/[name]-[hash] --entry-names=[dir]/[name] --format=esm --outdir=dist --platform=browser --splitting --sourcemap react=./src/index.js && touch ./package.json", "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", @@ -17,10 +21,7 @@ "precommit": "npm run precommit:eslint -- src && npm run precommit:typecheck", "precommit:eslint": "eslint --report-unused-disable-directives --max-warnings 0", "precommit:typecheck": "tsc --project ./src --emitDeclarationOnly false --esModuleInterop true --noEmit --pretty false", - "start": "npm run build -- --watch --watch-delay=200" - }, - "localDependencies": { - "@msinternal/object-assign": "development" + "start": "../../../scripts/npm/notify-build.sh \"./src/\" \"../object-assign/package.json\"" }, "pinDependencies": { "react": [ @@ -28,12 +29,15 @@ "Bump React manually" ] }, - "dependencies": { - "react": "16.8.6" + "localDependencies": { + "@msinternal/object-assign": "development" }, "devDependencies": { "@msinternal/object-assign": "0.0.0-0", "@types/react": "^16.14.65", "esbuild": "^0.25.10" + }, + "dependencies": { + "react": "16.8.6" } } diff --git a/packages/repack/react@umd/package.json b/packages/repack/react@umd/package.json index f70ba08e4b..d400956182 100644 --- a/packages/repack/react@umd/package.json +++ b/packages/repack/react@umd/package.json @@ -9,7 +9,11 @@ ".": "./dist/react.js" }, "scripts": { - "build": "esbuild --bundle=true --chunk-names=react/[name]-[hash] --entry-names=[dir]/[name] --format=esm --outdir=dist --platform=browser --splitting --sourcemap react=./src/index.js", + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch", + "build:pre:local-dependencies": "../../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../../scripts/npm/build-watch.sh", + "build:run": "esbuild --bundle=true --chunk-names=react/[name]-[hash] --entry-names=[dir]/[name] --format=esm --outdir=dist --platform=browser --splitting --sourcemap react=./src/index.js && touch ./package.json", "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", @@ -17,12 +21,10 @@ "precommit": "npm run precommit:eslint -- src && npm run precommit:typecheck", "precommit:eslint": "eslint --report-unused-disable-directives --max-warnings 0", "precommit:typecheck": "tsc --project ./src --emitDeclarationOnly false --esModuleInterop true --noEmit --pretty false", - "start": "npm run build -- --watch --watch-delay=200" - }, - "localDependencies": { - "@msinternal/object-assign": "development" + "start": "../../../scripts/npm/notify-build.sh \"./src/\"" }, "pinDependencies": {}, + "localDependencies": {}, "devDependencies": { "esbuild": "^0.25.10" } diff --git a/packages/styles/package.json b/packages/styles/package.json index 81938f7fb1..633622c925 100644 --- a/packages/styles/package.json +++ b/packages/styles/package.json @@ -56,7 +56,11 @@ ], "homepage": "https://github.com/microsoft/BotFramework-WebChat/tree/main/packages/styles#readme", "scripts": { - "build": "tsup", + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch", + "build:pre:local-dependencies": "../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../scripts/npm/build-watch.sh", + "build:run": "tsup", "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", @@ -66,11 +70,7 @@ "precommit:eslint": "../../node_modules/.bin/eslint --report-unused-disable-directives --max-warnings 0", "precommit:typecheck": "tsc --project ./src --emitDeclarationOnly false --esModuleInterop true --noEmit --pretty false", "preversion": "cat package.json | jq '(.localDependencies // {} | to_entries | map([if .value == \"production\" then \"dependencies\" else \"devDependencies\" end, .key])) as $P | delpaths($P)' > package-temp.json && mv package-temp.json package.json", - "start": "npm run build -- --watch" - }, - "localDependencies": { - "@msinternal/botframework-webchat-base": "development", - "@msinternal/botframework-webchat-tsconfig": "development" + "start": "../../scripts/npm/notify-build.sh \"./src/\" \"../base/package.json\" \"../tsconfig/package.json\"" }, "pinDependencies": { "typescript": [ @@ -78,6 +78,10 @@ "@typescript-eslint/parser@8.38.0 does not support typescript@5.9.2 yet" ] }, + "localDependencies": { + "@msinternal/botframework-webchat-base": "development", + "@msinternal/botframework-webchat-tsconfig": "development" + }, "devDependencies": { "@jridgewell/sourcemap-codec": "^1.5.4", "@msinternal/botframework-webchat-base": "0.0.0-0", diff --git a/packages/styles/tsup.config.ts b/packages/styles/tsup.config.ts index 3498f2c4d7..f6f428b43f 100644 --- a/packages/styles/tsup.config.ts +++ b/packages/styles/tsup.config.ts @@ -2,23 +2,20 @@ import { defineConfig } from 'tsup'; import { applyConfig } from '../../tsup.base.config'; -// TODO: [P1] Compute this automatically. -const DEPENDENT_PATHS = ['component/src/index.ts', 'fluent-theme/src/index.ts']; - const commonConfig = applyConfig(config => ({ ...config, entry: { 'botframework-webchat-styles': './src/index.ts', 'botframework-webchat-styles.build': './src/build/index.ts', 'botframework-webchat-styles.react': './src/react/index.ts' - }, - onSuccess: `touch ${DEPENDENT_PATHS.map(path => `../${path}`).join(' ')}` + } })); export default defineConfig([ { ...commonConfig, - format: 'esm' + format: 'esm', + onSuccess: 'touch ./package.json' }, { ...commonConfig, diff --git a/packages/support/cldr-data-downloader/package.json b/packages/support/cldr-data-downloader/package.json index 347dc5bb6c..91b85e9d23 100644 --- a/packages/support/cldr-data-downloader/package.json +++ b/packages/support/cldr-data-downloader/package.json @@ -39,6 +39,11 @@ "cldr-data-downloader": "./bin/download.sh" }, "scripts": { + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch", + "build:pre:local-dependencies": "../../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../../scripts/npm/build-watch.sh", + "build:run": "echo This package does not need to be built.", "bump": "npm run bump:dev && npm run bump:prod && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", @@ -46,8 +51,10 @@ "postversion": "cat package.json | jq '.version as $V | (.localDependencies // {} | with_entries(select(.value == \"production\") | { key: .key, value: $V })) as $L1 | (.localDependencies // {} | with_entries(select(.value == \"development\") | { key: .key, value: $V })) as $L2 | ((.dependencies // {}) + $L1 | to_entries | sort_by(.key) | from_entries) as $D1 | ((.devDependencies // {}) + $L2 | to_entries | sort_by(.key) | from_entries) as $D2 | . + { dependencies: $D1, devDependencies: $D2 }' > package-temp.json && mv package-temp.json package.json", "precommit": "npm run precommit:eslint -- src", "precommit:eslint": "node ../../../node_modules/eslint/bin/eslint.js --report-unused-disable-directives --max-warnings 0", - "preversion": "cat package.json | jq '(.localDependencies // {} | to_entries | map([if .value == \"production\" then \"dependencies\" else \"devDependencies\" end, .key])) as $P | delpaths($P)' > package-temp.json && mv package-temp.json package.json" + "preversion": "cat package.json | jq '(.localDependencies // {} | to_entries | map([if .value == \"production\" then \"dependencies\" else \"devDependencies\" end, .key])) as $P | delpaths($P)' > package-temp.json && mv package-temp.json package.json", + "start": "../../../scripts/npm/notify-build.sh \"./src/\"" }, + "pinDependencies": {}, "localDependencies": {}, "dependencies": { "adm-zip": "0.5.16", diff --git a/packages/support/cldr-data/package.json b/packages/support/cldr-data/package.json index 96fb9b63e0..724025ab59 100644 --- a/packages/support/cldr-data/package.json +++ b/packages/support/cldr-data/package.json @@ -39,7 +39,11 @@ "urls.json" ], "scripts": { - "build": "node ./src/install.mjs && node ./src/patch.mjs", + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch", + "build:pre:local-dependencies": "../../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../../scripts/npm/build-watch.sh", + "build:run": "node ./src/install.mjs && node ./src/patch.mjs && touch ./package.json", "bump": "npm run bump:dev && npm run bump:prod && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", @@ -47,10 +51,8 @@ "postversion": "cat package.json | jq '.version as $V | (.localDependencies // {} | with_entries(select(.value == \"production\") | { key: .key, value: $V })) as $L1 | (.localDependencies // {} | with_entries(select(.value == \"development\") | { key: .key, value: $V })) as $L2 | ((.dependencies // {}) + $L1 | to_entries | sort_by(.key) | from_entries) as $D1 | ((.devDependencies // {}) + $L2 | to_entries | sort_by(.key) | from_entries) as $D2 | . + { dependencies: $D1, devDependencies: $D2 }' > package-temp.json && mv package-temp.json package.json", "precommit": "npm run precommit:eslint -- src", "precommit:eslint": "node ../../../node_modules/eslint/bin/eslint.js --report-unused-disable-directives --max-warnings 0", - "preversion": "cat package.json | jq '(.localDependencies // {} | to_entries | map([if .value == \"production\" then \"dependencies\" else \"devDependencies\" end, .key])) as $P | delpaths($P)' > package-temp.json && mv package-temp.json package.json" - }, - "localDependencies": { - "@msinternal/botframework-webchat-cldr-data-downloader": "production" + "preversion": "cat package.json | jq '(.localDependencies // {} | to_entries | map([if .value == \"production\" then \"dependencies\" else \"devDependencies\" end, .key])) as $P | delpaths($P)' > package-temp.json && mv package-temp.json package.json", + "start": "../../../scripts/npm/notify-build.sh \"./src/\" \"../cldr-data-downloader/package.json\"" }, "pinDependencies": { "glob": [ @@ -58,6 +60,9 @@ "glob@10 updated the entrypoint, need funding to upgrade" ] }, + "localDependencies": { + "@msinternal/botframework-webchat-cldr-data-downloader": "production" + }, "dependencies": { "@msinternal/botframework-webchat-cldr-data-downloader": "0.3.5-0", "glob": "8.1.0", diff --git a/packages/test/dev-server/package.json b/packages/test/dev-server/package.json index 66054cba3c..a9a043e439 100644 --- a/packages/test/dev-server/package.json +++ b/packages/test/dev-server/package.json @@ -6,7 +6,12 @@ "author": "Microsoft Corporation", "license": "MIT", "private": true, + "type": "module", "scripts": { + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:pre": "npm run build:pre:local-dependencies", + "build:pre:local-dependencies": "../../../scripts/npm/build-local-dependencies.sh", + "build:run": "echo This project does not need to be built.", "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", @@ -14,9 +19,8 @@ "preversion": "cat package.json | jq '(.localDependencies // {} | to_entries | map([if .value == \"production\" then \"dependencies\" else \"devDependencies\" end, .key])) as $P | delpaths($P)' > package-temp.json && mv package-temp.json package.json", "start": "node ." }, - "type": "module", - "localDependencies": {}, "pinDependencies": {}, + "localDependencies": {}, "devDependencies": { "compression": "^1.8.1", "express": "^5.1.0", diff --git a/packages/test/harness/package.json b/packages/test/harness/package.json index 7ed228286e..3afb2510a3 100644 --- a/packages/test/harness/package.json +++ b/packages/test/harness/package.json @@ -12,10 +12,14 @@ "node": ">= 14.0.0" }, "scripts": { - "build": "npm run build:esbuild:esm && npm run build:esbuild:iife", - "build:esbuild:base": "esbuild test-harness=./src/browser/index.js --bundle --define:define=undefined --define:process.env.CI=undefined --minify --outdir=dist --sourcemap --target=chrome100", - "build:esbuild:esm": "npm run build:esbuild:base -- --format=esm --out-extension:.js=.mjs", - "build:esbuild:iife": "npm run build:esbuild:base -- --format=iife", + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch", + "build:pre:local-dependencies": "../../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../../scripts/npm/build-watch.sh", + "build:run": "concurrently --prefix-colors \"auto\" \"npm:build:run:esbuild:esm\" \"npm:build:run:esbuild:iife\" && touch ./package.json", + "build:run:esbuild:base": "esbuild test-harness=./src/browser/index.js --bundle --define:define=undefined --define:process.env.CI=undefined --minify --outdir=dist --sourcemap --target=chrome100", + "build:run:esbuild:esm": "npm run build:run:esbuild:base -- --format=esm --out-extension:.js=.mjs", + "build:run:esbuild:iife": "npm run build:run:esbuild:base -- --format=iife", "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", @@ -24,12 +28,9 @@ "precommit": "npm run precommit:eslint -- src", "precommit:eslint": "../../../node_modules/.bin/eslint --report-unused-disable-directives --max-warnings 0", "preversion": "cat package.json | jq '(.localDependencies // {} | to_entries | map([if .value == \"production\" then \"dependencies\" else \"devDependencies\" end, .key])) as $P | delpaths($P)' > package-temp.json && mv package-temp.json package.json", - "start": "concurrently --kill-others --prefix-colors \"auto\" \"npm:start:*\"", - "start:esbuild:esm": "npm run build:esbuild:esm -- --watch --watch-delay=200", - "start:esbuild:iife": "npm run build:esbuild:iife -- --watch --watch-delay=200", + "start": "../../../scripts/npm/notify-build.sh \"./src/\"", "test": "node node_modules/.bin/jest --maxWorkers=4" }, - "localDependencies": {}, "pinDependencies": { "expect": [ "25", @@ -52,16 +53,7 @@ "strip-ansi@7 dropped support of CommonJS" ] }, - "dependencies": { - "abort-controller": "3.0.0", - "axe-core": "4.10.3", - "core-js": "3.44.0", - "core-js-pure": "3.44.0", - "event-target-shim": "6.0.2", - "expect": "25.5.0", - "lolex": "6.0.0", - "math-random": "2.0.1" - }, + "localDependencies": {}, "devDependencies": { "concurrently": "^9.2.0", "esbuild": "^0.25.8", @@ -75,5 +67,15 @@ "selenium-webdriver": "^4.34.0", "serve": "^14.2.4", "strip-ansi": "^6.0.1" + }, + "dependencies": { + "abort-controller": "3.0.0", + "axe-core": "4.10.3", + "core-js": "3.44.0", + "core-js-pure": "3.44.0", + "event-target-shim": "6.0.2", + "expect": "25.5.0", + "lolex": "6.0.0", + "math-random": "2.0.1" } } diff --git a/packages/test/page-object/package.json b/packages/test/page-object/package.json index 81e83a1b0e..f49c18caa9 100644 --- a/packages/test/page-object/package.json +++ b/packages/test/page-object/package.json @@ -9,10 +9,14 @@ "node": ">= 14.0.0" }, "scripts": { - "build": "npm run build:esbuild:esm && npm run build:esbuild:iife", - "build:esbuild:base": "esbuild test-page-object=./src/index.js --bundle --define:define=undefined --loader:.js=jsx --minify --outdir=dist --sourcemap --target=chrome100", - "build:esbuild:esm": "npm run build:esbuild:base -- --format=esm --out-extension:.js=.mjs", - "build:esbuild:iife": "npm run build:esbuild:base -- --format=iife", + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch", + "build:pre:local-dependencies": "../../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../../scripts/npm/build-watch.sh", + "build:run": "concurrently --prefix-colors \"auto\" \"npm:build:run:esbuild:esm\" \"npm:build:run:esbuild:iife\" && touch ./package.json", + "build:run:esbuild:base": "esbuild test-page-object=./src/index.js --bundle --define:define=undefined --loader:.js=jsx --minify --outdir=dist --sourcemap --target=chrome100", + "build:run:esbuild:esm": "npm run build:run:esbuild:base -- --format=esm --out-extension:.js=.mjs", + "build:run:esbuild:iife": "npm run build:run:esbuild:base -- --format=iife", "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", @@ -21,17 +25,19 @@ "precommit": "npm run precommit:eslint -- src", "precommit:eslint": "../../../node_modules/.bin/eslint --report-unused-disable-directives --max-warnings 0", "preversion": "cat package.json | jq '(.localDependencies // {} | to_entries | map([if .value == \"production\" then \"dependencies\" else \"devDependencies\" end, .key])) as $P | delpaths($P)' > package-temp.json && mv package-temp.json package.json", - "start": "concurrently --kill-others --prefix-colors \"auto\" \"npm:start:*\"", - "start:esbuild:esm": "npm run build:esbuild:esm -- --watch --watch-delay=200", - "start:esbuild:iife": "npm run build:esbuild:iife -- --watch --watch-delay=200" + "start": "../../../scripts/npm/notify-build.sh \"./src/\"" }, - "localDependencies": {}, "pinDependencies": { "jest-mock": [ "28", ">= 29 will pull in Node.js dependencies" ] }, + "localDependencies": {}, + "devDependencies": { + "concurrently": "^9.2.0", + "esbuild": "^0.25.8" + }, "dependencies": { "base64-arraybuffer": "1.0.2", "bent": "7.3.12", @@ -45,9 +51,5 @@ "microsoft-cognitiveservices-speech-sdk": "1.17.0", "prop-types": "15.8.1", "simple-update-in": "2.2.0" - }, - "devDependencies": { - "concurrently": "^9.2.0", - "esbuild": "^0.25.8" } } diff --git a/packages/test/web-server/package.json b/packages/test/web-server/package.json index 9d7718bf23..715ef9061d 100644 --- a/packages/test/web-server/package.json +++ b/packages/test/web-server/package.json @@ -7,7 +7,11 @@ "license": "MIT", "main": "./dist/index.js", "scripts": { - "build": "tsup", + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch", + "build:pre:local-dependencies": "../../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../../scripts/npm/build-watch.sh", + "build:run": "tsup", "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", @@ -16,21 +20,21 @@ "precommit": "npm run precommit:eslint -- src", "precommit:eslint": "../../../node_modules/.bin/eslint --report-unused-disable-directives --max-warnings 0", "preversion": "cat package.json | jq '(.localDependencies // {} | to_entries | map([if .value == \"production\" then \"dependencies\" else \"devDependencies\" end, .key])) as $P | delpaths($P)' > package-temp.json && mv package-temp.json package.json", - "start": "npm run build -- --onSuccess=\"node .\" --watch" + "start": "../../../scripts/npm/notify-build.sh \"./src/\"" }, - "localDependencies": {}, "pinDependencies": { "typescript": [ "~5.8.3", "@typescript-eslint/parser@8.38.0 does not support typescript@5.9.2 yet" ] }, - "dependencies": { - "selfsigned": "3.0.1", - "serve-handler": "6.1.6" - }, + "localDependencies": {}, "devDependencies": { "tsup": "^8.5.0", "typescript": "~5.8.3" + }, + "dependencies": { + "selfsigned": "3.0.1", + "serve-handler": "6.1.6" } } diff --git a/packages/test/web-server/tsup.config.ts b/packages/test/web-server/tsup.config.ts index fe084a9c43..4143bcce57 100644 --- a/packages/test/web-server/tsup.config.ts +++ b/packages/test/web-server/tsup.config.ts @@ -6,5 +6,6 @@ export default defineConfig({ }, format: 'cjs', platform: 'node', - noExternal: ['selfsigned', 'serve-handler'] + noExternal: ['selfsigned', 'serve-handler'], + onSuccess: 'touch ./package.json' }); diff --git a/packages/tsconfig/package.json b/packages/tsconfig/package.json index 752e7a0ebd..bf95fe6ca8 100644 --- a/packages/tsconfig/package.json +++ b/packages/tsconfig/package.json @@ -22,16 +22,22 @@ ], "homepage": "https://github.com/microsoft/BotFramework-WebChat/tree/main/packages/tsconfig#readme", "scripts": { + "build": "npm run --if-present build:pre && npm run build:run && npm run --if-present build:post", + "build:pre": "npm run build:pre:local-dependencies && npm run build:pre:watch", + "build:pre:local-dependencies": "../../scripts/npm/build-local-dependencies.sh", + "build:pre:watch": "../../scripts/npm/build-watch.sh", + "build:run": "echo This project does not need to be built.", "bump": "npm run bump:prod && npm run bump:dev && (npm audit fix || exit 0)", "bump:dev": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.devDependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install $PACKAGES_TO_BUMP || true", "bump:prod": "PACKAGES_TO_BUMP=$(cat package.json | jq -r '(.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L | (.dependencies // {}) | to_entries | map(select(.key as $K | $L | contains([$K]) | not)) | map(.key + \"@\" + ($P[.key] // [\"latest\"])[0]) | join(\" \")') && [ ! -z \"$PACKAGES_TO_BUMP\" ] && npm install --save-exact $PACKAGES_TO_BUMP || true", "eslint": "npm run precommit", "postversion": "cat package.json | jq '.version as $V | (.localDependencies // {} | with_entries(select(.value == \"production\") | { key: .key, value: $V })) as $L1 | (.localDependencies // {} | with_entries(select(.value == \"development\") | { key: .key, value: $V })) as $L2 | ((.dependencies // {}) + $L1 | to_entries | sort_by(.key) | from_entries) as $D1 | ((.devDependencies // {}) + $L2 | to_entries | sort_by(.key) | from_entries) as $D2 | . + { dependencies: $D1, devDependencies: $D2 }' > package-temp.json && mv package-temp.json package.json", "precommit": "../../node_modules/.bin/eslint --report-unused-disable-directives --max-warnings 0", - "preversion": "cat package.json | jq '(.localDependencies // {} | to_entries | map([if .value == \"production\" then \"dependencies\" else \"devDependencies\" end, .key])) as $P | delpaths($P)' > package-temp.json && mv package-temp.json package.json" + "preversion": "cat package.json | jq '(.localDependencies // {} | to_entries | map([if .value == \"production\" then \"dependencies\" else \"devDependencies\" end, .key])) as $P | delpaths($P)' > package-temp.json && mv package-temp.json package.json", + "start": "../../scripts/npm/notify-build.sh \"./src/\"" }, - "localDependencies": {}, "pinDependencies": {}, + "localDependencies": {}, "devDependencies": { "@tsconfig/strictest": "^2.0.5", "typescript-plugin-css-modules": "^5.2.0" diff --git a/scripts/buildWatch.mjs b/scripts/buildWatch.mjs new file mode 100644 index 0000000000..d93dfb786c --- /dev/null +++ b/scripts/buildWatch.mjs @@ -0,0 +1,44 @@ +import { relative, resolve } from 'path'; +import { readPackageUp } from 'read-package-up'; +import { readPackage } from 'read-pkg'; + +const cwd = process.cwd(); +const currentPackageJSON = await readPackage(); + +const { + packageJson: { workspaces }, + path: rootPackageJSONPath +} = await readPackageUp({ cwd: resolve(cwd, '../') }); + +const root = resolve(rootPackageJSONPath, '../'); + +const watchPaths = ['./src/']; + +for (const [packageName] of Object.entries(currentPackageJSON.localDependencies || {})) { + for (const workspace of workspaces) { + const packageJSON = await readPackage({ cwd: resolve(root, workspace) }); + + if (packageJSON.name === packageName) { + watchPaths.push(relative(cwd, resolve(root, workspace, 'package.json'))); + } + } +} + +// "nodemon" will pick up every file change, trigger build too many times. +// console.log( +// `node --watch ${watchPaths.map(path => `--watch-path "${path}"`).join(' ')} --watch-preserve-output $(which npm) run build:run` +// ); + +// "inotifywait without --monitor" will not pick up changes while it is building +// console.log( +// `while true; do inotifywait ${['close_write', 'create', 'delete', 'modify', 'move'].map(event => `--event ${event}`).join(' ')} --recursive ${watchPaths.map(path => `"${path}"`).join(' ')}; $(which npm) run build:run; done` +// ); + +// "inotifywait with --monitor" will pick up every file change, trigger build too many times. +// console.log( +// `inotifywait ${['close_write', 'create', 'delete', 'modify', 'move'].map(event => `--event ${event}`).join(' ')} --monitor --recursive ${watchPaths.map(path => `"${path}"`).join(' ')} | while read changed; do $(which npm) run build:run; done` +// ); + +console.log( + `${relative(cwd, resolve(root, './scripts/npm/notify-build.sh'))} ${watchPaths.map(path => `"${path}"`).join(' ')}` +); diff --git a/scripts/npm/build-local-dependencies.sh b/scripts/npm/build-local-dependencies.sh new file mode 100755 index 0000000000..4b0e87b4be --- /dev/null +++ b/scripts/npm/build-local-dependencies.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +set -euo pipefail + +cat package.json | jq -r ' + .localDependencies = + ( + ( + .dependencies // {} + | to_entries + | map(select((.key | startswith("@msinternal/")) or .value == "0.0.0-0" or .value == "^0.0.0-0") | .value = "production") + | from_entries + ) + + ( + .devDependencies // {} + | to_entries + | map(select((.key | startswith("@msinternal/")) or .value == "0.0.0-0" or .value == "^0.0.0-0") | .value = "development") + | from_entries + ) + + ( + .peerDependencies // {} + | to_entries + | map(select((.key | startswith("@msinternal/")) or .value == "0.0.0-0" or .value == "^0.0.0-0") | .value = "peer") + | from_entries + ) + | to_entries + | sort_by(.key) + | from_entries + ) + | . +' > package-temp.json && mv package-temp.json package.json diff --git a/scripts/npm/build-watch.sh b/scripts/npm/build-watch.sh new file mode 100755 index 0000000000..93332629cf --- /dev/null +++ b/scripts/npm/build-watch.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +WATCH_SCRIPT=$(node "$SCRIPT_DIR/../buildWatch.mjs") + +cat package.json | jq --arg W "$WATCH_SCRIPT" -r ' + .scripts.start = $W + | . +' > package-temp.json && mv package-temp.json package.json diff --git a/scripts/npm/bump-dev.sh b/scripts/npm/bump-dev.sh index 3ba5dd09d0..7e8a86c082 100755 --- a/scripts/npm/bump-dev.sh +++ b/scripts/npm/bump-dev.sh @@ -1,5 +1,7 @@ #!/bin/bash +set -euo pipefail + PACKAGES_TO_BUMP=$(cat package.json | jq -r ' (.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L diff --git a/scripts/npm/bump-peer.sh b/scripts/npm/bump-peer.sh index d71d7a8e6c..c03b9743ac 100644 --- a/scripts/npm/bump-peer.sh +++ b/scripts/npm/bump-peer.sh @@ -1,5 +1,7 @@ #!/bin/bash +set -euo pipefail + PACKAGES_TO_BUMP=$(cat package.json | jq -r ' (.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L diff --git a/scripts/npm/bump-prod.sh b/scripts/npm/bump-prod.sh index 02eed3f35e..fcd3180ea6 100755 --- a/scripts/npm/bump-prod.sh +++ b/scripts/npm/bump-prod.sh @@ -1,5 +1,7 @@ #!/bin/bash +set -euo pipefail + PACKAGES_TO_BUMP=$(cat package.json | jq -r ' (.pinDependencies // {}) as $P | (.localDependencies // {} | keys) as $L diff --git a/scripts/npm/notify-build.sh b/scripts/npm/notify-build.sh new file mode 100755 index 0000000000..f8f3222bfa --- /dev/null +++ b/scripts/npm/notify-build.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +if [ "$#" -lt 1 ]; then + echo "Usage: $0 [directory_or_file2 ...]" + exit 1 +fi + +WATCH_DIRS=("$@") +EVENT_QUEUE="/tmp/inotify_event_queue_$$" + +# Create or clear the event queue +> "$EVENT_QUEUE" + +# Kill all background processes on exit +trap 'kill $(jobs -p)' EXIT + +# Monitor all directories/files and write events to the queue +# "close_write" is for touch +inotifywait --event close_write,create,delete,modify,move --monitor --outfile "$EVENT_QUEUE" --recursive ../tsconfig/package.json "${WATCH_DIRS[@]}" & + +while true; do + if [ -s "$EVENT_QUEUE" ]; then + # Clear the queue and execute the command + + echo "📫 Change detected: $(head -n 1 "$EVENT_QUEUE")" + + # VSCode fire MODIFY + CLOSE_WRITE on save. + # Group all changes within 100 ms, so we don't trigger it twice. + sleep 0.1s + + # Clear the event queue + > "$EVENT_QUEUE" + + echo "🚥 Build started." + + START_TIME=$(date +%s.%N) + npm run build:run + END_TIME=$(date +%s.%N) + + DURATION=$(awk "BEGIN {printf \"%.2f\", $END_TIME - $START_TIME}") + + # Display duration in light purple color + echo -e "🏁 Build completed in \033[1;35m${DURATION}\033[0m seconds." + else + inotifywait --event modify --quiet "$EVENT_QUEUE" >/dev/null 2>&1 + fi +done diff --git a/scripts/npm/postversion.sh b/scripts/npm/postversion.sh index 3f344447ec..8150a2b529 100755 --- a/scripts/npm/postversion.sh +++ b/scripts/npm/postversion.sh @@ -1,4 +1,7 @@ #!/bin/bash + +set -euo pipefail + cat package.json | jq ' .version as $V | ( diff --git a/scripts/npm/preversion.sh b/scripts/npm/preversion.sh index 7d63a6df96..f8a1d0d734 100755 --- a/scripts/npm/preversion.sh +++ b/scripts/npm/preversion.sh @@ -1,6 +1,7 @@ #!/bin/bash -# TODO: [P1] Instead of simply stripping out "localDependencies", it should: -# 1. Look at what has version "0.0.0-0", save them to "localDependencies" and mark it as "production"/"development"/"peer" + +set -euo pipefail + cat package.json | jq ' ( .localDependencies // {} diff --git a/tsup.base.config.ts b/tsup.base.config.ts index 929cf13d85..588d7e5742 100644 --- a/tsup.base.config.ts +++ b/tsup.base.config.ts @@ -22,22 +22,22 @@ const disablePlugin = (pluginName: string): EsbuildPlugin => ({ function applyConfig( overrideOptions: ( - options: Omit & { + options: Omit & { define: Record; esbuildPlugins: EsbuildPlugin[]; target: Target[]; } - ) => Omit & { + ) => Options & { define: Record; esbuildPlugins: EsbuildPlugin[]; target: Target[]; - } & { outDirWithTemp?: [`./${string}/`, `./${string}/`] | undefined } + } ): Options & { define: Record; esbuildPlugins: EsbuildPlugin[]; target: Target[]; } { - const nextOptions = overrideOptions({ + return overrideOptions({ define: { // TSD does not support define, thus we need to use `globalThis.*` instead. 'globalThis.WEB_CHAT_BUILD_INFO_BUILD_TOOL': '"tsup"', @@ -112,40 +112,6 @@ function applyConfig( splitting: true, target: ['chrome100', 'firefox100', 'safari15'] satisfies Target[] }); - - // tsup@8.5.0 do not write to output atomically. - // Thus, when building in parallel, some of the files will be emptied. - - // Writing output to /dist.tmp/ and copy everything back to /dist/ for better atomicity. - - // "onSuccess" runs in parallel of DTS, we need to wait until *.d.ts are emitted. - // Filed a bug, https://github.com/egoist/tsup/issues/1363. - - // All instances of tsup will try to copy at the same time and could fail with "cp: cannot create regular file './dist/...': File exists". - // We can have multiple config writing to their own folder and copy-merge. But then each config will own their version of `onSuccess`, could be messy. - - const [outDir = './dist/', tmpDir = './dist.tmp/'] = nextOptions.outDirWithTemp || []; - - // TODO: [P1] This merge is not elegant, we should move to Promise. - const rectifiedOptions = { - ...nextOptions, - onSuccess: nextOptions.dts - ? [ - `while [ -z "$(find ${tmpDir} \\( -name '*.d.ts' -o -name '*.d.mts' \\) -print -quit)" ]; do sleep 0.2; done; mkdir -p ${outDir}; sleep 0.5; until cp --recursive ${tmpDir}/* ${outDir} 2>/dev/null; do sleep 0.5; done`, - nextOptions.onSuccess - ] - .filter(Boolean) - .join(' && ') - : [ - `mkdir -p ${outDir}; sleep 0.5; until cp --recursive ${tmpDir}/* ${outDir} 2>/dev/null; do sleep 0.5; done`, - nextOptions.onSuccess - ] - .filter(Boolean) - .join(' && '), - outDir: tmpDir - }; - - return rectifiedOptions; } export { applyConfig }; From 89473084dd04591afe0c1906c784f88b07752297 Mon Sep 17 00:00:00 2001 From: William Wong Date: Thu, 16 Oct 2025 09:58:19 +0000 Subject: [PATCH 12/23] Ignore build error --- scripts/npm/notify-build.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/npm/notify-build.sh b/scripts/npm/notify-build.sh index f8f3222bfa..3c2615f1c9 100755 --- a/scripts/npm/notify-build.sh +++ b/scripts/npm/notify-build.sh @@ -1,5 +1,7 @@ #!/bin/bash +set -euo pipefail + if [ "$#" -lt 1 ]; then echo "Usage: $0 [directory_or_file2 ...]" exit 1 @@ -34,7 +36,10 @@ while true; do echo "🚥 Build started." START_TIME=$(date +%s.%N) - npm run build:run + + # Ignore error, let it rebuild + npm run build:run || true + END_TIME=$(date +%s.%N) DURATION=$(awk "BEGIN {printf \"%.2f\", $END_TIME - $START_TIME}") From 391703193f102cabb3b6b971b7322c148ad474d4 Mon Sep 17 00:00:00 2001 From: William Wong Date: Thu, 16 Oct 2025 10:03:24 +0000 Subject: [PATCH 13/23] Clean up /dist.tmp --- packages/api-middleware/.gitignore | 1 - packages/api/.gitignore | 1 - packages/base/.gitignore | 1 - packages/bundle/.gitignore | 1 - packages/component/.gitignore | 1 - packages/core/.gitignore | 1 - packages/debug-theme/.gitignore | 1 - packages/directlinespeech/.gitignore | 1 - packages/experience-chat-launcher/.gitignore | 1 - packages/fluent-theme/.gitignore | 1 - packages/react-hooks/.gitignore | 1 - packages/react-valibot/.gitignore | 1 - packages/redux-store/.gitignore | 1 - packages/styles/.gitignore | 1 - 14 files changed, 14 deletions(-) diff --git a/packages/api-middleware/.gitignore b/packages/api-middleware/.gitignore index 515a303349..52536cb56d 100644 --- a/packages/api-middleware/.gitignore +++ b/packages/api-middleware/.gitignore @@ -1,5 +1,4 @@ /*.tgz /dist/ -/dist.tmp/ /node_modules/ /tsup.config.bundled_*.mjs diff --git a/packages/api/.gitignore b/packages/api/.gitignore index d6ea368dc0..6df6822463 100644 --- a/packages/api/.gitignore +++ b/packages/api/.gitignore @@ -1,6 +1,5 @@ /*.tgz /dist/ -/dist.tmp/ /node_modules/ /tsup.config.bundled_*.mjs diff --git a/packages/base/.gitignore b/packages/base/.gitignore index 515a303349..52536cb56d 100644 --- a/packages/base/.gitignore +++ b/packages/base/.gitignore @@ -1,5 +1,4 @@ /*.tgz /dist/ -/dist.tmp/ /node_modules/ /tsup.config.bundled_*.mjs diff --git a/packages/bundle/.gitignore b/packages/bundle/.gitignore index d412f912e3..7f3c011f4b 100644 --- a/packages/bundle/.gitignore +++ b/packages/bundle/.gitignore @@ -1,6 +1,5 @@ /*.tgz /dist/ -/dist.tmp/ /node_modules/ /static/ /tsup.config.bundled_*.mjs diff --git a/packages/component/.gitignore b/packages/component/.gitignore index 515a303349..52536cb56d 100644 --- a/packages/component/.gitignore +++ b/packages/component/.gitignore @@ -1,5 +1,4 @@ /*.tgz /dist/ -/dist.tmp/ /node_modules/ /tsup.config.bundled_*.mjs diff --git a/packages/core/.gitignore b/packages/core/.gitignore index 515a303349..52536cb56d 100644 --- a/packages/core/.gitignore +++ b/packages/core/.gitignore @@ -1,5 +1,4 @@ /*.tgz /dist/ -/dist.tmp/ /node_modules/ /tsup.config.bundled_*.mjs diff --git a/packages/debug-theme/.gitignore b/packages/debug-theme/.gitignore index 515a303349..52536cb56d 100644 --- a/packages/debug-theme/.gitignore +++ b/packages/debug-theme/.gitignore @@ -1,5 +1,4 @@ /*.tgz /dist/ -/dist.tmp/ /node_modules/ /tsup.config.bundled_*.mjs diff --git a/packages/directlinespeech/.gitignore b/packages/directlinespeech/.gitignore index 3e330334ba..06e76e78ad 100644 --- a/packages/directlinespeech/.gitignore +++ b/packages/directlinespeech/.gitignore @@ -1,6 +1,5 @@ /*.tgz /dist/ -/dist.tmp/ /node_modules/ /tsup.config.bundled_*.mjs diff --git a/packages/experience-chat-launcher/.gitignore b/packages/experience-chat-launcher/.gitignore index 515a303349..52536cb56d 100644 --- a/packages/experience-chat-launcher/.gitignore +++ b/packages/experience-chat-launcher/.gitignore @@ -1,5 +1,4 @@ /*.tgz /dist/ -/dist.tmp/ /node_modules/ /tsup.config.bundled_*.mjs diff --git a/packages/fluent-theme/.gitignore b/packages/fluent-theme/.gitignore index d412f912e3..7f3c011f4b 100644 --- a/packages/fluent-theme/.gitignore +++ b/packages/fluent-theme/.gitignore @@ -1,6 +1,5 @@ /*.tgz /dist/ -/dist.tmp/ /node_modules/ /static/ /tsup.config.bundled_*.mjs diff --git a/packages/react-hooks/.gitignore b/packages/react-hooks/.gitignore index 515a303349..52536cb56d 100644 --- a/packages/react-hooks/.gitignore +++ b/packages/react-hooks/.gitignore @@ -1,5 +1,4 @@ /*.tgz /dist/ -/dist.tmp/ /node_modules/ /tsup.config.bundled_*.mjs diff --git a/packages/react-valibot/.gitignore b/packages/react-valibot/.gitignore index 515a303349..52536cb56d 100644 --- a/packages/react-valibot/.gitignore +++ b/packages/react-valibot/.gitignore @@ -1,5 +1,4 @@ /*.tgz /dist/ -/dist.tmp/ /node_modules/ /tsup.config.bundled_*.mjs diff --git a/packages/redux-store/.gitignore b/packages/redux-store/.gitignore index 515a303349..52536cb56d 100644 --- a/packages/redux-store/.gitignore +++ b/packages/redux-store/.gitignore @@ -1,5 +1,4 @@ /*.tgz /dist/ -/dist.tmp/ /node_modules/ /tsup.config.bundled_*.mjs diff --git a/packages/styles/.gitignore b/packages/styles/.gitignore index 515a303349..52536cb56d 100644 --- a/packages/styles/.gitignore +++ b/packages/styles/.gitignore @@ -1,5 +1,4 @@ /*.tgz /dist/ -/dist.tmp/ /node_modules/ /tsup.config.bundled_*.mjs From 3edc2ea33b9b6e1f86e88eab1c2a68b60a2f6450 Mon Sep 17 00:00:00 2001 From: William Wong Date: Thu, 16 Oct 2025 18:09:08 +0000 Subject: [PATCH 14/23] Fix cyclic dependencies --- packages/experience-chat-launcher/src/private/private/Icon.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/experience-chat-launcher/src/private/private/Icon.tsx b/packages/experience-chat-launcher/src/private/private/Icon.tsx index c85b52e854..5315535236 100644 --- a/packages/experience-chat-launcher/src/private/private/Icon.tsx +++ b/packages/experience-chat-launcher/src/private/private/Icon.tsx @@ -1,7 +1,7 @@ /* TODO: [P0] Dupe from packages/fluent-theme/src/components/icon/FluentIcon.tsx, should dedupe. */ import { validateProps } from '@msinternal/botframework-webchat-react-valibot'; import { useStyles } from '@msinternal/botframework-webchat-styles/react'; -import { createIconComponent } from 'botframework-webchat/internal'; +import { createIconComponent } from 'botframework-webchat-component/internal'; import cx from 'classnames'; import React, { memo, useMemo, type CSSProperties } from 'react'; import { object, optional, pipe, readonly, string, union, type InferInput } from 'valibot'; From 7de06682c110529f26d3c88706ac3a068611c1f2 Mon Sep 17 00:00:00 2001 From: William Wong Date: Thu, 16 Oct 2025 19:17:41 +0000 Subject: [PATCH 15/23] Add new schema exports --- packages/core/package.json | 10 ++++ packages/core/schema.js | 3 ++ .../src/schema/directLineJSBotConnection.ts | 46 +++++++++++++++++++ packages/core/src/schema/index.ts | 2 + packages/core/src/schema/observable.ts | 28 +++++++++++ .../core/src/schema/private/customFunction.ts | 16 +++++++ packages/core/tsup.config.ts | 3 +- .../src/private/ChatLauncher.tsx | 31 ++----------- 8 files changed, 110 insertions(+), 29 deletions(-) create mode 100644 packages/core/schema.js create mode 100644 packages/core/src/schema/directLineJSBotConnection.ts create mode 100644 packages/core/src/schema/index.ts create mode 100644 packages/core/src/schema/observable.ts create mode 100644 packages/core/src/schema/private/customFunction.ts diff --git a/packages/core/package.json b/packages/core/package.json index ec64272676..876eb81803 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -24,6 +24,16 @@ "types": "./dist/botframework-webchat-core.internal.d.ts", "default": "./dist/botframework-webchat-core.internal.js" } + }, + "./schema": { + "import": { + "types": "./dist/botframework-webchat-core.schema.d.mts", + "default": "./dist/botframework-webchat-core.schema.mjs" + }, + "require": { + "types": "./dist/botframework-webchat-core.schema.d.ts", + "default": "./dist/botframework-webchat-core.schema.js" + } } }, "publishConfig": { diff --git a/packages/core/schema.js b/packages/core/schema.js new file mode 100644 index 0000000000..32fd97f48f --- /dev/null +++ b/packages/core/schema.js @@ -0,0 +1,3 @@ +// This is required for Webpack 4 which does not support named exports. +// eslint-disable-next-line no-undef +module.exports = require('./dist/botframework-webchat-core.schema.js'); diff --git a/packages/core/src/schema/directLineJSBotConnection.ts b/packages/core/src/schema/directLineJSBotConnection.ts new file mode 100644 index 0000000000..34d8da2e54 --- /dev/null +++ b/packages/core/src/schema/directLineJSBotConnection.ts @@ -0,0 +1,46 @@ +import { + custom, + object, + optional, + safeParse, + string, + type CustomIssue, + type CustomSchema, + type ErrorMessage, + type InferOutput +} from 'valibot'; + +import { type WebChatActivity } from '../types/WebChatActivity'; +import observable, { type Observable } from './observable'; +import customFunction from './private/customFunction'; + +const directLineJSBotConnectionSchema = object({ + activity$: observable('activity$ must be an observable'), + connectionStatus$: observable('connectionStatus$ must be an observable'), + end: optional(customFunction<() => void>('end must be a function')), + getSessionId: optional(customFunction<() => Observable>('getSessionId must be a function')), + postActivity: customFunction<() => Observable>('postActivity must be a function'), + referenceGrammarId: optional(string('referenceGrammarId must be a string')), + setUserId: optional(customFunction<(userId: string | undefined) => void>('setUserId must be a function')) +}); + +type DirectLineJSBotConnection = InferOutput; + +function directLineJSBotConnection(): CustomSchema; + +function directLineJSBotConnection< + const TMessage extends ErrorMessage | undefined = ErrorMessage | undefined +>(message: TMessage): CustomSchema; + +function directLineJSBotConnection< + const TMessage extends ErrorMessage | undefined = ErrorMessage | undefined +>(message?: TMessage): CustomSchema { + return custom( + value => safeParse(directLineJSBotConnectionSchema, value).success, + // TODO: Probably lacking some undefined checks, thus, we need to force cast. + message as TMessage + ); +} + +export default directLineJSBotConnection; +export { type DirectLineJSBotConnection }; diff --git a/packages/core/src/schema/index.ts b/packages/core/src/schema/index.ts new file mode 100644 index 0000000000..49e2dce348 --- /dev/null +++ b/packages/core/src/schema/index.ts @@ -0,0 +1,2 @@ +export { default as directLineJSBotConnection, type DirectLineJSBotConnection } from './directLineJSBotConnection'; +export { default as observable, type Observable } from './observable'; diff --git a/packages/core/src/schema/observable.ts b/packages/core/src/schema/observable.ts new file mode 100644 index 0000000000..2c0a54a680 --- /dev/null +++ b/packages/core/src/schema/observable.ts @@ -0,0 +1,28 @@ +import { custom, function_, object, safeParse, type CustomIssue, type CustomSchema, type ErrorMessage } from 'valibot'; + +import { type Observable } from '../types/external/Observable'; + +const observableSchema = object({ subscribe: function_() }); + +function observable(): CustomSchema, undefined>; + +function observable(): CustomSchema, undefined>; + +function observable< + T, + const TMessage extends ErrorMessage | undefined = ErrorMessage | undefined +>(message: TMessage): CustomSchema, TMessage>; + +function observable< + T, + const TMessage extends ErrorMessage | undefined = ErrorMessage | undefined +>(message?: TMessage): CustomSchema, TMessage> { + return custom, TMessage>( + value => safeParse(observableSchema, value).success, + // TODO: Probably lacking some undefined checks, thus, we need to force cast. + message as TMessage + ); +} + +export default observable; +export { type Observable }; diff --git a/packages/core/src/schema/private/customFunction.ts b/packages/core/src/schema/private/customFunction.ts new file mode 100644 index 0000000000..1754465cd9 --- /dev/null +++ b/packages/core/src/schema/private/customFunction.ts @@ -0,0 +1,16 @@ +import { custom, function_, safeParse, type CustomIssue, type CustomSchema, type ErrorMessage } from 'valibot'; + +function customFunction(): CustomSchema<(...args: any[]) => any, undefined>; + +function customFunction any>(): CustomSchema; + +function customFunction< + T extends (...args: any[]) => any, + const TMessage extends ErrorMessage | undefined = ErrorMessage | undefined +>(message?: TMessage): CustomSchema; + +function customFunction any>() { + return custom(value => safeParse(function_(), value).success); +} + +export default customFunction; diff --git a/packages/core/tsup.config.ts b/packages/core/tsup.config.ts index 83f5c3b906..e54948934e 100644 --- a/packages/core/tsup.config.ts +++ b/packages/core/tsup.config.ts @@ -6,7 +6,8 @@ const commonConfig = applyConfig(config => ({ ...config, entry: { 'botframework-webchat-core': './src/index.ts', - 'botframework-webchat-core.internal': './src/internal/index.ts' + 'botframework-webchat-core.internal': './src/internal/index.ts', + 'botframework-webchat-core.schema': './src/schema/index.ts' } })); diff --git a/packages/experience-chat-launcher/src/private/ChatLauncher.tsx b/packages/experience-chat-launcher/src/private/ChatLauncher.tsx index d526f3b495..aee9ba0b17 100644 --- a/packages/experience-chat-launcher/src/private/ChatLauncher.tsx +++ b/packages/experience-chat-launcher/src/private/ChatLauncher.tsx @@ -10,44 +10,19 @@ import { type Polymiddleware } from 'botframework-webchat-api/middleware'; import { Composer } from 'botframework-webchat-component/component'; -import { DirectLineJSBotConnection } from 'botframework-webchat-core'; +import { directLineJSBotConnection } from 'botframework-webchat-core/schema'; import cx from 'classnames'; import React, { memo, useMemo } from 'react'; -import { - custom, - function_, - instance, - object, - optional, - pipe, - readonly, - safeParse, - string, - type InferInput -} from 'valibot'; +import { instance, object, optional, pipe, readonly, string, type InferInput } from 'valibot'; import ChatLauncherStylesheet from '../stylesheet/ChatLauncherStylesheet'; import styles from './ChatLauncher.module.css'; import ChatLauncherButton from './private/ChatLauncherButton'; import IconButton from './private/IconButton'; -// Best-effort to check if it is an Observable. -const observableSchema = object({ subscribe: function_() }); - -// TODO: [P0] Move this to botframework-webchat-core. -const directLineSchema = object({ - activity$: custom(value => safeParse(observableSchema, value).success), - connectionStatus$: custom(value => safeParse(observableSchema, value).success), - end: optional(function_()), - getSessionId: optional(function_()), - postActivity: function_(), - referenceGrammarId: optional(string()), - setUserId: optional(function_()) -}); - const chatLauncherPropsSchema = pipe( object({ - directLine: directLineSchema, + directLine: directLineJSBotConnection(), nonce: optional(string()), store: reduxStoreSchema, stylesRoot: optional(instance(Node)) From 85eb33f299287ad43e82c46cf52207928fbe5e6f Mon Sep 17 00:00:00 2001 From: William Wong Date: Fri, 17 Oct 2025 02:09:38 +0000 Subject: [PATCH 16/23] Add modal --- .../html2/experience/chatLauncher/simple.html | 6 + package-lock.json | 4 +- .../src/PolymiddlewareComposer.tsx | 18 ++- .../src/chatLauncherButtonPolymiddleware.tsx | 22 ++-- .../src/iconButtonPolymiddleware.tsx | 15 ++- packages/api-middleware/src/index.ts | 14 +++ .../src/popoverPolymiddleware.tsx | 108 ++++++++++++++++++ .../src/types/Polymiddleware.ts | 4 +- packages/api/src/boot/middleware.ts | 14 +++ packages/bundle/src/boot/actual/middleware.ts | 42 +++++++ .../experience-chat-launcher/package.json | 1 + .../src/private/ChatLauncher.tsx | 15 ++- .../private/ChatLauncherButton.module.css | 1 + .../private/private/ChatLauncherButton.tsx | 10 +- .../private/ChatLauncherPopover.module.css | 19 +++ .../private/private/ChatLauncherPopover.tsx | 27 +++++ .../ChatLauncherPopoverDismissButton.tsx | 40 +++++++ .../src/private/private/Icon.tsx | 5 +- .../src/private/private/IconButton.module.css | 2 +- .../src/private/private/IconButton.tsx | 18 ++- .../private/NonModalPopover.module.css | 19 +++ .../src/private/private/NonModalPopover.tsx | 49 ++++++++ packages/react-valibot/src/forwardedRef.ts | 28 +++++ packages/react-valibot/src/index.ts | 5 + .../react-valibot/src/mutableRefObject.ts | 25 ++++ packages/react-valibot/src/ref.ts | 26 +++++ packages/react-valibot/src/refCallback.ts | 25 ++++ packages/react-valibot/src/refObject.ts | 35 ++++++ 28 files changed, 566 insertions(+), 31 deletions(-) create mode 100644 packages/api-middleware/src/popoverPolymiddleware.tsx create mode 100644 packages/experience-chat-launcher/src/private/private/ChatLauncherPopover.module.css create mode 100644 packages/experience-chat-launcher/src/private/private/ChatLauncherPopover.tsx create mode 100644 packages/experience-chat-launcher/src/private/private/ChatLauncherPopoverDismissButton.tsx create mode 100644 packages/experience-chat-launcher/src/private/private/NonModalPopover.module.css create mode 100644 packages/experience-chat-launcher/src/private/private/NonModalPopover.tsx create mode 100644 packages/react-valibot/src/forwardedRef.ts create mode 100644 packages/react-valibot/src/mutableRefObject.ts create mode 100644 packages/react-valibot/src/ref.ts create mode 100644 packages/react-valibot/src/refCallback.ts create mode 100644 packages/react-valibot/src/refObject.ts diff --git a/__tests__/html2/experience/chatLauncher/simple.html b/__tests__/html2/experience/chatLauncher/simple.html index 1de09a18b6..93552ff302 100644 --- a/__tests__/html2/experience/chatLauncher/simple.html +++ b/__tests__/html2/experience/chatLauncher/simple.html @@ -11,6 +11,11 @@ /* Setting it to absolute for testing only. */ position: absolute !important; } + + .webchat .chat-launcher-modal { + height: 400px !important; + width: 320px !important; + } @@ -27,6 +32,7 @@ } } + + + + + diff --git a/__tests__/html2/experience/chatLauncher/simple.html b/__tests__/html2/experience/chatLauncher/simple.html index 93552ff302..b089930fb9 100644 --- a/__tests__/html2/experience/chatLauncher/simple.html +++ b/__tests__/html2/experience/chatLauncher/simple.html @@ -60,6 +60,30 @@ createRoot(document.getElementById('webchat')).render(createElement(ChatLauncher, { directLine, store })) ); + await pageConditions.uiConnected(); + + await directLine.emulateIncomingActivity({ + from: { id: 'bot', role: 'bot' }, + text: 'Mollit nostrud duis do sunt adipisicing excepteur deserunt anim qui eu. Eu occaecat sit laborum dolore sint ullamco. Enim aliquip culpa duis aute laborum cillum minim dolore dolore nulla voluptate ea. Qui exercitation minim velit adipisicing est eiusmod nisi et sint. Voluptate eiusmod aliqua quis esse sit excepteur culpa enim dolore voluptate irure cillum. Amet consequat ea ipsum laborum cillum non amet labore ad ipsum. Et nisi occaecat quis minim magna ea esse cillum.', + timestamp: -120000, + type: 'message' + }); + + await directLine.emulateIncomingActivity({ + from: { id: 'bot', role: 'bot' }, + text: 'Velit labore excepteur duis sit adipisicing quis eiusmod elit minim ea enim quis. Cillum ex adipisicing excepteur incididunt consectetur qui eu Lorem in minim ipsum. Commodo mollit aliqua ut sit ex commodo sit irure tempor adipisicing ipsum. Laboris fugiat labore eu est nulla cupidatat. Sit ut est laboris do.', + timestamp: -60000, + type: 'message' + }); + + await directLine.emulateIncomingActivity({ + from: { id: 'bot', role: 'bot' }, + text: 'Quis elit eiusmod minim anim dolor nostrud. Eu cillum anim reprehenderit ullamco non elit labore eiusmod. Aute esse adipisicing ad enim do aute tempor enim. Laborum anim qui esse sit. Amet elit incididunt reprehenderit enim dolor.', + timestamp: 0, + type: 'message' + }); + + await pageConditions.numActivitiesShown(3); await host.snapshot('local'); }); diff --git a/packages/experience-chat-launcher/src/private/private/ChatLauncherPopover/index.module.css b/packages/experience-chat-launcher/src/private/private/ChatLauncherPopover/index.module.css new file mode 100644 index 0000000000..8bbdb9297a --- /dev/null +++ b/packages/experience-chat-launcher/src/private/private/ChatLauncherPopover/index.module.css @@ -0,0 +1,19 @@ +:global(.webchat-experience-chat-launcher) .chat-launcher-popover { + --webchat-spacingHorizontalSNudge: var(--spacingHorizontalSNudge, 6px); + --webchat-spacingVerticalMNudge: var(--spacingVerticalMNudge, 10px); + --webchat-spacingVerticalSNudge: var(--spacingVerticalSNudge, 6px); +} + +:global(.webchat-experience-chat-launcher) .chat-launcher-popover__box { + display: grid; + grid-template-rows: auto 1fr; + height: 100%; + overflow: hidden; + width: 100%; +} + +:global(.webchat-experience-chat-launcher) .chat-launcher-popover .chat-launcher-popover__webchat { + box-sizing: border-box; + overflow: hidden; + padding: 0 var(--webchat-spacingHorizontalSNudge) var(--webchat-spacingVerticalMNudge); +} diff --git a/packages/experience-chat-launcher/src/private/private/ChatLauncherPopover/index.tsx b/packages/experience-chat-launcher/src/private/private/ChatLauncherPopover/index.tsx index ea6fc09339..03921d717f 100644 --- a/packages/experience-chat-launcher/src/private/private/ChatLauncherPopover/index.tsx +++ b/packages/experience-chat-launcher/src/private/private/ChatLauncherPopover/index.tsx @@ -1,9 +1,12 @@ +import { useStyles } from '@msinternal/botframework-webchat-styles/react'; import { PopoverPolymiddlewareProxy } from 'botframework-webchat-api/middleware'; +import { BasicWebChat } from 'botframework-webchat-component/component'; import mergeRefs from 'merge-refs'; import React, { forwardRef, memo, useRef, type ForwardedRef } from 'react'; import { object, pipe, readonly, type InferInput } from 'valibot'; import TitleBar from './TitleBar'; +import styles from './index.module.css'; const chatLauncherPopoverPropsSchema = pipe(object({}), readonly()); @@ -12,10 +15,20 @@ type ChatLauncherPopoverProps = InferInput) { const popoverRef = useRef(null); + const classNames = useStyles(styles); + return ( // TODO: [P2] Is it correct to force-cast ref to HTMLDivElement? - - + +
+ + +
); } From 5338030c4838df4b43fda73cb75834d18a7ea65e Mon Sep 17 00:00:00 2001 From: William Wong Date: Fri, 17 Oct 2025 08:10:51 +0000 Subject: [PATCH 23/23] Add background --- .../chatLauncher/assets/background.jpg | Bin 0 -> 54393 bytes .../experience/chatLauncher/fluentTheme.html | 20 ++++++++++++++++++ .../html2/experience/chatLauncher/simple.html | 6 ++++++ 3 files changed, 26 insertions(+) create mode 100644 __tests__/html2/experience/chatLauncher/assets/background.jpg diff --git a/__tests__/html2/experience/chatLauncher/assets/background.jpg b/__tests__/html2/experience/chatLauncher/assets/background.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d71b08ab577554784f8c94e5d44e39ba6f1cdf93 GIT binary patch literal 54393 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%0AHlUFO-XX~HsSvUTii`|%=I5V zSfFh7)=XMBYy8$P4i38W?zRGB&HN-F;}>teZCRqFz}ENSwM{`IF|e8m?ev^F%|DBT-0_`%?#P$c!!j?~t3@;P zJK-7wlf~{fnvE+mniX8rDd+ZT{*5gR&kBi;_nh5kdFk`C_w%T3BZMxo()h;PA$F!J z5%rEsJV1XQ%xlmJXUHD*$`QA`WG4@dCnMiI z!gs38j1VpdRl}=bwTb@7j8!2oZxPO;h6d>{PkDT4qhEq-4L;h}u{I<|u_Zz@hhdc` zQ!O$x`+-N?gXAy>TWI@(FVgB_R%}wOl2`FO(YwGtI}bLH1^Kz)WEq(1Kw-%o|B)27 z@`PbhAadEbWiVfq6#Vwt9yr6#gC7)d0^~;7672j#%GV0Qj|1bj@1MeJG#!|Ts>t~> zJ3X-p9F=mWuH*JP5_13+Qbkc@Cz~xbV+^22ZQ=kHIO^(=E1zDmvIXWGt)avm=dS-3!$R@>D0g%~ zOUWBc2~+$yv@gNg1~7wTt*JV<_9EPtca+z$X~;)Tff!=AdC70WD7$j6t){&m53>8j z7gb23-0)T9Hj6Gt&yA9bDRSiZFmvx)PukjRIct)0#PbOJ(R-q+@}Xf6oL=v0r!fkq z#aVC;0Iu0yB8*#yWhsCX2je`n)~sPZP* zHiHrNv?TmS`8vcB+8@N(2F^D{8&x(fb~Od{&~ThtSPn;^nyKjg%)-?VS=; zf=`;x17Ix%jJndE=6CH~)_jf0O<7{syHMq^ZGshsL$Us%U?uunewv?q#LU6|`^6S> zk&h@m&d2F$SREiXp$U2xLkraPzlZXUAtcHAtH?wnAR5&5LzdA7zBY{*#t(KV*lH#* z$idn4#$hu}LvTkp-vw7oD{AKqDMbpjWa}mc%o)HEqVGWC5*vG?kb=OR&W$j1-6ya? z;mKWJAEiVz@1!B;LB#Dz&V=`>IP7A>v(L`jyLUPiTLM-fkrz`MdJ@9jBhtq@VFQ4* zk5*wM=b?7+BXjglNoxX9*eVy?qkNsv?sD#{c=l$Tt&BDwUVh5^7bN1b+uwzOctK)2 zVkafVHxk#|!qbSmK-ccqF3elm|1QKiB0T|wT=}hAXX1$U?rG!b)qH!`K5^Z*?iIlX zla8W=&M$1oeu0nRysL&`oU_|ed;-Gu}q)J>aVJ{M2a-H&{)i!VCYwR287VXu! z$L*!ei)HG!U3mUsaMmO9DiV1y(~ZbRfiAY$ zxAARufBsnXTbuJCYqy1CoHXNHl+7UJ{PZwJ~&e`6bZ52 zduF~}$0gc@IS!^#9xjt)9+n-!S}4#Rjn2f8EX4O|v0CyfwCC=5)%3j-D=I8XWZR9f zMH`85La{wF%psXMkg6S>_H^6<$Ht=m}n~J$jl|ak7vp_exZh zP0@1J#yNe>*L#4DbV;(G2jE$ZqPSyCO->#>l3?b;yOP&X^L`V2Pyklt0=Dz;{;!2+ z-;8rEEDffagn%+J;p0AC6pBFTvk0y{DD&OBie3T1#saqEhxiXD{QzLX35n7tY0*I$ zWK)dx^0?bwTm_@-ZhgONY29*FtUaTJv2T`GqJjg!C^6rC3ZLz|Im`!jtXV&H7$ zS~dV5F>iKVkKO@u9`^mK7#R`k;W0p}*t1DWO-*mvnadtnsTB}@>oZIwaa=Hw+Lu!gg>3) z03DcMYQBG+@>ES9n*8sx3Q^(B2jua*7Z;f_nz_Toxp8SCv}~N~Qvdk89_~jM0i!>jbs0Y_N##r2M33 z+@8A8prxKa=V@GOI?f)kY>y%JD73QYc&D8GD@UrJ7Tk5gl7qWl$ws2i1LoaXmmCLm z?(vt{RgX^zqd-oe0S%AwbOKb_#i;j1%gW@3a}X-J`Sb`k%oc0is1_xBh zMmTn?&-VT$X|<5FYJavjA@@*HKulb43M6#Gy#;+{NfBZwMm5SUsH_!c;m-FSN`fZErzC~oKSO@*OW-u z^C(ua)Hu;?V{-iPnJfBe@2L{1-yR_theVpXoYWHPJ; z4o<-~Q-3Yho6OVe;K!oG!m*TKkLukqak<@?4Yri`F@RuvRjp>ivChky$D@? zmOltq8n=CU#84{Uo}cx7lv=Bx#rEM151&{HeO)wurZ<~+qxn)_4{<43lTWNMR^zuZ z_vzpWv9T4zJ*yH5^Ln56<*$sz=CkV%R^aodJlmN_)(;mm^f3qaXGB(qEUpD;I1)hT z!QrEvmH!DRooe`$2j6ady6XFLbj-U`Z@yt)I(;Lhr4{t>l2k%%k#T8_s%+9X+=?nx zXTB&#+k|+3x`h$6|KsPvw}wd}&^er=zW&MXMb}(cE(4{1T|3}9!Ea;bQ6UHRG8)U6 z?4ETJ(K#dS^*1IwjI{U~tWm8q%YKkdopM~N|25{74RMGu*9hbj?)2ZsP*$`t`6SKH zp*~6~h`Wtu*9vU~_GP4(*tuMnH^<;?cMhR7m`34hjd={c0ni zVHSvX=6y!)+kQt4#dxC)l0(em63^e`_mpK2tTMBM5E7BK9x}HN4ABjP4fS|QQ!c|r>>PV1Fsfn zs4NY&rl2b!y&h>7i+3kF0_6n(J*5={OOoa{bxLcLc)Fk3dDff?PlOTH_j6Y_AQaJv^&% zm&rMUSxeQ{+%D<4VrsRTknb?EoMEcRECv&U7S<9+xmIZ!d2ly6+p%dYsU*+RZz1_H znvxCEKO5xq*5rKSZb*5IN@ z+Mu*PF8-QIG9(o|@DXCsuz0vM+1awk0*Po#)8q-Yi}Pw(3P_SEXNaNpg|`ZqTESKr zxgsW0z?|R3$YJCP`4e!&Q(M*!egk`u0MsL|0WzdmZ^j+Be^SLJ z4f^dZ-6{W~wv$9*?69mr!cYt~?uc0eAKaC*^Q8>#uC`d%2B%$Kh}_SSJ=n_XGK0w* zVN<&K@mgXfhs(1ot($OgVzy|3Fc}&UVYenS*a7v#N8uX;To4FVVak{6NdV%D$l2 zm4o`419VUXrc=CyAlh!H1&n#NlfwMB%arqyBH+v|>>pl^S8qt%t^$)i`|ttXcz^bu zc(YzHoIe2ISZ5$lwpEq-*gB}~*cS50sQR5*oC!ZI{jgPXfrrEbM(VnC?aovQfOC6V zEYo66Sf5DIHjs$$&G8y$W8l zIl%`urrX{4{c1|HYpz1Bu~h{*+`CUo&+qroQG{fxJ}_uOC<w7z?hAyW8%$ZdJbAcs4C&#KjMi)hJ=zz~LQ7 zDXV~NBSq$R&Y>>fWQ_-L7a9;2WT?){Sv3EA=OX{3Y_mc8E@>H?E9lCwo@$c zMaaP;-1bL*2<-hE8*DwshIlZzslMLJ!+(be!GvK>UIdg9yqQ4w7Fe%+x9NhZGP)oU zf9VMA3Xfqn3N+y73ET#Gtq!%6@L82;d<8r;_;VU$pf>1r?a+>`hUJU0r-hq+oJ_D+ zj(Zh4HZ2`gv;)uWQMbb~xg0q&q=S|MkmI`+cb~~lcAnfo4KI*U;d3Vmx(I5K#Ja~+ zjFeL)D0#w!r{V_zR+~SmcIkHdt-~s{^i(g~m=7!)+8(kT z=*MoeIK;Ku)2=e2%^A0oA}Q&9dGr{?lDlq&{` zl#w_&^Q7nHwn+1e`-g=|%0NeD@)(FwrBdTt8;h0wcyYnL2?{oBf0T^mGD?a0(tbh| zj8rVFOuEX1VIJ;$c<}AMiz2&P^>Vrjl1XKy_fgkpp~{J3YIP@ib?_djX_ET(Q_(5O zBN_n$B(UGmVj*EdYlrMcfO6n*G8geby1`if_m2f2ME|+782#3G9y}}|-=Kdh6J@#WVWN&7K%W_fbf@^(@0A&hvrY1i zOKkX5IFz_+#BO$Tr|5Nf7_DU4EECXRzd22wPtpBIe-1zji5sRjmVpso2+Ja9ipWQ! z7Md#cLA0wm$X6>5;Ftn8YHH`^!a$Mu!!m9}v<@!hFM&2&3!MiKSXNri%UFNYgC>hnPp0|$`*9U z@HYP)TKzu3rOBjaW0hd<-Z$8xcS77>MpPBIc=GB6uouIZ0*HFXQ>Yf(^tKZm|?V2mM#*Izg32uO8exXR& zq=9Pn@&_XGZ}E}!M`Rp!KTW*;e+BTOUWZ%qm{F?d&VHn2kl}e2=@W2@tW;WWV+L~M zkqXS!vt{n=?q8ycO~Yh@ZIY1Y=P9Goh+FeW=4S6B?)b;qT*PfMitL1RR16pu{v8}r zZF86-#f{HZyFuM^_VJn)(v2JC%q_ktPPzOgDAu=3J`@*7$0aT&CfO=LiBxkE7#3=@ zJ!{^xFWFN+7s<(NTDJ*YLIN!t;`KV1ADVx&K?b1fDY@fYX+En>3&3_LT}r5K#1Q?f zH^SzgX>|L1vxqpS_8)iv0KnnlRNW&Q+8EZl?{7h0Yf?!8lzAd79^$E>I|1ZrOieKv z6j10DsLg6j_Hcy$pW!}MzzB&*$XKznSu<+`YJ;#BkHY=~Bq|2ETKIck z#{&Ie2&>)1Sso7Sb*#Xg+icv>!++0B`xA$AwBu>|iq5*!wxuHX2=O~3aZF2#h6+EB z(9_d({DG9-^>Y!_?E>MmZw|9BZ}#|02)oJ0$AuuK%;O&!PQDl)`(s2>*y;)TS1DIF$NJK(zR@5j^X#<-i*m@@5fG2AWdH@iQBR*qP@Utpai?EX3n zQXnpehznrvBC{l=zyLb`F5hDle4h!n^QNOuy3X@|(~XpuQ_aAMo4tmhy1>NFtN7~u zqRBkm=)o>TuoU#nU;*nDvztz(B-i+|)zx|eN3N?rNExIa&J{cr9b&GIqPs!)uTyR| zs`BUJXyOK1ZXAYwkaf9xa%~(9D>8I_6t^_*rXqAj<{LehYk#Q9AsX(wmu;hpxe1=A zy0MkvUaaKFyP|%cn+>Ll{z*v(9N`hUaWn!){-c)BM>psF1Xqr!A}u#Ly#lUSin;1t zM~~0pC6=plWKNJe&{I614Kv&)VcEcQW&a=lK(;UE~L diff --git a/__tests__/html2/experience/chatLauncher/simple.html b/__tests__/html2/experience/chatLauncher/simple.html index b089930fb9..7ef0979ea7 100644 --- a/__tests__/html2/experience/chatLauncher/simple.html +++ b/__tests__/html2/experience/chatLauncher/simple.html @@ -16,6 +16,12 @@ height: 400px !important; width: 320px !important; } + + #webchat { + background-image: url(./assets/background.jpg); + background-position: center; + background-size: cover; + }