diff --git a/__tests__/html2/experience/chatLauncher/assets/background.jpg b/__tests__/html2/experience/chatLauncher/assets/background.jpg new file mode 100644 index 0000000000..d71b08ab57 Binary files /dev/null and b/__tests__/html2/experience/chatLauncher/assets/background.jpg differ diff --git a/__tests__/html2/experience/chatLauncher/fluentTheme.html b/__tests__/html2/experience/chatLauncher/fluentTheme.html new file mode 100644 index 0000000000..2193fcc04f --- /dev/null +++ b/__tests__/html2/experience/chatLauncher/fluentTheme.html @@ -0,0 +1,136 @@ + + + + + + + +
+ + + + + diff --git a/__tests__/html2/experience/chatLauncher/simple.html b/__tests__/html2/experience/chatLauncher/simple.html new file mode 100644 index 0000000000..7ef0979ea7 --- /dev/null +++ b/__tests__/html2/experience/chatLauncher/simple.html @@ -0,0 +1,97 @@ + + + + + + + +
+ + + + + diff --git a/__tests__/html2/livestream/wrongId.html b/__tests__/html2/livestream/wrongId.html new file mode 100644 index 0000000000..e8e9b2f327 --- /dev/null +++ b/__tests__/html2/livestream/wrongId.html @@ -0,0 +1,131 @@ + + + + + + + + + + + + +
+ + + 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..43c5b238be 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", @@ -93,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", @@ -3051,6 +3054,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 @@ -3715,7 +3722,6 @@ }, "node_modules/@types/istanbul-reports": { "version": "3.0.4", - "dev": true, "license": "MIT", "dependencies": { "@types/istanbul-lib-report": "*" @@ -3942,7 +3948,6 @@ }, "node_modules/@types/yargs": { "version": "17.0.33", - "dev": true, "license": "MIT", "dependencies": { "@types/yargs-parser": "*" @@ -6196,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": { @@ -8388,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" @@ -12735,6 +12744,8 @@ }, "node_modules/merge-refs": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-refs/-/merge-refs-2.0.0.tgz", + "integrity": "sha512-3+B21mYK2IqUWnd2EivABLT7ueDhb0b8/dGK8LoFQPrU61YITeCMn14F7y7qZafWNZhUEKb24cJdiT5Wxs3prg==", "license": "MIT", "funding": { "url": "https://github.com/wojtekmaj/merge-refs?sponsor=1" @@ -13691,6 +13702,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": { @@ -14834,10 +14847,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", @@ -14854,6 +14899,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", @@ -17849,6 +17897,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", @@ -18652,7 +18702,7 @@ "@msinternal/botframework-webchat-react-valibot": "^0.0.0-0", "@types/node": "^22.13.4", "cross-env": "^7.0.3", - "type-fest": "^4.34.1", + "type-fest": "^4.41.0", "typescript": "^5.7.3" }, "peerDependencies": { @@ -18687,6 +18737,8 @@ }, "packages/api-middleware/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": { @@ -19064,6 +19116,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", @@ -19896,7 +19949,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", @@ -19928,6 +19980,46 @@ "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": { + "botframework-webchat-api": "0.0.0-0", + "botframework-webchat-component": "0.0.0-0", + "botframework-webchat-core": "0.0.0-0", + "merge-refs": "2.0.0", + "use-ref-from": "0.1.0", + "valibot": "1.1.0" + }, + "devDependencies": { + "@msinternal/botframework-webchat-react-valibot": "0.0.0-0", + "@msinternal/botframework-webchat-redux-store": "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", @@ -19945,6 +20037,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" }, @@ -20382,7 +20475,6 @@ "packages/repack/react-dom@umd": { "name": "@msinternal/react-dom-umd", "version": "0.0.0-0", - "license": "ISC", "devDependencies": { "esbuild": "^0.25.10" } @@ -20890,6 +20982,7 @@ "core-js-pure": "3.44.0", "event-iterator": "2.0.0", "event-target-shim": "6.0.2", + "jest-mock": "28.1.3", "math-random": "2.0.1", "microsoft-cognitiveservices-speech-sdk": "1.17.0", "prop-types": "15.8.1", @@ -20903,6 +20996,57 @@ "node": ">= 14.0.0" } }, + "packages/test/page-object/node_modules/@jest/schemas": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", + "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.24.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "packages/test/page-object/node_modules/@jest/types": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", + "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "license": "MIT", + "dependencies": { + "@jest/schemas": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "packages/test/page-object/node_modules/@sinclair/typebox": { + "version": "0.24.51", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", + "license": "MIT" + }, + "packages/test/page-object/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "packages/test/page-object/node_modules/event-target-shim": { "version": "6.0.2", "license": "MIT", @@ -20913,6 +21057,40 @@ "url": "https://github.com/sponsors/mysticatea" } }, + "packages/test/page-object/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "packages/test/page-object/node_modules/jest-mock": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz", + "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==", + "license": "MIT", + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "packages/test/page-object/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "packages/test/web-server": { "name": "@msinternal/test-web-server", "version": "0.0.0-0", diff --git a/package.json b/package.json index cd25e62695..7b8a32877f 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", @@ -256,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/.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-middleware/package.json b/packages/api-middleware/package.json index eb14dcf2a3..b3c1700b98 100644 --- a/packages/api-middleware/package.json +++ b/packages/api-middleware/package.json @@ -55,24 +55,30 @@ "./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", - "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", + "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", + "bump:prod": "../../scripts/npm/bump-prod.sh", "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", + "postversion": "node ../../scripts/npm/postversion.sh", "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" + "preversion": "node ../../scripts/npm/preversion.sh", + "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", @@ -81,16 +87,16 @@ "@msinternal/botframework-webchat-react-valibot": "^0.0.0-0", "@types/node": "^22.13.4", "cross-env": "^7.0.3", - "type-fest": "^4.34.1", + "type-fest": "^4.41.0", "typescript": "^5.7.3" }, - "peerDependencies": { - "react-chain-of-responsibility": "0.4.0-main.c2f17da", - "react": ">= 16.8.6" - }, "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/src/PolymiddlewareComposer.tsx b/packages/api-middleware/src/PolymiddlewareComposer.tsx index 4780e3fc86..1ab3beff1e 100644 --- a/packages/api-middleware/src/PolymiddlewareComposer.tsx +++ b/packages/api-middleware/src/PolymiddlewareComposer.tsx @@ -15,7 +15,13 @@ import { } from 'valibot'; import { ActivityPolymiddlewareProvider, extractActivityEnhancer } from './activityPolymiddleware'; +import { ButtonPolymiddlewareProvider, extractButtonEnhancer } from './buttonPolymiddleware'; +import { + ChatLauncherButtonPolymiddlewareProvider, + extractChatLauncherButtonEnhancer +} from './chatLauncherButtonPolymiddleware'; import { ErrorBoxPolymiddlewareProvider, extractErrorBoxEnhancer } from './errorBoxPolymiddleware'; +import { PopoverPolymiddlewareProvider, extractPopoverEnhancer } from './popoverPolymiddleware'; import { Polymiddleware } from './types/Polymiddleware'; const polymiddlewareComposerPropsSchema = pipe( @@ -49,6 +55,41 @@ function PolymiddlewareComposer(props: PolymiddlewareComposerProps) { const activityPolymiddleware = useMemo(() => activityEnhancers.map(enhancer => () => enhancer), [activityEnhancers]); + const buttonEnhancers = useMemoWithPrevious>( + (prevButtonEnhancers = []) => { + const buttonEnhancers = extractButtonEnhancer(polymiddleware); + + // Checks for array equality, return previous version if nothing has changed. + return prevButtonEnhancers.length === buttonEnhancers.length && + buttonEnhancers.every((middleware, index) => Object.is(middleware, prevButtonEnhancers.at(index))) + ? prevButtonEnhancers + : buttonEnhancers; + }, + [polymiddleware] + ); + + const buttonPolymiddleware = useMemo(() => buttonEnhancers.map(enhancer => () => enhancer), [buttonEnhancers]); + + const chatLauncherButtonEnhancers = useMemoWithPrevious>( + (prevChatLauncherButtonEnhancers = []) => { + const chatLauncherButtonEnhancers = extractChatLauncherButtonEnhancer(polymiddleware); + + // Checks for array equality, return previous version if nothing has changed. + return prevChatLauncherButtonEnhancers.length === chatLauncherButtonEnhancers.length && + chatLauncherButtonEnhancers.every((middleware, index) => + Object.is(middleware, prevChatLauncherButtonEnhancers.at(index)) + ) + ? prevChatLauncherButtonEnhancers + : chatLauncherButtonEnhancers; + }, + [polymiddleware] + ); + + const chatLauncherButtonPolymiddleware = useMemo( + () => chatLauncherButtonEnhancers.map(enhancer => () => enhancer), + [chatLauncherButtonEnhancers] + ); + const errorBoxEnhancers = useMemoWithPrevious>( (prevErrorBoxEnhancers = []) => { const errorBoxEnhancers = extractErrorBoxEnhancer(polymiddleware); @@ -64,6 +105,21 @@ function PolymiddlewareComposer(props: PolymiddlewareComposerProps) { const errorBoxPolymiddleware = useMemo(() => errorBoxEnhancers.map(enhancer => () => enhancer), [errorBoxEnhancers]); + const popoverEnhancers = useMemoWithPrevious>( + (prevPopoverEnhancers = []) => { + const popoverEnhancers = extractPopoverEnhancer(polymiddleware); + + // Checks for array equality, return previous version if nothing has changed. + return prevPopoverEnhancers.length === popoverEnhancers.length && + popoverEnhancers.every((middleware, index) => Object.is(middleware, prevPopoverEnhancers.at(index))) + ? prevPopoverEnhancers + : popoverEnhancers; + }, + [polymiddleware] + ); + + const popoverPolymiddleware = useMemo(() => popoverEnhancers.map(enhancer => () => enhancer), [popoverEnhancers]); + // Didn't thoroughly think through this part yet, but I am using the first approach for now: // 1. for every type of middleware @@ -75,7 +131,13 @@ function PolymiddlewareComposer(props: PolymiddlewareComposerProps) { return ( - {children} + + + + {children} + + + ); } diff --git a/packages/api-middleware/src/buttonPolymiddleware.tsx b/packages/api-middleware/src/buttonPolymiddleware.tsx new file mode 100644 index 0000000000..e47ad8a5e6 --- /dev/null +++ b/packages/api-middleware/src/buttonPolymiddleware.tsx @@ -0,0 +1,126 @@ +import { forwardedRef, reactNode, refObject, validateProps } from '@msinternal/botframework-webchat-react-valibot'; +import React, { forwardRef, memo, useMemo, type ForwardedRef, type ReactNode, type RefObject } from 'react'; +import { function_, object, optional, picklist, 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: createButtonPolymiddleware, + extractEnhancer: extractButtonEnhancer, + Provider, + Proxy, + reactComponent: buttonComponent, + useBuildRenderCallback: useBuildRenderButtonCallback +} = templatePolymiddleware< + { + readonly appearance?: 'elevated' | 'flat' | undefined; + readonly size?: 'hero' | 'normal' | undefined; + }, + { + readonly children?: ReactNode | undefined; + readonly className?: string | undefined; + readonly forwardedRef?: ForwardedRef | undefined; + readonly onClick?: (() => void) | undefined; + readonly popoverTargetAction?: 'hide' | 'show' | 'toggle' | undefined; + readonly popoverTargetRef?: RefObject | undefined; + } +>('Button'); + +type ButtonPolymiddleware = InferMiddleware; +type ButtonPolymiddlewareHandler = InferHandler; +type ButtonPolymiddlewareHandlerResult = InferHandlerResult; +type ButtonPolymiddlewareProps = InferProps; +type ButtonPolymiddlewareRenderer = InferRenderer; +type ButtonPolymiddlewareRequest = InferRequest; +type ButtonPolymiddlewareProviderProps = InferProviderProps; + +const buttonPolymiddlewareProxyPropsSchema = pipe( + object({ + appearance: optional(picklist(['elevated', 'flat'])), + children: optional(reactNode()), + className: optional(string()), + forwardedRef: optional(forwardedRef()), + onClick: optional(function_()), + popoverTargetAction: optional(picklist(['hide', 'show', 'toggle'])), + popoverTargetRef: optional(refObject()), + size: optional(picklist(['hero', 'normal'])) + }), + readonly() +); + +type ButtonPolymiddlewareProxyProps = Readonly>; + +// A friendlier version than the organic . +const ButtonPolymiddlewareProxy = memo( + forwardRef(function ButtonPolymiddlewareProxy(props: ButtonPolymiddlewareProxyProps, ref: ForwardedRef) { + const { appearance, children, className, onClick, popoverTargetAction, popoverTargetRef, size } = validateProps( + buttonPolymiddlewareProxyPropsSchema, + props + ); + + const request = useMemo(() => Object.freeze({ appearance, size }), [appearance, size]); + + return ( + + {children} + + ); + }) +); + +const ButtonPolymiddlewareProvider = memo(function ButtonPolymiddlewareProvider({ + children, + middleware +}: ButtonPolymiddlewareProviderProps) { + // 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: createButtonPolymiddleware, + reactComponent: buttonComponent, + where: 'Button polymiddleware' + }), + ...middleware + ]), + [middleware] + ); + + return {children}; +}); + +export { + buttonComponent, + ButtonPolymiddlewareProvider, + ButtonPolymiddlewareProxy, + createButtonPolymiddleware, + extractButtonEnhancer, + useBuildRenderButtonCallback, + type ButtonPolymiddleware, + type ButtonPolymiddlewareHandler, + type ButtonPolymiddlewareHandlerResult, + type ButtonPolymiddlewareProps, + type ButtonPolymiddlewareProviderProps, + type ButtonPolymiddlewareProxyProps, + type ButtonPolymiddlewareRenderer, + type ButtonPolymiddlewareRequest +}; diff --git a/packages/api-middleware/src/chatLauncherButtonPolymiddleware.tsx b/packages/api-middleware/src/chatLauncherButtonPolymiddleware.tsx new file mode 100644 index 0000000000..16456a65e3 --- /dev/null +++ b/packages/api-middleware/src/chatLauncherButtonPolymiddleware.tsx @@ -0,0 +1,103 @@ +import { refObject, validateProps } from '@msinternal/botframework-webchat-react-valibot'; +import React, { memo, useMemo, type RefObject } from 'react'; +import { type EmptyObject } from 'type-fest'; +import { boolean, object, pipe, readonly, 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: createChatLauncherButtonPolymiddleware, + extractEnhancer: extractChatLauncherButtonEnhancer, + Provider, + Proxy, + reactComponent: chatLauncherButtonComponent, + useBuildRenderCallback: useBuildRenderChatLauncherButtonCallback +} = templatePolymiddleware< + EmptyObject, + { + readonly children?: never; + readonly hasMessage: boolean; + readonly popoverTargetRef: RefObject; + } +>('ChatLauncherButton'); + +type ChatLauncherButtonPolymiddleware = InferMiddleware; +type ChatLauncherButtonPolymiddlewareHandler = InferHandler; +type ChatLauncherButtonPolymiddlewareHandlerResult = InferHandlerResult; +type ChatLauncherButtonPolymiddlewareProps = InferProps; +type ChatLauncherButtonPolymiddlewareRenderer = InferRenderer; +type ChatLauncherButtonPolymiddlewareRequest = InferRequest; +type ChatLauncherButtonPolymiddlewareProviderProps = InferProviderProps; + +const chatLauncherButtonPolymiddlewareProxyPropsSchema = pipe( + object({ + hasMessage: boolean(), + popoverTargetRef: refObject() + }), + readonly() +); + +type ChatLauncherButtonPolymiddlewareProxyProps = Readonly< + InferInput +>; + +const EMPTY_OBJECT = Object.freeze({}); + +// A friendlier version than the organic . +const ChatLauncherButtonPolymiddlewareProxy = memo(function ChatLauncherButtonPolymiddlewareProxy( + props: ChatLauncherButtonPolymiddlewareProxyProps +) { + const { hasMessage, popoverTargetRef } = validateProps(chatLauncherButtonPolymiddlewareProxyPropsSchema, props); + + return ; +}); + +const ChatLauncherButtonPolymiddlewareProvider = memo(function ChatLauncherButtonPolymiddlewareProvider({ + children, + middleware +}: ChatLauncherButtonPolymiddlewareProviderProps) { + // 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: createChatLauncherButtonPolymiddleware, + reactComponent: chatLauncherButtonComponent, + where: 'Chat launcher polymiddleware' + }), + ...middleware + ]), + [middleware] + ); + + return {children}; +}); + +export { + chatLauncherButtonComponent, + ChatLauncherButtonPolymiddlewareProvider, + ChatLauncherButtonPolymiddlewareProxy, + createChatLauncherButtonPolymiddleware, + extractChatLauncherButtonEnhancer, + useBuildRenderChatLauncherButtonCallback, + type ChatLauncherButtonPolymiddleware, + type ChatLauncherButtonPolymiddlewareHandler, + type ChatLauncherButtonPolymiddlewareHandlerResult, + type ChatLauncherButtonPolymiddlewareProps, + type ChatLauncherButtonPolymiddlewareProviderProps, + type ChatLauncherButtonPolymiddlewareProxyProps, + type ChatLauncherButtonPolymiddlewareRenderer, + type ChatLauncherButtonPolymiddlewareRequest +}; diff --git a/packages/api-middleware/src/index.ts b/packages/api-middleware/src/index.ts index 9e0a8aeb8e..a43438f023 100644 --- a/packages/api-middleware/src/index.ts +++ b/packages/api-middleware/src/index.ts @@ -12,6 +12,34 @@ export { type ActivityPolymiddlewareRequest } from './activityPolymiddleware'; +export { + buttonComponent, + ButtonPolymiddlewareProxy, + createButtonPolymiddleware, + useBuildRenderButtonCallback, + type ButtonPolymiddleware, + type ButtonPolymiddlewareHandler, + type ButtonPolymiddlewareHandlerResult, + type ButtonPolymiddlewareProps, + type ButtonPolymiddlewareProxyProps, + type ButtonPolymiddlewareRenderer, + type ButtonPolymiddlewareRequest +} from './buttonPolymiddleware'; + +export { + chatLauncherButtonComponent, + ChatLauncherButtonPolymiddlewareProxy, + createChatLauncherButtonPolymiddleware, + useBuildRenderChatLauncherButtonCallback, + type ChatLauncherButtonPolymiddleware, + type ChatLauncherButtonPolymiddlewareHandler, + type ChatLauncherButtonPolymiddlewareHandlerResult, + type ChatLauncherButtonPolymiddlewareProps, + type ChatLauncherButtonPolymiddlewareProxyProps, + type ChatLauncherButtonPolymiddlewareRenderer, + type ChatLauncherButtonPolymiddlewareRequest +} from './chatLauncherButtonPolymiddleware'; + export { createErrorBoxPolymiddleware, errorBoxComponent, @@ -26,6 +54,20 @@ export { type ErrorBoxPolymiddlewareRequest } from './errorBoxPolymiddleware'; +export { + createPopoverPolymiddleware, + popoverComponent, + PopoverPolymiddlewareProxy, + useBuildRenderPopoverCallback, + type PopoverPolymiddleware, + type PopoverPolymiddlewareHandler, + type PopoverPolymiddlewareHandlerResult, + type PopoverPolymiddlewareProps, + type PopoverPolymiddlewareProxyProps, + type PopoverPolymiddlewareRenderer, + type PopoverPolymiddlewareRequest +} from './popoverPolymiddleware'; + // 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/popoverPolymiddleware.tsx b/packages/api-middleware/src/popoverPolymiddleware.tsx new file mode 100644 index 0000000000..4ae3dae8f8 --- /dev/null +++ b/packages/api-middleware/src/popoverPolymiddleware.tsx @@ -0,0 +1,108 @@ +import { forwardedRef, reactNode, validateProps } from '@msinternal/botframework-webchat-react-valibot'; +import React, { forwardRef, memo, useMemo, type ForwardedRef, type ReactNode } from 'react'; +import { literal, object, optional, picklist, 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: createPopoverPolymiddleware, + extractEnhancer: extractPopoverEnhancer, + Provider, + Proxy, + reactComponent: popoverComponent, + useBuildRenderCallback: useBuildRenderPopoverCallback +} = templatePolymiddleware< + { readonly type: 'nonmodal' }, + { + readonly children?: ReactNode | undefined; + readonly className?: string | undefined; + readonly forwardedRef?: ForwardedRef | undefined; + readonly popover?: 'auto' | 'hint' | 'manual' | undefined; + } +>('Popover'); + +type PopoverPolymiddleware = InferMiddleware; +type PopoverPolymiddlewareHandler = InferHandler; +type PopoverPolymiddlewareHandlerResult = InferHandlerResult; +type PopoverPolymiddlewareProps = InferProps; +type PopoverPolymiddlewareRenderer = InferRenderer; +type PopoverPolymiddlewareRequest = InferRequest; +type PopoverPolymiddlewareProviderProps = InferProviderProps; + +const popoverPolymiddlewareProxyPropsSchema = pipe( + object({ + children: optional(reactNode()), + className: optional(string()), + forwardedRef: optional(forwardedRef()), + popover: optional(picklist(['auto', 'hint', 'manual'])), + type: literal('nonmodal') + }), + readonly() +); + +type PopoverPolymiddlewareProxyProps = Readonly>; + +// A friendlier version than the organic . +const PopoverPolymiddlewareProxy = memo( + forwardRef(function PopoverPolymiddlewareProxy(props: PopoverPolymiddlewareProxyProps, ref: ForwardedRef) { + const { children, className, popover, type } = validateProps(popoverPolymiddlewareProxyPropsSchema, props); + + const request = useMemo(() => ({ type }), [type]); + + return ( + + {children} + + ); + }) +); + +const PopoverPolymiddlewareProvider = memo(function PopoverPolymiddlewareProvider({ + children, + middleware +}: PopoverPolymiddlewareProviderProps) { + // 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: createPopoverPolymiddleware, + reactComponent: popoverComponent, + where: 'Popover polymiddleware' + }), + ...middleware + ]), + [middleware] + ); + + return {children}; +}); + +export { + createPopoverPolymiddleware, + extractPopoverEnhancer, + popoverComponent, + PopoverPolymiddlewareProvider, + PopoverPolymiddlewareProxy, + useBuildRenderPopoverCallback, + type PopoverPolymiddleware, + type PopoverPolymiddlewareHandler, + type PopoverPolymiddlewareHandlerResult, + type PopoverPolymiddlewareProps, + type PopoverPolymiddlewareProviderProps, + type PopoverPolymiddlewareProxyProps, + type PopoverPolymiddlewareRenderer, + type PopoverPolymiddlewareRequest +}; diff --git a/packages/api-middleware/src/private/templatePolymiddleware.tsx b/packages/api-middleware/src/private/templatePolymiddleware.tsx index 9b9cd80541..02cb85a1e2 100644 --- a/packages/api-middleware/src/private/templatePolymiddleware.tsx +++ b/packages/api-middleware/src/private/templatePolymiddleware.tsx @@ -74,7 +74,7 @@ function templatePolymiddleware(name: string) { return false; } else if (!safeParse(middlewareSchema, result).success) { - console.warn(`botframework-webchat: ${name}.middleware must be created using factory function`); + console.warn(`botframework-webchat: ${name}.middleware must be created using factory function`, result); return false; } diff --git a/packages/api-middleware/src/types/Polymiddleware.ts b/packages/api-middleware/src/types/Polymiddleware.ts index 729d27c87d..9711740175 100644 --- a/packages/api-middleware/src/types/Polymiddleware.ts +++ b/packages/api-middleware/src/types/Polymiddleware.ts @@ -1,4 +1,12 @@ import { type ActivityPolymiddleware } from '../activityPolymiddleware'; +import { type ButtonPolymiddleware } from '../buttonPolymiddleware'; +import { type ChatLauncherButtonPolymiddleware } from '../chatLauncherButtonPolymiddleware'; import { type ErrorBoxPolymiddleware } from '../errorBoxPolymiddleware'; +import { type PopoverPolymiddleware } from '../popoverPolymiddleware'; -export type Polymiddleware = ActivityPolymiddleware | ErrorBoxPolymiddleware; +export type Polymiddleware = + | ActivityPolymiddleware + | ButtonPolymiddleware + | ChatLauncherButtonPolymiddleware + | ErrorBoxPolymiddleware + | PopoverPolymiddleware; 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/.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/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/src/StyleOptions.ts b/packages/api/src/StyleOptions.ts index aa846b7cf6..44f9427c54 100644 --- a/packages/api/src/StyleOptions.ts +++ b/packages/api/src/StyleOptions.ts @@ -894,7 +894,7 @@ type StyleOptions = { * * @default document.head */ - stylesRoot?: Node; + stylesRoot?: Node | undefined; /** * Border animation diff --git a/packages/api/src/boot/middleware.ts b/packages/api/src/boot/middleware.ts index 8d42eb1069..35fdde598a 100644 --- a/packages/api/src/boot/middleware.ts +++ b/packages/api/src/boot/middleware.ts @@ -16,6 +16,34 @@ export { type ActivityPolymiddlewareRequest } from '@msinternal/botframework-webchat-api-middleware'; +export { + buttonComponent, + ButtonPolymiddlewareProxy, + createButtonPolymiddleware, + useBuildRenderButtonCallback, + type ButtonPolymiddleware, + type ButtonPolymiddlewareHandler, + type ButtonPolymiddlewareHandlerResult, + type ButtonPolymiddlewareProps, + type ButtonPolymiddlewareProxyProps, + type ButtonPolymiddlewareRenderer, + type ButtonPolymiddlewareRequest +} from '@msinternal/botframework-webchat-api-middleware'; + +export { + chatLauncherButtonComponent, + ChatLauncherButtonPolymiddlewareProxy, + createChatLauncherButtonPolymiddleware, + useBuildRenderChatLauncherButtonCallback, + type ChatLauncherButtonPolymiddleware, + type ChatLauncherButtonPolymiddlewareHandler, + type ChatLauncherButtonPolymiddlewareHandlerResult, + type ChatLauncherButtonPolymiddlewareProps, + type ChatLauncherButtonPolymiddlewareProxyProps, + type ChatLauncherButtonPolymiddlewareRenderer, + type ChatLauncherButtonPolymiddlewareRequest +} from '@msinternal/botframework-webchat-api-middleware'; + export { createErrorBoxPolymiddleware, errorBoxComponent, @@ -30,4 +58,18 @@ export { type ErrorBoxPolymiddlewareRequest } from '@msinternal/botframework-webchat-api-middleware'; +export { + createPopoverPolymiddleware, + popoverComponent, + PopoverPolymiddlewareProxy, + useBuildRenderPopoverCallback, + type PopoverPolymiddleware, + type PopoverPolymiddlewareHandler, + type PopoverPolymiddlewareHandlerResult, + type PopoverPolymiddlewareProps, + type PopoverPolymiddlewareProxyProps, + type PopoverPolymiddlewareRenderer, + type PopoverPolymiddlewareRequest +} from '@msinternal/botframework-webchat-api-middleware'; + export { default as createActivityPolymiddlewareFromLegacy } from '../legacy/createActivityPolymiddlewareFromLegacy'; 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/.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/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/.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/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..418f2c1bd3 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/**/*" ], @@ -98,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", @@ -113,38 +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-webchat-base": "development", - "@msinternal/botframework-directlinejs": "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", @@ -171,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", @@ -212,6 +187,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", @@ -239,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/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/actual/middleware.ts b/packages/bundle/src/boot/actual/middleware.ts index 9641816786..7cb70c59a2 100644 --- a/packages/bundle/src/boot/actual/middleware.ts +++ b/packages/bundle/src/boot/actual/middleware.ts @@ -9,8 +9,35 @@ export { type ActivityPolymiddlewareProps, type ActivityPolymiddlewareProxyProps, type ActivityPolymiddlewareRenderer, - type ActivityPolymiddlewareRequest, - type Polymiddleware + type ActivityPolymiddlewareRequest +} from 'botframework-webchat-api/middleware'; + +export { + buttonComponent, + ButtonPolymiddlewareProxy, + createButtonPolymiddleware, + useBuildRenderButtonCallback, + type ButtonPolymiddleware, + type ButtonPolymiddlewareHandler, + type ButtonPolymiddlewareHandlerResult, + type ButtonPolymiddlewareProps, + type ButtonPolymiddlewareProxyProps, + type ButtonPolymiddlewareRenderer, + type ButtonPolymiddlewareRequest +} from 'botframework-webchat-api/middleware'; + +export { + chatLauncherButtonComponent, + ChatLauncherButtonPolymiddlewareProxy, + createChatLauncherButtonPolymiddleware, + useBuildRenderChatLauncherButtonCallback, + type ChatLauncherButtonPolymiddleware, + type ChatLauncherButtonPolymiddlewareHandler, + type ChatLauncherButtonPolymiddlewareHandlerResult, + type ChatLauncherButtonPolymiddlewareProps, + type ChatLauncherButtonPolymiddlewareProxyProps, + type ChatLauncherButtonPolymiddlewareRenderer, + type ChatLauncherButtonPolymiddlewareRequest } from 'botframework-webchat-api/middleware'; export { @@ -27,4 +54,18 @@ export { type ErrorBoxPolymiddlewareRequest } from 'botframework-webchat-api/middleware'; -export { createActivityPolymiddlewareFromLegacy } from 'botframework-webchat-api/middleware'; +export { + createPopoverPolymiddleware, + popoverComponent, + PopoverPolymiddlewareProxy, + useBuildRenderPopoverCallback, + type PopoverPolymiddleware, + type PopoverPolymiddlewareHandler, + type PopoverPolymiddlewareHandlerResult, + type PopoverPolymiddlewareProps, + type PopoverPolymiddlewareProxyProps, + type PopoverPolymiddlewareRenderer, + type PopoverPolymiddlewareRequest +} from 'botframework-webchat-api/middleware'; + +export { createActivityPolymiddlewareFromLegacy, type Polymiddleware } from 'botframework-webchat-api/middleware'; 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/src/index.ts b/packages/bundle/src/index.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/bundle/tsup.config.ts b/packages/bundle/tsup.config.ts index c885f394f9..f323c70d18 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' @@ -76,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/.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/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/.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/core/package.json b/packages/core/package.json index 647c86544a..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": { @@ -57,11 +67,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 +84,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/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 6b9b56867b..e54948934e 100644 --- a/packages/core/tsup.config.ts +++ b/packages/core/tsup.config.ts @@ -2,16 +2,13 @@ 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(' ')}` + 'botframework-webchat-core.internal': './src/internal/index.ts', + 'botframework-webchat-core.schema': './src/schema/index.ts' + } })); export default defineConfig([ @@ -21,7 +18,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/.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/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/.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/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/.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..52536cb56d --- /dev/null +++ b/packages/experience-chat-launcher/.gitignore @@ -0,0 +1,4 @@ +/*.tgz +/dist/ +/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..2374223aae --- /dev/null +++ b/packages/experience-chat-launcher/package.json @@ -0,0 +1,76 @@ +{ + "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" + ], + "homepage": "https://github.com/microsoft/BotFramework-WebChat/tree/main/packages/experience-chat-launcher#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": "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", + "bump:prod": "../../scripts/npm/bump-prod.sh", + "eslint": "npm run precommit", + "postversion": "../../scripts/npm/postversion.sh", + "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": "../../scripts/npm/preversion.sh", + "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", + "@msinternal/botframework-webchat-redux-store": "0.0.0-0", + "@msinternal/botframework-webchat-styles": "0.0.0-0", + "@types/node": "^22.13.4", + "typescript": "^5.7.3" + }, + "dependencies": { + "botframework-webchat-api": "0.0.0-0", + "botframework-webchat-component": "0.0.0-0", + "botframework-webchat-core": "0.0.0-0", + "merge-refs": "2.0.0", + "use-ref-from": "0.1.0", + "valibot": "1.1.0" + }, + "peerDependencies": { + "react": ">= 16.8.6" + } +} 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..ba230092c1 --- /dev/null +++ b/packages/experience-chat-launcher/src/private/ChatLauncher.tsx @@ -0,0 +1,73 @@ +import { validateProps } from '@msinternal/botframework-webchat-react-valibot'; +import { reduxStoreSchema } from '@msinternal/botframework-webchat-redux-store'; +import { useStyles } from '@msinternal/botframework-webchat-styles/react'; +import { + buttonComponent, + chatLauncherButtonComponent, + ChatLauncherButtonPolymiddlewareProxy, + createButtonPolymiddleware, + createChatLauncherButtonPolymiddleware, + createPopoverPolymiddleware, + popoverComponent, + type Polymiddleware +} from 'botframework-webchat-api/middleware'; +import { Composer } from 'botframework-webchat-component/component'; +import { directLineJSBotConnection } from 'botframework-webchat-core/schema'; +import cx from 'classnames'; +import React, { memo, useMemo, useRef } from 'react'; +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 ChatLauncherPopover from './private/ChatLauncherPopover'; +import Button from './private/polymiddleware/Button'; +import NonModalPopover from './private/polymiddleware/NonModalPopover'; + +const chatLauncherPropsSchema = pipe( + object({ + directLine: directLineJSBotConnection(), + nonce: optional(string()), + store: reduxStoreSchema, + stylesRoot: optional(instance(Node)) + }), + readonly() +); + +type ChatLauncherProps = InferInput; + +function ChatLauncher(props: ChatLauncherProps) { + const { directLine, nonce, store, stylesRoot } = validateProps(chatLauncherPropsSchema, props); + + const classNames = useStyles(styles); + const styleOptions = useMemo(() => ({ stylesRoot }), [stylesRoot]); + const popoverTargetRef = useRef(null); + + const polymiddleware = useMemo( + () => + Object.freeze([ + createButtonPolymiddleware( + () => request => buttonComponent(Button, { appearance: request.appearance, size: request.size }) + ), + createChatLauncherButtonPolymiddleware(() => () => chatLauncherButtonComponent(ChatLauncherButton)), + createPopoverPolymiddleware( + next => request => (request.type === 'nonmodal' ? popoverComponent(NonModalPopover) : next(request)) + ) + ]), + [] + ); + + 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..93805ef4d2 --- /dev/null +++ b/packages/experience-chat-launcher/src/private/private/ChatLauncherButton.module.css @@ -0,0 +1,9 @@ +:global(.webchat-experience-chat-launcher) .chat-launcher-button { + /* TODO: [P*] Is this a good naming system? Sorry I forgot if we should still go CSS BEM-alike or not. */ + --webchat-chatLauncherButton-margin: 40px; + + bottom: 0; + position: fixed; + margin: var(--webchat-chatLauncherButton-margin); + right: 0; +} 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..e86e609b4c --- /dev/null +++ b/packages/experience-chat-launcher/src/private/private/ChatLauncherButton.tsx @@ -0,0 +1,41 @@ +import { refObject, validateProps } from '@msinternal/botframework-webchat-react-valibot'; +import { useStyles } from '@msinternal/botframework-webchat-styles/react'; +import { ButtonPolymiddlewareProxy } from 'botframework-webchat-api/middleware'; +import React, { memo } from 'react'; +import { boolean, object, pipe, readonly, type InferInput } from 'valibot'; + +import styles from './ChatLauncherButton.module.css'; +import Icon from './Icon'; + +const chatLauncherButtonPropsSchema = pipe( + object({ + hasMessage: boolean(), + popoverTargetRef: refObject() + }), + readonly() +); + +type ChatLauncherButtonProps = InferInput; + +// TODO: [P0] Should we make an IconButton polymiddleware and ChatLauncherButton is based from that? +function ChatLauncherButton(props: ChatLauncherButtonProps) { + const { hasMessage, popoverTargetRef } = validateProps(chatLauncherButtonPropsSchema, props); + + const classNames = useStyles(styles); + + return ( + + {hasMessage ? : } + + ); +} + +ChatLauncherButton.displayName = 'ChatLauncherButton'; + +export default memo(ChatLauncherButton); +export { chatLauncherButtonPropsSchema, type ChatLauncherButtonProps }; diff --git a/packages/experience-chat-launcher/src/private/private/ChatLauncherPopover/DismissButton.tsx b/packages/experience-chat-launcher/src/private/private/ChatLauncherPopover/DismissButton.tsx new file mode 100644 index 0000000000..35e9889944 --- /dev/null +++ b/packages/experience-chat-launcher/src/private/private/ChatLauncherPopover/DismissButton.tsx @@ -0,0 +1,30 @@ +import { refObject, validateProps } from '@msinternal/botframework-webchat-react-valibot'; +import { ButtonPolymiddlewareProxy } from 'botframework-webchat-api/middleware'; +import React, { memo } from 'react'; +import { object, optional, pipe, readonly, type InferInput } from 'valibot'; + +import Icon from '../Icon'; + +const dismissButtonPropsSchema = pipe( + object({ + popoverRef: optional(refObject()) + }), + readonly() +); + +type DismissButtonProps = InferInput; + +function DismissButton(props: DismissButtonProps) { + const { popoverRef } = validateProps(dismissButtonPropsSchema, props); + + return ( + + + + ); +} + +DismissButton.displayName = 'ChatLauncherPopover/DismissButton'; + +export default memo(DismissButton); +export { dismissButtonPropsSchema, type DismissButtonProps }; diff --git a/packages/experience-chat-launcher/src/private/private/ChatLauncherPopover/RestartButton.tsx b/packages/experience-chat-launcher/src/private/private/ChatLauncherPopover/RestartButton.tsx new file mode 100644 index 0000000000..a802852bc1 --- /dev/null +++ b/packages/experience-chat-launcher/src/private/private/ChatLauncherPopover/RestartButton.tsx @@ -0,0 +1,22 @@ +import { ButtonPolymiddlewareProxy } from 'botframework-webchat-api/middleware'; +import React, { memo } from 'react'; +import { object, pipe, readonly, type InferInput } from 'valibot'; + +import Icon from '../Icon'; + +const restartButtonPropsSchema = pipe(object({}), readonly()); + +type RestartButtonProps = InferInput; + +function RestartButton(_: RestartButtonProps) { + return ( + + + + ); +} + +RestartButton.displayName = 'ChatLauncherPopover/RestartButton'; + +export default memo(RestartButton); +export { restartButtonPropsSchema, type RestartButtonProps }; diff --git a/packages/experience-chat-launcher/src/private/private/ChatLauncherPopover/TitleBar.module.css b/packages/experience-chat-launcher/src/private/private/ChatLauncherPopover/TitleBar.module.css new file mode 100644 index 0000000000..1837b32a4b --- /dev/null +++ b/packages/experience-chat-launcher/src/private/private/ChatLauncherPopover/TitleBar.module.css @@ -0,0 +1,20 @@ +:global(.webchat-experience-chat-launcher) .chat-launcher-popover__title-bar { + --webchat-spacingHorizontalL: var(--spacingHorizontalL, 16px); + --webchat-spacingHorizontalS: var(--spacingHorizontalS, 8px); + --webchat-spacingVerticalM: var(--spacingVerticalM, 12px); + --webchat-spacingVerticalS: var(--spacingVerticalS, 8px); + --webchat-colorNeutralStroke3: var(--colorNeutralStroke3, #f0f0f0); + --webchat-strokeWidthThin: var(--strokeWidthThin, 1px); + + --webchat-chatLauncherPopoverTitleBar-border-block-end-width: var(--webchat-strokeWidthThin); + --webchat-chatLauncherPopoverTitleBar-border-block-end-color: var(--webchat-colorNeutralStroke3); + --webchat-chatLauncherPopoverTitleBar-padding: var(--webchat-spacingVerticalM) var(--webchat-spacingHorizontalL); + + align-items: center; + border-block-end-color: var(--webchat-chatLauncherPopoverTitleBar-border-block-end-color); + border-block-end-style: solid; + border-block-end-width: var(--webchat-chatLauncherPopoverTitleBar-border-block-end-width); + display: grid; + grid-template-columns: 1fr auto auto; + padding: var(--webchat-chatLauncherPopoverTitleBar-padding); +} diff --git a/packages/experience-chat-launcher/src/private/private/ChatLauncherPopover/TitleBar.tsx b/packages/experience-chat-launcher/src/private/private/ChatLauncherPopover/TitleBar.tsx new file mode 100644 index 0000000000..8a6f4cd703 --- /dev/null +++ b/packages/experience-chat-launcher/src/private/private/ChatLauncherPopover/TitleBar.tsx @@ -0,0 +1,37 @@ +import { refObject, validateProps } from '@msinternal/botframework-webchat-react-valibot'; +import { useStyles } from '@msinternal/botframework-webchat-styles/react'; +import React, { forwardRef, memo } from 'react'; +import { object, optional, pipe, readonly, type InferInput } from 'valibot'; + +import DismissButton from './DismissButton'; +import RestartButton from './RestartButton'; +import styles from './TitleBar.module.css'; +import TitleText from './TitleText'; + +const titleBarPropsSchema = pipe( + object({ + popoverRef: optional(refObject()) + }), + readonly() +); + +type TitleBarProps = InferInput; + +function TitleBar(props: TitleBarProps) { + const { popoverRef } = validateProps(titleBarPropsSchema, props); + + const classNames = useStyles(styles); + + return ( +
+ {'Contoso agent'} + + +
+ ); +} + +TitleBar.displayName = 'ChatLauncherPopover/TitleBar'; + +export default memo(forwardRef(TitleBar)); +export { titleBarPropsSchema, type TitleBarProps }; diff --git a/packages/experience-chat-launcher/src/private/private/ChatLauncherPopover/TitleText.module.css b/packages/experience-chat-launcher/src/private/private/ChatLauncherPopover/TitleText.module.css new file mode 100644 index 0000000000..01dc4f7d74 --- /dev/null +++ b/packages/experience-chat-launcher/src/private/private/ChatLauncherPopover/TitleText.module.css @@ -0,0 +1,27 @@ +:global(.webchat-experience-chat-launcher) .chat-launcher-popover__title-text { + /* TODO: [P0] Refactor variables to global or upper layer. */ + --webchat-colorNeutralForeground1: var(--colorNeutralForeground1, #242424); + --webchat-fontFamilyBase: var( + --fontFamilyBase, + 'Segoe UI', + 'Segoe UI Web (West European)', + -apple-system, + BlinkMacSystemFont, + Roboto, + 'Helvetica Neue', + sans-serif + ); + --webchat-fontSizeBase400: var(--fontSizeBase400, 16px); + --webchat-fontWeightSemibold: var(--fontWeightSemibold, 600); + + /* TODO: [P*] Is this a good naming system? Sorry I forgot if we should still go CSS BEM-alike or not. */ + --webchat-chatLauncherPopoverTitleText-color: var(--webchat-colorNeutralForeground1); + --webchat-chatLauncherPopoverTitleText-fontFamily: var(--webchat-fontFamilyBase); + --webchat-chatLauncherPopoverTitleText-fontSize: var(--webchat-fontSizeBase400); + --webchat-chatLauncherPopoverTitleText-fontWeight: var(--webchat-fontWeightSemibold); + + color: var(--webchat-chatLauncherPopoverTitleText-color); + font-family: var(--webchat-chatLauncherPopoverTitleText-fontFamily); + font-size: var(--webchat-chatLauncherPopoverTitleText-fontSize); + font-weight: var(--webchat-chatLauncherPopoverTitleText-fontWeight); +} diff --git a/packages/experience-chat-launcher/src/private/private/ChatLauncherPopover/TitleText.tsx b/packages/experience-chat-launcher/src/private/private/ChatLauncherPopover/TitleText.tsx new file mode 100644 index 0000000000..db51d262c5 --- /dev/null +++ b/packages/experience-chat-launcher/src/private/private/ChatLauncherPopover/TitleText.tsx @@ -0,0 +1,28 @@ +import { reactNode, validateProps } from '@msinternal/botframework-webchat-react-valibot'; +import { useStyles } from '@msinternal/botframework-webchat-styles/react'; +import React, { memo } from 'react'; +import { object, optional, pipe, readonly, type InferInput } from 'valibot'; + +import styles from './TitleText.module.css'; + +const titleTextPropsSchema = pipe( + object({ + children: optional(reactNode()) + }), + readonly() +); + +type TitleTextProps = InferInput; + +function TitleText(props: TitleTextProps) { + const { children } = validateProps(titleTextPropsSchema, props); + + const classNames = useStyles(styles); + + return
{children}
; +} + +TitleText.displayName = 'ChatLauncherPopover/TitleText'; + +export default memo(TitleText); +export { titleTextPropsSchema, type TitleTextProps }; 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 new file mode 100644 index 0000000000..03921d717f --- /dev/null +++ b/packages/experience-chat-launcher/src/private/private/ChatLauncherPopover/index.tsx @@ -0,0 +1,39 @@ +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()); + +type ChatLauncherPopoverProps = InferInput; + +function ChatLauncherPopover(_: ChatLauncherPopoverProps, ref: ForwardedRef) { + const popoverRef = useRef(null); + + const classNames = useStyles(styles); + + return ( + // TODO: [P2] Is it correct to force-cast ref to HTMLDivElement? + +
+ + +
+
+ ); +} + +ChatLauncherPopover.displayName = 'ChatLauncherPopover'; + +export default memo(forwardRef(ChatLauncherPopover)); +export { chatLauncherPopoverPropsSchema, type ChatLauncherPopoverProps }; 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..a86bca41d9 --- /dev/null +++ b/packages/experience-chat-launcher/src/private/private/Icon.module.css @@ -0,0 +1,70 @@ +/* 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--button { + --webchat__icon--color: currentColor; + --webchat__icon--size: 20px; +} + +: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,'); +} + +:global(.webchat) .icon--dismiss { + --webchat__icon--mask: url('data:image/svg+xml;utf8,'); +} + +:global(.webchat) .icon--arrow-clockwise { + --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..54e553816f --- /dev/null +++ b/packages/experience-chat-launcher/src/private/private/Icon.tsx @@ -0,0 +1,42 @@ +/* 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-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'; + +import styles from './Icon.module.css'; + +const baseIconPropsSchema = pipe( + object({ + 'aria-label': optional(string()), + className: optional(string()), + mask: optional(string()) + }), + readonly() +); + +function BaseIcon(props: InferInput) { + const { 'aria-label': ariaLabel, 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/polymiddleware/Button.module.css b/packages/experience-chat-launcher/src/private/private/polymiddleware/Button.module.css new file mode 100644 index 0000000000..57749bcfb0 --- /dev/null +++ b/packages/experience-chat-launcher/src/private/private/polymiddleware/Button.module.css @@ -0,0 +1,67 @@ +/* TODO: [P0] Put this inside botframework-webchat-component as default implementation. */ +:global(.webchat-experience-chat-launcher) .button { + /* TODO: [P0] Expose some CSS classes to white-label. */ + --webchat-borderRadiusMedium: var(--borderRadiusMedium, 4px); + --webchat-colorNeutralBackground1: var(--colorNeutralBackground1, #ffffff); + --webchat-colorNeutralBackground1Hover: var(--colorNeutralBackground1Hover, #f5f5f5); + --webchat-colorNeutralBackground1Pressed: var(--colorNeutralBackground1Pressed, #e0e0e0); + --webchat-colorNeutralBackground1Selected: var(--colorNeutralBackground1Selected, #ebebeb); + --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: [P*] Is this a good naming system? Sorry I forgot if we should still go CSS BEM-alike or not. */ + /* TODO: [P0] Should we expose some customizable CSS variables? */ + /* TODO: [P0] Should it be customizable to anchor to bottom/left instead? */ + --webchat-button-background: var(--webchat-colorNeutralBackground1); + --webchat-button-borderColor: var(--webchat-colorNeutralStroke1); + --webchat-button-borderColor--active: var(--webchat-colorNeutralStroke1Pressed); + --webchat-button-borderColor--hover: var(--webchat-colorNeutralStroke1Hover); + --webchat-button-borderColor--disabled: var(--webchat-colorNeutralStrokeDisabled); + --webchat-button-borderRadius: var(--webchat-borderRadiusMedium); + --webchat-button-borderWidth: 0px; + --webchat-button-color: var(--webchat-colorNeutralForeground1); + --webchat-button-shadow: 0; + --webchat-button-size: 32px; + + appearance: none; + background: var(--webchat-button-background); + border-color: var(--webchat-button-borderColor); + border-radius: var(--webchat-button-borderRadius); + border-style: solid; + border-width: var(--webchat-button-borderWidth); + box-shadow: var(--webchat-button-shadow); + color: var(--webchat-button-color); + height: var(--webchat-button-size); + min-width: var(--webchat-button-size); + padding: 0; +} + +:global(.webchat-experience-chat-launcher) .button:hover { + --webchat-button-background: var(--webchat-colorNeutralBackground1Hover); + --webchat-button-borderColor: var(--webchat-button-borderColor--hover); +} + +:global(.webchat-experience-chat-launcher) .button:active { + --webchat-button-background: var(--webchat-colorNeutralBackground1Pressed); + --webchat-button-borderColor: var(--webchat-button-borderColor--active); +} + +:global(.webchat-experience-chat-launcher) .button:disabled { + --webchat-button-background: var(--webchat-colorNeutralBackground1Pressed); + --webchat-button-borderColor: var(--webchat-button-borderColor--disabled); +} + +:global(.webchat-experience-chat-launcher) .button.button--appearance-elevated { + /* We think elevated button is perfectly round, while others has rounded corners. */ + --webchat-button-borderRadius: var(--webchat-button-size); + --webchat-button-borderWidth: 1px; + --webchat-button-shadow: var(--webchat-shadow8); +} + +:global(.webchat-experience-chat-launcher) .button.button--size-hero { + --webchat-button-size: 60px; +} diff --git a/packages/experience-chat-launcher/src/private/private/polymiddleware/Button.tsx b/packages/experience-chat-launcher/src/private/private/polymiddleware/Button.tsx new file mode 100644 index 0000000000..1e058ee17c --- /dev/null +++ b/packages/experience-chat-launcher/src/private/private/polymiddleware/Button.tsx @@ -0,0 +1,74 @@ +import { forwardedRef, reactNode, refObject, validateProps } from '@msinternal/botframework-webchat-react-valibot'; +import { useStyles } from '@msinternal/botframework-webchat-styles/react'; +import cx from 'classnames'; +import React, { memo, useCallback, useEffect, useRef } from 'react'; +import { useRefFrom } from 'use-ref-from'; +import { function_, object, optional, picklist, pipe, readonly, string, type InferInput } from 'valibot'; + +import styles from './Button.module.css'; + +const buttonPropsSchema = pipe( + object({ + appearance: optional(picklist(['flat', 'elevated'])), + children: optional(reactNode()), + className: optional(string()), + forwardedRef: optional(forwardedRef()), + onClick: optional(function_()), + popoverTargetAction: optional(picklist(['hide', 'show', 'toggle'])), + popoverTargetRef: optional(refObject()), + size: optional(picklist(['hero', 'normal'])) + }), + readonly() +); + +type ButtonProps = InferInput; + +function Button(props: ButtonProps) { + const { appearance, children, className, onClick, popoverTargetAction, popoverTargetRef, size } = validateProps( + buttonPropsSchema, + props + ); + const classNames = useStyles(styles); + + const onClickRef = useRefFrom(onClick); + const ref = useRef(null); + + const handleClick = useCallback(() => onClickRef.current?.(), [onClickRef]); + + useEffect(() => { + if (ref.current) { + if (popoverTargetAction) { + ref.current.popoverTargetAction = popoverTargetAction; + } else { + ref.current.removeAttribute('popovertargetaction'); + } + + ref.current.popoverTargetElement = popoverTargetRef?.current || null; + } + }, [popoverTargetAction, popoverTargetRef, ref]); + + return ( + + ); +} + +Button.displayName = 'Button'; + +export default memo(Button); +export { buttonPropsSchema, type ButtonProps }; diff --git a/packages/experience-chat-launcher/src/private/private/polymiddleware/NonModalPopover.module.css b/packages/experience-chat-launcher/src/private/private/polymiddleware/NonModalPopover.module.css new file mode 100644 index 0000000000..7ac39bb18b --- /dev/null +++ b/packages/experience-chat-launcher/src/private/private/polymiddleware/NonModalPopover.module.css @@ -0,0 +1,20 @@ +:global(.webchat-experience-chat-launcher) .popover--non-modal { + /* TODO: [P0] Refactor variables to global or upper layer. */ + --webchat-colorNeutralBackground1: var(--colorNeutralBackground1, #ffffff); + --webchat-shadow64: var(--shadow64, 0 0 8px rgba(0, 0, 0, 0.12), 0 32px 64px rgba(0, 0, 0, 0.14)); + + /* TODO: [P*] Is this a good naming system? Sorry I forgot if we should still go CSS BEM-alike or not. */ + --webchat-popover-background: var(--webchat-colorNeutralBackground1); + --webchat-popover-box-shadow: var(--webchat-shadow64); + --webchat-popover-border-radius: 20px; + --webchat-popover-height: 610px; + --webchat-popover-width: 340px; + + background: var(--webchat-popover-background); + border: 0; + border-radius: var(--webchat-popover-border-radius); + box-shadow: var(--webchat-popover-box-shadow); + height: var(--webchat-popover-height); + padding: 0; + width: var(--webchat-popover-width); +} diff --git a/packages/experience-chat-launcher/src/private/private/polymiddleware/NonModalPopover.tsx b/packages/experience-chat-launcher/src/private/private/polymiddleware/NonModalPopover.tsx new file mode 100644 index 0000000000..3a3934aca5 --- /dev/null +++ b/packages/experience-chat-launcher/src/private/private/polymiddleware/NonModalPopover.tsx @@ -0,0 +1,49 @@ +import { forwardedRef, reactNode, validateProps } from '@msinternal/botframework-webchat-react-valibot'; +import { useStyles } from '@msinternal/botframework-webchat-styles/react'; +import cx from 'classnames'; +import mergeRefs from 'merge-refs'; +import React, { memo, useEffect, useRef } from 'react'; +import { object, optional, picklist, pipe, readonly, string, type InferInput } from 'valibot'; + +import styles from './NonModalPopover.module.css'; + +const popoverPropsSchema = pipe( + object({ + children: optional(reactNode()), + className: optional(string()), + forwardedRef: optional(forwardedRef()), + popover: optional(picklist(['auto', 'hint', 'manual'])) + }), + readonly() +); + +type PopoverProps = InferInput; + +function Popover(props: PopoverProps) { + const { className, children, forwardedRef, popover } = validateProps(popoverPropsSchema, props); + + const ref = useRef(null); + + const classNames = useStyles(styles); + + // React 16.8.6 does not support "popover" prop yet. + useEffect(() => { + ref && + typeof ref === 'object' && + 'current' in ref && + ref.current && + ref.current.setAttribute('popover', popover || ''); + }, [popover, ref]); + + return ( + // TODO: [P2] Is it correct to force-cast ref to HTMLDivElement? +
+ {children} +
+ ); +} + +Popover.displayName = 'Popover'; + +export default memo(Popover); +export { popoverPropsSchema, type PopoverProps }; 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..7d12dc2106 --- /dev/null +++ b/packages/experience-chat-launcher/tsup.config.ts @@ -0,0 +1,54 @@ +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'; + +// 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: ['botframework-webchat-component', 'react', 'react-dom'], + esbuildPlugins: [ + ...config.esbuildPlugins, + isomorphicReactPlugin, + injectCSSPlugin({ stylesPlaceholder: chatLauncherStyleContentPlaceholder }) + ] +})); + +export default defineConfig([ + { + ...commonConfig, + format: 'esm', + onSuccess: 'touch ./package.json' + }, + { + ...commonConfig, + format: 'cjs', + target: [...commonConfig.target, 'es2019'] + } +]); 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/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/src/components/theme/Theme.module.css b/packages/fluent-theme/src/components/theme/Theme.module.css index 25f1979a5a..1fa15fae83 100644 --- a/packages/fluent-theme/src/components/theme/Theme.module.css +++ b/packages/fluent-theme/src/components/theme/Theme.module.css @@ -19,6 +19,7 @@ --webchat-colorNeutralBackground1: var(--colorNeutralBackground1, #ffffff); --webchat-colorNeutralBackground1Hover: var(--colorNeutralBackground1Hover, #f5f5f5); --webchat-colorNeutralBackground1Pressed: var(--colorNeutralBackground1Pressed, #e0e0e0); + --webchat-colorNeutralBackground1Selected: var(--colorNeutralBackground1Selected, #ebebeb); --webchat-colorNeutralBackground3: var(--colorNeutralBackground3, #f5f5f5); --webchat-colorNeutralBackground4: var(--colorNeutralBackground4, #f0f0f0); --webchat-colorNeutralBackground5: var(--colorNeutralBackground5, #ebebeb); @@ -81,6 +82,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)); 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/.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-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/.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/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/src/forwardedRef.ts b/packages/react-valibot/src/forwardedRef.ts new file mode 100644 index 0000000000..1f9c90a4f7 --- /dev/null +++ b/packages/react-valibot/src/forwardedRef.ts @@ -0,0 +1,28 @@ +import { type ForwardedRef } from 'react'; +import { custom, nullable, safeParse, union, type CustomIssue, type CustomSchema, type ErrorMessage } from 'valibot'; + +import mutableRefObject from './mutableRefObject'; +import refCallback from './refCallback'; + +const forwardedRefSchema = nullable(union([refCallback(), mutableRefObject()])); + +function forwardedRef(): CustomSchema, undefined>; +function forwardedRef(): CustomSchema, undefined>; + +function forwardedRef< + T, + const TMessage extends ErrorMessage | undefined = ErrorMessage | undefined +>(message: TMessage): CustomSchema, TMessage>; + +function forwardedRef< + T, + const TMessage extends ErrorMessage | undefined = ErrorMessage | undefined +>(message?: TMessage): CustomSchema, TMessage> { + return custom, TMessage>( + value => safeParse(forwardedRefSchema, value).success, + // TODO: Probably lacking some undefined checks, thus, we need to force cast. + message as TMessage + ); +} + +export default forwardedRef; diff --git a/packages/react-valibot/src/index.ts b/packages/react-valibot/src/index.ts index d1e6fe3bb5..4e15bbea37 100644 --- a/packages/react-valibot/src/index.ts +++ b/packages/react-valibot/src/index.ts @@ -1,2 +1,7 @@ +export { default as forwardedRef } from './forwardedRef'; +export { default as mutableRefObject } from './mutableRefObject'; export { default as reactNode } from './reactNode'; +export { default as ref } from './ref'; +export { default as refCallback } from './refCallback'; +export { default as refObject } from './refObject'; export { default as validateProps } from './validateProps'; diff --git a/packages/react-valibot/src/mutableRefObject.ts b/packages/react-valibot/src/mutableRefObject.ts new file mode 100644 index 0000000000..efc2e625c3 --- /dev/null +++ b/packages/react-valibot/src/mutableRefObject.ts @@ -0,0 +1,25 @@ +import { type RefObject } from 'react'; +import { any, custom, object, safeParse, type CustomIssue, type CustomSchema, type ErrorMessage } from 'valibot'; + +const mutableRefObjectSchema = object({ current: any() }); + +function mutableRefObject(): CustomSchema, undefined>; +function mutableRefObject(): CustomSchema, undefined>; + +function mutableRefObject< + T, + const TMessage extends ErrorMessage | undefined = ErrorMessage | undefined +>(message: TMessage): CustomSchema, TMessage>; + +function mutableRefObject< + T, + const TMessage extends ErrorMessage | undefined = ErrorMessage | undefined +>(message?: TMessage): CustomSchema, TMessage> { + return custom, TMessage>( + value => safeParse(mutableRefObjectSchema, value).success, + // TODO: Probably lacking some undefined checks, thus, we need to force cast. + message as TMessage + ); +} + +export default mutableRefObject; diff --git a/packages/react-valibot/src/ref.ts b/packages/react-valibot/src/ref.ts new file mode 100644 index 0000000000..ed4db72581 --- /dev/null +++ b/packages/react-valibot/src/ref.ts @@ -0,0 +1,26 @@ +import { type Ref } from 'react'; +import { custom, nullable, safeParse, union, type CustomIssue, type CustomSchema, type ErrorMessage } from 'valibot'; + +import refCallback from './refCallback'; +import refObject from './refObject'; + +const refSchema = nullable(union([refCallback(), refObject()])); + +function ref(): CustomSchema, undefined>; +function ref(): CustomSchema, undefined>; + +function ref | undefined = ErrorMessage | undefined>( + message: TMessage +): CustomSchema, TMessage>; + +function ref | undefined = ErrorMessage | undefined>( + message?: TMessage +): CustomSchema, TMessage> { + return custom, TMessage>( + value => safeParse(refSchema, value).success, + // TODO: Probably lacking some undefined checks, thus, we need to force cast. + message as TMessage + ); +} + +export default ref; diff --git a/packages/react-valibot/src/refCallback.ts b/packages/react-valibot/src/refCallback.ts new file mode 100644 index 0000000000..7e0a2192b7 --- /dev/null +++ b/packages/react-valibot/src/refCallback.ts @@ -0,0 +1,25 @@ +import { type RefCallback } from 'react'; +import { custom, safeParse, type CustomIssue, type CustomSchema, type ErrorMessage } from 'valibot'; + +const refCallbackSchema = custom<(current: any) => void>(value => typeof value === 'function'); + +function refCallback(): CustomSchema, undefined>; +function refCallback(): CustomSchema, undefined>; + +function refCallback< + T, + const TMessage extends ErrorMessage | undefined = ErrorMessage | undefined +>(message: TMessage): CustomSchema, TMessage>; + +function refCallback< + T, + const TMessage extends ErrorMessage | undefined = ErrorMessage | undefined +>(message?: TMessage): CustomSchema, TMessage> { + return custom, TMessage>( + value => safeParse(refCallbackSchema, value).success, + // TODO: Probably lacking some undefined checks, thus, we need to force cast. + message as TMessage + ); +} + +export default refCallback; diff --git a/packages/react-valibot/src/refObject.ts b/packages/react-valibot/src/refObject.ts new file mode 100644 index 0000000000..e9394568d4 --- /dev/null +++ b/packages/react-valibot/src/refObject.ts @@ -0,0 +1,35 @@ +import { type RefObject } from 'react'; +import { + any, + custom, + object, + pipe, + readonly, + safeParse, + type CustomIssue, + type CustomSchema, + type ErrorMessage +} from 'valibot'; + +const refObjectSchema = pipe(object({ current: any() }), readonly()); + +function refObject(): CustomSchema, undefined>; +function refObject(): CustomSchema, undefined>; + +function refObject< + T, + const TMessage extends ErrorMessage | undefined = ErrorMessage | undefined +>(message: TMessage): CustomSchema, TMessage>; + +function refObject< + T, + const TMessage extends ErrorMessage | undefined = ErrorMessage | undefined +>(message?: TMessage): CustomSchema, TMessage> { + return custom, TMessage>( + value => safeParse(refObjectSchema, value).success, + // TODO: Probably lacking some undefined checks, thus, we need to force cast. + message as TMessage + ); +} + +export default refObject; 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/.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/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/src/index.ts b/packages/redux-store/src/index.ts index 9ae00a060e..191af8a2cf 100644 --- a/packages/redux-store/src/index.ts +++ b/packages/redux-store/src/index.ts @@ -1,2 +1,3 @@ +export { default as reduxStoreSchema } from './private/reduxStoreSchema'; export { default as ReduxStoreComposer } from './ReduxStoreComposer'; export { default as useSuggestedActionsHooks } from './suggestedActions/useSuggestedActionsHooks'; 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/.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 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 c08a496755..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,12 +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/\"" + }, + "pinDependencies": { + "jest-mock": [ + "28", + ">= 29 will pull in Node.js dependencies" + ] }, "localDependencies": {}, - "pinDependencies": {}, + "devDependencies": { + "concurrently": "^9.2.0", + "esbuild": "^0.25.8" + }, "dependencies": { "base64-arraybuffer": "1.0.2", "bent": "7.3.12", @@ -35,13 +46,10 @@ "core-js-pure": "3.44.0", "event-iterator": "2.0.0", "event-target-shim": "6.0.2", + "jest-mock": "28.1.3", "math-random": "2.0.1", "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/page-object/src/globals/testHelpers/ignoreReactDeprecation.js b/packages/test/page-object/src/globals/testHelpers/ignoreReactDeprecation.js new file mode 100644 index 0000000000..57180bf205 --- /dev/null +++ b/packages/test/page-object/src/globals/testHelpers/ignoreReactDeprecation.js @@ -0,0 +1,21 @@ +import { spyOn } from 'jest-mock'; + +export default function ignoreReactDeprecation(init = {}) { + const consoleError = console.error.bind(console); + + spyOn(console, 'error').mockImplementation((...args) => { + let shouldIgnore = false; + + if ( + init.defaultProps && + typeof args[0] === 'string' && + args[0].includes( + 'Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.' + ) + ) { + shouldIgnore = true; + } + + shouldIgnore || consoleError(...args); + }); +} diff --git a/packages/test/page-object/src/globals/testHelpers/index.js b/packages/test/page-object/src/globals/testHelpers/index.js index 0c1024b04a..b5fabd342f 100644 --- a/packages/test/page-object/src/globals/testHelpers/index.js +++ b/packages/test/page-object/src/globals/testHelpers/index.js @@ -1,40 +1,19 @@ -import * as accessibility from './accessibility/index'; -import * as activityGrouping from './activityGrouping/index'; -import * as speech from './speech/index'; -import * as token from './token/index'; -import * as transcriptNavigation from './transcriptNavigation'; -import arrayBufferToBase64 from './arrayBufferToBase64'; -import createAudioInputStreamFromRiffWavArrayBuffer from './speech/audioConfig/createAudioInputStreamFromRiffWavArrayBuffer'; -import createDirectLineEmulator from './createDirectLineEmulator'; -import createDirectLineWithTranscript from './createDirectLineWithTranscript'; -import createRenderWebChatWithHook from './createRenderWebChatWithHook'; -import createRunHookActivityMiddleware from './createRunHookActivityMiddleware'; -import createStore, { createStoreWithOptions } from './createStore'; -import depthFirstWalk from './depthFirstWalk'; -import getAllTextContents from './getAllTextContents'; -import iterateAsyncIterable from './iterateAsyncIterable'; -import shareObservable from './shareObservable'; -import sleep from './sleep'; -import stringToArrayBuffer from './stringToArrayBuffer'; - -export { - accessibility, - activityGrouping, - arrayBufferToBase64, - createAudioInputStreamFromRiffWavArrayBuffer, - createDirectLineEmulator, - createDirectLineWithTranscript, - createRenderWebChatWithHook, - createRunHookActivityMiddleware, - createStore, - createStoreWithOptions, - depthFirstWalk, - getAllTextContents, - iterateAsyncIterable, - shareObservable, - sleep, - speech, - stringToArrayBuffer, - token, - transcriptNavigation -}; +export * as accessibility from './accessibility/index'; +export * as activityGrouping from './activityGrouping/index'; +export { default as arrayBufferToBase64 } from './arrayBufferToBase64'; +export { default as createDirectLineEmulator } from './createDirectLineEmulator'; +export { default as createDirectLineWithTranscript } from './createDirectLineWithTranscript'; +export { default as createRenderWebChatWithHook } from './createRenderWebChatWithHook'; +export { default as createRunHookActivityMiddleware } from './createRunHookActivityMiddleware'; +export { default as createStore, createStoreWithOptions } from './createStore'; +export { default as depthFirstWalk } from './depthFirstWalk'; +export { default as getAllTextContents } from './getAllTextContents'; +export { default as ignoreReactDeprecation } from './ignoreReactDeprecation'; +export { default as iterateAsyncIterable } from './iterateAsyncIterable'; +export { default as shareObservable } from './shareObservable'; +export { default as sleep } from './sleep'; +export { default as createAudioInputStreamFromRiffWavArrayBuffer } from './speech/audioConfig/createAudioInputStreamFromRiffWavArrayBuffer'; +export * as speech from './speech/index'; +export { default as stringToArrayBuffer } from './stringToArrayBuffer'; +export * as token from './token/index'; +export * as transcriptNavigation from './transcriptNavigation'; 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 new file mode 100755 index 0000000000..7e8a86c082 --- /dev/null +++ b/scripts/npm/bump-dev.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +set -euo pipefail + +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 --save-dev $PACKAGES_TO_BUMP || true diff --git a/scripts/npm/bump-peer.sh b/scripts/npm/bump-peer.sh new file mode 100644 index 0000000000..c03b9743ac --- /dev/null +++ b/scripts/npm/bump-peer.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +set -euo pipefail + +PACKAGES_TO_BUMP=$(cat package.json | jq -r ' + (.pinDependencies // {}) as $P + | (.localDependencies // {} | keys) as $L + | (.peerDependencies // {}) + | 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 --save-peer $PACKAGES_TO_BUMP || true diff --git a/scripts/npm/bump-prod.sh b/scripts/npm/bump-prod.sh new file mode 100755 index 0000000000..fcd3180ea6 --- /dev/null +++ b/scripts/npm/bump-prod.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +set -euo pipefail + +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 diff --git a/scripts/npm/notify-build.sh b/scripts/npm/notify-build.sh new file mode 100755 index 0000000000..3c2615f1c9 --- /dev/null +++ b/scripts/npm/notify-build.sh @@ -0,0 +1,52 @@ +#!/bin/bash + +set -euo pipefail + +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) + + # Ignore error, let it rebuild + npm run build:run || true + + 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 new file mode 100755 index 0000000000..8150a2b529 --- /dev/null +++ b/scripts/npm/postversion.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +set -euo pipefail + +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 + | ( + .localDependencies // {} + | with_entries(select(.value == "peer") + | { key: .key, value: $V }) + ) as $L3 + | ( + (.dependencies // {}) + $L1 + | to_entries + | sort_by(.key) + | from_entries + ) as $D1 + | ( + (.devDependencies // {}) + $L2 + | to_entries + | sort_by(.key) + | from_entries + ) as $D2 + | ( + (.peerDependencies // {}) + $L3 + | to_entries + | sort_by(.key) + | from_entries + ) as $D3 + | . + { + dependencies: $D1, + devDependencies: $D2, + peerDependencies: $D3 + } +' > package-temp.json && mv package-temp.json package.json diff --git a/scripts/npm/preversion.sh b/scripts/npm/preversion.sh new file mode 100755 index 0000000000..f8a1d0d734 --- /dev/null +++ b/scripts/npm/preversion.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +set -euo pipefail + +cat package.json | jq ' + ( + .localDependencies // {} + | to_entries + | map([ + if .value == "production" then + "dependencies" + elif .value == "development" then + "devDependencies" + else + "peerDependencies" + end, + .key + ]) + ) as $P + | delpaths($P) +' > package-temp.json && mv package-temp.json package.json 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" 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 };