From 70cd0a00624d3cd1940d5d2b6c147b8eb7ea1a7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20Nyga=CC=8Ard?= Date: Mon, 12 Oct 2026 09:08:19 +0300 Subject: [PATCH 01/36] add CLAUDE.md --- CLAUDE.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..43c994c2 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md From 20570c5f7af86066e2236047b5fe09c1ae67e44b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20Nyga=CC=8Ard?= Date: Mon, 9 Mar 2026 18:52:17 +0200 Subject: [PATCH 02/36] chore(peer): drop React 17 support Raise the supported React peer range to ^18 || ^19 for v6 and update the compatibility docs accordingly. This is the planned v6 breaking change for React support, following the v5 line where React 17 remained supported through 5.3. --- .changeset/sour-bulldogs-hunt.md | 5 +++++ README.md | 1 + package.json | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 .changeset/sour-bulldogs-hunt.md diff --git a/.changeset/sour-bulldogs-hunt.md b/.changeset/sour-bulldogs-hunt.md new file mode 100644 index 00000000..b95cc994 --- /dev/null +++ b/.changeset/sour-bulldogs-hunt.md @@ -0,0 +1,5 @@ +--- +'react-share': major +--- + +Drop React 17 support in v6 and require React 18 or 19 via `peerDependencies`. diff --git a/README.md b/README.md index d5860201..33022d4e 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ npm install react-share | Version | Compatible React versions | | ------- | ----------------------------- | +| 6 | `18`, `19` | | 5 | `17`, `18`, `19` | | 4 | `^16.3`, `17`, `18` | | 3.0.1 | `^16.3.` | diff --git a/package.json b/package.json index d27a52c1..de08f95f 100644 --- a/package.json +++ b/package.json @@ -91,6 +91,6 @@ "jsonp": "^0.2.1" }, "peerDependencies": { - "react": "^17 || ^18 || ^19" + "react": "^18 || ^19" } } From 2dc35f21e332171cc8cbee5b3469727c250ac22f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20Nyga=CC=8Ard?= Date: Mon, 9 Mar 2026 18:53:27 +0200 Subject: [PATCH 03/36] refactor(x): remove Twitter aliases in favor of X exports Remove the deprecated TwitterShareButton and TwitterIcon aliases from the public package surface, leaving XShareButton and XIcon as the canonical X exports in v6. This follows the v5 deprecation where the X exports were introduced and the Twitter aliases were kept only for backwards compatibility. --- .changeset/odd-lions-share.md | 5 +++++ README.md | 1 - demo/Demo.tsx | 6 +++--- src/TwitterIcon.tsx | 9 -------- src/TwitterShareButton.tsx | 13 ------------ src/index.ts | 3 --- test/PackageBuilds.test.ts | 5 ++--- test/PackageExports.test.tsx | 1 - ...eButton.test.tsx => XShareButton.test.tsx} | 21 +++++++------------ 9 files changed, 17 insertions(+), 47 deletions(-) create mode 100644 .changeset/odd-lions-share.md delete mode 100644 src/TwitterIcon.tsx delete mode 100644 src/TwitterShareButton.tsx rename test/{TwitterShareButton.test.tsx => XShareButton.test.tsx} (81%) diff --git a/.changeset/odd-lions-share.md b/.changeset/odd-lions-share.md new file mode 100644 index 00000000..142e56b2 --- /dev/null +++ b/.changeset/odd-lions-share.md @@ -0,0 +1,5 @@ +--- +'react-share': major +--- + +Remove the deprecated `TwitterShareButton` and `TwitterIcon` aliases in v6. Use `XShareButton` and `XIcon` instead. diff --git a/README.md b/README.md index 33022d4e..9fd1f25c 100644 --- a/README.md +++ b/README.md @@ -456,7 +456,6 @@ Optional props: - `related` (`string[]`): Related accounts to recommend. - Notes: - X can ignore or rewrite some share fields. - - `TwitterShareButton` remains available as a deprecated alias for backwards compatibility. ```jsx import { XShareButton, XIcon } from "react-share"; diff --git a/demo/Demo.tsx b/demo/Demo.tsx index 62177b02..1e307f71 100644 --- a/demo/Demo.tsx +++ b/demo/Demo.tsx @@ -36,7 +36,6 @@ import { ThreadsShareButton, TumblrIcon, TumblrShareButton, - TwitterShareButton, ViberIcon, ViberShareButton, VKIcon, @@ -48,6 +47,7 @@ import { WorkplaceIcon, WorkplaceShareButton, XIcon, + XShareButton, } from '../src'; import './Demo.css'; @@ -83,14 +83,14 @@ export function Demo() {
- - +
diff --git a/src/TwitterIcon.tsx b/src/TwitterIcon.tsx deleted file mode 100644 index 15fc28ae..00000000 --- a/src/TwitterIcon.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import IconBase, { type IconProps } from './icons/IconBase'; - -export default function TwitterIcon(props: IconProps) { - return ( - - - - ); -} diff --git a/src/TwitterShareButton.tsx b/src/TwitterShareButton.tsx deleted file mode 100644 index 30e66bf3..00000000 --- a/src/TwitterShareButton.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { forwardRef, type ComponentPropsWithoutRef } from 'react'; -import XShareButton from './XShareButton'; - -type TwitterShareButtonProps = ComponentPropsWithoutRef; - -/** @deprecated Use XShareButton instead. */ -const TwitterShareButton = forwardRef((props, ref) => ( - -)); - -TwitterShareButton.displayName = 'TwitterShareButton'; - -export default TwitterShareButton; diff --git a/src/index.ts b/src/index.ts index e09c6d15..7039e934 100644 --- a/src/index.ts +++ b/src/index.ts @@ -46,10 +46,7 @@ export { default as TumblrIcon } from './TumblrIcon'; export { default as TumblrShareButton } from './TumblrShareButton'; /** @deprecated Share counts are deprecated and will be removed in v6. */ export { default as TumblrShareCount } from './TumblrShareCount'; -export { default as TwitterIcon } from './TwitterIcon'; export { default as XShareButton } from './XShareButton'; -/** @deprecated Use XShareButton instead. */ -export { default as TwitterShareButton } from './TwitterShareButton'; export { default as ViberIcon } from './ViberIcon'; export { default as ViberShareButton } from './ViberShareButton'; export { default as VKIcon } from './VKIcon'; diff --git a/test/PackageBuilds.test.ts b/test/PackageBuilds.test.ts index ec34eac9..0806bee5 100644 --- a/test/PackageBuilds.test.ts +++ b/test/PackageBuilds.test.ts @@ -32,7 +32,6 @@ const isUsableExport = (value: unknown) => const representativeExportNames = [ 'FacebookShareButton', 'XShareButton', - 'TwitterShareButton', 'FacebookIcon', 'PinterestShareCount', ] as const; @@ -62,10 +61,10 @@ describe('package builds', () => { const declarationBundle = await readFile(distPaths.types, 'utf8'); expect(declarationBundle).toContain('@deprecated Share counts are deprecated and will be removed in v6.'); - expect(declarationBundle).toContain('@deprecated Use XShareButton instead.'); expect(declarationBundle).toContain('FacebookShareCount'); expect(declarationBundle).toContain('PinterestShareCount'); - expect(declarationBundle).toContain('TwitterShareButton'); + expect(declarationBundle).not.toContain('TwitterShareButton'); + expect(declarationBundle).not.toContain('TwitterIcon'); }); it('exports the same public surface from the ESM and CJS builds', async () => { diff --git a/test/PackageExports.test.tsx b/test/PackageExports.test.tsx index 7d342383..318c78f6 100644 --- a/test/PackageExports.test.tsx +++ b/test/PackageExports.test.tsx @@ -11,7 +11,6 @@ const iconExportNames = Object.keys(ReactShare) const representativeExportNames = [ 'FacebookShareButton', 'XShareButton', - 'TwitterShareButton', 'FacebookShareCount', 'FacebookIcon', 'XIcon', diff --git a/test/TwitterShareButton.test.tsx b/test/XShareButton.test.tsx similarity index 81% rename from test/TwitterShareButton.test.tsx rename to test/XShareButton.test.tsx index 6b3ef182..055e418c 100644 --- a/test/TwitterShareButton.test.tsx +++ b/test/XShareButton.test.tsx @@ -3,19 +3,12 @@ import { fireEvent, render, screen } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; import XShareButton from '../src/XShareButton'; -import TwitterShareButton from '../src/TwitterShareButton'; - -const shareButtons = [ - ['XShareButton', XShareButton], - ['TwitterShareButton', TwitterShareButton], -] as const; - -describe.each(shareButtons)('%s', (_, ShareButtonComponent) => { +describe('XShareButton', () => { it('builds a share url with hashtags and related accounts', () => { const openSpy = vi.spyOn(window, 'open').mockReturnValue(null); render( - { via="reactshare" > Share on X - , + , ); fireEvent.click(screen.getByRole('button', { name: 'Share on X' })); @@ -39,7 +32,7 @@ describe.each(shareButtons)('%s', (_, ShareButtonComponent) => { const openSpy = vi.spyOn(window, 'open').mockReturnValue(null); render( - { url="https://example.com" > Share on X - , + , ); const button = screen.getByRole('button', { name: 'Share on X' }); @@ -68,9 +61,9 @@ describe.each(shareButtons)('%s', (_, ShareButtonComponent) => { const openSpy = vi.spyOn(window, 'open').mockReturnValue(null); render( - + Share on X - , + , ); fireEvent.click(screen.getByRole('button', { name: 'Share on X' })); From e72b45eecc9ed927b03f7803967df77cb5512d4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20Nyga=CC=8Ard?= Date: Mon, 9 Mar 2026 19:00:07 +0200 Subject: [PATCH 04/36] refactor(counts): remove deprecated share count exports Remove all share count components, the shared createShareCount implementation, and the jsonp dependency from the published package surface in v6. This follows the v5 deprecation where share counts were kept only as best-effort compatibility and documented for removal in the next major release. --- .changeset/mean-pans-marry.md | 5 + README.md | 51 +-------- package-lock.json | 85 ++++---------- package.json | 11 +- src/FacebookShareCount.ts | 22 ---- src/HatenaShareCount.ts | 25 ---- src/OKShareCount.ts | 58 ---------- src/PinterestShareCount.ts | 25 ---- src/RedditShareCount.ts | 26 ----- src/TumblrShareCount.ts | 25 ---- src/VKShareCount.ts | 44 ------- src/hocs/createShareCount.tsx | 53 --------- src/index.ts | 14 --- test/NetworkShareCounts.test.tsx | 170 ---------------------------- test/PackageBuilds.test.ts | 9 +- test/PackageExports.test.tsx | 1 - test/SocialMediaShareCount.test.tsx | 87 -------------- 17 files changed, 38 insertions(+), 673 deletions(-) create mode 100644 .changeset/mean-pans-marry.md delete mode 100644 src/FacebookShareCount.ts delete mode 100644 src/HatenaShareCount.ts delete mode 100644 src/OKShareCount.ts delete mode 100644 src/PinterestShareCount.ts delete mode 100644 src/RedditShareCount.ts delete mode 100644 src/TumblrShareCount.ts delete mode 100644 src/VKShareCount.ts delete mode 100644 src/hocs/createShareCount.tsx delete mode 100644 test/NetworkShareCounts.test.tsx delete mode 100644 test/SocialMediaShareCount.test.tsx diff --git a/.changeset/mean-pans-marry.md b/.changeset/mean-pans-marry.md new file mode 100644 index 00000000..ff9336ac --- /dev/null +++ b/.changeset/mean-pans-marry.md @@ -0,0 +1,5 @@ +--- +'react-share': major +--- + +Remove all deprecated share count exports in v6, including the underlying share count implementation and the `jsonp` dependency that previously powered them. diff --git a/README.md b/README.md index 9fd1f25c..0ea81031 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![NPM](https://img.shields.io/npm/v/react-share.svg)](https://www.npmjs.com/package/react-share) ![npm bundle size](https://img.shields.io/bundlephobia/min/react-share) [![downloads](https://img.shields.io/npm/dm/react-share.svg?label=monthly%20downloads)](https://www.npmjs.com/package/react-share) [![downloads](https://img.shields.io/npm/dt/react-share.svg?label=total%20downloads)](https://www.npmjs.com/package/react-share) -> Social media share buttons and share counts for your React apps. +> Social media share buttons for your React apps. ![Share buttons screenshot](example.png) @@ -42,7 +42,6 @@ npm install react-share - [Whatsapp](#whatsappsharebutton) - [Workplace](#workplacesharebutton) - [X](#xsharebutton) -- [share counts](#share-counts) (deprecated) - social media icons included in the library - supports also custom icons @@ -567,52 +566,6 @@ import { WorkplaceShareButton, WorkplaceIcon } from "react-share"; ; ``` -### Share counts - -Share count components are deprecated and will be removed in v6. They remain available in v5 as best-effort compatibility only, and upstream networks may stop returning counts without notice. - -```js -import { - FacebookShareCount, - HatenaShareCount, - OKShareCount, - PinterestShareCount, - RedditShareCount, - TumblrShareCount, - VKShareCount, -} from "react-share"; -``` - -Supported components: - -- `FacebookShareCount` -- `HatenaShareCount` -- `OKShareCount` -- `PinterestShareCount` -- `RedditShareCount` -- `TumblrShareCount` -- `VKShareCount` - -Shared props: - -- `url` (`string`, required): URL to look up. -- `children` (`(shareCount: number) => ReactNode`, optional): Render prop for custom output. -- Standard `` attributes such as `className`, `aria-*`, and `data-*`. - -Example: - -```jsx - -``` - -Custom rendering: - -```jsx - - {(shareCount) => {shareCount}} - -``` - ### Icons All exported icons share the same props: @@ -645,7 +598,7 @@ Standalone icons are raw SVG elements. Mark decorative icons with `aria-hidden=" ### About semantic versioning -This library uses the standard semver convention. However, the share buttons and and counts are prone to lots of changes that are not in control of this library. For example: if Facebook decides to change or deprecate it's API in a major way, this library will not get a major version bump just because of that. Keep this in mind when you are planning the maintenance of your application. +This library uses the standard semver convention. However, the share buttons are prone to lots of changes that are not in control of this library. For example: if Facebook decides to change or deprecate its API in a major way, this library will not get a major version bump just because of that. Keep this in mind when you are planning the maintenance of your application. ## License diff --git a/package-lock.json b/package-lock.json index ec2534ca..3ba91cf9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,23 +1,21 @@ { "name": "react-share", - "version": "5.2.2", + "version": "5.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "react-share", - "version": "5.2.2", + "version": "5.3.0", "license": "MIT", "dependencies": { - "classnames": "^2.3.2", - "jsonp": "^0.2.1" + "classnames": "^2.3.2" }, "devDependencies": { "@changesets/cli": "^2.30.0", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@types/assert": "^1.5.11", - "@types/jsonp": "^0.2.3", "@types/node": "^24.12.0", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", @@ -44,7 +42,7 @@ "vitest": "^4.0.18" }, "peerDependencies": { - "react": "^17 || ^18 || ^19" + "react": "^18 || ^19" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -159,7 +157,6 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -826,7 +823,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=20.19.0" }, @@ -867,7 +863,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=20.19.0" } @@ -1600,6 +1595,7 @@ "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", "dev": true, "optional": true, + "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" @@ -2348,7 +2344,6 @@ "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -2528,6 +2523,7 @@ "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -2548,6 +2544,7 @@ "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "dequal": "^2.0.3" } @@ -2619,7 +2616,8 @@ "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/assert": { "version": "1.5.11", @@ -2707,20 +2705,12 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, - "node_modules/@types/jsonp": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@types/jsonp/-/jsonp-0.2.3.tgz", - "integrity": "sha512-+bbowFC2n6TkpbQ369/tqL8pRPF4JRfRNf7Z6cpnAlLlEdy6YxG4HYZE7Qkeg2otcDbwNPIuSMux+P3pE4viFA==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/node": { "version": "24.12.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.0.tgz", "integrity": "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -2731,7 +2721,6 @@ "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -2742,7 +2731,6 @@ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "dev": true, "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -2792,7 +2780,6 @@ "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/types": "8.56.1", @@ -3332,7 +3319,6 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3450,6 +3436,7 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, @@ -3823,7 +3810,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -3843,7 +3829,8 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true, - "optional": true + "optional": true, + "peer": true }, "node_modules/call-bind": { "version": "1.0.8", @@ -4197,6 +4184,7 @@ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=6" } @@ -4239,7 +4227,8 @@ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/dunder-proto": { "version": "1.0.1", @@ -4537,7 +4526,6 @@ "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -4598,7 +4586,6 @@ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", - "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -6390,7 +6377,6 @@ "integrity": "sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@acemir/cssom": "^0.9.31", "@asamuzakjp/dom-selector": "^6.8.1", @@ -6481,27 +6467,6 @@ "graceful-fs": "^4.1.6" } }, - "node_modules/jsonp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/jsonp/-/jsonp-0.2.1.tgz", - "integrity": "sha512-pfog5gdDxPdV4eP7Kg87M8/bHgshlZ5pybl+yKxAnCZ5O7lCIn7Ixydj03wOlnDQesky2BPyA91SQ+5Y/mNwzw==", - "dependencies": { - "debug": "^2.1.3" - } - }, - "node_modules/jsonp/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/jsonp/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -6644,6 +6609,7 @@ "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -7274,7 +7240,6 @@ "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -7304,6 +7269,7 @@ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -7318,7 +7284,8 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/prop-types": { "version": "15.8.1", @@ -7384,7 +7351,6 @@ "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -7395,7 +7361,6 @@ "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -7561,7 +7526,6 @@ "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -7923,6 +7887,7 @@ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "optional": true, + "peer": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -8213,6 +8178,7 @@ "integrity": "sha512-ZpGR4Hy3+wBEzVEnHvstMvqpD/nABNelQn/z2r0fjVWGQsN3bpOLzQlqDxmb4CDZnXq5lpjnQ+mHQLAOpfM5iw==", "dev": true, "optional": true, + "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -8231,7 +8197,8 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true, - "optional": true + "optional": true, + "peer": true }, "node_modules/tinybench": { "version": "2.9.0", @@ -8291,7 +8258,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -8501,7 +8467,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8608,7 +8573,6 @@ "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -8729,7 +8693,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -8743,7 +8706,6 @@ "integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@vitest/expect": "4.0.18", "@vitest/mocker": "4.0.18", @@ -9048,7 +9010,6 @@ "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/package.json b/package.json index de08f95f..06282eb3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "react-share", "version": "5.3.0", - "description": "Social media share buttons and share counts for React.", + "description": "Social media share buttons for React.", "type": "module", "main": "./dist/index.cjs", "types": "./dist/index.d.ts", @@ -36,12 +36,11 @@ "media", "social-media", "share", - "button", - "count" + "button" ], "repository": { "type": "git", - "url": "git+https://github.com/nygardk/react-share.git" + "url": "https://github.com/nygardk/react-share" }, "author": { "name": "Klaus Nygård", @@ -60,7 +59,6 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@types/assert": "^1.5.11", - "@types/jsonp": "^0.2.3", "@types/node": "^24.12.0", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", @@ -87,8 +85,7 @@ "vitest": "^4.0.18" }, "dependencies": { - "classnames": "^2.3.2", - "jsonp": "^0.2.1" + "classnames": "^2.3.2" }, "peerDependencies": { "react": "^18 || ^19" diff --git a/src/FacebookShareCount.ts b/src/FacebookShareCount.ts deleted file mode 100644 index 9464e97f..00000000 --- a/src/FacebookShareCount.ts +++ /dev/null @@ -1,22 +0,0 @@ -import jsonp from 'jsonp'; - -import createShareCount from './hocs/createShareCount'; - -function getFacebookShareCount(shareUrl: string, callback: (shareCount?: number) => void) { - const endpoint = `https://graph.facebook.com/?id=${shareUrl}&fields=og_object{engagement}`; - - jsonp(endpoint, (err, data) => { - callback( - !err && data && data.og_object && data.og_object.engagement - ? data.og_object.engagement.count - : undefined, - ); - }); -} - -/** - * @deprecated Share counts are deprecated and will be removed in v6. - */ -const FacebookShareCount = createShareCount(getFacebookShareCount); - -export default FacebookShareCount; diff --git a/src/HatenaShareCount.ts b/src/HatenaShareCount.ts deleted file mode 100644 index ff42056e..00000000 --- a/src/HatenaShareCount.ts +++ /dev/null @@ -1,25 +0,0 @@ -import jsonp from 'jsonp'; - -import objectToGetParams from './utils/objectToGetParams'; -import createShareCount from './hocs/createShareCount'; - -function getHatenaShareCount(shareUrl: string, callback: (shareCount?: number) => void) { - const url = 'https://bookmark.hatenaapis.com/count/entry'; - - jsonp( - url + - objectToGetParams({ - url: shareUrl, - }), - (err, data) => { - callback(data ?? undefined); - }, - ); -} - -/** - * @deprecated Share counts are deprecated and will be removed in v6. - */ -const HatenaShareCount = createShareCount(getHatenaShareCount); - -export default HatenaShareCount; diff --git a/src/OKShareCount.ts b/src/OKShareCount.ts deleted file mode 100644 index dbcadcb6..00000000 --- a/src/OKShareCount.ts +++ /dev/null @@ -1,58 +0,0 @@ -import jsonp from 'jsonp'; - -import objectToGetParams from './utils/objectToGetParams'; -import createShareCount from './hocs/createShareCount'; - -declare global { - interface Window { - OK: { - Share: { - count: (index: number, _count: number) => void; - }; - callbacks: ((count?: number) => void)[]; - }; - ODKL: { - updateCount: (index: string, count: string) => void; - }; - } -} - -function getOKShareCount(shareUrl: string, callback: (shareCount?: number) => void) { - if (!window.OK) { - window.OK = { - Share: { - count: function count(index, _count) { - window.OK.callbacks[index]?.(_count); - }, - }, - callbacks: [], - }; - } - - const url = 'https://connect.ok.ru/dk'; - const index = window.OK.callbacks.length; - - window.ODKL = { - updateCount(index, count) { - const callbackIndex = index === '' ? 0 : parseInt(index.replace('react-share-', ''), 10); - window.OK.callbacks[callbackIndex]?.(count === '' ? undefined : parseInt(count, 10)); - }, - }; - window.OK.callbacks.push(callback); - - return jsonp( - url + - objectToGetParams({ - 'st.cmd': 'extLike', - uid: `react-share-${index}`, - ref: shareUrl, - }), - ); -} - -/** - * @deprecated Share counts are deprecated and will be removed in v6. - */ -const OKShareCount = createShareCount(getOKShareCount); - -export default OKShareCount; diff --git a/src/PinterestShareCount.ts b/src/PinterestShareCount.ts deleted file mode 100644 index 23b3966c..00000000 --- a/src/PinterestShareCount.ts +++ /dev/null @@ -1,25 +0,0 @@ -import jsonp from 'jsonp'; - -import objectToGetParams from './utils/objectToGetParams'; -import createShareCount from './hocs/createShareCount'; - -function getPinterestShareCount(shareUrl: string, callback: (shareCount?: number) => void) { - const url = 'https://api.pinterest.com/v1/urls/count.json'; - - jsonp( - url + - objectToGetParams({ - url: shareUrl, - }), - (err, data) => { - callback(data ? data.count : undefined); - }, - ); -} - -/** - * @deprecated Share counts are deprecated and will be removed in v6. - */ -const PinterestShareCount = createShareCount(getPinterestShareCount); - -export default PinterestShareCount; diff --git a/src/RedditShareCount.ts b/src/RedditShareCount.ts deleted file mode 100644 index 269feea5..00000000 --- a/src/RedditShareCount.ts +++ /dev/null @@ -1,26 +0,0 @@ -import jsonp from 'jsonp'; - -import createShareCount from './hocs/createShareCount'; - -function getRedditShareCount(shareUrl: string, callback: (shareCount?: number) => void) { - const endpoint = `https://www.reddit.com/api/info.json?limit=1&url=${shareUrl}`; - - jsonp(endpoint, { param: 'jsonp' }, (err, response) => { - callback( - !err && - response && - response.data && - response.data.children.length > 0 && - response.data.children[0].data.score - ? response.data.children[0].data.score - : undefined, - ); - }); -} - -/** - * @deprecated Share counts are deprecated and will be removed in v6. - */ -const RedditShareCount = createShareCount(getRedditShareCount); - -export default RedditShareCount; diff --git a/src/TumblrShareCount.ts b/src/TumblrShareCount.ts deleted file mode 100644 index ebf39ac4..00000000 --- a/src/TumblrShareCount.ts +++ /dev/null @@ -1,25 +0,0 @@ -import jsonp from 'jsonp'; - -import objectToGetParams from './utils/objectToGetParams'; -import createShareCount from './hocs/createShareCount'; - -function getTumblrShareCount(shareUrl: string, callback: (shareCount?: number) => void) { - const endpoint = 'https://api.tumblr.com/v2/share/stats'; - - return jsonp( - endpoint + - objectToGetParams({ - url: shareUrl, - }), - (err, data) => { - callback(!err && data && data.response ? data.response.note_count : undefined); - }, - ); -} - -/** - * @deprecated Share counts are deprecated and will be removed in v6. - */ -const TumblrShareCount = createShareCount(getTumblrShareCount); - -export default TumblrShareCount; diff --git a/src/VKShareCount.ts b/src/VKShareCount.ts deleted file mode 100644 index f9596ced..00000000 --- a/src/VKShareCount.ts +++ /dev/null @@ -1,44 +0,0 @@ -import jsonp from 'jsonp'; - -import objectToGetParams from './utils/objectToGetParams'; -import createShareCount from './hocs/createShareCount'; - -declare global { - interface Window { - VK: { - Share?: { - count: (index: number, count: number) => void; - }; - callbacks?: ((count?: number) => void)[]; - }; - } -} - -function getVKShareCount(shareUrl: string, callback: (shareCount?: number) => void) { - if (!window.VK) window.VK = {}; - window.VK.Share = { - count: (index, count) => window.VK.callbacks?.[index]?.(count), - }; - window.VK.callbacks = []; - - const url = 'https://vk.com/share.php'; - const index = window.VK.callbacks.length; - - window.VK.callbacks.push(callback); - - return jsonp( - url + - objectToGetParams({ - act: 'count', - index, - url: shareUrl, - }), - ); -} - -/** - * @deprecated Share counts are deprecated and will be removed in v6. - */ -const VKShareCount = createShareCount(getVKShareCount); - -export default VKShareCount; diff --git a/src/hocs/createShareCount.tsx b/src/hocs/createShareCount.tsx deleted file mode 100644 index ffedc9ab..00000000 --- a/src/hocs/createShareCount.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import cx from 'classnames'; -import { useIsMounted } from '../hooks/useIsMounted'; - -type ShareCountContainerProps = Omit, 'children'>; - -type SocialMediaShareCountProps = ShareCountContainerProps & { - children?: (shareCount: number) => React.ReactNode; - getCount: (url: string, callback: (shareCount?: number) => void) => void; - url: string; -}; - -type ShareCountProps = Omit; - -export function SocialMediaShareCount({ - children = (shareCount: number) => shareCount, - className, - getCount, - url, - ...rest -}: SocialMediaShareCountProps) { - const isMounted = useIsMounted(); - const [count, setCount] = useState(undefined); - const [isLoading, setIsLoading] = useState(false); - - useEffect(() => { - setIsLoading(true); - - getCount(url, count => { - if (isMounted()) { - setCount(count); - setIsLoading(false); - } - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [url]); - - return ( - - {!isLoading && count !== undefined && children(count)} - - ); -} - -export default function createShareCount(getCount: SocialMediaShareCountProps['getCount']) { - const ShareCount = (props: ShareCountProps) => ( - - ); - - ShareCount.displayName = `ShareCount(${getCount.name})`; - - return ShareCount; -} diff --git a/src/index.ts b/src/index.ts index 7039e934..2cfff186 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,12 +6,8 @@ export { default as FacebookIcon } from './FacebookIcon'; export { default as FacebookMessengerIcon } from './FacebookMessengerIcon'; export { default as FacebookMessengerShareButton } from './FacebookMessengerShareButton'; export { default as FacebookShareButton } from './FacebookShareButton'; -/** @deprecated Share counts are deprecated and will be removed in v6. */ -export { default as FacebookShareCount } from './FacebookShareCount'; export { default as HatenaIcon } from './HatenaIcon'; export { default as HatenaShareButton } from './HatenaShareButton'; -/** @deprecated Share counts are deprecated and will be removed in v6. */ -export { default as HatenaShareCount } from './HatenaShareCount'; export { default as InstapaperIcon } from './InstapaperIcon'; export { default as InstapaperShareButton } from './InstapaperShareButton'; export { default as LineIcon } from './LineIcon'; @@ -24,35 +20,25 @@ export { default as MailruIcon } from './MailruIcon'; export { default as MailruShareButton } from './MailruShareButton'; export { default as OKIcon } from './OKIcon'; export { default as OKShareButton } from './OKShareButton'; -/** @deprecated Share counts are deprecated and will be removed in v6. */ -export { default as OKShareCount } from './OKShareCount'; export { default as PinterestIcon } from './PinterestIcon'; export { default as PinterestShareButton } from './PinterestShareButton'; -/** @deprecated Share counts are deprecated and will be removed in v6. */ -export { default as PinterestShareCount } from './PinterestShareCount'; export { default as PocketIcon } from './PocketIcon'; export { default as PocketShareButton } from './PocketShareButton'; export { default as RedditIcon } from './RedditIcon'; export { default as RedditShareButton } from './RedditShareButton'; export { default as GabShareButton } from './GabShareButton'; export { default as GabIcon } from './GabIcon'; -/** @deprecated Share counts are deprecated and will be removed in v6. */ -export { default as RedditShareCount } from './RedditShareCount'; export { default as TelegramIcon } from './TelegramIcon'; export { default as TelegramShareButton } from './TelegramShareButton'; export { default as ThreadsIcon } from './ThreadsIcon'; export { default as ThreadsShareButton } from './ThreadsShareButton'; export { default as TumblrIcon } from './TumblrIcon'; export { default as TumblrShareButton } from './TumblrShareButton'; -/** @deprecated Share counts are deprecated and will be removed in v6. */ -export { default as TumblrShareCount } from './TumblrShareCount'; export { default as XShareButton } from './XShareButton'; export { default as ViberIcon } from './ViberIcon'; export { default as ViberShareButton } from './ViberShareButton'; export { default as VKIcon } from './VKIcon'; export { default as VKShareButton } from './VKShareButton'; -/** @deprecated Share counts are deprecated and will be removed in v6. */ -export { default as VKShareCount } from './VKShareCount'; export { default as WeiboIcon } from './WeiboIcon'; export { default as WeiboShareButton } from './WeiboShareButton'; export { default as WhatsappIcon } from './WhatsappIcon'; diff --git a/test/NetworkShareCounts.test.tsx b/test/NetworkShareCounts.test.tsx deleted file mode 100644 index 1a9aab18..00000000 --- a/test/NetworkShareCounts.test.tsx +++ /dev/null @@ -1,170 +0,0 @@ -import React from 'react'; -import { act, render, screen, waitFor } from '@testing-library/react'; -import { beforeEach, describe, expect, it, vi } from 'vitest'; - -vi.mock('jsonp', () => ({ - default: vi.fn(), -})); - -import jsonp from 'jsonp'; - -import FacebookShareCount from '../src/FacebookShareCount'; -import HatenaShareCount from '../src/HatenaShareCount'; -import OKShareCount from '../src/OKShareCount'; -import PinterestShareCount from '../src/PinterestShareCount'; -import RedditShareCount from '../src/RedditShareCount'; -import TumblrShareCount from '../src/TumblrShareCount'; -import VKShareCount from '../src/VKShareCount'; - -const jsonpMock = vi.mocked(jsonp); - -const getJsonpCallback = (callIndex: number) => { - const call = jsonpMock.mock.calls[callIndex]; - - if (!call) { - throw new Error(`Expected jsonp call ${callIndex} to exist`); - } - - const callback = call.at(-1); - - if (typeof callback !== 'function') { - throw new Error(`Expected jsonp call ${callIndex} to end with a callback`); - } - - return callback; -}; - -describe('network share count modules', () => { - beforeEach(() => { - jsonpMock.mockReset(); - Reflect.deleteProperty(window, 'OK'); - Reflect.deleteProperty(window, 'ODKL'); - Reflect.deleteProperty(window, 'VK'); - }); - - it('requests and resolves Facebook share counts', async () => { - render(); - - expect(jsonpMock).toHaveBeenCalledWith( - 'https://graph.facebook.com/?id=https://example.com&fields=og_object{engagement}', - expect.any(Function), - ); - - act(() => { - const callback = getJsonpCallback(0) as ( - err: Error | null, - data?: { og_object?: { engagement?: { count?: number } } }, - ) => void; - - callback(null, { og_object: { engagement: { count: 12 } } }); - }); - - await waitFor(() => expect(screen.getByText('12')).toBeInTheDocument()); - }); - - it('requests and resolves Hatena share counts', async () => { - render(); - - expect(jsonpMock).toHaveBeenCalledWith( - 'https://bookmark.hatenaapis.com/count/entry?url=https%3A%2F%2Fexample.com', - expect.any(Function), - ); - - act(() => { - const callback = getJsonpCallback(0) as (err: Error | null, data?: number) => void; - - callback(null, 8); - }); - - await waitFor(() => expect(screen.getByText('8')).toBeInTheDocument()); - }); - - it('requests and resolves Pinterest share counts', async () => { - render(); - - expect(jsonpMock).toHaveBeenCalledWith( - 'https://api.pinterest.com/v1/urls/count.json?url=https%3A%2F%2Fexample.com', - expect.any(Function), - ); - - act(() => { - const callback = getJsonpCallback(0) as ( - err: Error | null, - data?: { count?: number }, - ) => void; - - callback(null, { count: 15 }); - }); - - await waitFor(() => expect(screen.getByText('15')).toBeInTheDocument()); - }); - - it('requests and resolves Reddit share counts', async () => { - render(); - - expect(jsonpMock).toHaveBeenCalledWith( - 'https://www.reddit.com/api/info.json?limit=1&url=https://example.com', - { param: 'jsonp' }, - expect.any(Function), - ); - - act(() => { - const callback = getJsonpCallback(0) as ( - err: Error | null, - data?: { data?: { children: Array<{ data: { score?: number } }> } }, - ) => void; - - callback(null, { data: { children: [{ data: { score: 6 } }] } }); - }); - - await waitFor(() => expect(screen.getByText('6')).toBeInTheDocument()); - }); - - it('requests and resolves Tumblr share counts', async () => { - render(); - - expect(jsonpMock).toHaveBeenCalledWith( - 'https://api.tumblr.com/v2/share/stats?url=https%3A%2F%2Fexample.com', - expect.any(Function), - ); - - act(() => { - const callback = getJsonpCallback(0) as ( - err: Error | null, - data?: { response?: { note_count?: number } }, - ) => void; - - callback(null, { response: { note_count: 19 } }); - }); - - await waitFor(() => expect(screen.getByText('19')).toBeInTheDocument()); - }); - - it('registers OK callbacks and resolves counts through ODKL', async () => { - render(); - - expect(jsonpMock).toHaveBeenCalledWith( - 'https://connect.ok.ru/dk?st.cmd=extLike&uid=react-share-0&ref=https%3A%2F%2Fexample.com', - ); - - act(() => { - window.ODKL.updateCount('react-share-0', '11'); - }); - - await waitFor(() => expect(screen.getByText('11')).toBeInTheDocument()); - }); - - it('registers VK callbacks and resolves counts through VK.Share.count', async () => { - render(); - - expect(jsonpMock).toHaveBeenCalledWith( - 'https://vk.com/share.php?act=count&index=0&url=https%3A%2F%2Fexample.com', - ); - - act(() => { - window.VK.Share?.count(0, 4); - }); - - await waitFor(() => expect(screen.getByText('4')).toBeInTheDocument()); - }); -}); diff --git a/test/PackageBuilds.test.ts b/test/PackageBuilds.test.ts index 0806bee5..5b3a5b16 100644 --- a/test/PackageBuilds.test.ts +++ b/test/PackageBuilds.test.ts @@ -33,7 +33,7 @@ const representativeExportNames = [ 'FacebookShareButton', 'XShareButton', 'FacebookIcon', - 'PinterestShareCount', + 'XIcon', ] as const; describe('package builds', () => { @@ -57,12 +57,11 @@ describe('package builds', () => { await expect(access(distPaths.typesCjs)).resolves.toBeUndefined(); }); - it('marks share count exports as deprecated in the built type declarations', async () => { + it('does not emit removed share count or Twitter alias exports in the built type declarations', async () => { const declarationBundle = await readFile(distPaths.types, 'utf8'); - expect(declarationBundle).toContain('@deprecated Share counts are deprecated and will be removed in v6.'); - expect(declarationBundle).toContain('FacebookShareCount'); - expect(declarationBundle).toContain('PinterestShareCount'); + expect(declarationBundle).not.toContain('FacebookShareCount'); + expect(declarationBundle).not.toContain('PinterestShareCount'); expect(declarationBundle).not.toContain('TwitterShareButton'); expect(declarationBundle).not.toContain('TwitterIcon'); }); diff --git a/test/PackageExports.test.tsx b/test/PackageExports.test.tsx index 318c78f6..eaea67a4 100644 --- a/test/PackageExports.test.tsx +++ b/test/PackageExports.test.tsx @@ -11,7 +11,6 @@ const iconExportNames = Object.keys(ReactShare) const representativeExportNames = [ 'FacebookShareButton', 'XShareButton', - 'FacebookShareCount', 'FacebookIcon', 'XIcon', ] as const; diff --git a/test/SocialMediaShareCount.test.tsx b/test/SocialMediaShareCount.test.tsx deleted file mode 100644 index 669c71c8..00000000 --- a/test/SocialMediaShareCount.test.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import React from 'react'; -import { act, render, screen, waitFor } from '@testing-library/react'; -import { describe, expect, it, vi } from 'vitest'; - -import createShareCount, { SocialMediaShareCount } from '../src/hocs/createShareCount'; - -describe('SocialMediaShareCount', () => { - it('renders children with the resolved count', async () => { - let callback: ((shareCount?: number) => void) | undefined; - - render( - { - callback = cb; - }} - url="https://example.com" - > - {count => Count: {count}} - , - ); - - expect(screen.queryByText('Count: 12')).toBeNull(); - - act(() => { - callback?.(12); - }); - - await waitFor(() => expect(screen.getByText('Count: 12')).toBeInTheDocument()); - }); - - it('re-fetches when the url changes', async () => { - const getCount = vi - .fn<(url: string, callback: (shareCount?: number) => void) => void>() - .mockImplementation((url, callback) => { - callback(url === 'https://example.com/one' ? 1 : 2); - }); - - const { rerender } = render( - , - ); - - await waitFor(() => expect(screen.getByText('1')).toBeInTheDocument()); - - rerender(); - - await waitFor(() => expect(screen.getByText('2')).toBeInTheDocument()); - expect(getCount).toHaveBeenNthCalledWith( - 1, - 'https://example.com/one', - expect.any(Function), - ); - expect(getCount).toHaveBeenNthCalledWith( - 2, - 'https://example.com/two', - expect.any(Function), - ); - }); - - it('does not update state after unmount', () => { - let callback: ((shareCount?: number) => void) | undefined; - - const { unmount } = render( - { - callback = cb; - }} - url="https://example.com" - />, - ); - - unmount(); - - expect(() => { - act(() => { - callback?.(10); - }); - }).not.toThrow(); - }); - - it('creates share count components with the provided getCount implementation', async () => { - const ShareCount = createShareCount((_, callback) => callback(7)); - - render(); - - await waitFor(() => expect(screen.getByText('7')).toBeInTheDocument()); - }); -}); From b2c18ff5f8e1555b8d5a2cda5bc903180e92fe2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20Nyga=CC=8Ard?= Date: Mon, 9 Mar 2026 19:00:55 +0200 Subject: [PATCH 05/36] refactor(threads): remove legacy no-op props Remove the deprecated hashtags, related, and via props from ThreadsShareButton in v6. These props were ignored in v5 and documented only as compatibility shims ahead of their planned removal in the next major release. --- .changeset/wise-moles-travel.md | 5 +++++ README.md | 3 --- src/ThreadsShareButton.tsx | 34 ++++++++++------------------- test/ShareButtonComponents.test.tsx | 34 ++--------------------------- 4 files changed, 18 insertions(+), 58 deletions(-) create mode 100644 .changeset/wise-moles-travel.md diff --git a/.changeset/wise-moles-travel.md b/.changeset/wise-moles-travel.md new file mode 100644 index 00000000..e24a906a --- /dev/null +++ b/.changeset/wise-moles-travel.md @@ -0,0 +1,5 @@ +--- +'react-share': major +--- + +Remove the deprecated `hashtags`, `related`, and `via` props from `ThreadsShareButton` in v6. Those props were ignored in v5 and were kept only for compatibility ahead of this major release. diff --git a/README.md b/README.md index 0ea81031..33b9dd90 100644 --- a/README.md +++ b/README.md @@ -410,9 +410,6 @@ import { TelegramShareButton, TelegramIcon } from "react-share"; Optional props: - `title` (`string`): Title of the shared page. -- `hashtags` (`string[]`): Deprecated compatibility prop. Ignored in v5 and removed in v6. -- `related` (`string[]`): Deprecated compatibility prop. Ignored in v5 and removed in v6. -- `via` (`string`): Deprecated compatibility prop. Ignored in v5 and removed in v6. ```jsx import { ThreadsShareButton, ThreadsIcon } from "react-share"; diff --git a/src/ThreadsShareButton.tsx b/src/ThreadsShareButton.tsx index 903d1bcb..657586bc 100644 --- a/src/ThreadsShareButton.tsx +++ b/src/ThreadsShareButton.tsx @@ -16,33 +16,21 @@ function threadsLink(url: string, { title }: { title?: string }) { } type ThreadsShareButtonProps = Omit, 'title'> & { - /** @deprecated This prop is ignored and will be removed in v6. */ - hashtags?: string[]; - /** @deprecated This prop is ignored and will be removed in v6. */ - related?: string[]; title?: string; - /** @deprecated This prop is ignored and will be removed in v6. */ - via?: string; }; const ThreadsShareButton = forwardRef( - ({ hashtags, related, title, via, ...props }, ref) => { - void hashtags; - void related; - void via; - - return ( - - ); - }, + ({ title, ...props }, ref) => ( + + ), ); ThreadsShareButton.displayName = 'ThreadsShareButton'; diff --git a/test/ShareButtonComponents.test.tsx b/test/ShareButtonComponents.test.tsx index 2ee4dcf3..0feb0793 100644 --- a/test/ShareButtonComponents.test.tsx +++ b/test/ShareButtonComponents.test.tsx @@ -1,12 +1,11 @@ import React, { createRef } from 'react'; -import { fireEvent, render, screen } from '@testing-library/react'; -import { describe, expect, it, vi } from 'vitest'; +import { render, screen } from '@testing-library/react'; +import { describe, expect, it } from 'vitest'; import EmailIcon from '../src/EmailIcon'; import EmailShareButton from '../src/EmailShareButton'; import PinterestIcon from '../src/PinterestIcon'; import PinterestShareButton from '../src/PinterestShareButton'; -import ThreadsShareButton from '../src/ThreadsShareButton'; import TumblrShareButton from '../src/TumblrShareButton'; describe('share button components', () => { @@ -64,35 +63,6 @@ describe('share button components', () => { expect(button).not.toHaveAttribute('posttype'); }); - it('keeps legacy Threads props as ignored no-ops', () => { - const openSpy = vi.spyOn(window, 'open').mockReturnValue(null); - - render( - - Share on Threads - , - ); - - const button = screen.getByRole('button', { name: 'Share on Threads' }); - - fireEvent.click(button); - - expect(button).not.toHaveAttribute('hashtags'); - expect(button).not.toHaveAttribute('related'); - expect(button).not.toHaveAttribute('via'); - expect(openSpy).toHaveBeenCalledWith( - 'https://threads.net/intent/post?url=https%3A%2F%2Fexample.com&text=Example%20title', - '', - expect.any(String), - ); - }); - it('adds the email fallback label for icon-only buttons', () => { render( From 28da24808394dc359f05d42a8690301fed70d273 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20Nyga=CC=8Ard?= Date: Mon, 9 Mar 2026 19:06:43 +0200 Subject: [PATCH 06/36] refactor(share-buttons): rename v6 payload props Replace the ambiguous share button title prop with explicit v6 payload names: text, message, shareTitle, and postTitle depending on the target network. Keep htmlTitle as the native button tooltip/title attribute and update the demo, README, and button tests to reflect the renamed public API. --- .changeset/wise-years-rest.md | 12 ++++ README.md | 86 ++++++++++++++--------------- demo/Demo.tsx | 32 +++++------ src/BlueskyShareButton.tsx | 12 ++-- src/GabShareButton.tsx | 12 ++-- src/HatenaShareButton.tsx | 12 ++-- src/InstapaperShareButton.tsx | 12 ++-- src/LineShareButton.tsx | 12 ++-- src/LinkedinShareButton.tsx | 10 ++-- src/LivejournalShareButton.tsx | 12 ++-- src/MailruShareButton.tsx | 16 ++++-- src/OKShareButton.tsx | 16 ++++-- src/PocketShareButton.tsx | 12 ++-- src/RedditShareButton.tsx | 12 ++-- src/ShareButton.tsx | 4 +- src/TelegramShareButton.tsx | 12 ++-- src/ThreadsShareButton.tsx | 12 ++-- src/TumblrShareButton.tsx | 12 ++-- src/VKShareButton.tsx | 10 ++-- src/ViberShareButton.tsx | 12 ++-- src/WeiboShareButton.tsx | 18 ++++-- src/WhatsappShareButton.tsx | 15 +++-- src/XShareButton.tsx | 14 ++--- test/NetworkShareButtons.test.tsx | 32 +++++------ test/RedditShareButton.test.tsx | 2 +- test/ShareButtonComponents.test.tsx | 2 +- test/WhatsappShareButton.test.tsx | 2 +- test/XShareButton.test.tsx | 6 +- 28 files changed, 225 insertions(+), 196 deletions(-) create mode 100644 .changeset/wise-years-rest.md diff --git a/.changeset/wise-years-rest.md b/.changeset/wise-years-rest.md new file mode 100644 index 00000000..f87d8f4c --- /dev/null +++ b/.changeset/wise-years-rest.md @@ -0,0 +1,12 @@ +--- +'react-share': major +--- + +Replace the share button `title` prop with explicit v6 payload names: + +- `text` for Bluesky, Gab, Line, Telegram, Threads, and X +- `message` for WhatsApp and Viber +- `shareTitle` for Hatena, Instapaper, LiveJournal, LinkedIn, Mail.ru, OK, Pocket, Reddit, VK, and Weibo +- `postTitle` for Tumblr + +`htmlTitle` remains the native button tooltip/title attribute. diff --git a/README.md b/README.md index 33b9dd90..95e97196 100644 --- a/README.md +++ b/README.md @@ -92,13 +92,13 @@ Required: Optional on popup-based share buttons: - `openShareDialogOnClick` (`boolean`, default `true`): Opens the popup automatically on click. `EmailShareButton` does not expose this prop because it always uses `mailto:` navigation. -The native button `title` attribute uses `htmlTitle` so it does not conflict with network share props named `title`: +The native button `title` attribute uses `htmlTitle` so it does not conflict with share payload props such as `text`, `message`, `shareTitle`, or `postTitle`: ```jsx import { XShareButton, XIcon } from "react-share"; + ; ``` @@ -193,12 +193,12 @@ import { FacebookShareButton, FacebookIcon } from "react-share"; Optional props: -- `title` (`string`): Title of the shared page. +- `text` (`string`): Text to share before the URL. ```jsx import { GabShareButton, GabIcon } from "react-share"; - + ; ``` @@ -207,12 +207,12 @@ import { GabShareButton, GabIcon } from "react-share"; Optional props: -- `title` (`string`): Title of the shared page. +- `shareTitle` (`string`): Title sent in the share payload. ```jsx import { HatenaShareButton, HatenaIcon } from "react-share"; - + ; ``` @@ -221,14 +221,14 @@ import { HatenaShareButton, HatenaIcon } from "react-share"; Optional props: -- `title` (`string`): Title of the shared page. +- `shareTitle` (`string`): Title sent in the share payload. - `description` (`string`): Description of the shared page. ```jsx import { InstapaperShareButton, InstapaperIcon } from "react-share"; + ; ``` @@ -255,7 +255,7 @@ import { LineShareButton, LineIcon } from "react-share"; Optional props: -- `title` (`string`): Title of the shared page. +- `shareTitle` (`string`): Title sent in the share payload. - `summary` (`string`): Description of the shared page. - `source` (`string`): Source label such as your app or site name. - Notes: @@ -265,7 +265,7 @@ Optional props: import { LinkedinShareButton, LinkedinIcon } from "react-share"; + ; ``` @@ -381,12 +381,12 @@ import { PocketShareButton, PocketIcon } from "react-share"; Optional props: -- `title` (`string`): Title of the shared page. +- `shareTitle` (`string`): Title sent in the share payload. ```jsx import { RedditShareButton, RedditIcon } from "react-share"; - + ; ``` @@ -395,12 +395,12 @@ import { RedditShareButton, RedditIcon } from "react-share"; Optional props: -- `title` (`string`): Title of the shared page. +- `text` (`string`): Text to share before the URL. ```jsx import { TelegramShareButton, TelegramIcon } from "react-share"; - + ; ``` @@ -409,12 +409,12 @@ import { TelegramShareButton, TelegramIcon } from "react-share"; Optional props: -- `title` (`string`): Title of the shared page. +- `text` (`string`): Text to share before the URL. ```jsx import { ThreadsShareButton, ThreadsIcon } from "react-share"; - + ; ``` @@ -423,7 +423,7 @@ import { ThreadsShareButton, ThreadsIcon } from "react-share"; Optional props: -- `title` (`string`): Title of the shared page. +- `postTitle` (`string`): Title sent in the Tumblr post payload. - `tags` (`string[]`): Tags to attach to the post. - `caption` (`string`): Description shown with the shared page. - `posttype` (`string`, default `"link"`): Tumblr post type. @@ -432,7 +432,7 @@ Optional props: import { TumblrShareButton, TumblrIcon } from "react-share"; + ; ``` @@ -486,7 +486,7 @@ import { ViberShareButton, ViberIcon } from "react-share"; Optional props: -- `title` (`string`): Title of the shared page. +- `shareTitle` (`string`): Title sent in the share payload. - `image` (`string`): Absolute image URL to include in the share payload. - `noParse` (`boolean`): Prevent VK from retrieving URL information. - `noVkLinks` (`boolean`): Hide profile links in the window. Relevant on mobile devices. @@ -495,7 +495,7 @@ Optional props: import { VKShareButton, VKIcon } from "react-share"; + ; ``` diff --git a/demo/Demo.tsx b/demo/Demo.tsx index 1e307f71..7e6b80a8 100644 --- a/demo/Demo.tsx +++ b/demo/Demo.tsx @@ -64,7 +64,7 @@ export function Demo() { @@ -86,7 +86,7 @@ export function Demo() { @@ -118,7 +118,7 @@ export function Demo() { @@ -129,7 +129,7 @@ export function Demo() { @@ -164,7 +164,7 @@ export function Demo() { @@ -176,7 +176,7 @@ export function Demo() { @@ -187,7 +187,7 @@ export function Demo() { @@ -223,7 +223,7 @@ export function Demo() { @@ -234,7 +234,7 @@ export function Demo() { @@ -245,7 +245,7 @@ export function Demo() { @@ -278,7 +278,7 @@ export function Demo() { @@ -289,7 +289,7 @@ export function Demo() { @@ -301,7 +301,7 @@ export function Demo() { , + ShareButtonProps<{ text?: string; separator?: string }>, 'title' > & { separator?: string; - title?: string; + text?: string; }; -function blueskyLink(url: string, { title, separator }: { title?: string; separator?: string }) { +function blueskyLink(url: string, { text, separator }: { text?: string; separator?: string }) { assert(url, 'bluesky.url'); return ( 'https://bsky.app/intent/compose' + objectToGetParams({ - text: title ? title + separator + url : url, + text: text ? text + separator + url : url, }) ); } const BlueskyShareButton = forwardRef( - ({ separator, title, ...props }, ref) => ( + ({ separator, text, ...props }, ref) => ( , 'title'> & { - title?: string; +type GabShareButtonProps = Omit, 'title'> & { + text?: string; }; -function gabLink(url: string, { title }: { title?: string }) { +function gabLink(url: string, { text }: { text?: string }) { assert(url, 'gab.url'); return ( 'https://gab.com/compose' + objectToGetParams({ url, - text: title, + text, }) ); } const GabShareButton = forwardRef( - ({ title, ...props }, ref) => ( + ({ text, ...props }, ref) => ( , 'title'> & { - title?: string; +type HatenaShareButtonProps = Omit, 'title'> & { + shareTitle?: string; }; -function hatenaLink(url: string, { title }: { title?: string }) { +function hatenaLink(url: string, { shareTitle }: { shareTitle?: string }) { assert(url, 'hatena.url'); - return `http://b.hatena.ne.jp/add?mode=confirm&url=${url}&title=${title}`; + return `http://b.hatena.ne.jp/add?mode=confirm&url=${url}&title=${shareTitle}`; } const HatenaShareButton = forwardRef( - ({ title, ...props }, ref) => ( + ({ shareTitle, ...props }, ref) => ( , + ShareButtonProps<{ shareTitle?: string; description?: string }>, 'title' > & { description?: string; - title?: string; + shareTitle?: string; }; function instapaperLink( url: string, - { title, description }: { title?: string; description?: string }, + { shareTitle, description }: { shareTitle?: string; description?: string }, ) { assert(url, 'instapaper.url'); @@ -21,21 +21,21 @@ function instapaperLink( 'http://www.instapaper.com/hello2' + objectToGetParams({ url, - title, + title: shareTitle, description, }) ); } const InstapaperShareButton = forwardRef( - ({ description, title, ...props }, ref) => ( + ({ description, shareTitle, ...props }, ref) => ( , 'title'> & { - title?: string; +type LineShareButtonProps = Omit, 'title'> & { + text?: string; }; -function lineLink(url: string, { title }: { title?: string }) { +function lineLink(url: string, { text }: { text?: string }) { assert(url, 'line.url'); return ( 'https://social-plugins.line.me/lineit/share' + objectToGetParams({ url, - text: title, + text, }) ); } const LineShareButton = forwardRef( - ({ title, ...props }, ref) => ( + ({ text, ...props }, ref) => ( diff --git a/src/LinkedinShareButton.tsx b/src/LinkedinShareButton.tsx index 55110ffa..a179cdcd 100644 --- a/src/LinkedinShareButton.tsx +++ b/src/LinkedinShareButton.tsx @@ -5,7 +5,7 @@ import ShareButton, { type ShareButtonProps } from './ShareButton'; type Options = { /** The url-encoded title value that you wish you use. */ - title?: string; + shareTitle?: string; /** The url-encoded description that you wish you use. */ summary?: string; /** The url-encoded source of the content (e.g. your website or application name) */ @@ -14,23 +14,23 @@ type Options = { type LinkedinShareButtonProps = Omit, 'title'> & Options; -function linkedinLink(url: string, { title, summary, source }: Options) { +function linkedinLink(url: string, { shareTitle, summary, source }: Options) { assert(url, 'linkedin.url'); return ( 'https://linkedin.com/shareArticle' + - objectToGetParams({ url, mini: 'true', title, summary, source }) + objectToGetParams({ url, mini: 'true', title: shareTitle, summary, source }) ); } const LinkedinShareButton = forwardRef( - ({ source, summary, title, ...props }, ref) => ( + ({ shareTitle, source, summary, ...props }, ref) => ( diff --git a/src/LivejournalShareButton.tsx b/src/LivejournalShareButton.tsx index 36abe143..8ee175da 100644 --- a/src/LivejournalShareButton.tsx +++ b/src/LivejournalShareButton.tsx @@ -4,37 +4,37 @@ import objectToGetParams from './utils/objectToGetParams'; import ShareButton, { type ShareButtonProps } from './ShareButton'; type LivejournalShareButtonProps = Omit< - ShareButtonProps<{ title?: string; description?: string }>, + ShareButtonProps<{ shareTitle?: string; description?: string }>, 'title' > & { description?: string; - title?: string; + shareTitle?: string; }; function livejournalLink( url: string, - { title, description }: { title?: string; description?: string }, + { shareTitle, description }: { shareTitle?: string; description?: string }, ) { assert(url, 'livejournal.url'); return ( 'https://www.livejournal.com/update.bml' + objectToGetParams({ - subject: title, + subject: shareTitle, event: description, }) ); } const LivejournalShareButton = forwardRef( - ({ description, title, ...props }, ref) => ( + ({ description, shareTitle, ...props }, ref) => ( , + ShareButtonProps<{ shareTitle?: string; description?: string; imageUrl?: string }>, 'title' > & { description?: string; imageUrl?: string; - title?: string; + shareTitle?: string; }; const MailruShareButton = forwardRef( - ({ description, imageUrl, title, ...props }, ref) => ( + ({ description, imageUrl, shareTitle, ...props }, ref) => ( , + ShareButtonProps<{ shareTitle?: string; description?: string; image?: string }>, 'title' > & { description?: string; image?: string; - title?: string; + shareTitle?: string; }; const OKShareButton = forwardRef( - ({ description, image, title, ...props }, ref) => ( + ({ description, image, shareTitle, ...props }, ref) => ( , 'title'> & { - title?: string; +type PocketShareButtonProps = Omit, 'title'> & { + shareTitle?: string; }; -function pocketLink(url: string, { title }: { title?: string }) { +function pocketLink(url: string, { shareTitle }: { shareTitle?: string }) { assert(url, 'pocket.url'); return ( 'https://getpocket.com/save' + objectToGetParams({ url, - title, + title: shareTitle, }) ); } const PocketShareButton = forwardRef( - ({ title, ...props }, ref) => ( + ({ shareTitle, ...props }, ref) => ( diff --git a/src/RedditShareButton.tsx b/src/RedditShareButton.tsx index b6d683ca..dab59004 100644 --- a/src/RedditShareButton.tsx +++ b/src/RedditShareButton.tsx @@ -3,30 +3,30 @@ import assert from './utils/assert'; import objectToGetParams from './utils/objectToGetParams'; import ShareButton, { type ShareButtonProps } from './ShareButton'; -type RedditShareButtonProps = Omit, 'title'> & { - title?: string; +type RedditShareButtonProps = Omit, 'title'> & { + shareTitle?: string; }; -function redditLink(url: string, { title }: { title?: string }) { +function redditLink(url: string, { shareTitle }: { shareTitle?: string }) { assert(url, 'reddit.url'); return ( 'https://www.reddit.com/submit' + objectToGetParams({ url, - title, + title: shareTitle, }) ); } const RedditShareButton = forwardRef( - ({ title, ...props }, ref) => ( + ({ shareTitle, ...props }, ref) => ( extends NativeButtonProps { forwardedRef?: React.Ref; /** * Passes as the native `title` attribute for the `button` element. - * This uses `htmlTitle` instead of `title` because many share buttons - * already use `title` for the share payload sent to the target network. + * This uses `htmlTitle` instead of `title` so it does not collide with + * share payload props exposed by the button components. */ htmlTitle?: HTMLButtonElement['title']; networkName: string; diff --git a/src/TelegramShareButton.tsx b/src/TelegramShareButton.tsx index 8199109e..4a7c4239 100644 --- a/src/TelegramShareButton.tsx +++ b/src/TelegramShareButton.tsx @@ -3,30 +3,30 @@ import assert from './utils/assert'; import objectToGetParams from './utils/objectToGetParams'; import ShareButton, { type ShareButtonProps } from './ShareButton'; -type TelegramShareButtonProps = Omit, 'title'> & { - title?: string; +type TelegramShareButtonProps = Omit, 'title'> & { + text?: string; }; -function telegramLink(url: string, { title }: { title?: string }) { +function telegramLink(url: string, { text }: { text?: string }) { assert(url, 'telegram.url'); return ( 'https://telegram.me/share/url' + objectToGetParams({ url, - text: title, + text, }) ); } const TelegramShareButton = forwardRef( - ({ title, ...props }, ref) => ( + ({ text, ...props }, ref) => ( diff --git a/src/ThreadsShareButton.tsx b/src/ThreadsShareButton.tsx index 657586bc..96007a90 100644 --- a/src/ThreadsShareButton.tsx +++ b/src/ThreadsShareButton.tsx @@ -3,30 +3,30 @@ import assert from './utils/assert'; import objectToGetParams from './utils/objectToGetParams'; import ShareButton, { type ShareButtonProps } from './ShareButton'; -function threadsLink(url: string, { title }: { title?: string }) { +function threadsLink(url: string, { text }: { text?: string }) { assert(url, 'threads.url'); return ( 'https://threads.net/intent/post' + objectToGetParams({ url, - text: title, + text, }) ); } -type ThreadsShareButtonProps = Omit, 'title'> & { - title?: string; +type ThreadsShareButtonProps = Omit, 'title'> & { + text?: string; }; const ThreadsShareButton = forwardRef( - ({ title, ...props }, ref) => ( + ({ text, ...props }, ref) => ( diff --git a/src/TumblrShareButton.tsx b/src/TumblrShareButton.tsx index f469f664..63431dbf 100644 --- a/src/TumblrShareButton.tsx +++ b/src/TumblrShareButton.tsx @@ -6,11 +6,11 @@ import ShareButton, { type ShareButtonProps } from './ShareButton'; function tumblrLink( url: string, { - title, + postTitle, caption, tags, posttype, - }: { title?: string; caption?: string; tags?: string; posttype?: 'link' | string }, + }: { postTitle?: string; caption?: string; tags?: string; posttype?: 'link' | string }, ) { assert(url, 'tumblr.url'); @@ -18,7 +18,7 @@ function tumblrLink( 'https://www.tumblr.com/widgets/share/tool' + objectToGetParams({ canonicalUrl: url, - title, + title: postTitle, caption, tags, posttype, @@ -27,7 +27,7 @@ function tumblrLink( } type Options = { - title?: string; + postTitle?: string; caption?: string; posttype?: 'link' | string; }; @@ -36,14 +36,14 @@ type TumblrShareButtonProps = Omit, Options & { tags?: string[] }; const TumblrShareButton = forwardRef( - ({ caption, posttype, tags, title, ...props }, ref) => ( + ({ caption, postTitle, posttype, tags, ...props }, ref) => ( , 'title'> & Options; -function vkLink(url: string, { title, image, noParse, noVkLinks }: Options) { +function vkLink(url: string, { shareTitle, image, noParse, noVkLinks }: Options) { assert(url, 'vk.url'); return ( 'https://vk.com/share.php' + objectToGetParams({ url, - title, + title: shareTitle, image, noparse: noParse ? 1 : 0, no_vk_links: noVkLinks ? 1 : 0, @@ -28,14 +28,14 @@ function vkLink(url: string, { title, image, noParse, noVkLinks }: Options) { } const VKShareButton = forwardRef( - ({ image, noParse, noVkLinks, title, ...props }, ref) => ( + ({ image, noParse, noVkLinks, shareTitle, ...props }, ref) => ( , + ShareButtonProps<{ message?: string; separator?: string }>, 'title' > & { + message?: string; separator?: string; - title?: string; }; -function viberLink(url: string, { title, separator }: { title?: string; separator?: string }) { +function viberLink(url: string, { message, separator }: { message?: string; separator?: string }) { assert(url, 'viber.url'); return ( 'viber://forward' + objectToGetParams({ - text: title ? title + separator + url : url, + text: message ? message + separator + url : url, }) ); } const ViberShareButton = forwardRef( - ({ separator, title, ...props }, ref) => ( + ({ message, separator, ...props }, ref) => ( , 'title'> & { +type WeiboShareButtonProps = Omit< + ShareButtonProps<{ shareTitle?: string; image?: string }>, + 'title' +> & { image?: string; - title?: string; + shareTitle?: string; }; -function weiboLink(url: string, { title, image }: { title?: string; image?: string }) { +function weiboLink( + url: string, + { shareTitle, image }: { shareTitle?: string; image?: string }, +) { assert(url, 'weibo.url'); return ( 'http://service.weibo.com/share/share.php' + objectToGetParams({ url, - title, + title: shareTitle, pic: image, }) ); } const WeiboShareButton = forwardRef( - ({ image, title, ...props }, ref) => ( + ({ image, shareTitle, ...props }, ref) => ( , + ShareButtonProps<{ message?: string; separator?: string }>, 'title' > & { + message?: string; separator?: string; - title?: string; }; -function whatsappLink(url: string, { title, separator }: { title?: string; separator?: string }) { +function whatsappLink( + url: string, + { message, separator }: { message?: string; separator?: string }, +) { assert(url, 'whatsapp.url'); return ( 'https://api.whatsapp.com/send' + objectToGetParams({ - text: title ? title + separator + url : url, + text: message ? message + separator + url : url, }) ); } const WhatsappShareButton = forwardRef( - ({ separator, title, ...props }, ref) => ( + ({ message, separator, ...props }, ref) => ( 0 ? hashtags.join(',') : undefined, related: related.length > 0 ? related.join(',') : undefined, @@ -30,21 +30,21 @@ function xLink( type XShareButtonProps = Omit< ShareButtonProps<{ - title?: string; + text?: string; via?: string; hashtags?: string[]; related?: string[]; }>, 'title' > & { - title?: string; + text?: string; via?: string; hashtags?: string[]; related?: string[]; }; const XShareButton = forwardRef( - ({ hashtags, related, title, via, ...props }, ref) => ( + ({ hashtags, related, text, via, ...props }, ref) => ( ( networkLink={xLink} opts={{ hashtags, - title, + text, via, related, }} diff --git a/test/NetworkShareButtons.test.tsx b/test/NetworkShareButtons.test.tsx index 811cf0dd..d86fd9fa 100644 --- a/test/NetworkShareButtons.test.tsx +++ b/test/NetworkShareButtons.test.tsx @@ -34,7 +34,7 @@ const shareButtonCases: ShareButtonCase[] = [ Component: BlueskyShareButton, expectedUrl: 'https://bsky.app/intent/compose?text=Example%20title%20https%3A%2F%2Fexample.com', props: { - title: 'Example title', + text: 'Example title', url: 'https://example.com', }, }, @@ -53,7 +53,7 @@ const shareButtonCases: ShareButtonCase[] = [ Component: GabShareButton, expectedUrl: 'https://gab.com/compose?url=https%3A%2F%2Fexample.com&text=Example%20title', props: { - title: 'Example title', + text: 'Example title', url: 'https://example.com', }, }, @@ -62,7 +62,7 @@ const shareButtonCases: ShareButtonCase[] = [ Component: HatenaShareButton, expectedUrl: 'http://b.hatena.ne.jp/add?mode=confirm&url=https://example.com&title=Example', props: { - title: 'Example', + shareTitle: 'Example', url: 'https://example.com', }, }, @@ -73,7 +73,7 @@ const shareButtonCases: ShareButtonCase[] = [ 'http://www.instapaper.com/hello2?url=https%3A%2F%2Fexample.com&title=Example%20title&description=Example%20description', props: { description: 'Example description', - title: 'Example title', + shareTitle: 'Example title', url: 'https://example.com', }, }, @@ -83,7 +83,7 @@ const shareButtonCases: ShareButtonCase[] = [ expectedUrl: 'https://social-plugins.line.me/lineit/share?url=https%3A%2F%2Fexample.com&text=Example%20title', props: { - title: 'Example title', + text: 'Example title', url: 'https://example.com', }, }, @@ -95,7 +95,7 @@ const shareButtonCases: ShareButtonCase[] = [ props: { source: 'Example source', summary: 'Example summary', - title: 'Example title', + shareTitle: 'Example title', url: 'https://example.com', }, }, @@ -106,7 +106,7 @@ const shareButtonCases: ShareButtonCase[] = [ 'https://www.livejournal.com/update.bml?subject=Example%20title&event=Example%20description', props: { description: 'Example description', - title: 'Example title', + shareTitle: 'Example title', url: 'https://example.com', }, }, @@ -118,7 +118,7 @@ const shareButtonCases: ShareButtonCase[] = [ props: { description: 'Example description', imageUrl: 'https://example.com/image.png', - title: 'Example title', + shareTitle: 'Example title', url: 'https://example.com', }, }, @@ -130,7 +130,7 @@ const shareButtonCases: ShareButtonCase[] = [ props: { description: 'Example description', image: 'https://example.com/image.png', - title: 'Example title', + shareTitle: 'Example title', url: 'https://example.com', }, }, @@ -139,7 +139,7 @@ const shareButtonCases: ShareButtonCase[] = [ Component: PocketShareButton, expectedUrl: 'https://getpocket.com/save?url=https%3A%2F%2Fexample.com&title=Example%20title', props: { - title: 'Example title', + shareTitle: 'Example title', url: 'https://example.com', }, }, @@ -148,7 +148,7 @@ const shareButtonCases: ShareButtonCase[] = [ Component: TelegramShareButton, expectedUrl: 'https://telegram.me/share/url?url=https%3A%2F%2Fexample.com&text=Example%20title', props: { - title: 'Example title', + text: 'Example title', url: 'https://example.com', }, }, @@ -157,7 +157,7 @@ const shareButtonCases: ShareButtonCase[] = [ Component: ThreadsShareButton, expectedUrl: 'https://threads.net/intent/post?url=https%3A%2F%2Fexample.com&text=Example%20title', props: { - title: 'Example title', + text: 'Example title', url: 'https://example.com', }, }, @@ -168,8 +168,8 @@ const shareButtonCases: ShareButtonCase[] = [ 'https://www.tumblr.com/widgets/share/tool?canonicalUrl=https%3A%2F%2Fexample.com&title=Example%20title&caption=Example%20caption&tags=react%2Cshare&posttype=link', props: { caption: 'Example caption', + postTitle: 'Example title', tags: ['react', 'share'], - title: 'Example title', url: 'https://example.com', }, }, @@ -178,7 +178,7 @@ const shareButtonCases: ShareButtonCase[] = [ Component: ViberShareButton, expectedUrl: 'viber://forward?text=Example%20title%20https%3A%2F%2Fexample.com', props: { - title: 'Example title', + message: 'Example title', url: 'https://example.com', }, }, @@ -191,7 +191,7 @@ const shareButtonCases: ShareButtonCase[] = [ image: 'https://example.com/image.png', noParse: true, noVkLinks: true, - title: 'Example title', + shareTitle: 'Example title', url: 'https://example.com', }, }, @@ -202,7 +202,7 @@ const shareButtonCases: ShareButtonCase[] = [ 'http://service.weibo.com/share/share.php?url=https%3A%2F%2Fexample.com&title=Example%20title&pic=https%3A%2F%2Fexample.com%2Fimage.png', props: { image: 'https://example.com/image.png', - title: 'Example title', + shareTitle: 'Example title', url: 'https://example.com', }, }, diff --git a/test/RedditShareButton.test.tsx b/test/RedditShareButton.test.tsx index cd521ef0..063b230d 100644 --- a/test/RedditShareButton.test.tsx +++ b/test/RedditShareButton.test.tsx @@ -9,7 +9,7 @@ describe('RedditShareButton', () => { const openSpy = vi.spyOn(window, 'open').mockReturnValue(null); render( - + Share to Reddit , ); diff --git a/test/ShareButtonComponents.test.tsx b/test/ShareButtonComponents.test.tsx index 0feb0793..24b143db 100644 --- a/test/ShareButtonComponents.test.tsx +++ b/test/ShareButtonComponents.test.tsx @@ -48,8 +48,8 @@ describe('share button components', () => { caption="Example caption" openShareDialogOnClick={false} posttype="photo" + postTitle="Example title" tags={['react', 'share']} - title="Example title" url="https://example.com" > Share on Tumblr diff --git a/test/WhatsappShareButton.test.tsx b/test/WhatsappShareButton.test.tsx index 29d24f47..cacce31b 100644 --- a/test/WhatsappShareButton.test.tsx +++ b/test/WhatsappShareButton.test.tsx @@ -9,7 +9,7 @@ describe('WhatsappShareButton', () => { const openSpy = vi.spyOn(window, 'open').mockReturnValue(null); render( - + Share to WhatsApp , ); diff --git a/test/XShareButton.test.tsx b/test/XShareButton.test.tsx index 055e418c..f07811a1 100644 --- a/test/XShareButton.test.tsx +++ b/test/XShareButton.test.tsx @@ -11,7 +11,7 @@ describe('XShareButton', () => { @@ -36,7 +36,7 @@ describe('XShareButton', () => { hashtags={['react']} htmlTitle="Native title" related={['openai']} - title="Share title" + text="Share title" url="https://example.com" > Share on X @@ -61,7 +61,7 @@ describe('XShareButton', () => { const openSpy = vi.spyOn(window, 'open').mockReturnValue(null); render( - + Share on X , ); From f978a65cff3a0307b87529104493cdf4418346da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20Nyga=CC=8Ard?= Date: Mon, 9 Mar 2026 19:10:44 +0200 Subject: [PATCH 07/36] feat(helpers): add low-level share URL and popup helpers Export per-network build*ShareUrl helpers from the package root and expose openShareWindow(url, options) as the shared popup helper used internally by ShareButton. This keeps the button components as thin wrappers over the same helper implementations and adds direct source and built-package coverage for the new advanced-usage API. --- .changeset/small-keys-smash.md | 5 ++ README.md | 13 ++++++ src/BlueskyShareButton.tsx | 4 +- src/EmailShareButton.tsx | 4 +- src/FacebookMessengerShareButton.tsx | 4 +- src/FacebookShareButton.tsx | 4 +- src/GabShareButton.tsx | 4 +- src/HatenaShareButton.tsx | 4 +- src/InstapaperShareButton.tsx | 4 +- src/LineShareButton.tsx | 4 +- src/LinkedinShareButton.tsx | 4 +- src/LivejournalShareButton.tsx | 4 +- src/MailruShareButton.tsx | 4 +- src/OKShareButton.tsx | 4 +- src/PinterestShareButton.tsx | 4 +- src/PocketShareButton.tsx | 4 +- src/RedditShareButton.tsx | 4 +- src/ShareButton.tsx | 67 ++------------------------- src/TelegramShareButton.tsx | 4 +- src/ThreadsShareButton.tsx | 4 +- src/TumblrShareButton.tsx | 4 +- src/VKShareButton.tsx | 4 +- src/ViberShareButton.tsx | 4 +- src/WeiboShareButton.tsx | 4 +- src/WhatsappShareButton.tsx | 4 +- src/WorkplaceShareButton.tsx | 4 +- src/XShareButton.tsx | 4 +- src/index.ts | 25 ++++++++++ src/shareWindow.ts | 68 ++++++++++++++++++++++++++++ test/PackageBuilds.test.ts | 3 ++ test/PackageExports.test.tsx | 3 ++ test/ShareHelpers.test.ts | 60 ++++++++++++++++++++++++ 32 files changed, 254 insertions(+), 86 deletions(-) create mode 100644 .changeset/small-keys-smash.md create mode 100644 src/shareWindow.ts create mode 100644 test/ShareHelpers.test.ts diff --git a/.changeset/small-keys-smash.md b/.changeset/small-keys-smash.md new file mode 100644 index 00000000..e3461828 --- /dev/null +++ b/.changeset/small-keys-smash.md @@ -0,0 +1,5 @@ +--- +'react-share': minor +--- + +Add low-level helper exports for advanced usage in v6, including per-network `build*ShareUrl` helpers and `openShareWindow(url, options)`. diff --git a/README.md b/README.md index 95e97196..4c7c2378 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,19 @@ import { FacebookShareButton, FacebookIcon } from "react-share"; ; ``` +Low-level helpers are also available when you want to generate a share URL or open a popup yourself. Every popup-based button has a matching `build*ShareUrl` export, such as `buildFacebookShareUrl`, `buildLinkedinShareUrl`, `buildWhatsappShareUrl`, and `buildXShareUrl`. `openShareWindow` exposes the same popup behavior used internally by the button components: + +```jsx +import { buildXShareUrl, openShareWindow } from "react-share"; + +const url = buildXShareUrl(shareUrl, { + text: "Read this next", + hashtags: ["react", "share"], +}); + +openShareWindow(url, { width: 550, height: 400 }); +``` + #### BlueskyShareButton Optional props: diff --git a/src/BlueskyShareButton.tsx b/src/BlueskyShareButton.tsx index 1a0b6206..9fd9131a 100644 --- a/src/BlueskyShareButton.tsx +++ b/src/BlueskyShareButton.tsx @@ -22,13 +22,15 @@ function blueskyLink(url: string, { text, separator }: { text?: string; separato ); } +export const buildBlueskyShareUrl = blueskyLink; + const BlueskyShareButton = forwardRef( ({ separator, text, ...props }, ref) => ( ( ({ body, separator, subject, ...props }, ref) => ( { window.location.href = link; }} diff --git a/src/FacebookMessengerShareButton.tsx b/src/FacebookMessengerShareButton.tsx index b11e9b43..4490293c 100644 --- a/src/FacebookMessengerShareButton.tsx +++ b/src/FacebookMessengerShareButton.tsx @@ -27,6 +27,8 @@ function facebookMessengerLink(url: string, { appId, redirectUri, to }: Options) ); } +export const buildFacebookMessengerShareUrl = facebookMessengerLink; + const FacebookMessengerShareButton = forwardRef< HTMLButtonElement, FacebookMessengerShareButtonProps @@ -35,7 +37,7 @@ const FacebookMessengerShareButton = forwardRef< {...props} forwardedRef={ref} networkName="facebookmessenger" - networkLink={facebookMessengerLink} + networkLink={buildFacebookMessengerShareUrl} opts={{ appId, redirectUri, diff --git a/src/FacebookShareButton.tsx b/src/FacebookShareButton.tsx index c50ca170..cacfebf4 100644 --- a/src/FacebookShareButton.tsx +++ b/src/FacebookShareButton.tsx @@ -13,13 +13,15 @@ function facebookLink(url: string, { hashtag }: { hashtag?: string }) { return 'https://www.facebook.com/sharer/sharer.php' + objectToGetParams({ u: url, hashtag }); } +export const buildFacebookShareUrl = facebookLink; + const FacebookShareButton = forwardRef( ({ hashtag, ...props }, ref) => ( ( ({ text, ...props }, ref) => ( ( ({ shareTitle, ...props }, ref) => ( ( ({ description, shareTitle, ...props }, ref) => ( ( ({ text, ...props }, ref) => ( ( ({ shareTitle, source, summary, ...props }, ref) => ( ( ({ description, shareTitle, ...props }, ref) => ( , 'title' @@ -39,7 +41,7 @@ const MailruShareButton = forwardRef( {...props} forwardedRef={ref} networkName="mailru" - networkLink={mailruLink} + networkLink={buildMailruShareUrl} opts={{ shareTitle, description, diff --git a/src/OKShareButton.tsx b/src/OKShareButton.tsx index b90516f2..8e7479a0 100644 --- a/src/OKShareButton.tsx +++ b/src/OKShareButton.tsx @@ -24,6 +24,8 @@ function okLink( ); } +export const buildOKShareUrl = okLink; + type OKShareButtonProps = Omit< ShareButtonProps<{ shareTitle?: string; description?: string; image?: string }>, 'title' @@ -39,7 +41,7 @@ const OKShareButton = forwardRef( {...props} forwardedRef={ref} networkName="ok" - networkLink={okLink} + networkLink={buildOKShareUrl} opts={{ shareTitle, description, diff --git a/src/PinterestShareButton.tsx b/src/PinterestShareButton.tsx index 8118191c..6c55084b 100644 --- a/src/PinterestShareButton.tsx +++ b/src/PinterestShareButton.tsx @@ -29,13 +29,15 @@ function pinterestLink(url: string, { media, description, pinId }: PinterestShar ); } +export const buildPinterestShareUrl = pinterestLink; + const PinterestShareButton = forwardRef( ({ description, media, pinId, ...props }, ref) => ( ( ({ shareTitle, ...props }, ref) => ( ( ({ shareTitle, ...props }, ref) => ( = (url: string, options: LinkOptions) => string; -type WindowPosition = 'windowCenter' | 'screenCenter'; type NativeButtonProps = Omit, 'onClick'>; type IconShapeProps = { borderRadius?: number; round?: boolean }; @@ -41,16 +41,6 @@ const isPromise = (obj: unknown): obj is Promise => 'then' in obj && typeof (obj as Promise).then === 'function'; -const getBoxPositionOnWindowCenter = (width: number, height: number) => ({ - left: window.outerWidth / 2 + (window.screenX || window.screenLeft || 0) - width / 2, - top: window.outerHeight / 2 + (window.screenY || window.screenTop || 0) - height / 2, -}); - -const getBoxPositionOnScreenCenter = (width: number, height: number) => ({ - top: (window.screen.height - height) / 2, - left: (window.screen.width - width) / 2, -}); - function getButtonBorderRadius(children: React.ReactNode) { const childNodes = Children.toArray(children); @@ -89,50 +79,6 @@ function hasTextContent(children: React.ReactNode): boolean { }); } -function windowOpen( - url: string, - { height, width, ...configRest }: { height: number; width: number; [key: string]: unknown }, - onClose?: (dialog: Window | null) => void, -) { - const config: { [key: string]: string | number } = { - height, - width, - location: 'no', - toolbar: 'no', - status: 'no', - directories: 'no', - menubar: 'no', - scrollbars: 'yes', - resizable: 'no', - centerscreen: 'yes', - chrome: 'yes', - ...configRest, - }; - - const shareDialog = window.open( - url, - '', - Object.keys(config) - .map(key => `${key}=${config[key]}`) - .join(', '), - ); - - if (onClose) { - const interval = window.setInterval(() => { - try { - if (shareDialog === null || shareDialog.closed) { - window.clearInterval(interval); - onClose(shareDialog); - } - } catch (e) { - console.error(e); - } - }, 1000); - } - - return shareDialog; -} - export interface Props extends NativeButtonProps { /** * Takes a function that returns a Promise to be fulfilled before calling @@ -231,15 +177,12 @@ export default function ShareButton> } if (openShareDialogOnClick) { - const windowConfig = { + openShareWindow(link, { height: windowHeight, + onClose: onShareWindowClose, + position: windowPosition, width: windowWidth, - ...(windowPosition === 'windowCenter' - ? getBoxPositionOnWindowCenter(windowWidth, windowHeight) - : getBoxPositionOnScreenCenter(windowWidth, windowHeight)), - }; - - windowOpen(link, windowConfig, onShareWindowClose); + }); } if (onClick) { diff --git a/src/TelegramShareButton.tsx b/src/TelegramShareButton.tsx index 4a7c4239..27cb6bcc 100644 --- a/src/TelegramShareButton.tsx +++ b/src/TelegramShareButton.tsx @@ -19,13 +19,15 @@ function telegramLink(url: string, { text }: { text?: string }) { ); } +export const buildTelegramShareUrl = telegramLink; + const TelegramShareButton = forwardRef( ({ text, ...props }, ref) => ( , 'title'> & { text?: string; }; @@ -25,7 +27,7 @@ const ThreadsShareButton = forwardRef( {...props} forwardedRef={ref} networkName="tumblr" - networkLink={tumblrLink} + networkLink={buildTumblrShareUrl} opts={{ postTitle, tags: (tags || []).join(','), diff --git a/src/VKShareButton.tsx b/src/VKShareButton.tsx index af16ceda..b81ad998 100644 --- a/src/VKShareButton.tsx +++ b/src/VKShareButton.tsx @@ -27,13 +27,15 @@ function vkLink(url: string, { shareTitle, image, noParse, noVkLinks }: Options) ); } +export const buildVKShareUrl = vkLink; + const VKShareButton = forwardRef( ({ image, noParse, noVkLinks, shareTitle, ...props }, ref) => ( ( ({ message, separator, ...props }, ref) => ( ( ({ image, shareTitle, ...props }, ref) => ( ( ({ message, separator, ...props }, ref) => ( ( ({ hashtag, quote, ...props }, ref) => ( ( {...props} forwardedRef={ref} networkName="twitter" - networkLink={xLink} + networkLink={buildXShareUrl} opts={{ hashtags, text, diff --git a/src/index.ts b/src/index.ts index 2cfff186..34a010a2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,48 +1,73 @@ export { default as BlueskyIcon } from './BlueskyIcon'; export { default as BlueskyShareButton } from './BlueskyShareButton'; +export { buildBlueskyShareUrl } from './BlueskyShareButton'; export { default as EmailIcon } from './EmailIcon'; export { default as EmailShareButton } from './EmailShareButton'; +export { buildEmailShareUrl } from './EmailShareButton'; export { default as FacebookIcon } from './FacebookIcon'; export { default as FacebookMessengerIcon } from './FacebookMessengerIcon'; export { default as FacebookMessengerShareButton } from './FacebookMessengerShareButton'; +export { buildFacebookMessengerShareUrl } from './FacebookMessengerShareButton'; export { default as FacebookShareButton } from './FacebookShareButton'; +export { buildFacebookShareUrl } from './FacebookShareButton'; export { default as HatenaIcon } from './HatenaIcon'; export { default as HatenaShareButton } from './HatenaShareButton'; +export { buildHatenaShareUrl } from './HatenaShareButton'; export { default as InstapaperIcon } from './InstapaperIcon'; export { default as InstapaperShareButton } from './InstapaperShareButton'; +export { buildInstapaperShareUrl } from './InstapaperShareButton'; export { default as LineIcon } from './LineIcon'; export { default as LineShareButton } from './LineShareButton'; +export { buildLineShareUrl } from './LineShareButton'; export { default as LinkedinIcon } from './LinkedinIcon'; export { default as LinkedinShareButton } from './LinkedinShareButton'; +export { buildLinkedinShareUrl } from './LinkedinShareButton'; export { default as LivejournalIcon } from './LivejournalIcon'; export { default as LivejournalShareButton } from './LivejournalShareButton'; +export { buildLivejournalShareUrl } from './LivejournalShareButton'; export { default as MailruIcon } from './MailruIcon'; export { default as MailruShareButton } from './MailruShareButton'; +export { buildMailruShareUrl } from './MailruShareButton'; export { default as OKIcon } from './OKIcon'; export { default as OKShareButton } from './OKShareButton'; +export { buildOKShareUrl } from './OKShareButton'; export { default as PinterestIcon } from './PinterestIcon'; export { default as PinterestShareButton } from './PinterestShareButton'; +export { buildPinterestShareUrl } from './PinterestShareButton'; export { default as PocketIcon } from './PocketIcon'; export { default as PocketShareButton } from './PocketShareButton'; +export { buildPocketShareUrl } from './PocketShareButton'; export { default as RedditIcon } from './RedditIcon'; export { default as RedditShareButton } from './RedditShareButton'; +export { buildRedditShareUrl } from './RedditShareButton'; export { default as GabShareButton } from './GabShareButton'; export { default as GabIcon } from './GabIcon'; +export { buildGabShareUrl } from './GabShareButton'; export { default as TelegramIcon } from './TelegramIcon'; export { default as TelegramShareButton } from './TelegramShareButton'; +export { buildTelegramShareUrl } from './TelegramShareButton'; export { default as ThreadsIcon } from './ThreadsIcon'; export { default as ThreadsShareButton } from './ThreadsShareButton'; +export { buildThreadsShareUrl } from './ThreadsShareButton'; export { default as TumblrIcon } from './TumblrIcon'; export { default as TumblrShareButton } from './TumblrShareButton'; +export { buildTumblrShareUrl } from './TumblrShareButton'; export { default as XShareButton } from './XShareButton'; +export { buildXShareUrl } from './XShareButton'; export { default as ViberIcon } from './ViberIcon'; export { default as ViberShareButton } from './ViberShareButton'; +export { buildViberShareUrl } from './ViberShareButton'; export { default as VKIcon } from './VKIcon'; export { default as VKShareButton } from './VKShareButton'; +export { buildVKShareUrl } from './VKShareButton'; export { default as WeiboIcon } from './WeiboIcon'; export { default as WeiboShareButton } from './WeiboShareButton'; +export { buildWeiboShareUrl } from './WeiboShareButton'; export { default as WhatsappIcon } from './WhatsappIcon'; export { default as WhatsappShareButton } from './WhatsappShareButton'; +export { buildWhatsappShareUrl } from './WhatsappShareButton'; export { default as WorkplaceIcon } from './WorkplaceIcon'; export { default as WorkplaceShareButton } from './WorkplaceShareButton'; +export { buildWorkplaceShareUrl } from './WorkplaceShareButton'; export { default as XIcon } from './XIcon'; +export { openShareWindow, type ShareWindowOptions, type WindowPosition } from './shareWindow'; diff --git a/src/shareWindow.ts b/src/shareWindow.ts new file mode 100644 index 00000000..f2d94439 --- /dev/null +++ b/src/shareWindow.ts @@ -0,0 +1,68 @@ +export type WindowPosition = 'windowCenter' | 'screenCenter'; + +export type ShareWindowOptions = { + height?: number; + onClose?: (dialog: Window | null) => void; + position?: WindowPosition; + width?: number; +}; + +const getBoxPositionOnWindowCenter = (width: number, height: number) => ({ + left: window.outerWidth / 2 + (window.screenX || window.screenLeft || 0) - width / 2, + top: window.outerHeight / 2 + (window.screenY || window.screenTop || 0) - height / 2, +}); + +const getBoxPositionOnScreenCenter = (width: number, height: number) => ({ + top: (window.screen.height - height) / 2, + left: (window.screen.width - width) / 2, +}); + +export function openShareWindow( + url: string, + { + height = 400, + onClose, + position = 'windowCenter', + width = 550, + }: ShareWindowOptions = {}, +) { + const config: { [key: string]: string | number } = { + height, + width, + location: 'no', + toolbar: 'no', + status: 'no', + directories: 'no', + menubar: 'no', + scrollbars: 'yes', + resizable: 'no', + centerscreen: 'yes', + chrome: 'yes', + ...(position === 'windowCenter' + ? getBoxPositionOnWindowCenter(width, height) + : getBoxPositionOnScreenCenter(width, height)), + }; + + const shareDialog = window.open( + url, + '', + Object.keys(config) + .map(key => `${key}=${config[key]}`) + .join(', '), + ); + + if (onClose) { + const interval = window.setInterval(() => { + try { + if (shareDialog === null || shareDialog.closed) { + window.clearInterval(interval); + onClose(shareDialog); + } + } catch (error) { + console.error(error); + } + }, 1000); + } + + return shareDialog; +} diff --git a/test/PackageBuilds.test.ts b/test/PackageBuilds.test.ts index 5b3a5b16..32dd09ee 100644 --- a/test/PackageBuilds.test.ts +++ b/test/PackageBuilds.test.ts @@ -32,8 +32,11 @@ const isUsableExport = (value: unknown) => const representativeExportNames = [ 'FacebookShareButton', 'XShareButton', + 'buildFacebookShareUrl', + 'buildXShareUrl', 'FacebookIcon', 'XIcon', + 'openShareWindow', ] as const; describe('package builds', () => { diff --git a/test/PackageExports.test.tsx b/test/PackageExports.test.tsx index eaea67a4..ea56d88b 100644 --- a/test/PackageExports.test.tsx +++ b/test/PackageExports.test.tsx @@ -11,8 +11,11 @@ const iconExportNames = Object.keys(ReactShare) const representativeExportNames = [ 'FacebookShareButton', 'XShareButton', + 'buildFacebookShareUrl', + 'buildXShareUrl', 'FacebookIcon', 'XIcon', + 'openShareWindow', ] as const; describe('package exports', () => { diff --git a/test/ShareHelpers.test.ts b/test/ShareHelpers.test.ts new file mode 100644 index 00000000..12deb0f9 --- /dev/null +++ b/test/ShareHelpers.test.ts @@ -0,0 +1,60 @@ +import { describe, expect, it, vi } from 'vitest'; + +import { buildFacebookShareUrl, buildXShareUrl, openShareWindow } from '../src'; + +describe('share helpers', () => { + it('builds a Facebook share URL directly', () => { + expect(buildFacebookShareUrl('https://example.com', { hashtag: '#reactshare' })).toBe( + 'https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fexample.com&hashtag=%23reactshare', + ); + }); + + it('builds an X share URL directly', () => { + expect( + buildXShareUrl('https://example.com', { + hashtags: ['react', 'share'], + related: ['openai'], + text: 'Example title', + via: 'reactshare', + }), + ).toBe( + 'https://twitter.com/intent/tweet?url=https%3A%2F%2Fexample.com&text=Example%20title&via=reactshare&hashtags=react%2Cshare&related=openai', + ); + }); + + it('opens a centered share window with the requested size', () => { + const openSpy = vi.spyOn(window, 'open').mockReturnValue(null); + + Object.defineProperty(window, 'screen', { + configurable: true, + value: { height: 900, width: 1600 }, + }); + + openShareWindow('https://example.com/share', { + height: 500, + position: 'screenCenter', + width: 600, + }); + + expect(openSpy).toHaveBeenCalledWith( + 'https://example.com/share', + '', + expect.stringContaining('height=500'), + ); + expect(openSpy).toHaveBeenCalledWith( + 'https://example.com/share', + '', + expect.stringContaining('width=600'), + ); + expect(openSpy).toHaveBeenCalledWith( + 'https://example.com/share', + '', + expect.stringContaining('top=200'), + ); + expect(openSpy).toHaveBeenCalledWith( + 'https://example.com/share', + '', + expect.stringContaining('left=500'), + ); + }); +}); From 3ff7746d5187aaafc3b28757a3661156d894f8f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20Nyga=CC=8Ard?= Date: Mon, 9 Mar 2026 19:21:52 +0200 Subject: [PATCH 08/36] refactor(exports): remove default exports Add an ESLint no-default-export rule for src, demo, and test files, and convert local modules to named exports so the rule is enforced by construction. The root barrel now re-exports one symbol per file line instead of aliasing defaults, while the published package surface remains the same through named root exports. --- demo/Demo.tsx | 1 - eslint.config.mjs | 13 ++- package.json | 2 +- src/BlueskyIcon.tsx | 4 +- src/BlueskyShareButton.tsx | 9 +- src/EmailIcon.tsx | 4 +- src/EmailShareButton.tsx | 7 +- src/FacebookIcon.tsx | 4 +- src/FacebookMessengerIcon.tsx | 4 +- src/FacebookMessengerShareButton.tsx | 7 +- src/FacebookShareButton.tsx | 9 +- src/GabIcon.tsx | 4 +- src/GabShareButton.tsx | 9 +- src/HatenaIcon.tsx | 4 +- src/HatenaShareButton.tsx | 7 +- src/InstapaperIcon.tsx | 4 +- src/InstapaperShareButton.tsx | 9 +- src/LineIcon.tsx | 4 +- src/LineShareButton.tsx | 9 +- src/LinkedinIcon.tsx | 4 +- src/LinkedinShareButton.tsx | 9 +- src/LivejournalIcon.tsx | 4 +- src/LivejournalShareButton.tsx | 9 +- src/MailruIcon.tsx | 4 +- src/MailruShareButton.tsx | 9 +- src/OKIcon.tsx | 4 +- src/OKShareButton.tsx | 9 +- src/PinterestIcon.tsx | 4 +- src/PinterestShareButton.tsx | 9 +- src/PocketIcon.tsx | 4 +- src/PocketShareButton.tsx | 9 +- src/RedditIcon.tsx | 4 +- src/RedditShareButton.tsx | 9 +- src/ShareButton.tsx | 2 +- src/TelegramIcon.tsx | 4 +- src/TelegramShareButton.tsx | 9 +- src/ThreadsIcon.tsx | 4 +- src/ThreadsShareButton.tsx | 9 +- src/TumblrIcon.tsx | 4 +- src/TumblrShareButton.tsx | 9 +- src/VKIcon.tsx | 4 +- src/VKShareButton.tsx | 9 +- src/ViberIcon.tsx | 4 +- src/ViberShareButton.tsx | 9 +- src/WeiboIcon.tsx | 4 +- src/WeiboShareButton.tsx | 9 +- src/WhatsappIcon.tsx | 4 +- src/WhatsappShareButton.tsx | 9 +- src/WorkplaceIcon.tsx | 4 +- src/WorkplaceShareButton.tsx | 9 +- src/XIcon.tsx | 4 +- src/XShareButton.tsx | 9 +- src/icons/IconBase.tsx | 2 +- src/index.ts | 96 +++++++++++----------- src/utils/assert.ts | 2 +- src/utils/objectToGetParams.ts | 2 +- test/EmailShareButton.test.tsx | 2 +- test/FacebookMessengerShareButton.test.tsx | 2 +- test/IconBase.test.tsx | 32 ++++---- test/NetworkShareButtons.test.tsx | 36 ++++---- test/PinterestShareButton.test.tsx | 2 +- test/RedditShareButton.test.tsx | 2 +- test/ShareButton.test.tsx | 4 +- test/ShareButtonComponents.test.tsx | 10 +-- test/Utilities.test.ts | 4 +- test/WhatsappShareButton.test.tsx | 2 +- test/XShareButton.test.tsx | 2 +- 67 files changed, 255 insertions(+), 269 deletions(-) diff --git a/demo/Demo.tsx b/demo/Demo.tsx index 7e6b80a8..89adde2a 100644 --- a/demo/Demo.tsx +++ b/demo/Demo.tsx @@ -337,4 +337,3 @@ export function Demo() { ); } -export default Demo; diff --git a/eslint.config.mjs b/eslint.config.mjs index 0e2f008d..efd14c46 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -4,6 +4,7 @@ import tsParser from "@typescript-eslint/parser"; import react from "eslint-plugin-react"; import reactHooks from "eslint-plugin-react-hooks"; import jsxA11y from "eslint-plugin-jsx-a11y"; +import importPlugin from "eslint-plugin-import"; import prettier from "eslint-plugin-prettier"; import prettierConfig from "eslint-config-prettier"; import globals from "globals"; @@ -31,7 +32,10 @@ export default [ ...globals.node, }, }, - plugins: { "@typescript-eslint": ts }, + plugins: { + "@typescript-eslint": ts, + import: importPlugin, + }, rules: { ...ts.configs.strict.rules, "@typescript-eslint/consistent-type-imports": [ @@ -43,6 +47,13 @@ export default [ ], }, }, + { + files: ["src/**/*.{ts,tsx}", "test/**/*.{ts,tsx}", "demo/**/*.{ts,tsx}"], + ignores: ["demo/declarations.d.ts"], + rules: { + "import/no-default-export": "error", + }, + }, { files: ["**/*.jsx", "**/*.tsx"], plugins: { diff --git a/package.json b/package.json index 06282eb3..d8f00f66 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "build-demo": "vite build --config vite.demo.config.js", "check-types": "tsc --noEmit -p tsconfig.json && tsc --noEmit -p tsconfig.demo.json", "demo": "vite --config vite.demo.config.js", - "lint": "eslint ./src ./demo", + "lint": "eslint ./src ./demo ./test", "lint-package": "npx publint && npx @arethetypeswrong/cli --pack .", "prepublishOnly": "npm run build", "start": "npm run demo", diff --git a/src/BlueskyIcon.tsx b/src/BlueskyIcon.tsx index 8ec32542..8383ff55 100644 --- a/src/BlueskyIcon.tsx +++ b/src/BlueskyIcon.tsx @@ -1,6 +1,6 @@ -import IconBase, { type IconProps } from './icons/IconBase'; +import { IconBase, type IconProps } from './icons/IconBase'; -export default function BlueskyIcon(props: IconProps) { +export function BlueskyIcon(props: IconProps) { return ( diff --git a/src/BlueskyShareButton.tsx b/src/BlueskyShareButton.tsx index 9fd9131a..cc2946b0 100644 --- a/src/BlueskyShareButton.tsx +++ b/src/BlueskyShareButton.tsx @@ -1,7 +1,7 @@ import { forwardRef } from 'react'; -import assert from './utils/assert'; -import objectToGetParams from './utils/objectToGetParams'; -import ShareButton, { type ShareButtonProps } from './ShareButton'; +import { assert } from './utils/assert'; +import { objectToGetParams } from './utils/objectToGetParams'; +import { ShareButton, type ShareButtonProps } from './ShareButton'; type BlueskyShareButtonProps = Omit< ShareButtonProps<{ text?: string; separator?: string }>, @@ -24,7 +24,7 @@ function blueskyLink(url: string, { text, separator }: { text?: string; separato export const buildBlueskyShareUrl = blueskyLink; -const BlueskyShareButton = forwardRef( +export const BlueskyShareButton = forwardRef( ({ separator, text, ...props }, ref) => ( diff --git a/src/EmailShareButton.tsx b/src/EmailShareButton.tsx index 8b6a2910..6cd011af 100644 --- a/src/EmailShareButton.tsx +++ b/src/EmailShareButton.tsx @@ -1,6 +1,6 @@ import { forwardRef } from 'react'; -import objectToGetParams from './utils/objectToGetParams'; -import ShareButton, { type ShareButtonProps } from './ShareButton'; +import { objectToGetParams } from './utils/objectToGetParams'; +import { ShareButton, type ShareButtonProps } from './ShareButton'; type Options = { body?: string; @@ -17,7 +17,7 @@ function emailLink(url: string, { subject, body, separator }: Options) { export const buildEmailShareUrl = emailLink; -const EmailShareButton = forwardRef( +export const EmailShareButton = forwardRef( ({ body, separator, subject, ...props }, ref) => ( ( EmailShareButton.displayName = 'EmailShareButton'; -export default EmailShareButton; diff --git a/src/FacebookIcon.tsx b/src/FacebookIcon.tsx index e16187e9..210c707f 100644 --- a/src/FacebookIcon.tsx +++ b/src/FacebookIcon.tsx @@ -1,6 +1,6 @@ -import IconBase, { type IconProps } from './icons/IconBase'; +import { IconBase, type IconProps } from './icons/IconBase'; -export default function FacebookIcon(props: IconProps) { +export function FacebookIcon(props: IconProps) { return ( diff --git a/src/FacebookMessengerIcon.tsx b/src/FacebookMessengerIcon.tsx index af4a693a..fade8b81 100644 --- a/src/FacebookMessengerIcon.tsx +++ b/src/FacebookMessengerIcon.tsx @@ -1,6 +1,6 @@ -import IconBase, { type IconProps } from './icons/IconBase'; +import { IconBase, type IconProps } from './icons/IconBase'; -export default function FacebookMessengerIcon(props: IconProps) { +export function FacebookMessengerIcon(props: IconProps) { return ( diff --git a/src/FacebookMessengerShareButton.tsx b/src/FacebookMessengerShareButton.tsx index 4490293c..e49cfc16 100644 --- a/src/FacebookMessengerShareButton.tsx +++ b/src/FacebookMessengerShareButton.tsx @@ -1,6 +1,6 @@ import { forwardRef } from 'react'; -import objectToGetParams from './utils/objectToGetParams'; -import ShareButton, { type ShareButtonProps } from './ShareButton'; +import { objectToGetParams } from './utils/objectToGetParams'; +import { ShareButton, type ShareButtonProps } from './ShareButton'; type Options = { /** Your app's unique identifier. */ @@ -29,7 +29,7 @@ function facebookMessengerLink(url: string, { appId, redirectUri, to }: Options) export const buildFacebookMessengerShareUrl = facebookMessengerLink; -const FacebookMessengerShareButton = forwardRef< +export const FacebookMessengerShareButton = forwardRef< HTMLButtonElement, FacebookMessengerShareButtonProps >(({ appId, redirectUri, to, ...props }, ref) => ( @@ -50,4 +50,3 @@ const FacebookMessengerShareButton = forwardRef< FacebookMessengerShareButton.displayName = 'FacebookMessengerShareButton'; -export default FacebookMessengerShareButton; diff --git a/src/FacebookShareButton.tsx b/src/FacebookShareButton.tsx index cacfebf4..57b432ff 100644 --- a/src/FacebookShareButton.tsx +++ b/src/FacebookShareButton.tsx @@ -1,7 +1,7 @@ import { forwardRef } from 'react'; -import assert from './utils/assert'; -import objectToGetParams from './utils/objectToGetParams'; -import ShareButton, { type ShareButtonProps } from './ShareButton'; +import { assert } from './utils/assert'; +import { objectToGetParams } from './utils/objectToGetParams'; +import { ShareButton, type ShareButtonProps } from './ShareButton'; type FacebookShareButtonProps = ShareButtonProps<{ hashtag?: string }> & { hashtag?: string; @@ -15,7 +15,7 @@ function facebookLink(url: string, { hashtag }: { hashtag?: string }) { export const buildFacebookShareUrl = facebookLink; -const FacebookShareButton = forwardRef( +export const FacebookShareButton = forwardRef( ({ hashtag, ...props }, ref) => ( diff --git a/src/GabShareButton.tsx b/src/GabShareButton.tsx index 9025d3f9..9f530fc5 100644 --- a/src/GabShareButton.tsx +++ b/src/GabShareButton.tsx @@ -1,7 +1,7 @@ import { forwardRef } from 'react'; -import assert from './utils/assert'; -import objectToGetParams from './utils/objectToGetParams'; -import ShareButton, { type ShareButtonProps } from './ShareButton'; +import { assert } from './utils/assert'; +import { objectToGetParams } from './utils/objectToGetParams'; +import { ShareButton, type ShareButtonProps } from './ShareButton'; type GabShareButtonProps = Omit, 'title'> & { text?: string; @@ -21,7 +21,7 @@ function gabLink(url: string, { text }: { text?: string }) { export const buildGabShareUrl = gabLink; -const GabShareButton = forwardRef( +export const GabShareButton = forwardRef( ({ text, ...props }, ref) => ( ( GabShareButton.displayName = 'GabShareButton'; -export default GabShareButton; diff --git a/src/HatenaIcon.tsx b/src/HatenaIcon.tsx index 3e3fb5ff..da3ca7e8 100644 --- a/src/HatenaIcon.tsx +++ b/src/HatenaIcon.tsx @@ -1,6 +1,6 @@ -import IconBase, { type IconProps } from './icons/IconBase'; +import { IconBase, type IconProps } from './icons/IconBase'; -export default function HatenaIcon(props: IconProps) { +export function HatenaIcon(props: IconProps) { return ( diff --git a/src/HatenaShareButton.tsx b/src/HatenaShareButton.tsx index 47805c7a..c92ff214 100644 --- a/src/HatenaShareButton.tsx +++ b/src/HatenaShareButton.tsx @@ -1,6 +1,6 @@ import { forwardRef } from 'react'; -import assert from './utils/assert'; -import ShareButton, { type ShareButtonProps } from './ShareButton'; +import { assert } from './utils/assert'; +import { ShareButton, type ShareButtonProps } from './ShareButton'; type HatenaShareButtonProps = Omit, 'title'> & { shareTitle?: string; @@ -14,7 +14,7 @@ function hatenaLink(url: string, { shareTitle }: { shareTitle?: string }) { export const buildHatenaShareUrl = hatenaLink; -const HatenaShareButton = forwardRef( +export const HatenaShareButton = forwardRef( ({ shareTitle, ...props }, ref) => ( ( HatenaShareButton.displayName = 'HatenaShareButton'; -export default HatenaShareButton; diff --git a/src/InstapaperIcon.tsx b/src/InstapaperIcon.tsx index d7721f3c..002de608 100644 --- a/src/InstapaperIcon.tsx +++ b/src/InstapaperIcon.tsx @@ -1,6 +1,6 @@ -import IconBase, { type IconProps } from './icons/IconBase'; +import { IconBase, type IconProps } from './icons/IconBase'; -export default function InstapaperIcon(props: IconProps) { +export function InstapaperIcon(props: IconProps) { return ( diff --git a/src/InstapaperShareButton.tsx b/src/InstapaperShareButton.tsx index e115b421..0e07ad92 100644 --- a/src/InstapaperShareButton.tsx +++ b/src/InstapaperShareButton.tsx @@ -1,7 +1,7 @@ import { forwardRef } from 'react'; -import assert from './utils/assert'; -import objectToGetParams from './utils/objectToGetParams'; -import ShareButton, { type ShareButtonProps } from './ShareButton'; +import { assert } from './utils/assert'; +import { objectToGetParams } from './utils/objectToGetParams'; +import { ShareButton, type ShareButtonProps } from './ShareButton'; type InstapaperShareButtonProps = Omit< ShareButtonProps<{ shareTitle?: string; description?: string }>, @@ -29,7 +29,7 @@ function instapaperLink( export const buildInstapaperShareUrl = instapaperLink; -const InstapaperShareButton = forwardRef( +export const InstapaperShareButton = forwardRef( ({ description, shareTitle, ...props }, ref) => ( diff --git a/src/LineShareButton.tsx b/src/LineShareButton.tsx index 63dc4101..af3e54dd 100644 --- a/src/LineShareButton.tsx +++ b/src/LineShareButton.tsx @@ -1,7 +1,7 @@ import { forwardRef } from 'react'; -import assert from './utils/assert'; -import objectToGetParams from './utils/objectToGetParams'; -import ShareButton, { type ShareButtonProps } from './ShareButton'; +import { assert } from './utils/assert'; +import { objectToGetParams } from './utils/objectToGetParams'; +import { ShareButton, type ShareButtonProps } from './ShareButton'; type LineShareButtonProps = Omit, 'title'> & { text?: string; @@ -21,7 +21,7 @@ function lineLink(url: string, { text }: { text?: string }) { export const buildLineShareUrl = lineLink; -const LineShareButton = forwardRef( +export const LineShareButton = forwardRef( ({ text, ...props }, ref) => ( ( LineShareButton.displayName = 'LineShareButton'; -export default LineShareButton; diff --git a/src/LinkedinIcon.tsx b/src/LinkedinIcon.tsx index c68cecee..4e074199 100644 --- a/src/LinkedinIcon.tsx +++ b/src/LinkedinIcon.tsx @@ -1,6 +1,6 @@ -import IconBase, { type IconProps } from './icons/IconBase'; +import { IconBase, type IconProps } from './icons/IconBase'; -export default function LinkedinIcon(props: IconProps) { +export function LinkedinIcon(props: IconProps) { return ( diff --git a/src/LinkedinShareButton.tsx b/src/LinkedinShareButton.tsx index 93f6a90a..324868c6 100644 --- a/src/LinkedinShareButton.tsx +++ b/src/LinkedinShareButton.tsx @@ -1,7 +1,7 @@ import { forwardRef } from 'react'; -import assert from './utils/assert'; -import objectToGetParams from './utils/objectToGetParams'; -import ShareButton, { type ShareButtonProps } from './ShareButton'; +import { assert } from './utils/assert'; +import { objectToGetParams } from './utils/objectToGetParams'; +import { ShareButton, type ShareButtonProps } from './ShareButton'; type Options = { /** The url-encoded title value that you wish you use. */ @@ -25,7 +25,7 @@ function linkedinLink(url: string, { shareTitle, summary, source }: Options) { export const buildLinkedinShareUrl = linkedinLink; -const LinkedinShareButton = forwardRef( +export const LinkedinShareButton = forwardRef( ({ shareTitle, source, summary, ...props }, ref) => ( diff --git a/src/LivejournalShareButton.tsx b/src/LivejournalShareButton.tsx index 785e776e..02729ee4 100644 --- a/src/LivejournalShareButton.tsx +++ b/src/LivejournalShareButton.tsx @@ -1,7 +1,7 @@ import { forwardRef } from 'react'; -import assert from './utils/assert'; -import objectToGetParams from './utils/objectToGetParams'; -import ShareButton, { type ShareButtonProps } from './ShareButton'; +import { assert } from './utils/assert'; +import { objectToGetParams } from './utils/objectToGetParams'; +import { ShareButton, type ShareButtonProps } from './ShareButton'; type LivejournalShareButtonProps = Omit< ShareButtonProps<{ shareTitle?: string; description?: string }>, @@ -28,7 +28,7 @@ function livejournalLink( export const buildLivejournalShareUrl = livejournalLink; -const LivejournalShareButton = forwardRef( +export const LivejournalShareButton = forwardRef( ({ description, shareTitle, ...props }, ref) => ( diff --git a/src/MailruShareButton.tsx b/src/MailruShareButton.tsx index ca2c432f..d9a5019e 100644 --- a/src/MailruShareButton.tsx +++ b/src/MailruShareButton.tsx @@ -1,7 +1,7 @@ import { forwardRef } from 'react'; -import assert from './utils/assert'; -import objectToGetParams from './utils/objectToGetParams'; -import ShareButton, { type ShareButtonProps } from './ShareButton'; +import { assert } from './utils/assert'; +import { objectToGetParams } from './utils/objectToGetParams'; +import { ShareButton, type ShareButtonProps } from './ShareButton'; function mailruLink( url: string, @@ -35,7 +35,7 @@ type MailruShareButtonProps = Omit< shareTitle?: string; }; -const MailruShareButton = forwardRef( +export const MailruShareButton = forwardRef( ({ description, imageUrl, shareTitle, ...props }, ref) => ( ( MailruShareButton.displayName = 'MailruShareButton'; -export default MailruShareButton; diff --git a/src/OKIcon.tsx b/src/OKIcon.tsx index e288161f..f0101c5b 100644 --- a/src/OKIcon.tsx +++ b/src/OKIcon.tsx @@ -1,6 +1,6 @@ -import IconBase, { type IconProps } from './icons/IconBase'; +import { IconBase, type IconProps } from './icons/IconBase'; -export default function OKIcon(props: IconProps) { +export function OKIcon(props: IconProps) { return ( diff --git a/src/OKShareButton.tsx b/src/OKShareButton.tsx index 8e7479a0..48807a88 100644 --- a/src/OKShareButton.tsx +++ b/src/OKShareButton.tsx @@ -1,7 +1,7 @@ import { forwardRef } from 'react'; -import assert from './utils/assert'; -import objectToGetParams from './utils/objectToGetParams'; -import ShareButton, { type ShareButtonProps } from './ShareButton'; +import { assert } from './utils/assert'; +import { objectToGetParams } from './utils/objectToGetParams'; +import { ShareButton, type ShareButtonProps } from './ShareButton'; function okLink( url: string, @@ -35,7 +35,7 @@ type OKShareButtonProps = Omit< shareTitle?: string; }; -const OKShareButton = forwardRef( +export const OKShareButton = forwardRef( ({ description, image, shareTitle, ...props }, ref) => ( ( OKShareButton.displayName = 'OKShareButton'; -export default OKShareButton; diff --git a/src/PinterestIcon.tsx b/src/PinterestIcon.tsx index 8906f693..bf5b175e 100644 --- a/src/PinterestIcon.tsx +++ b/src/PinterestIcon.tsx @@ -1,6 +1,6 @@ -import IconBase, { type IconProps } from './icons/IconBase'; +import { IconBase, type IconProps } from './icons/IconBase'; -export default function PinterestIcon(props: IconProps) { +export function PinterestIcon(props: IconProps) { return ( diff --git a/src/PinterestShareButton.tsx b/src/PinterestShareButton.tsx index 6c55084b..a7f1fe9b 100644 --- a/src/PinterestShareButton.tsx +++ b/src/PinterestShareButton.tsx @@ -1,7 +1,7 @@ import { forwardRef } from 'react'; -import assert from './utils/assert'; -import objectToGetParams from './utils/objectToGetParams'; -import ShareButton, { type ShareButtonProps } from './ShareButton'; +import { assert } from './utils/assert'; +import { objectToGetParams } from './utils/objectToGetParams'; +import { ShareButton, type ShareButtonProps } from './ShareButton'; type PinterestShareProps = { media: string; @@ -31,7 +31,7 @@ function pinterestLink(url: string, { media, description, pinId }: PinterestShar export const buildPinterestShareUrl = pinterestLink; -const PinterestShareButton = forwardRef( +export const PinterestShareButton = forwardRef( ({ description, media, pinId, ...props }, ref) => ( diff --git a/src/PocketShareButton.tsx b/src/PocketShareButton.tsx index c4fca633..07765273 100644 --- a/src/PocketShareButton.tsx +++ b/src/PocketShareButton.tsx @@ -1,7 +1,7 @@ import { forwardRef } from 'react'; -import assert from './utils/assert'; -import objectToGetParams from './utils/objectToGetParams'; -import ShareButton, { type ShareButtonProps } from './ShareButton'; +import { assert } from './utils/assert'; +import { objectToGetParams } from './utils/objectToGetParams'; +import { ShareButton, type ShareButtonProps } from './ShareButton'; type PocketShareButtonProps = Omit, 'title'> & { shareTitle?: string; @@ -21,7 +21,7 @@ function pocketLink(url: string, { shareTitle }: { shareTitle?: string }) { export const buildPocketShareUrl = pocketLink; -const PocketShareButton = forwardRef( +export const PocketShareButton = forwardRef( ({ shareTitle, ...props }, ref) => ( ( PocketShareButton.displayName = 'PocketShareButton'; -export default PocketShareButton; diff --git a/src/RedditIcon.tsx b/src/RedditIcon.tsx index d2d9a1cb..4542cc8d 100644 --- a/src/RedditIcon.tsx +++ b/src/RedditIcon.tsx @@ -1,6 +1,6 @@ -import IconBase, { type IconProps } from './icons/IconBase'; +import { IconBase, type IconProps } from './icons/IconBase'; -export default function RedditIcon(props: IconProps) { +export function RedditIcon(props: IconProps) { return ( diff --git a/src/RedditShareButton.tsx b/src/RedditShareButton.tsx index a2813499..c0e1317d 100644 --- a/src/RedditShareButton.tsx +++ b/src/RedditShareButton.tsx @@ -1,7 +1,7 @@ import { forwardRef } from 'react'; -import assert from './utils/assert'; -import objectToGetParams from './utils/objectToGetParams'; -import ShareButton, { type ShareButtonProps } from './ShareButton'; +import { assert } from './utils/assert'; +import { objectToGetParams } from './utils/objectToGetParams'; +import { ShareButton, type ShareButtonProps } from './ShareButton'; type RedditShareButtonProps = Omit, 'title'> & { shareTitle?: string; @@ -21,7 +21,7 @@ function redditLink(url: string, { shareTitle }: { shareTitle?: string }) { export const buildRedditShareUrl = redditLink; -const RedditShareButton = forwardRef( +export const RedditShareButton = forwardRef( ({ shareTitle, ...props }, ref) => ( ( RedditShareButton.displayName = 'RedditShareButton'; -export default RedditShareButton; diff --git a/src/ShareButton.tsx b/src/ShareButton.tsx index f14ebbba..15d9c7d9 100644 --- a/src/ShareButton.tsx +++ b/src/ShareButton.tsx @@ -126,7 +126,7 @@ export type ShareButtonProps> = Omit 'forwardedRef' | 'networkLink' | 'networkName' | 'opts' >; -export default function ShareButton>({ +export function ShareButton>({ 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, beforeOnClick, diff --git a/src/TelegramIcon.tsx b/src/TelegramIcon.tsx index 89821351..658546ba 100644 --- a/src/TelegramIcon.tsx +++ b/src/TelegramIcon.tsx @@ -1,6 +1,6 @@ -import IconBase, { type IconProps } from './icons/IconBase'; +import { IconBase, type IconProps } from './icons/IconBase'; -export default function TelegramIcon(props: IconProps) { +export function TelegramIcon(props: IconProps) { return ( diff --git a/src/TelegramShareButton.tsx b/src/TelegramShareButton.tsx index 27cb6bcc..4937f72f 100644 --- a/src/TelegramShareButton.tsx +++ b/src/TelegramShareButton.tsx @@ -1,7 +1,7 @@ import { forwardRef } from 'react'; -import assert from './utils/assert'; -import objectToGetParams from './utils/objectToGetParams'; -import ShareButton, { type ShareButtonProps } from './ShareButton'; +import { assert } from './utils/assert'; +import { objectToGetParams } from './utils/objectToGetParams'; +import { ShareButton, type ShareButtonProps } from './ShareButton'; type TelegramShareButtonProps = Omit, 'title'> & { text?: string; @@ -21,7 +21,7 @@ function telegramLink(url: string, { text }: { text?: string }) { export const buildTelegramShareUrl = telegramLink; -const TelegramShareButton = forwardRef( +export const TelegramShareButton = forwardRef( ({ text, ...props }, ref) => ( diff --git a/src/ThreadsShareButton.tsx b/src/ThreadsShareButton.tsx index 0dd9f4f1..59743bc8 100644 --- a/src/ThreadsShareButton.tsx +++ b/src/ThreadsShareButton.tsx @@ -1,7 +1,7 @@ import { forwardRef } from 'react'; -import assert from './utils/assert'; -import objectToGetParams from './utils/objectToGetParams'; -import ShareButton, { type ShareButtonProps } from './ShareButton'; +import { assert } from './utils/assert'; +import { objectToGetParams } from './utils/objectToGetParams'; +import { ShareButton, type ShareButtonProps } from './ShareButton'; function threadsLink(url: string, { text }: { text?: string }) { assert(url, 'threads.url'); @@ -21,7 +21,7 @@ type ThreadsShareButtonProps = Omit, 'title' text?: string; }; -const ThreadsShareButton = forwardRef( +export const ThreadsShareButton = forwardRef( ({ text, ...props }, ref) => ( diff --git a/src/TumblrShareButton.tsx b/src/TumblrShareButton.tsx index 5afe81ec..5e354b60 100644 --- a/src/TumblrShareButton.tsx +++ b/src/TumblrShareButton.tsx @@ -1,7 +1,7 @@ import { forwardRef } from 'react'; -import assert from './utils/assert'; -import objectToGetParams from './utils/objectToGetParams'; -import ShareButton, { type ShareButtonProps } from './ShareButton'; +import { assert } from './utils/assert'; +import { objectToGetParams } from './utils/objectToGetParams'; +import { ShareButton, type ShareButtonProps } from './ShareButton'; function tumblrLink( url: string, @@ -37,7 +37,7 @@ type Options = { type TumblrShareButtonProps = Omit, 'title'> & Options & { tags?: string[] }; -const TumblrShareButton = forwardRef( +export const TumblrShareButton = forwardRef( ({ caption, postTitle, posttype, tags, ...props }, ref) => ( ( TumblrShareButton.displayName = 'TumblrShareButton'; -export default TumblrShareButton; diff --git a/src/VKIcon.tsx b/src/VKIcon.tsx index af559ec2..d3a417ba 100644 --- a/src/VKIcon.tsx +++ b/src/VKIcon.tsx @@ -1,6 +1,6 @@ -import IconBase, { type IconProps } from './icons/IconBase'; +import { IconBase, type IconProps } from './icons/IconBase'; -export default function VKIcon(props: IconProps) { +export function VKIcon(props: IconProps) { return ( diff --git a/src/VKShareButton.tsx b/src/VKShareButton.tsx index b81ad998..ff7ab7d6 100644 --- a/src/VKShareButton.tsx +++ b/src/VKShareButton.tsx @@ -1,7 +1,7 @@ import { forwardRef } from 'react'; -import assert from './utils/assert'; -import objectToGetParams from './utils/objectToGetParams'; -import ShareButton, { type ShareButtonProps } from './ShareButton'; +import { assert } from './utils/assert'; +import { objectToGetParams } from './utils/objectToGetParams'; +import { ShareButton, type ShareButtonProps } from './ShareButton'; type Options = { shareTitle?: string; @@ -29,7 +29,7 @@ function vkLink(url: string, { shareTitle, image, noParse, noVkLinks }: Options) export const buildVKShareUrl = vkLink; -const VKShareButton = forwardRef( +export const VKShareButton = forwardRef( ({ image, noParse, noVkLinks, shareTitle, ...props }, ref) => ( ( VKShareButton.displayName = 'VKShareButton'; -export default VKShareButton; diff --git a/src/ViberIcon.tsx b/src/ViberIcon.tsx index 98ac90d4..0112da7a 100644 --- a/src/ViberIcon.tsx +++ b/src/ViberIcon.tsx @@ -1,6 +1,6 @@ -import IconBase, { type IconProps } from './icons/IconBase'; +import { IconBase, type IconProps } from './icons/IconBase'; -export default function ViberIcon(props: IconProps) { +export function ViberIcon(props: IconProps) { return ( diff --git a/src/ViberShareButton.tsx b/src/ViberShareButton.tsx index a2627b26..99fa48a3 100644 --- a/src/ViberShareButton.tsx +++ b/src/ViberShareButton.tsx @@ -1,7 +1,7 @@ import { forwardRef } from 'react'; -import assert from './utils/assert'; -import objectToGetParams from './utils/objectToGetParams'; -import ShareButton, { type ShareButtonProps } from './ShareButton'; +import { assert } from './utils/assert'; +import { objectToGetParams } from './utils/objectToGetParams'; +import { ShareButton, type ShareButtonProps } from './ShareButton'; type ViberShareButtonProps = Omit< ShareButtonProps<{ message?: string; separator?: string }>, @@ -23,7 +23,7 @@ function viberLink(url: string, { message, separator }: { message?: string; sepa export const buildViberShareUrl = viberLink; -const ViberShareButton = forwardRef( +export const ViberShareButton = forwardRef( ({ message, separator, ...props }, ref) => ( ( ViberShareButton.displayName = 'ViberShareButton'; -export default ViberShareButton; diff --git a/src/WeiboIcon.tsx b/src/WeiboIcon.tsx index 8596579b..2bf2cd5e 100644 --- a/src/WeiboIcon.tsx +++ b/src/WeiboIcon.tsx @@ -1,6 +1,6 @@ -import IconBase, { type IconProps } from './icons/IconBase'; +import { IconBase, type IconProps } from './icons/IconBase'; -export default function WeiboIcon(props: IconProps) { +export function WeiboIcon(props: IconProps) { return ( diff --git a/src/WeiboShareButton.tsx b/src/WeiboShareButton.tsx index 6fed787d..318b44cc 100644 --- a/src/WeiboShareButton.tsx +++ b/src/WeiboShareButton.tsx @@ -1,7 +1,7 @@ import { forwardRef } from 'react'; -import assert from './utils/assert'; -import objectToGetParams from './utils/objectToGetParams'; -import ShareButton, { type ShareButtonProps } from './ShareButton'; +import { assert } from './utils/assert'; +import { objectToGetParams } from './utils/objectToGetParams'; +import { ShareButton, type ShareButtonProps } from './ShareButton'; type WeiboShareButtonProps = Omit< ShareButtonProps<{ shareTitle?: string; image?: string }>, @@ -29,7 +29,7 @@ function weiboLink( export const buildWeiboShareUrl = weiboLink; -const WeiboShareButton = forwardRef( +export const WeiboShareButton = forwardRef( ({ image, shareTitle, ...props }, ref) => ( ( WeiboShareButton.displayName = 'WeiboShareButton'; -export default WeiboShareButton; diff --git a/src/WhatsappIcon.tsx b/src/WhatsappIcon.tsx index f50425ec..56357bd3 100644 --- a/src/WhatsappIcon.tsx +++ b/src/WhatsappIcon.tsx @@ -1,6 +1,6 @@ -import IconBase, { type IconProps } from './icons/IconBase'; +import { IconBase, type IconProps } from './icons/IconBase'; -export default function WhatsappIcon(props: IconProps) { +export function WhatsappIcon(props: IconProps) { return ( diff --git a/src/WhatsappShareButton.tsx b/src/WhatsappShareButton.tsx index d7e92f50..498ede0d 100644 --- a/src/WhatsappShareButton.tsx +++ b/src/WhatsappShareButton.tsx @@ -1,7 +1,7 @@ import { forwardRef } from 'react'; -import assert from './utils/assert'; -import objectToGetParams from './utils/objectToGetParams'; -import ShareButton, { type ShareButtonProps } from './ShareButton'; +import { assert } from './utils/assert'; +import { objectToGetParams } from './utils/objectToGetParams'; +import { ShareButton, type ShareButtonProps } from './ShareButton'; type WhatsappShareButtonProps = Omit< ShareButtonProps<{ message?: string; separator?: string }>, @@ -26,7 +26,7 @@ function whatsappLink( export const buildWhatsappShareUrl = whatsappLink; -const WhatsappShareButton = forwardRef( +export const WhatsappShareButton = forwardRef( ({ message, separator, ...props }, ref) => ( diff --git a/src/WorkplaceShareButton.tsx b/src/WorkplaceShareButton.tsx index 7fc71c20..88f906a1 100644 --- a/src/WorkplaceShareButton.tsx +++ b/src/WorkplaceShareButton.tsx @@ -1,7 +1,7 @@ import { forwardRef } from 'react'; -import assert from './utils/assert'; -import objectToGetParams from './utils/objectToGetParams'; -import ShareButton, { type ShareButtonProps } from './ShareButton'; +import { assert } from './utils/assert'; +import { objectToGetParams } from './utils/objectToGetParams'; +import { ShareButton, type ShareButtonProps } from './ShareButton'; type WorkplaceShareButtonProps = ShareButtonProps<{ quote?: string; hashtag?: string }> & { hashtag?: string; @@ -23,7 +23,7 @@ function workplaceLink(url: string, { quote, hashtag }: { quote?: string; hashta export const buildWorkplaceShareUrl = workplaceLink; -const WorkplaceShareButton = forwardRef( +export const WorkplaceShareButton = forwardRef( ({ hashtag, quote, ...props }, ref) => ( diff --git a/src/XShareButton.tsx b/src/XShareButton.tsx index d293ad9e..e632e80b 100644 --- a/src/XShareButton.tsx +++ b/src/XShareButton.tsx @@ -1,7 +1,7 @@ import { forwardRef } from 'react'; -import assert from './utils/assert'; -import objectToGetParams from './utils/objectToGetParams'; -import ShareButton, { type ShareButtonProps } from './ShareButton'; +import { assert } from './utils/assert'; +import { objectToGetParams } from './utils/objectToGetParams'; +import { ShareButton, type ShareButtonProps } from './ShareButton'; function xLink( url: string, @@ -45,7 +45,7 @@ type XShareButtonProps = Omit< related?: string[]; }; -const XShareButton = forwardRef( +export const XShareButton = forwardRef( ({ hashtags, related, text, via, ...props }, ref) => ( ( XShareButton.displayName = 'XShareButton'; -export default XShareButton; diff --git a/src/icons/IconBase.tsx b/src/icons/IconBase.tsx index 280f989a..f7d4df74 100644 --- a/src/icons/IconBase.tsx +++ b/src/icons/IconBase.tsx @@ -26,7 +26,7 @@ function withDefaultFill(children: ReactNode, iconFillColor: string) { }); } -export default function IconBase({ +export function IconBase({ bgStyle = {}, borderRadius = 0, children, diff --git a/src/index.ts b/src/index.ts index 34a010a2..266795c7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,73 +1,73 @@ -export { default as BlueskyIcon } from './BlueskyIcon'; -export { default as BlueskyShareButton } from './BlueskyShareButton'; +export { BlueskyIcon } from './BlueskyIcon'; +export { BlueskyShareButton } from './BlueskyShareButton'; export { buildBlueskyShareUrl } from './BlueskyShareButton'; -export { default as EmailIcon } from './EmailIcon'; -export { default as EmailShareButton } from './EmailShareButton'; +export { EmailIcon } from './EmailIcon'; +export { EmailShareButton } from './EmailShareButton'; export { buildEmailShareUrl } from './EmailShareButton'; -export { default as FacebookIcon } from './FacebookIcon'; -export { default as FacebookMessengerIcon } from './FacebookMessengerIcon'; -export { default as FacebookMessengerShareButton } from './FacebookMessengerShareButton'; +export { FacebookIcon } from './FacebookIcon'; +export { FacebookMessengerIcon } from './FacebookMessengerIcon'; +export { FacebookMessengerShareButton } from './FacebookMessengerShareButton'; export { buildFacebookMessengerShareUrl } from './FacebookMessengerShareButton'; -export { default as FacebookShareButton } from './FacebookShareButton'; +export { FacebookShareButton } from './FacebookShareButton'; export { buildFacebookShareUrl } from './FacebookShareButton'; -export { default as HatenaIcon } from './HatenaIcon'; -export { default as HatenaShareButton } from './HatenaShareButton'; +export { HatenaIcon } from './HatenaIcon'; +export { HatenaShareButton } from './HatenaShareButton'; export { buildHatenaShareUrl } from './HatenaShareButton'; -export { default as InstapaperIcon } from './InstapaperIcon'; -export { default as InstapaperShareButton } from './InstapaperShareButton'; +export { InstapaperIcon } from './InstapaperIcon'; +export { InstapaperShareButton } from './InstapaperShareButton'; export { buildInstapaperShareUrl } from './InstapaperShareButton'; -export { default as LineIcon } from './LineIcon'; -export { default as LineShareButton } from './LineShareButton'; +export { LineIcon } from './LineIcon'; +export { LineShareButton } from './LineShareButton'; export { buildLineShareUrl } from './LineShareButton'; -export { default as LinkedinIcon } from './LinkedinIcon'; -export { default as LinkedinShareButton } from './LinkedinShareButton'; +export { LinkedinIcon } from './LinkedinIcon'; +export { LinkedinShareButton } from './LinkedinShareButton'; export { buildLinkedinShareUrl } from './LinkedinShareButton'; -export { default as LivejournalIcon } from './LivejournalIcon'; -export { default as LivejournalShareButton } from './LivejournalShareButton'; +export { LivejournalIcon } from './LivejournalIcon'; +export { LivejournalShareButton } from './LivejournalShareButton'; export { buildLivejournalShareUrl } from './LivejournalShareButton'; -export { default as MailruIcon } from './MailruIcon'; -export { default as MailruShareButton } from './MailruShareButton'; +export { MailruIcon } from './MailruIcon'; +export { MailruShareButton } from './MailruShareButton'; export { buildMailruShareUrl } from './MailruShareButton'; -export { default as OKIcon } from './OKIcon'; -export { default as OKShareButton } from './OKShareButton'; +export { OKIcon } from './OKIcon'; +export { OKShareButton } from './OKShareButton'; export { buildOKShareUrl } from './OKShareButton'; -export { default as PinterestIcon } from './PinterestIcon'; -export { default as PinterestShareButton } from './PinterestShareButton'; +export { PinterestIcon } from './PinterestIcon'; +export { PinterestShareButton } from './PinterestShareButton'; export { buildPinterestShareUrl } from './PinterestShareButton'; -export { default as PocketIcon } from './PocketIcon'; -export { default as PocketShareButton } from './PocketShareButton'; +export { PocketIcon } from './PocketIcon'; +export { PocketShareButton } from './PocketShareButton'; export { buildPocketShareUrl } from './PocketShareButton'; -export { default as RedditIcon } from './RedditIcon'; -export { default as RedditShareButton } from './RedditShareButton'; +export { RedditIcon } from './RedditIcon'; +export { RedditShareButton } from './RedditShareButton'; export { buildRedditShareUrl } from './RedditShareButton'; -export { default as GabShareButton } from './GabShareButton'; -export { default as GabIcon } from './GabIcon'; +export { GabShareButton } from './GabShareButton'; +export { GabIcon } from './GabIcon'; export { buildGabShareUrl } from './GabShareButton'; -export { default as TelegramIcon } from './TelegramIcon'; -export { default as TelegramShareButton } from './TelegramShareButton'; +export { TelegramIcon } from './TelegramIcon'; +export { TelegramShareButton } from './TelegramShareButton'; export { buildTelegramShareUrl } from './TelegramShareButton'; -export { default as ThreadsIcon } from './ThreadsIcon'; -export { default as ThreadsShareButton } from './ThreadsShareButton'; +export { ThreadsIcon } from './ThreadsIcon'; +export { ThreadsShareButton } from './ThreadsShareButton'; export { buildThreadsShareUrl } from './ThreadsShareButton'; -export { default as TumblrIcon } from './TumblrIcon'; -export { default as TumblrShareButton } from './TumblrShareButton'; +export { TumblrIcon } from './TumblrIcon'; +export { TumblrShareButton } from './TumblrShareButton'; export { buildTumblrShareUrl } from './TumblrShareButton'; -export { default as XShareButton } from './XShareButton'; +export { XShareButton } from './XShareButton'; export { buildXShareUrl } from './XShareButton'; -export { default as ViberIcon } from './ViberIcon'; -export { default as ViberShareButton } from './ViberShareButton'; +export { ViberIcon } from './ViberIcon'; +export { ViberShareButton } from './ViberShareButton'; export { buildViberShareUrl } from './ViberShareButton'; -export { default as VKIcon } from './VKIcon'; -export { default as VKShareButton } from './VKShareButton'; +export { VKIcon } from './VKIcon'; +export { VKShareButton } from './VKShareButton'; export { buildVKShareUrl } from './VKShareButton'; -export { default as WeiboIcon } from './WeiboIcon'; -export { default as WeiboShareButton } from './WeiboShareButton'; +export { WeiboIcon } from './WeiboIcon'; +export { WeiboShareButton } from './WeiboShareButton'; export { buildWeiboShareUrl } from './WeiboShareButton'; -export { default as WhatsappIcon } from './WhatsappIcon'; -export { default as WhatsappShareButton } from './WhatsappShareButton'; +export { WhatsappIcon } from './WhatsappIcon'; +export { WhatsappShareButton } from './WhatsappShareButton'; export { buildWhatsappShareUrl } from './WhatsappShareButton'; -export { default as WorkplaceIcon } from './WorkplaceIcon'; -export { default as WorkplaceShareButton } from './WorkplaceShareButton'; +export { WorkplaceIcon } from './WorkplaceIcon'; +export { WorkplaceShareButton } from './WorkplaceShareButton'; export { buildWorkplaceShareUrl } from './WorkplaceShareButton'; -export { default as XIcon } from './XIcon'; +export { XIcon } from './XIcon'; export { openShareWindow, type ShareWindowOptions, type WindowPosition } from './shareWindow'; diff --git a/src/utils/assert.ts b/src/utils/assert.ts index 81370f13..e21126ed 100644 --- a/src/utils/assert.ts +++ b/src/utils/assert.ts @@ -5,7 +5,7 @@ class AssertionError extends Error { } } -export default function assert(value: unknown, message: string) { +export function assert(value: unknown, message: string) { if (!value) { throw new AssertionError(message); } diff --git a/src/utils/objectToGetParams.ts b/src/utils/objectToGetParams.ts index 94d65293..6288f39f 100644 --- a/src/utils/objectToGetParams.ts +++ b/src/utils/objectToGetParams.ts @@ -1,4 +1,4 @@ -export default function objectToGetParams(object: { +export function objectToGetParams(object: { [key: string]: string | number | undefined | null; }) { const params = Object.entries(object) diff --git a/test/EmailShareButton.test.tsx b/test/EmailShareButton.test.tsx index 17f54507..c76bafe6 100644 --- a/test/EmailShareButton.test.tsx +++ b/test/EmailShareButton.test.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { fireEvent, render, screen } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; -import EmailShareButton from '../src/EmailShareButton'; +import { EmailShareButton } from '../src/EmailShareButton'; describe('EmailShareButton', () => { it('navigates to a mailto link instead of opening a popup', () => { diff --git a/test/FacebookMessengerShareButton.test.tsx b/test/FacebookMessengerShareButton.test.tsx index d6c2d597..a3416187 100644 --- a/test/FacebookMessengerShareButton.test.tsx +++ b/test/FacebookMessengerShareButton.test.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { fireEvent, render, screen } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; -import FacebookMessengerShareButton from '../src/FacebookMessengerShareButton'; +import { FacebookMessengerShareButton } from '../src/FacebookMessengerShareButton'; describe('FacebookMessengerShareButton', () => { it('defaults redirectUri to the shared url', () => { diff --git a/test/IconBase.test.tsx b/test/IconBase.test.tsx index 96d554dd..08c767c0 100644 --- a/test/IconBase.test.tsx +++ b/test/IconBase.test.tsx @@ -2,22 +2,22 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; import { describe, expect, it } from 'vitest'; -import FacebookIcon from '../src/FacebookIcon'; -import FacebookMessengerIcon from '../src/FacebookMessengerIcon'; -import HatenaIcon from '../src/HatenaIcon'; -import IconBase from '../src/icons/IconBase'; -import LineIcon from '../src/LineIcon'; -import LivejournalIcon from '../src/LivejournalIcon'; -import MailruIcon from '../src/MailruIcon'; -import OKIcon from '../src/OKIcon'; -import PinterestIcon from '../src/PinterestIcon'; -import RedditIcon from '../src/RedditIcon'; -import TelegramIcon from '../src/TelegramIcon'; -import TumblrIcon from '../src/TumblrIcon'; -import VKIcon from '../src/VKIcon'; -import ViberIcon from '../src/ViberIcon'; -import WeiboIcon from '../src/WeiboIcon'; -import WorkplaceIcon from '../src/WorkplaceIcon'; +import { FacebookIcon } from '../src/FacebookIcon'; +import { FacebookMessengerIcon } from '../src/FacebookMessengerIcon'; +import { HatenaIcon } from '../src/HatenaIcon'; +import { IconBase } from '../src/icons/IconBase'; +import { LineIcon } from '../src/LineIcon'; +import { LivejournalIcon } from '../src/LivejournalIcon'; +import { MailruIcon } from '../src/MailruIcon'; +import { OKIcon } from '../src/OKIcon'; +import { PinterestIcon } from '../src/PinterestIcon'; +import { RedditIcon } from '../src/RedditIcon'; +import { TelegramIcon } from '../src/TelegramIcon'; +import { TumblrIcon } from '../src/TumblrIcon'; +import { VKIcon } from '../src/VKIcon'; +import { ViberIcon } from '../src/ViberIcon'; +import { WeiboIcon } from '../src/WeiboIcon'; +import { WorkplaceIcon } from '../src/WorkplaceIcon'; describe('IconBase', () => { it('renders exported icons with the shared rectangular background by default', () => { diff --git a/test/NetworkShareButtons.test.tsx b/test/NetworkShareButtons.test.tsx index d86fd9fa..f50deac5 100644 --- a/test/NetworkShareButtons.test.tsx +++ b/test/NetworkShareButtons.test.tsx @@ -2,24 +2,24 @@ import React from 'react'; import { fireEvent, render, screen } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; -import BlueskyShareButton from '../src/BlueskyShareButton'; -import FacebookShareButton from '../src/FacebookShareButton'; -import GabShareButton from '../src/GabShareButton'; -import HatenaShareButton from '../src/HatenaShareButton'; -import InstapaperShareButton from '../src/InstapaperShareButton'; -import LineShareButton from '../src/LineShareButton'; -import LinkedinShareButton from '../src/LinkedinShareButton'; -import LivejournalShareButton from '../src/LivejournalShareButton'; -import MailruShareButton from '../src/MailruShareButton'; -import OKShareButton from '../src/OKShareButton'; -import PocketShareButton from '../src/PocketShareButton'; -import TelegramShareButton from '../src/TelegramShareButton'; -import ThreadsShareButton from '../src/ThreadsShareButton'; -import TumblrShareButton from '../src/TumblrShareButton'; -import ViberShareButton from '../src/ViberShareButton'; -import VKShareButton from '../src/VKShareButton'; -import WeiboShareButton from '../src/WeiboShareButton'; -import WorkplaceShareButton from '../src/WorkplaceShareButton'; +import { BlueskyShareButton } from '../src/BlueskyShareButton'; +import { FacebookShareButton } from '../src/FacebookShareButton'; +import { GabShareButton } from '../src/GabShareButton'; +import { HatenaShareButton } from '../src/HatenaShareButton'; +import { InstapaperShareButton } from '../src/InstapaperShareButton'; +import { LineShareButton } from '../src/LineShareButton'; +import { LinkedinShareButton } from '../src/LinkedinShareButton'; +import { LivejournalShareButton } from '../src/LivejournalShareButton'; +import { MailruShareButton } from '../src/MailruShareButton'; +import { OKShareButton } from '../src/OKShareButton'; +import { PocketShareButton } from '../src/PocketShareButton'; +import { TelegramShareButton } from '../src/TelegramShareButton'; +import { ThreadsShareButton } from '../src/ThreadsShareButton'; +import { TumblrShareButton } from '../src/TumblrShareButton'; +import { ViberShareButton } from '../src/ViberShareButton'; +import { VKShareButton } from '../src/VKShareButton'; +import { WeiboShareButton } from '../src/WeiboShareButton'; +import { WorkplaceShareButton } from '../src/WorkplaceShareButton'; type ShareButtonCase = { children: string; diff --git a/test/PinterestShareButton.test.tsx b/test/PinterestShareButton.test.tsx index 561d23ba..ce9bb5a5 100644 --- a/test/PinterestShareButton.test.tsx +++ b/test/PinterestShareButton.test.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { fireEvent, render, screen } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; -import PinterestShareButton from '../src/PinterestShareButton'; +import { PinterestShareButton } from '../src/PinterestShareButton'; describe('PinterestShareButton', () => { it('builds a create-pin url when media is provided', () => { diff --git a/test/RedditShareButton.test.tsx b/test/RedditShareButton.test.tsx index 063b230d..ea005bb7 100644 --- a/test/RedditShareButton.test.tsx +++ b/test/RedditShareButton.test.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { fireEvent, render, screen } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; -import RedditShareButton from '../src/RedditShareButton'; +import { RedditShareButton } from '../src/RedditShareButton'; describe('RedditShareButton', () => { it('uses the current Reddit share URL', () => { diff --git a/test/ShareButton.test.tsx b/test/ShareButton.test.tsx index 063a7e19..4046d7a1 100644 --- a/test/ShareButton.test.tsx +++ b/test/ShareButton.test.tsx @@ -2,8 +2,8 @@ import React from 'react'; import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import { afterEach, describe, expect, it, vi } from 'vitest'; -import FacebookIcon from '../src/FacebookIcon'; -import ShareButton from '../src/ShareButton'; +import { FacebookIcon } from '../src/FacebookIcon'; +import { ShareButton } from '../src/ShareButton'; describe('ShareButton', () => { afterEach(() => { diff --git a/test/ShareButtonComponents.test.tsx b/test/ShareButtonComponents.test.tsx index 24b143db..e114b669 100644 --- a/test/ShareButtonComponents.test.tsx +++ b/test/ShareButtonComponents.test.tsx @@ -2,11 +2,11 @@ import React, { createRef } from 'react'; import { render, screen } from '@testing-library/react'; import { describe, expect, it } from 'vitest'; -import EmailIcon from '../src/EmailIcon'; -import EmailShareButton from '../src/EmailShareButton'; -import PinterestIcon from '../src/PinterestIcon'; -import PinterestShareButton from '../src/PinterestShareButton'; -import TumblrShareButton from '../src/TumblrShareButton'; +import { EmailIcon } from '../src/EmailIcon'; +import { EmailShareButton } from '../src/EmailShareButton'; +import { PinterestIcon } from '../src/PinterestIcon'; +import { PinterestShareButton } from '../src/PinterestShareButton'; +import { TumblrShareButton } from '../src/TumblrShareButton'; describe('share button components', () => { it('does not forward network-specific props to the DOM', () => { diff --git a/test/Utilities.test.ts b/test/Utilities.test.ts index d185b119..fd394a57 100644 --- a/test/Utilities.test.ts +++ b/test/Utilities.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; -import assert from '../src/utils/assert'; -import objectToGetParams from '../src/utils/objectToGetParams'; +import { assert } from '../src/utils/assert'; +import { objectToGetParams } from '../src/utils/objectToGetParams'; describe('utilities', () => { it('encodes query parameters and skips nullish values', () => { diff --git a/test/WhatsappShareButton.test.tsx b/test/WhatsappShareButton.test.tsx index cacce31b..acc5abcd 100644 --- a/test/WhatsappShareButton.test.tsx +++ b/test/WhatsappShareButton.test.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { fireEvent, render, screen } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; -import WhatsappShareButton from '../src/WhatsappShareButton'; +import { WhatsappShareButton } from '../src/WhatsappShareButton'; describe('WhatsappShareButton', () => { it('uses the api host for generated share links', () => { diff --git a/test/XShareButton.test.tsx b/test/XShareButton.test.tsx index f07811a1..54f2fb4e 100644 --- a/test/XShareButton.test.tsx +++ b/test/XShareButton.test.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { fireEvent, render, screen } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; -import XShareButton from '../src/XShareButton'; +import { XShareButton } from '../src/XShareButton'; describe('XShareButton', () => { it('builds a share url with hashtags and related accounts', () => { const openSpy = vi.spyOn(window, 'open').mockReturnValue(null); From e05b396b076cf74d79a53f95f42ad8bd26d18717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20Nyga=CC=8Ard?= Date: Mon, 9 Mar 2026 19:46:42 +0200 Subject: [PATCH 09/36] feat(mobile): add opt-in app handoff for messaging buttons Address recurring mobile handoff reports from #577, #551, #529, and the earlier Messenger deep-link proposal in #357 without changing the default web-link behavior. The new mobileBehavior prop keeps the existing web flow as the default and adds best-effort app-link behavior for WhatsApp, Telegram, Viber, and Messenger when consumers explicitly opt in. --- .changeset/silent-peaches-smell.md | 7 ++ README.md | 37 ++++++-- src/FacebookMessengerShareButton.tsx | 55 ++++++++---- src/ShareButton.tsx | 12 ++- src/TelegramShareButton.tsx | 46 +++++++--- src/ViberShareButton.tsx | 40 +++++---- src/WhatsappShareButton.tsx | 53 +++++++---- src/mobile.ts | 63 +++++++++++++ src/shareWindow.ts | 1 + test/FacebookMessengerShareButton.test.tsx | 59 ++++++++++++ test/MobileShareButtons.test.tsx | 100 +++++++++++++++++++++ test/WhatsappShareButton.test.tsx | 57 ++++++++++++ 12 files changed, 464 insertions(+), 66 deletions(-) create mode 100644 .changeset/silent-peaches-smell.md create mode 100644 src/mobile.ts create mode 100644 test/MobileShareButtons.test.tsx diff --git a/.changeset/silent-peaches-smell.md b/.changeset/silent-peaches-smell.md new file mode 100644 index 00000000..2fedf774 --- /dev/null +++ b/.changeset/silent-peaches-smell.md @@ -0,0 +1,7 @@ +--- +'react-share': minor +--- + +Add an explicit `mobileBehavior` prop to `FacebookMessengerShareButton`, `TelegramShareButton`, `ViberShareButton`, and `WhatsappShareButton`. + +This follows recurring mobile browser and app-handoff reports in #577, #551, #529, and #357. The new prop keeps web sharing as the default while allowing best-effort app-link behavior with `mobileBehavior="app"` or `mobileBehavior="auto"`. diff --git a/README.md b/README.md index 4c7c2378..9bed9533 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ npm install react-share - no external script loading, i.e. no dependencies on SDKs - supports tree shaking with ES modules - opens a popup share-window +- includes opt-in mobile app handoff for selected messaging networks - share buttons for: - [Bluesky](#blueskysharebutton) - [Email](#emailsharebutton) @@ -91,6 +92,7 @@ Required: - `windowPosition` (`"windowCenter" | "screenCenter"`): Controls popup positioning on popup-based buttons. Optional on popup-based share buttons: - `openShareDialogOnClick` (`boolean`, default `true`): Opens the popup automatically on click. `EmailShareButton` does not expose this prop because it always uses `mailto:` navigation. +- `mobileBehavior` (`"web" | "app" | "auto"`): Supported only by `FacebookMessengerShareButton`, `TelegramShareButton`, `ViberShareButton`, and `WhatsappShareButton`. Defaults to `"web"`. `"app"` uses a best-effort app/deep link. `"auto"` prefers that app/deep link on detected mobile environments and otherwise keeps the web flow. The native button `title` attribute uses `htmlTitle` so it does not conflict with share payload props such as `text`, `message`, `shareTitle`, or `postTitle`: @@ -177,13 +179,19 @@ Extra required props: Optional props: - `redirectUri` (`string`): Redirect target after sharing. Defaults to the shared URL. - `to` (`string`): Recipient user ID. +- `mobileBehavior` (`"web" | "app" | "auto"`, default `"web"`): Best-effort mobile strategy. `"app"` uses a Messenger deep link and `"auto"` only does that on detected mobile environments. - Notes: - - Mobile behavior depends on Meta's current app and dialog support. + - Mobile behavior depends on Meta's current app and dialog support and is less reliable than WhatsApp or Telegram app handoff. ```jsx import { FacebookMessengerShareButton, FacebookMessengerIcon } from "react-share"; - + ; ``` @@ -207,6 +215,7 @@ import { FacebookShareButton, FacebookIcon } from "react-share"; Optional props: - `text` (`string`): Text to share before the URL. +- `mobileBehavior` (`"web" | "app" | "auto"`, default `"web"`): `"app"` uses a Telegram app link and `"auto"` uses it only on detected mobile environments. ```jsx import { GabShareButton, GabIcon } from "react-share"; @@ -413,7 +422,12 @@ Optional props: ```jsx import { TelegramShareButton, TelegramIcon } from "react-share"; - + ; ``` @@ -486,11 +500,17 @@ Optional props: - `message` (`string`): Message to share before the URL. - `separator` (`string`, default `" "`): Separator between the message and URL. +- `mobileBehavior` (`"web" | "app" | "auto"`, default `"web"`): `"app"` navigates to the Viber URL scheme directly and `"auto"` uses that path only on detected mobile environments. ```jsx import { ViberShareButton, ViberIcon } from "react-share"; - + ; ``` @@ -545,13 +565,19 @@ Optional props: - `message` (`string`): Message to share before the URL. - `separator` (`string`, default `" "`): Separator between the message and URL. +- `mobileBehavior` (`"web" | "app" | "auto"`, default `"web"`): `"app"` uses a WhatsApp app link and `"auto"` uses it only on detected mobile environments. - Notes: - Generates an `api.whatsapp.com` share URL. ```jsx import { WhatsappShareButton, WhatsappIcon } from "react-share"; - + ; ``` @@ -604,6 +630,7 @@ Standalone icons are raw SVG elements. Mark decorative icons with `aria-hidden=" - Popup blockers: share buttons use `window.open` from the click handler. If your app does async work in `beforeOnClick`, some browsers may treat the eventual popup as script-triggered and block it. In those cases, set `openShareDialogOnClick={false}` and manage the resulting URL yourself. - Accessibility: icon-only buttons should always have an accessible name. Use `aria-label` or `aria-labelledby` on the share button; `htmlTitle` is only a tooltip. - Mobile app handoff: some platforms open the browser, an in-app browser, or an extra tab before handing off to a native app. This behavior is controlled by the target platform and browser, not by `react-share`. +- Mobile app strategies: `mobileBehavior="app"` and `mobileBehavior="auto"` are best-effort only. The library does not try to detect whether an app-open succeeded or guarantee a browser fallback path. - iOS PWAs: standalone mode can show a blank intermediary page for schemes such as `mailto:` or native-app handoff URLs. This is a platform limitation. ### About semantic versioning diff --git a/src/FacebookMessengerShareButton.tsx b/src/FacebookMessengerShareButton.tsx index e49cfc16..91237ff9 100644 --- a/src/FacebookMessengerShareButton.tsx +++ b/src/FacebookMessengerShareButton.tsx @@ -1,10 +1,13 @@ import { forwardRef } from 'react'; +import { type MobileBehavior, resolveMobileShareStrategy } from './mobile'; import { objectToGetParams } from './utils/objectToGetParams'; import { ShareButton, type ShareButtonProps } from './ShareButton'; type Options = { /** Your app's unique identifier. */ appId: string; + /** Best-effort mobile strategy. `app` and `auto` prefer a Messenger deep link. */ + mobileBehavior?: MobileBehavior; /** The URL to redirect to after a person clicks a button on the dialog. * Required when using URL redirection. */ redirectUri?: string; @@ -29,24 +32,44 @@ function facebookMessengerLink(url: string, { appId, redirectUri, to }: Options) export const buildFacebookMessengerShareUrl = facebookMessengerLink; +function facebookMessengerAppLink(url: string, { appId, redirectUri, to }: Options) { + return ( + 'fb-messenger://share' + + objectToGetParams({ + link: url, + redirect_uri: redirectUri || url, + app_id: appId, + to, + }) + ); +} + export const FacebookMessengerShareButton = forwardRef< HTMLButtonElement, FacebookMessengerShareButtonProps ->(({ appId, redirectUri, to, ...props }, ref) => ( - -)); +>(({ appId, mobileBehavior, redirectUri, to, ...props }, ref) => { + const { linkBehavior, networkLink } = resolveMobileShareStrategy( + mobileBehavior, + buildFacebookMessengerShareUrl, + facebookMessengerAppLink, + ); -FacebookMessengerShareButton.displayName = 'FacebookMessengerShareButton'; + return ( + + ); +}); +FacebookMessengerShareButton.displayName = 'FacebookMessengerShareButton'; diff --git a/src/ShareButton.tsx b/src/ShareButton.tsx index 15d9c7d9..6c7978ac 100644 --- a/src/ShareButton.tsx +++ b/src/ShareButton.tsx @@ -1,7 +1,7 @@ import { Children, isValidElement } from 'react'; import type React from 'react'; import cx from 'classnames'; -import { openShareWindow, type WindowPosition } from './shareWindow'; +import { openShareWindow, type LinkBehavior, type WindowPosition } from './shareWindow'; type NetworkLink = (url: string, options: LinkOptions) => string; @@ -101,6 +101,7 @@ export interface Props extends NativeButtonProps { * share payload props exposed by the button components. */ htmlTitle?: HTMLButtonElement['title']; + linkBehavior?: LinkBehavior; networkName: string; networkLink: NetworkLink; onClick?: (event: React.MouseEvent, link: string) => void; @@ -123,7 +124,7 @@ export interface Props extends NativeButtonProps { export type ShareButtonProps> = Omit< Props, - 'forwardedRef' | 'networkLink' | 'networkName' | 'opts' + 'forwardedRef' | 'linkBehavior' | 'networkLink' | 'networkName' | 'opts' >; export function ShareButton>({ @@ -136,6 +137,7 @@ export function ShareButton>({ disabledStyle = { opacity: 0.6 }, forwardedRef, htmlTitle, + linkBehavior = 'popup', networkLink, networkName, // deconstructed from ...rest to prevent passing it to the button element onClick, @@ -176,7 +178,7 @@ export function ShareButton>({ } } - if (openShareDialogOnClick) { + if (openShareDialogOnClick && linkBehavior === 'popup') { openShareWindow(link, { height: windowHeight, onClose: onShareWindowClose, @@ -185,6 +187,10 @@ export function ShareButton>({ }); } + if (openShareDialogOnClick && linkBehavior === 'navigate') { + window.location.href = link; + } + if (onClick) { onClick(event, link); } diff --git a/src/TelegramShareButton.tsx b/src/TelegramShareButton.tsx index 4937f72f..95e78e85 100644 --- a/src/TelegramShareButton.tsx +++ b/src/TelegramShareButton.tsx @@ -1,9 +1,11 @@ import { forwardRef } from 'react'; +import { type MobileBehavior, resolveMobileShareStrategy } from './mobile'; import { assert } from './utils/assert'; import { objectToGetParams } from './utils/objectToGetParams'; import { ShareButton, type ShareButtonProps } from './ShareButton'; type TelegramShareButtonProps = Omit, 'title'> & { + mobileBehavior?: MobileBehavior; text?: string; }; @@ -21,19 +23,39 @@ function telegramLink(url: string, { text }: { text?: string }) { export const buildTelegramShareUrl = telegramLink; +function telegramAppLink(url: string, { text }: { text?: string }) { + assert(url, 'telegram.url'); + + return ( + 'tg://msg_url' + + objectToGetParams({ + url, + text, + }) + ); +} + export const TelegramShareButton = forwardRef( - ({ text, ...props }, ref) => ( - - ), + ({ mobileBehavior, text, ...props }, ref) => { + const { linkBehavior, networkLink } = resolveMobileShareStrategy( + mobileBehavior, + buildTelegramShareUrl, + telegramAppLink, + ); + + return ( + + ); + }, ); TelegramShareButton.displayName = 'TelegramShareButton'; - diff --git a/src/ViberShareButton.tsx b/src/ViberShareButton.tsx index 99fa48a3..cb00d689 100644 --- a/src/ViberShareButton.tsx +++ b/src/ViberShareButton.tsx @@ -1,4 +1,5 @@ import { forwardRef } from 'react'; +import { type MobileBehavior, resolveMobileShareStrategy } from './mobile'; import { assert } from './utils/assert'; import { objectToGetParams } from './utils/objectToGetParams'; import { ShareButton, type ShareButtonProps } from './ShareButton'; @@ -8,6 +9,7 @@ type ViberShareButtonProps = Omit< 'title' > & { message?: string; + mobileBehavior?: MobileBehavior; separator?: string; }; @@ -24,21 +26,29 @@ function viberLink(url: string, { message, separator }: { message?: string; sepa export const buildViberShareUrl = viberLink; export const ViberShareButton = forwardRef( - ({ message, separator, ...props }, ref) => ( - - ), + ({ message, mobileBehavior, separator, ...props }, ref) => { + const { linkBehavior, networkLink } = resolveMobileShareStrategy( + mobileBehavior, + buildViberShareUrl, + buildViberShareUrl, + ); + + return ( + + ); + }, ); ViberShareButton.displayName = 'ViberShareButton'; - diff --git a/src/WhatsappShareButton.tsx b/src/WhatsappShareButton.tsx index 498ede0d..780e59cc 100644 --- a/src/WhatsappShareButton.tsx +++ b/src/WhatsappShareButton.tsx @@ -1,4 +1,5 @@ import { forwardRef } from 'react'; +import { type MobileBehavior, resolveMobileShareStrategy } from './mobile'; import { assert } from './utils/assert'; import { objectToGetParams } from './utils/objectToGetParams'; import { ShareButton, type ShareButtonProps } from './ShareButton'; @@ -8,6 +9,7 @@ type WhatsappShareButtonProps = Omit< 'title' > & { message?: string; + mobileBehavior?: MobileBehavior; separator?: string; }; @@ -26,22 +28,43 @@ function whatsappLink( export const buildWhatsappShareUrl = whatsappLink; +function whatsappAppLink( + url: string, + { message, separator }: { message?: string; separator?: string }, +) { + assert(url, 'whatsapp.url'); + return ( + 'whatsapp://send' + + objectToGetParams({ + text: message ? message + separator + url : url, + }) + ); +} + export const WhatsappShareButton = forwardRef( - ({ message, separator, ...props }, ref) => ( - - ), + ({ message, mobileBehavior, separator, ...props }, ref) => { + const { linkBehavior, networkLink } = resolveMobileShareStrategy( + mobileBehavior, + buildWhatsappShareUrl, + whatsappAppLink, + ); + + return ( + + ); + }, ); WhatsappShareButton.displayName = 'WhatsappShareButton'; - diff --git a/src/mobile.ts b/src/mobile.ts new file mode 100644 index 00000000..ea3b0d4c --- /dev/null +++ b/src/mobile.ts @@ -0,0 +1,63 @@ +export type MobileBehavior = 'web' | 'app' | 'auto'; + +type LinkBehavior = 'popup' | 'navigate'; +type LinkBuilder = (url: string, options: LinkOptions) => string; + +type ResolvedShareStrategy = { + linkBehavior: LinkBehavior; + networkLink: LinkBuilder; +}; + +function matchesMobileUserAgent(userAgent: string) { + return /Android|iPhone|iPad|iPod|IEMobile|Opera Mini/i.test(userAgent); +} + +export function isMobileShareEnvironment() { + if (typeof navigator === 'undefined') { + return false; + } + + if ('userAgentData' in navigator) { + const userAgentData = navigator.userAgentData as { mobile?: boolean }; + + if (typeof userAgentData.mobile === 'boolean') { + return userAgentData.mobile; + } + } + + if (matchesMobileUserAgent(navigator.userAgent)) { + return true; + } + + return navigator.maxTouchPoints > 1 && /Mac/i.test(navigator.platform); +} + +export function resolveMobileShareStrategy( + mobileBehavior: MobileBehavior | undefined, + webLink: LinkBuilder, + appLink: LinkBuilder, +): ResolvedShareStrategy { + switch (mobileBehavior) { + case 'app': + return { + linkBehavior: 'navigate', + networkLink: appLink, + }; + case 'auto': + return isMobileShareEnvironment() + ? { + linkBehavior: 'navigate', + networkLink: appLink, + } + : { + linkBehavior: 'popup', + networkLink: webLink, + }; + case 'web': + default: + return { + linkBehavior: 'popup', + networkLink: webLink, + }; + } +} diff --git a/src/shareWindow.ts b/src/shareWindow.ts index f2d94439..f87ca872 100644 --- a/src/shareWindow.ts +++ b/src/shareWindow.ts @@ -1,4 +1,5 @@ export type WindowPosition = 'windowCenter' | 'screenCenter'; +export type LinkBehavior = 'popup' | 'navigate'; export type ShareWindowOptions = { height?: number; diff --git a/test/FacebookMessengerShareButton.test.tsx b/test/FacebookMessengerShareButton.test.tsx index a3416187..8b2e6e4b 100644 --- a/test/FacebookMessengerShareButton.test.tsx +++ b/test/FacebookMessengerShareButton.test.tsx @@ -5,6 +5,24 @@ import { describe, expect, it, vi } from 'vitest'; import { FacebookMessengerShareButton } from '../src/FacebookMessengerShareButton'; describe('FacebookMessengerShareButton', () => { + function stubLocation() { + const hrefState = { value: 'https://example.test/' }; + + Object.defineProperty(window, 'location', { + configurable: true, + value: { + get href() { + return hrefState.value; + }, + set href(value: string) { + hrefState.value = value; + }, + }, + }); + + return hrefState; + } + it('defaults redirectUri to the shared url', () => { const openSpy = vi.spyOn(window, 'open').mockReturnValue(null); @@ -45,4 +63,45 @@ describe('FacebookMessengerShareButton', () => { expect.any(String), ); }); + + it('uses a Messenger app deep link when mobileBehavior is app', () => { + const openSpy = vi.spyOn(window, 'open').mockReturnValue(null); + const hrefState = stubLocation(); + + render( + + Share in Messenger + , + ); + + fireEvent.click(screen.getByRole('button', { name: 'Share in Messenger' })); + + expect(openSpy).not.toHaveBeenCalled(); + expect(hrefState.value).toBe( + 'fb-messenger://share?link=https%3A%2F%2Fexample.com&redirect_uri=https%3A%2F%2Fexample.com&app_id=123', + ); + }); + + it('keeps the web dialog on desktop when mobileBehavior is auto', () => { + const openSpy = vi.spyOn(window, 'open').mockReturnValue(null); + + Object.defineProperty(window.navigator, 'userAgent', { + configurable: true, + value: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)', + }); + + render( + + Share in Messenger + , + ); + + fireEvent.click(screen.getByRole('button', { name: 'Share in Messenger' })); + + expect(openSpy).toHaveBeenCalledWith( + 'https://www.facebook.com/dialog/send?link=https%3A%2F%2Fexample.com&redirect_uri=https%3A%2F%2Fexample.com&app_id=123', + '', + expect.any(String), + ); + }); }); diff --git a/test/MobileShareButtons.test.tsx b/test/MobileShareButtons.test.tsx new file mode 100644 index 00000000..bfc34471 --- /dev/null +++ b/test/MobileShareButtons.test.tsx @@ -0,0 +1,100 @@ +import React from 'react'; +import { fireEvent, render, screen } from '@testing-library/react'; +import { afterEach, describe, expect, it, vi } from 'vitest'; + +import { TelegramShareButton } from '../src/TelegramShareButton'; +import { ViberShareButton } from '../src/ViberShareButton'; + +function stubLocation() { + const hrefState = { value: 'https://example.test/' }; + + Object.defineProperty(window, 'location', { + configurable: true, + value: { + get href() { + return hrefState.value; + }, + set href(value: string) { + hrefState.value = value; + }, + }, + }); + + return hrefState; +} + +function setDesktopUserAgent() { + Object.defineProperty(window.navigator, 'userAgent', { + configurable: true, + value: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)', + }); +} + +afterEach(() => { + vi.restoreAllMocks(); +}); + +describe('mobile share buttons', () => { + it('uses a Telegram app link when mobileBehavior is app', () => { + const openSpy = vi.spyOn(window, 'open').mockReturnValue(null); + const hrefState = stubLocation(); + + render( + + Share on Telegram + , + ); + + fireEvent.click(screen.getByRole('button', { name: 'Share on Telegram' })); + + expect(openSpy).not.toHaveBeenCalled(); + expect(hrefState.value).toBe('tg://msg_url?url=https%3A%2F%2Fexample.com&text=Example'); + }); + + it('keeps the Telegram web URL on desktop when mobileBehavior is auto', () => { + const openSpy = vi.spyOn(window, 'open').mockReturnValue(null); + setDesktopUserAgent(); + + render( + + Share on Telegram + , + ); + + fireEvent.click(screen.getByRole('button', { name: 'Share on Telegram' })); + + expect(openSpy).toHaveBeenCalledWith( + 'https://telegram.me/share/url?url=https%3A%2F%2Fexample.com&text=Example', + '', + expect.any(String), + ); + }); + + it('navigates directly for Viber when mobileBehavior is app', () => { + const openSpy = vi.spyOn(window, 'open').mockReturnValue(null); + const hrefState = stubLocation(); + + render( + + Share on Viber + , + ); + + fireEvent.click(screen.getByRole('button', { name: 'Share on Viber' })); + + expect(openSpy).not.toHaveBeenCalled(); + expect(hrefState.value).toBe('viber://forward?text=Example%20https%3A%2F%2Fexample.com'); + }); + + it('does not forward mobileBehavior to the DOM', () => { + render( + + Share on Telegram + , + ); + + expect(screen.getByRole('button', { name: 'Share on Telegram' })).not.toHaveAttribute( + 'mobileBehavior', + ); + }); +}); diff --git a/test/WhatsappShareButton.test.tsx b/test/WhatsappShareButton.test.tsx index acc5abcd..2855de4e 100644 --- a/test/WhatsappShareButton.test.tsx +++ b/test/WhatsappShareButton.test.tsx @@ -5,6 +5,24 @@ import { describe, expect, it, vi } from 'vitest'; import { WhatsappShareButton } from '../src/WhatsappShareButton'; describe('WhatsappShareButton', () => { + function stubLocation() { + const hrefState = { value: 'https://example.test/' }; + + Object.defineProperty(window, 'location', { + configurable: true, + value: { + get href() { + return hrefState.value; + }, + set href(value: string) { + hrefState.value = value; + }, + }, + }); + + return hrefState; + } + it('uses the api host for generated share links', () => { const openSpy = vi.spyOn(window, 'open').mockReturnValue(null); @@ -22,4 +40,43 @@ describe('WhatsappShareButton', () => { expect.any(String), ); }); + + it('uses the app deep link when mobileBehavior is app', () => { + const openSpy = vi.spyOn(window, 'open').mockReturnValue(null); + const hrefState = stubLocation(); + + render( + + Share to WhatsApp + , + ); + + fireEvent.click(screen.getByRole('button', { name: 'Share to WhatsApp' })); + + expect(openSpy).not.toHaveBeenCalled(); + expect(hrefState.value).toBe('whatsapp://send?text=Example%20https%3A%2F%2Fexample.com'); + }); + + it('keeps the web URL on desktop when mobileBehavior is auto', () => { + const openSpy = vi.spyOn(window, 'open').mockReturnValue(null); + + Object.defineProperty(window.navigator, 'userAgent', { + configurable: true, + value: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)', + }); + + render( + + Share to WhatsApp + , + ); + + fireEvent.click(screen.getByRole('button', { name: 'Share to WhatsApp' })); + + expect(openSpy).toHaveBeenCalledWith( + 'https://api.whatsapp.com/send?text=Example%20https%3A%2F%2Fexample.com', + '', + expect.any(String), + ); + }); }); From 9e44ba2092580619dc14642fb26dd8c2d252cdb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20Nyga=CC=8Ard?= Date: Mon, 9 Mar 2026 19:48:46 +0200 Subject: [PATCH 10/36] feat(native-share): add Web Share API support Introduce a first-class native share surface for mobile and PWA flows instead of forcing those use cases through popup-based network URLs. This addresses the file-sharing and platform-handoff needs behind #576 and complements the iOS/PWA limitations reported in #551 with canNativeShare(), shareNative(), and NativeShareButton. --- .changeset/tender-rivers-fly.md | 7 ++ README.md | 28 ++++++++ demo/Demo.tsx | 14 +++- src/NativeShareButton.tsx | 124 ++++++++++++++++++++++++++++++++ src/NativeShareIcon.tsx | 9 +++ src/ShareButton.tsx | 47 +----------- src/index.ts | 5 ++ src/nativeShare.ts | 52 ++++++++++++++ src/utils/buttonHelpers.ts | 48 +++++++++++++ test/IconBase.test.tsx | 2 + test/NativeShare.test.tsx | 101 ++++++++++++++++++++++++++ test/PackageBuilds.test.ts | 4 ++ test/PackageExports.test.tsx | 4 ++ 13 files changed, 398 insertions(+), 47 deletions(-) create mode 100644 .changeset/tender-rivers-fly.md create mode 100644 src/NativeShareButton.tsx create mode 100644 src/NativeShareIcon.tsx create mode 100644 src/nativeShare.ts create mode 100644 src/utils/buttonHelpers.ts create mode 100644 test/NativeShare.test.tsx diff --git a/.changeset/tender-rivers-fly.md b/.changeset/tender-rivers-fly.md new file mode 100644 index 00000000..2d6fc6ae --- /dev/null +++ b/.changeset/tender-rivers-fly.md @@ -0,0 +1,7 @@ +--- +'react-share': minor +--- + +Add `canNativeShare(data)`, `shareNative(data)`, `NativeShareButton`, and a generic `NativeShareIcon` for browser-native sharing flows. + +This gives mobile and PWA use cases a first-class Web Share API path instead of forcing them through network-specific popup URLs, which is especially relevant for requests like #576 and the iOS/PWA limitations described in #551. diff --git a/README.md b/README.md index 9bed9533..6f2bc56c 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ npm install react-share - supports tree shaking with ES modules - opens a popup share-window - includes opt-in mobile app handoff for selected messaging networks +- includes Web Share API helpers and a native share button - share buttons for: - [Bluesky](#blueskysharebutton) - [Email](#emailsharebutton) @@ -132,6 +133,33 @@ const url = buildXShareUrl(shareUrl, { openShareWindow(url, { width: 550, height: 400 }); ``` +For mobile-first or PWA-first flows, the package also exports `canNativeShare(data)` and `shareNative(data)` for the browser Web Share API. + +#### NativeShareButton + +Optional props: + +- `title` (`string`): Native share title. +- `text` (`string`): Native share text. +- `url` (`string`): Native share URL. +- `files` (`File[]`): Files to share when the platform supports them. +- Notes: + - Uses `navigator.share()` and disables itself when native sharing is unavailable. + - For file sharing, capability checks rely on `navigator.canShare()`. + +```jsx +import { NativeShareButton, NativeShareIcon } from "react-share"; + + + +; +``` + #### BlueskyShareButton Optional props: diff --git a/demo/Demo.tsx b/demo/Demo.tsx index 89adde2a..b9bb0263 100644 --- a/demo/Demo.tsx +++ b/demo/Demo.tsx @@ -22,6 +22,8 @@ import { LivejournalShareButton, MailruIcon, MailruShareButton, + NativeShareButton, + NativeShareIcon, OKIcon, OKShareButton, PinterestIcon, @@ -219,6 +221,17 @@ export function Demo() {
+
+ + + +
+
); } - diff --git a/src/NativeShareButton.tsx b/src/NativeShareButton.tsx new file mode 100644 index 00000000..7a2e799c --- /dev/null +++ b/src/NativeShareButton.tsx @@ -0,0 +1,124 @@ +import { forwardRef } from 'react'; +import type React from 'react'; +import cx from 'classnames'; +import { canNativeShare, shareNative, type NativeShareData } from './nativeShare'; +import { getButtonBorderRadius, hasTextContent, isPromise } from './utils/buttonHelpers'; + +type NativeButtonProps = Omit, 'title'>; + +export type NativeShareButtonProps = NativeButtonProps & + NativeShareData & { + beforeOnClick?: () => Promise | void; + children: React.ReactNode; + disabled?: boolean; + disabledStyle?: React.CSSProperties; + htmlTitle?: HTMLButtonElement['title']; + resetButtonStyle?: boolean; + }; + +export const NativeShareButton = forwardRef( + ( + { + 'aria-label': ariaLabel, + 'aria-labelledby': ariaLabelledBy, + beforeOnClick, + children, + className, + disabled, + disabledStyle = { opacity: 0.6 }, + files, + htmlTitle, + onClick, + resetButtonStyle = true, + style, + text, + title, + type = 'button', + url, + ...rest + }, + ref, + ) => { + const buttonBorderRadius = getButtonBorderRadius(children); + const shareData = { files, text, title, url }; + const fallbackAriaLabel = + !ariaLabel && !ariaLabelledBy && !hasTextContent(children) ? 'Share' : undefined; + const isUnavailable = !canNativeShare(shareData); + + const handleClick = async (event: React.MouseEvent) => { + if (disabled) { + return; + } + + if (beforeOnClick) { + const returnVal = beforeOnClick(); + + if (isPromise(returnVal)) { + await returnVal; + } + } + + onClick?.(event); + + if (event.defaultPrevented || isUnavailable) { + return; + } + + try { + await shareNative(shareData); + } catch (error) { + // Closing the share sheet rejects with AbortError, which is not a failure. + if (!(error instanceof DOMException && error.name === 'AbortError')) { + throw error; + } + } + }; + + const newClassName = cx( + 'react-share__NativeShareButton', + { + 'react-share__NativeShareButton--disabled': !!disabled || isUnavailable, + disabled: !!disabled || isUnavailable, + }, + className, + ); + + const newStyle = resetButtonStyle + ? { + backgroundColor: 'transparent', + border: 'none', + padding: 0, + display: 'inline-flex', + borderRadius: buttonBorderRadius, + outlineOffset: 2, + font: 'inherit', + color: 'inherit', + cursor: disabled || isUnavailable ? 'not-allowed' : 'pointer', + ...style, + ...((disabled || isUnavailable) && disabledStyle), + } + : { + ...style, + ...((disabled || isUnavailable) && disabledStyle), + }; + + return ( + + ); + }, +); + +NativeShareButton.displayName = 'NativeShareButton'; diff --git a/src/NativeShareIcon.tsx b/src/NativeShareIcon.tsx new file mode 100644 index 00000000..4f2edc85 --- /dev/null +++ b/src/NativeShareIcon.tsx @@ -0,0 +1,9 @@ +import { IconBase, type IconProps } from './icons/IconBase'; + +export function NativeShareIcon(props: IconProps) { + return ( + + + + ); +} diff --git a/src/ShareButton.tsx b/src/ShareButton.tsx index 6c7978ac..6b388de4 100644 --- a/src/ShareButton.tsx +++ b/src/ShareButton.tsx @@ -1,12 +1,11 @@ -import { Children, isValidElement } from 'react'; import type React from 'react'; import cx from 'classnames'; import { openShareWindow, type LinkBehavior, type WindowPosition } from './shareWindow'; +import { getButtonBorderRadius, hasTextContent, isPromise } from './utils/buttonHelpers'; type NetworkLink = (url: string, options: LinkOptions) => string; type NativeButtonProps = Omit, 'onClick'>; -type IconShapeProps = { borderRadius?: number; round?: boolean }; const DEFAULT_ARIA_LABELS = { bluesky: 'Share on Bluesky', @@ -35,50 +34,6 @@ const DEFAULT_ARIA_LABELS = { workplace: 'Share on Workplace', } as const; -const isPromise = (obj: unknown): obj is Promise => - !!obj && - (typeof obj === 'object' || typeof obj === 'function') && - 'then' in obj && - typeof (obj as Promise).then === 'function'; - -function getButtonBorderRadius(children: React.ReactNode) { - const childNodes = Children.toArray(children); - - if (childNodes.length !== 1) { - return undefined; - } - - const [child] = childNodes; - - if (!isValidElement(child)) { - return undefined; - } - - if (child.props.round) { - return '50%'; - } - - return child.props.borderRadius ?? 0; -} - -function hasTextContent(children: React.ReactNode): boolean { - return Children.toArray(children).some(child => { - if (typeof child === 'string') { - return child.trim().length > 0; - } - - if (typeof child === 'number') { - return true; - } - - if (!isValidElement<{ children?: React.ReactNode }>(child)) { - return false; - } - - return hasTextContent(child.props.children); - }); -} - export interface Props extends NativeButtonProps { /** * Takes a function that returns a Promise to be fulfilled before calling diff --git a/src/index.ts b/src/index.ts index 266795c7..7fe65b47 100644 --- a/src/index.ts +++ b/src/index.ts @@ -28,6 +28,11 @@ export { buildLivejournalShareUrl } from './LivejournalShareButton'; export { MailruIcon } from './MailruIcon'; export { MailruShareButton } from './MailruShareButton'; export { buildMailruShareUrl } from './MailruShareButton'; +export { NativeShareButton } from './NativeShareButton'; +export { NativeShareIcon } from './NativeShareIcon'; +export { canNativeShare } from './nativeShare'; +export { shareNative } from './nativeShare'; +export type { NativeShareData } from './nativeShare'; export { OKIcon } from './OKIcon'; export { OKShareButton } from './OKShareButton'; export { buildOKShareUrl } from './OKShareButton'; diff --git a/src/nativeShare.ts b/src/nativeShare.ts new file mode 100644 index 00000000..f2f096a6 --- /dev/null +++ b/src/nativeShare.ts @@ -0,0 +1,52 @@ +export type NativeShareData = Pick; + +function normalizeNativeShareData(data: NativeShareData): ShareData { + return { + ...(data.title ? { title: data.title } : {}), + ...(data.text ? { text: data.text } : {}), + ...(data.url ? { url: data.url } : {}), + ...(data.files?.length ? { files: data.files } : {}), + }; +} + +function hasSharePayload(data: NativeShareData) { + return Boolean(data.title || data.text || data.url || data.files?.length); +} + +export function canNativeShare(data: NativeShareData = {}) { + if (typeof navigator === 'undefined' || typeof navigator.share !== 'function') { + return false; + } + + if (!hasSharePayload(data)) { + return false; + } + + const shareData = normalizeNativeShareData(data); + + if (shareData.files?.length) { + return typeof navigator.canShare === 'function' && navigator.canShare(shareData); + } + + if (typeof navigator.canShare === 'function') { + try { + return navigator.canShare(shareData); + } catch { + return true; + } + } + + return true; +} + +export async function shareNative(data: NativeShareData) { + if (!canNativeShare(data)) { + throw new Error( + data.files?.length + ? 'native share with files is not supported' + : 'native share is not supported', + ); + } + + await navigator.share(normalizeNativeShareData(data)); +} diff --git a/src/utils/buttonHelpers.ts b/src/utils/buttonHelpers.ts new file mode 100644 index 00000000..7fae93e3 --- /dev/null +++ b/src/utils/buttonHelpers.ts @@ -0,0 +1,48 @@ +import { Children, isValidElement } from 'react'; +import type React from 'react'; + +type IconShapeProps = { borderRadius?: number; round?: boolean }; + +export const isPromise = (obj: unknown): obj is Promise => + !!obj && + (typeof obj === 'object' || typeof obj === 'function') && + 'then' in obj && + typeof (obj as Promise).then === 'function'; + +export function getButtonBorderRadius(children: React.ReactNode) { + const childNodes = Children.toArray(children); + + if (childNodes.length !== 1) { + return undefined; + } + + const [child] = childNodes; + + if (!isValidElement(child)) { + return undefined; + } + + if (child.props.round) { + return '50%'; + } + + return child.props.borderRadius ?? 0; +} + +export function hasTextContent(children: React.ReactNode): boolean { + return Children.toArray(children).some(child => { + if (typeof child === 'string') { + return child.trim().length > 0; + } + + if (typeof child === 'number') { + return true; + } + + if (!isValidElement<{ children?: React.ReactNode }>(child)) { + return false; + } + + return hasTextContent(child.props.children); + }); +} diff --git a/test/IconBase.test.tsx b/test/IconBase.test.tsx index 08c767c0..42f2a62c 100644 --- a/test/IconBase.test.tsx +++ b/test/IconBase.test.tsx @@ -9,6 +9,7 @@ import { IconBase } from '../src/icons/IconBase'; import { LineIcon } from '../src/LineIcon'; import { LivejournalIcon } from '../src/LivejournalIcon'; import { MailruIcon } from '../src/MailruIcon'; +import { NativeShareIcon } from '../src/NativeShareIcon'; import { OKIcon } from '../src/OKIcon'; import { PinterestIcon } from '../src/PinterestIcon'; import { RedditIcon } from '../src/RedditIcon'; @@ -40,6 +41,7 @@ describe('IconBase', () => { ['line', LineIcon, '#06C755'], ['livejournal', LivejournalIcon, '#00B0EA'], ['mailru', MailruIcon, '#005FF9'], + ['native-share', NativeShareIcon, '#0A84FF'], ['ok', OKIcon, '#FF7700'], ['pinterest', PinterestIcon, '#E60023'], ['reddit', RedditIcon, '#FF4500'], diff --git a/test/NativeShare.test.tsx b/test/NativeShare.test.tsx new file mode 100644 index 00000000..4798e815 --- /dev/null +++ b/test/NativeShare.test.tsx @@ -0,0 +1,101 @@ +import React, { createRef } from 'react'; +import { fireEvent, render, screen, waitFor } from '@testing-library/react'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { NativeShareButton } from '../src/NativeShareButton'; +import { canNativeShare, shareNative } from '../src/nativeShare'; + +describe('native share', () => { + beforeEach(() => { + Object.defineProperty(window.navigator, 'canShare', { + configurable: true, + value: undefined, + }); + Object.defineProperty(window.navigator, 'share', { + configurable: true, + value: undefined, + }); + }); + + it('returns false when navigator.share is unavailable', () => { + expect(canNativeShare({ text: 'Example' })).toBe(false); + }); + + it('uses navigator.canShare for file-sharing capability checks', () => { + const canShareMock = vi.fn().mockReturnValue(true); + + Object.defineProperty(window.navigator, 'share', { + configurable: true, + value: vi.fn(), + }); + Object.defineProperty(window.navigator, 'canShare', { + configurable: true, + value: canShareMock, + }); + + const file = new File(['example'], 'example.pdf', { type: 'application/pdf' }); + + expect(canNativeShare({ files: [file], title: 'Example' })).toBe(true); + expect(canShareMock).toHaveBeenCalledWith({ files: [file], title: 'Example' }); + }); + + it('rejects predictably when native share is unavailable', async () => { + await expect(shareNative({ text: 'Example' })).rejects.toThrow('native share is not supported'); + }); + + it('triggers native share only after the user clicks the button', async () => { + const shareMock = vi.fn().mockResolvedValue(undefined); + + Object.defineProperty(window.navigator, 'share', { + configurable: true, + value: shareMock, + }); + + render( + + Share natively + , + ); + + expect(shareMock).not.toHaveBeenCalled(); + + fireEvent.click(screen.getByRole('button', { name: 'Share natively' })); + + await waitFor(() => { + expect(shareMock).toHaveBeenCalledWith({ + text: 'Example', + url: 'https://example.com', + }); + }); + }); + + it('forwards refs to the underlying button', () => { + const ref = createRef(); + + render( + + Share natively + , + ); + + expect(ref.current).toBe(screen.getByRole('button', { name: 'Share natively' })); + }); + + it('ignores share sheet cancellation by the user', async () => { + const shareMock = vi.fn().mockRejectedValue(new DOMException('Share canceled', 'AbortError')); + + Object.defineProperty(window.navigator, 'share', { + configurable: true, + value: shareMock, + }); + + render(Share natively); + + fireEvent.click(screen.getByRole('button', { name: 'Share natively' })); + + await waitFor(() => { + expect(shareMock).toHaveBeenCalled(); + }); + await new Promise(resolve => setTimeout(resolve, 0)); + }); +}); diff --git a/test/PackageBuilds.test.ts b/test/PackageBuilds.test.ts index 32dd09ee..28105835 100644 --- a/test/PackageBuilds.test.ts +++ b/test/PackageBuilds.test.ts @@ -31,12 +31,16 @@ const isUsableExport = (value: unknown) => const representativeExportNames = [ 'FacebookShareButton', + 'NativeShareIcon', + 'NativeShareButton', 'XShareButton', + 'canNativeShare', 'buildFacebookShareUrl', 'buildXShareUrl', 'FacebookIcon', 'XIcon', 'openShareWindow', + 'shareNative', ] as const; describe('package builds', () => { diff --git a/test/PackageExports.test.tsx b/test/PackageExports.test.tsx index ea56d88b..ce5a0a56 100644 --- a/test/PackageExports.test.tsx +++ b/test/PackageExports.test.tsx @@ -10,12 +10,16 @@ const iconExportNames = Object.keys(ReactShare) const representativeExportNames = [ 'FacebookShareButton', + 'NativeShareIcon', + 'NativeShareButton', 'XShareButton', + 'canNativeShare', 'buildFacebookShareUrl', 'buildXShareUrl', 'FacebookIcon', 'XIcon', 'openShareWindow', + 'shareNative', ] as const; describe('package exports', () => { From 08b5638cbe065f8321b6ae353d8cd614859ae7d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20Nyga=CC=8Ard?= Date: Mon, 9 Mar 2026 19:50:12 +0200 Subject: [PATCH 11/36] refactor(linkedin): make LinkedIn sharing URL-only Follow the LinkedIn compatibility reports in #550 and #362 by removing prefill props that no longer have a reliable public URL-based effect. The LinkedIn button and helper now only model the URL that is being shared, which matches the current best-effort behavior more honestly for v6. --- .changeset/swifty-bulldogs-shop.md | 7 +++++++ .changeset/wise-years-rest.md | 2 +- README.md | 16 +++------------- src/LinkedinShareButton.tsx | 23 ++++++----------------- test/NetworkShareButtons.test.tsx | 6 +----- test/ShareHelpers.test.ts | 13 ++++++++++++- 6 files changed, 30 insertions(+), 37 deletions(-) create mode 100644 .changeset/swifty-bulldogs-shop.md diff --git a/.changeset/swifty-bulldogs-shop.md b/.changeset/swifty-bulldogs-shop.md new file mode 100644 index 00000000..1812c408 --- /dev/null +++ b/.changeset/swifty-bulldogs-shop.md @@ -0,0 +1,7 @@ +--- +'react-share': major +--- + +Remove `title`, `summary`, and `source` from `LinkedinShareButton` and make `buildLinkedinShareUrl` URL-only. + +This follows the long-running LinkedIn compatibility problems in #550 and #362. LinkedIn no longer provides a reliable public URL-based way to prefill those fields, so v6 stops exposing them as if they were dependable. diff --git a/.changeset/wise-years-rest.md b/.changeset/wise-years-rest.md index f87d8f4c..d9f23421 100644 --- a/.changeset/wise-years-rest.md +++ b/.changeset/wise-years-rest.md @@ -6,7 +6,7 @@ Replace the share button `title` prop with explicit v6 payload names: - `text` for Bluesky, Gab, Line, Telegram, Threads, and X - `message` for WhatsApp and Viber -- `shareTitle` for Hatena, Instapaper, LiveJournal, LinkedIn, Mail.ru, OK, Pocket, Reddit, VK, and Weibo +- `shareTitle` for Hatena, Instapaper, LiveJournal, Mail.ru, OK, Pocket, Reddit, VK, and Weibo - `postTitle` for Tumblr `htmlTitle` remains the native button tooltip/title attribute. diff --git a/README.md b/README.md index 6f2bc56c..baf0592d 100644 --- a/README.md +++ b/README.md @@ -303,24 +303,14 @@ import { LineShareButton, LineIcon } from "react-share"; #### LinkedinShareButton -Optional props: - -- `shareTitle` (`string`): Title sent in the share payload. -- `summary` (`string`): Description of the shared page. -- `source` (`string`): Source label such as your app or site name. - Notes: - - These fields are best-effort only and may be ignored or rewritten by LinkedIn. + - LinkedIn sharing is URL-first and best-effort. + - The button only sends the shared URL because LinkedIn no longer provides a reliable public URL-based way to prefill title, summary, or source values. ```jsx import { LinkedinShareButton, LinkedinIcon } from "react-share"; - + ; ``` diff --git a/src/LinkedinShareButton.tsx b/src/LinkedinShareButton.tsx index 324868c6..3cbf62bf 100644 --- a/src/LinkedinShareButton.tsx +++ b/src/LinkedinShareButton.tsx @@ -3,36 +3,26 @@ import { assert } from './utils/assert'; import { objectToGetParams } from './utils/objectToGetParams'; import { ShareButton, type ShareButtonProps } from './ShareButton'; -type Options = { - /** The url-encoded title value that you wish you use. */ - shareTitle?: string; - /** The url-encoded description that you wish you use. */ - summary?: string; - /** The url-encoded source of the content (e.g. your website or application name) */ - source?: string; -}; +type Options = Record; -type LinkedinShareButtonProps = Omit, 'title'> & Options; +type LinkedinShareButtonProps = Omit, 'title'>; -function linkedinLink(url: string, { shareTitle, summary, source }: Options) { +function linkedinLink(url: string) { assert(url, 'linkedin.url'); - return ( - 'https://linkedin.com/shareArticle' + - objectToGetParams({ url, mini: 'true', title: shareTitle, summary, source }) - ); + return 'https://linkedin.com/shareArticle' + objectToGetParams({ url, mini: 'true' }); } export const buildLinkedinShareUrl = linkedinLink; export const LinkedinShareButton = forwardRef( - ({ shareTitle, source, summary, ...props }, ref) => ( + (props, ref) => ( @@ -40,4 +30,3 @@ export const LinkedinShareButton = forwardRef { it('builds a Facebook share URL directly', () => { @@ -22,6 +27,12 @@ describe('share helpers', () => { ); }); + it('builds a LinkedIn share URL directly from only the URL', () => { + expect(buildLinkedinShareUrl('https://example.com')).toBe( + 'https://linkedin.com/shareArticle?url=https%3A%2F%2Fexample.com&mini=true', + ); + }); + it('opens a centered share window with the requested size', () => { const openSpy = vi.spyOn(window, 'open').mockReturnValue(null); From e9650470bce45ac71923dcbcf7ea9623fd7a2605 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20Nyga=CC=8Ard?= Date: Mon, 9 Mar 2026 20:11:02 +0200 Subject: [PATCH 12/36] docs(guides): add v6 migration and native sharing guides Add a dedicated v5-to-v6 migration guide and a native/mobile sharing behavior guide under docs/, and link both from the main README for discoverability. --- README.md | 5 + docs/migrate-v5-to-v6.md | 242 +++++++++++++++++++++++++++++++++++++++ docs/native-sharing.md | 170 +++++++++++++++++++++++++++ 3 files changed, 417 insertions(+) create mode 100644 docs/migrate-v5-to-v6.md create mode 100644 docs/native-sharing.md diff --git a/README.md b/README.md index baf0592d..afcfd430 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,11 @@ npm install react-share - Locally: clone the repository and run `npm install && npm run demo`. +## Guides + +- [Migrate v5 to v6](./docs/migrate-v5-to-v6.md) +- [Native and mobile sharing behavior](./docs/native-sharing.md) + ## React compatibility | Version | Compatible React versions | diff --git a/docs/migrate-v5-to-v6.md b/docs/migrate-v5-to-v6.md new file mode 100644 index 00000000..a22c1e7d --- /dev/null +++ b/docs/migrate-v5-to-v6.md @@ -0,0 +1,242 @@ +# Migrate v5 to v6 guide + +`react-share` v6 is a cleanup release. It removes deprecated exports, tightens some platform-specific APIs, and adds first-class native/mobile sharing support. + +## Minimum requirements + +- React 18 or 19 is required. +- React 17 is no longer supported. + +## Removed exports + +### X/Twitter aliases + +`TwitterShareButton` and `TwitterIcon` were removed. + +#### Before (v5) + +```jsx +import { TwitterIcon, TwitterShareButton } from 'react-share'; +``` + +#### After (v6) + +```jsx +import { XIcon, XShareButton } from 'react-share'; +``` + +### Share counts + +All share count exports were removed in v6: + +- `FacebookShareCount` +- `HatenaShareCount` +- `OKShareCount` +- `PinterestShareCount` +- `RedditShareCount` +- `TumblrShareCount` +- `VKShareCount` + +If your app still depends on share counts, keep using `react-share` v5 until you replace that UI. + +## Renamed share payload props + +`htmlTitle` is still the native button `title` attribute. The old overloaded share-button `title` prop was replaced with explicit payload names. + +### `title` -> `text` + +Used by: + +- `BlueskyShareButton` +- `GabShareButton` +- `LineShareButton` +- `TelegramShareButton` +- `ThreadsShareButton` +- `XShareButton` + +#### Before (v5) + +```jsx + +``` + +#### After (v6) + +```jsx + +``` + +### `title` -> `message` + +Used by: + +- `WhatsappShareButton` +- `ViberShareButton` + +#### Before (v5) + +```jsx + +``` + +#### After (v6) + +```jsx + +``` + +### `title` -> `shareTitle` + +Used by: + +- `HatenaShareButton` +- `InstapaperShareButton` +- `LivejournalShareButton` +- `LinkedinShareButton` in v5 only +- `MailruShareButton` +- `OKShareButton` +- `PocketShareButton` +- `RedditShareButton` +- `VKShareButton` +- `WeiboShareButton` + +#### Before (v5) + +```jsx + +``` + +#### After (v6) + +```jsx + +``` + +### `title` -> `postTitle` + +Used by `TumblrShareButton`. + +#### Before (v5) + +```jsx + +``` + +#### After (v6) + +```jsx + +``` + +## LinkedIn is now URL-only + +`LinkedinShareButton` no longer accepts: + +- `shareTitle` +- `summary` +- `source` + +`buildLinkedinShareUrl` is also URL-only now. + +#### Before (v5) + +```jsx + +``` + +#### After (v6) + +```jsx + +``` + +## Threads cleanup + +`ThreadsShareButton` no longer accepts the legacy compatibility props that were ignored in v5: + +- `hashtags` +- `related` +- `via` + +## New helper exports + +v6 adds low-level helpers from the package root: + +- `buildShareUrl(...)` +- `openShareWindow(url, options)` +- `canNativeShare(data)` +- `shareNative(data)` + +Example: + +```jsx +import { buildXShareUrl, openShareWindow } from 'react-share'; + +const url = buildXShareUrl('https://example.com', { + text: 'Read this next', + hashtags: ['react', 'share'], +}); + +openShareWindow(url, { width: 550, height: 400 }); +``` + +## New native/mobile sharing APIs + +v6 adds: + +- `NativeShareButton` +- `NativeShareIcon` +- `canNativeShare(data)` +- `shareNative(data)` + +Use these when you want browser-native sharing instead of network-specific popup URLs, especially for mobile and PWA flows. + +```jsx +import { NativeShareButton, NativeShareIcon } from 'react-share'; + + + +; +``` + +## New mobile app handoff option + +These buttons now support `mobileBehavior?: 'web' | 'app' | 'auto'`: + +- `FacebookMessengerShareButton` +- `TelegramShareButton` +- `ViberShareButton` +- `WhatsappShareButton` + +Default behavior is still `mobileBehavior="web"`. + +```jsx + +``` + +Use `mobileBehavior="app"` and `mobileBehavior="auto"` only when best-effort app handoff is acceptable. The library does not try to detect whether the native app actually opened. + +## Upgrade checklist + +1. Upgrade your app to React 18 or 19. +2. Replace `TwitterShareButton` and `TwitterIcon` with `XShareButton` and `XIcon`. +3. Remove all share count usage. +4. Rename every old share-button `title` prop to the correct v6 payload prop. +5. Remove `hashtags`, `related`, and `via` from `ThreadsShareButton`. +6. Remove `shareTitle`, `summary`, and `source` from `LinkedinShareButton`. +7. Decide whether any messaging buttons should opt into `mobileBehavior`. +8. For mobile/PWA or file-sharing flows, prefer `NativeShareButton` or `shareNative(...)`. diff --git a/docs/native-sharing.md b/docs/native-sharing.md new file mode 100644 index 00000000..de1d2d32 --- /dev/null +++ b/docs/native-sharing.md @@ -0,0 +1,170 @@ +# Native and mobile sharing behavior + +This guide explains how sharing behaves in v6 across popup-based buttons, mobile app handoff, and the browser Web Share API. + +## Three sharing modes in v6 + +### 1. Web popup sharing + +This is the default behavior for most share buttons. + +- The button generates a network URL. +- Clicking the button opens a popup with `window.open(...)`. +- The popup URL is the same URL returned by that button's `build*ShareUrl(...)` helper. + +This is still the default for all buttons unless noted otherwise. + +### 2. Best-effort mobile app handoff + +These buttons support `mobileBehavior`: + +- `FacebookMessengerShareButton` +- `TelegramShareButton` +- `ViberShareButton` +- `WhatsappShareButton` + +Supported values: + +- `'web'`: always use the web popup flow +- `'app'`: navigate directly to the app/deep link +- `'auto'`: use the app/deep link only on detected mobile environments, otherwise keep the web popup flow + +Important limits: + +- App-open success is not detectable in a reliable cross-browser way. +- The library does not use timer-based fallback hacks. +- The library does not guarantee a web fallback if the app is missing. +- iOS standalone PWAs and in-app browsers can still show blank intermediary pages or other platform-specific behavior. + +### 3. Browser-native sharing + +Use `NativeShareButton`, `canNativeShare(data)`, or `shareNative(data)` when you want the browser or OS share sheet instead of a network popup. + +- `NativeShareButton` uses `navigator.share()`. +- File sharing depends on `navigator.canShare()`. +- The button disables itself when native sharing is unavailable for the current payload. + +This is the preferred path for: + +- mobile-first product flows +- PWA share actions +- file sharing such as PDFs or images +- generic “share this” UI that should use the device share sheet + +## How `mobileBehavior` works by button + +### `WhatsappShareButton` + +- `mobileBehavior="web"`: uses `https://api.whatsapp.com/send?...` in a popup +- `mobileBehavior="app"`: navigates to `whatsapp://send?...` +- `mobileBehavior="auto"`: uses the app link on detected mobile environments and the popup URL elsewhere + +```jsx + + + +``` + +### `TelegramShareButton` + +- `mobileBehavior="web"`: uses `https://telegram.me/share/url?...` in a popup +- `mobileBehavior="app"`: navigates to `tg://msg_url?...` +- `mobileBehavior="auto"`: uses the app link on detected mobile environments and the popup URL elsewhere + +### `ViberShareButton` + +- `mobileBehavior="web"`: opens the Viber URL in a popup +- `mobileBehavior="app"`: navigates directly to the Viber URL +- `mobileBehavior="auto"`: uses direct navigation on detected mobile environments and a popup elsewhere + +Viber is a special case: the generated share URL stays the same, but the way the button opens it changes. + +### `FacebookMessengerShareButton` + +- `mobileBehavior="web"`: uses `https://www.facebook.com/dialog/send?...` in a popup +- `mobileBehavior="app"`: navigates to `fb-messenger://share?...` +- `mobileBehavior="auto"`: uses the app link on detected mobile environments and the popup URL elsewhere + +Messenger support is the least stable of the four. Platform behavior depends heavily on Meta's current app and dialog handling. + +## How mobile detection works for `auto` + +`mobileBehavior="auto"` is a lightweight best-effort choice, not a strong device capability system. + +It currently prefers: + +- `navigator.userAgentData.mobile` when available +- otherwise a mobile user-agent match +- otherwise a touch-capable Mac heuristic for iPad-style environments + +This only decides whether to choose the app link or the web link. It does not verify that the app handoff succeeded. + +## When to use `NativeShareButton` instead + +Prefer `NativeShareButton` or `shareNative(...)` when: + +- you want the platform share sheet instead of one network +- you need to share files +- you are building a PWA action bar or mobile-only UI +- you want one generic share action that adapts to the device + +Example: + +```jsx +import { NativeShareButton, NativeShareIcon } from 'react-share'; + + + +; +``` + +For low-level control: + +```jsx +import { canNativeShare, shareNative } from 'react-share'; + +if (canNativeShare({ title: 'Read this next', url: shareUrl })) { + await shareNative({ title: 'Read this next', url: shareUrl }); +} +``` + +## Behavior with `openShareDialogOnClick={false}` + +For popup-based buttons, setting `openShareDialogOnClick={false}` still prevents the button from opening the popup or navigating on its own. + +That is useful when you want to: + +- inspect the generated link through `onClick` +- open it yourself +- perform your own routing or analytics logic first + +This applies to both normal popup mode and mobile-aware buttons. + +## Recommended patterns + +### Generic share button for mobile/PWA + +Use `NativeShareButton`. + +### Social network icon row on desktop + +Use the normal network buttons with default behavior. + +### Messaging buttons where app handoff matters + +Use `mobileBehavior="app"` if direct app navigation is the intended experience and best-effort behavior is acceptable. + +Use `mobileBehavior="auto"` if you want desktop to stay popup-based while preferring app links on mobile. + +### Strictly predictable web behavior + +Leave `mobileBehavior` unset or set it to `'web'`. From 22249a654fdfd7d22f68306284c062db8c4dfc9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20Nyga=CC=8Ard?= Date: Mon, 9 Mar 2026 21:00:56 +0200 Subject: [PATCH 13/36] refactor(icons): split raw and legacy icon families Rename the existing badge-style icons to *LegacyIcon and reuse the original *Icon names for raw SVG marks in v6. Vendored Simple Icons data is used where there is a strong canonical match, while unsupported networks and the generic native-share glyph keep repo-local raw icons. The old badge visuals remain available through the legacy exports. The docs, migration guide, and package/tests are updated so the raw icons are the default surface and the legacy family is the migration path for preserving v5 visuals. --- .changeset/three-icons-divide.md | 11 ++ README.md | 92 ++++++++------ demo/Demo.tsx | 50 ++++---- docs/migrate-v5-to-v6.md | 32 ++++- docs/native-sharing.md | 4 +- src/BlueskyIcon.tsx | 10 +- src/BlueskyLegacyIcon.tsx | 9 ++ src/EmailIcon.tsx | 8 +- src/EmailLegacyIcon.tsx | 9 ++ src/FacebookIcon.tsx | 10 +- src/FacebookLegacyIcon.tsx | 9 ++ src/FacebookMessengerIcon.tsx | 10 +- src/FacebookMessengerLegacyIcon.tsx | 9 ++ src/GabIcon.tsx | 8 +- src/GabLegacyIcon.tsx | 9 ++ src/HatenaIcon.tsx | 10 +- src/HatenaLegacyIcon.tsx | 9 ++ src/InstapaperIcon.tsx | 10 +- src/InstapaperLegacyIcon.tsx | 9 ++ src/LineIcon.tsx | 10 +- src/LineLegacyIcon.tsx | 9 ++ src/LinkedinIcon.tsx | 8 +- src/LinkedinLegacyIcon.tsx | 9 ++ src/LivejournalIcon.tsx | 10 +- src/LivejournalLegacyIcon.tsx | 9 ++ src/MailruIcon.tsx | 10 +- src/MailruLegacyIcon.tsx | 9 ++ src/NativeShareIcon.tsx | 8 +- src/NativeShareLegacyIcon.tsx | 9 ++ src/OKIcon.tsx | 10 +- src/OKLegacyIcon.tsx | 9 ++ src/PinterestIcon.tsx | 10 +- src/PinterestLegacyIcon.tsx | 9 ++ src/PocketIcon.tsx | 8 +- src/PocketLegacyIcon.tsx | 9 ++ src/RedditIcon.tsx | 10 +- src/RedditLegacyIcon.tsx | 9 ++ src/TelegramIcon.tsx | 10 +- src/TelegramLegacyIcon.tsx | 9 ++ src/ThreadsIcon.tsx | 10 +- src/ThreadsLegacyIcon.tsx | 9 ++ src/TumblrIcon.tsx | 10 +- src/TumblrLegacyIcon.tsx | 9 ++ src/VKIcon.tsx | 10 +- src/VKLegacyIcon.tsx | 9 ++ src/ViberIcon.tsx | 10 +- src/ViberLegacyIcon.tsx | 9 ++ src/WeiboIcon.tsx | 10 +- src/WeiboLegacyIcon.tsx | 9 ++ src/WhatsappIcon.tsx | 10 +- src/WhatsappLegacyIcon.tsx | 9 ++ src/WorkplaceIcon.tsx | 10 +- src/WorkplaceLegacyIcon.tsx | 9 ++ src/XIcon.tsx | 10 +- src/XLegacyIcon.tsx | 9 ++ .../{IconBase.tsx => LegacyIconBase.tsx} | 8 +- src/icons/RawIconBase.tsx | 26 ++++ src/index.ts | 35 ++++- test/IconBase.test.tsx | 103 --------------- test/IconFamilies.test.tsx | 120 ++++++++++++++++++ test/PackageBuilds.test.ts | 2 + test/PackageExports.test.tsx | 2 + test/ShareButton.test.tsx | 13 +- test/ShareButtonComponents.test.tsx | 4 +- 64 files changed, 662 insertions(+), 305 deletions(-) create mode 100644 .changeset/three-icons-divide.md create mode 100644 src/BlueskyLegacyIcon.tsx create mode 100644 src/EmailLegacyIcon.tsx create mode 100644 src/FacebookLegacyIcon.tsx create mode 100644 src/FacebookMessengerLegacyIcon.tsx create mode 100644 src/GabLegacyIcon.tsx create mode 100644 src/HatenaLegacyIcon.tsx create mode 100644 src/InstapaperLegacyIcon.tsx create mode 100644 src/LineLegacyIcon.tsx create mode 100644 src/LinkedinLegacyIcon.tsx create mode 100644 src/LivejournalLegacyIcon.tsx create mode 100644 src/MailruLegacyIcon.tsx create mode 100644 src/NativeShareLegacyIcon.tsx create mode 100644 src/OKLegacyIcon.tsx create mode 100644 src/PinterestLegacyIcon.tsx create mode 100644 src/PocketLegacyIcon.tsx create mode 100644 src/RedditLegacyIcon.tsx create mode 100644 src/TelegramLegacyIcon.tsx create mode 100644 src/ThreadsLegacyIcon.tsx create mode 100644 src/TumblrLegacyIcon.tsx create mode 100644 src/VKLegacyIcon.tsx create mode 100644 src/ViberLegacyIcon.tsx create mode 100644 src/WeiboLegacyIcon.tsx create mode 100644 src/WhatsappLegacyIcon.tsx create mode 100644 src/WorkplaceLegacyIcon.tsx create mode 100644 src/XLegacyIcon.tsx rename src/icons/{IconBase.tsx => LegacyIconBase.tsx} (89%) create mode 100644 src/icons/RawIconBase.tsx delete mode 100644 test/IconBase.test.tsx create mode 100644 test/IconFamilies.test.tsx diff --git a/.changeset/three-icons-divide.md b/.changeset/three-icons-divide.md new file mode 100644 index 00000000..95a0159a --- /dev/null +++ b/.changeset/three-icons-divide.md @@ -0,0 +1,11 @@ +--- +'react-share': major +--- + +Split the icon exports into raw and legacy families for v6. + +The existing `*Icon` exports now render background-free SVG logos that are closer to the underlying network marks. The previous badge-style icons remain available as `*LegacyIcon` exports with the existing `round`, `borderRadius`, `bgStyle`, and `iconFillColor` props. + +If you want to preserve the v5 visuals, rename imports such as `FacebookIcon` to `FacebookLegacyIcon`, `XIcon` to `XLegacyIcon`, and `WhatsappIcon` to `WhatsappLegacyIcon`. + +If you keep the `*Icon` names, note that raw icons default to `size={24}` instead of `64`, and no longer accept `round`, `borderRadius`, `bgStyle`, or `iconFillColor`. diff --git a/README.md b/README.md index afcfd430..ee5c68db 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,7 @@ import { XShareButton, XIcon } from "react-share"; url={shareUrl} aria-label="Share on X" > - + ; ``` @@ -121,7 +121,7 @@ Basic example: import { FacebookShareButton, FacebookIcon } from "react-share"; - + ; ``` @@ -161,7 +161,7 @@ import { NativeShareButton, NativeShareIcon } from "react-share"; url={shareUrl} aria-label="Share with your device" > - + ; ``` @@ -176,7 +176,7 @@ Optional props: import { BlueskyShareButton, BlueskyIcon } from "react-share"; - + ; ``` @@ -200,7 +200,7 @@ import { EmailShareButton, EmailIcon } from "react-share"; url={shareUrl} aria-label="Share by email" > - + ; ``` @@ -225,7 +225,7 @@ import { FacebookMessengerShareButton, FacebookMessengerIcon } from "react-share url={shareUrl} aria-label="Send in Messenger" > - + ; ``` @@ -239,7 +239,7 @@ Optional props: import { FacebookShareButton, FacebookIcon } from "react-share"; - + ; ``` @@ -248,13 +248,12 @@ import { FacebookShareButton, FacebookIcon } from "react-share"; Optional props: - `text` (`string`): Text to share before the URL. -- `mobileBehavior` (`"web" | "app" | "auto"`, default `"web"`): `"app"` uses a Telegram app link and `"auto"` uses it only on detected mobile environments. ```jsx import { GabShareButton, GabIcon } from "react-share"; - + ; ``` @@ -268,7 +267,7 @@ Optional props: import { HatenaShareButton, HatenaIcon } from "react-share"; - + ; ``` @@ -288,7 +287,7 @@ import { InstapaperShareButton, InstapaperIcon } from "react-share"; url={shareUrl} aria-label="Save to Instapaper" > - + ; ``` @@ -302,7 +301,7 @@ Optional props: import { LineShareButton, LineIcon } from "react-share"; - + ; ``` @@ -316,7 +315,7 @@ import { LineShareButton, LineIcon } from "react-share"; import { LinkedinShareButton, LinkedinIcon } from "react-share"; - + ; ``` @@ -336,7 +335,7 @@ import { LivejournalShareButton, LivejournalIcon } from "react-share"; url={shareUrl} aria-label="Share on LiveJournal" > - + ; ``` @@ -358,7 +357,7 @@ import { MailruShareButton, MailruIcon } from "react-share"; url={shareUrl} aria-label="Share on Mail.ru" > - + ; ``` @@ -380,7 +379,7 @@ import { OKShareButton, OKIcon } from "react-share"; url={shareUrl} aria-label="Share on OK" > - + ; ``` @@ -402,7 +401,7 @@ import { PinterestShareButton, PinterestIcon } from "react-share"; url={shareUrl} aria-label="Pin on Pinterest" > - + ; ``` @@ -418,7 +417,7 @@ Optional props: import { PocketShareButton, PocketIcon } from "react-share"; - + ; ``` @@ -432,7 +431,7 @@ Optional props: import { RedditShareButton, RedditIcon } from "react-share"; - + ; ``` @@ -451,7 +450,7 @@ import { TelegramShareButton, TelegramIcon } from "react-share"; url={shareUrl} aria-label="Share on Telegram" > - + ; ``` @@ -465,7 +464,7 @@ Optional props: import { ThreadsShareButton, ThreadsIcon } from "react-share"; - + ; ``` @@ -488,7 +487,7 @@ import { TumblrShareButton, TumblrIcon } from "react-share"; url={shareUrl} aria-label="Share on Tumblr" > - + ; ``` @@ -513,7 +512,7 @@ import { XShareButton, XIcon } from "react-share"; url={shareUrl} aria-label="Share on X" > - + ; ``` @@ -534,7 +533,7 @@ import { ViberShareButton, ViberIcon } from "react-share"; url={shareUrl} aria-label="Share on Viber" > - + ; ``` @@ -558,7 +557,7 @@ import { VKShareButton, VKIcon } from "react-share"; url={shareUrl} aria-label="Share on VK" > - + ; ``` @@ -578,7 +577,7 @@ import { WeiboShareButton, WeiboIcon } from "react-share"; url={shareUrl} aria-label="Share on Weibo" > - + ; ``` @@ -601,7 +600,7 @@ import { WhatsappShareButton, WhatsappIcon } from "react-share"; url={shareUrl} aria-label="Share on WhatsApp" > - + ; ``` @@ -621,13 +620,35 @@ import { WorkplaceShareButton, WorkplaceIcon } from "react-share"; url={shareUrl} aria-label="Share on Workplace" > - + ; ``` ### Icons -All exported icons share the same props: +v6 exports two icon families: + +- `*Icon`: raw, background-free network logos. +- `*LegacyIcon`: the v5 badge-style icons with customizable backgrounds. + +Raw icons use a smaller, simpler prop surface: + +| Prop | Type | Default | Description | +| ------------- | ------------------------- | ------- | ---------------------------------------------------------------------- | +| `size` | `number \| string` | `24` | Icon size. | +| `...svgProps` | `SVGProps` | - | Standard SVG attributes such as `className`, `aria-hidden`, `aria-label`, or `role`. | + +Example: + +```jsx +import { XIcon } from "react-share"; + +
@@ -80,7 +80,7 @@ export function Demo() { url={shareUrl} className="Demo__some-network__share-button" > - + @@ -91,7 +91,7 @@ export function Demo() { text={title} className="Demo__some-network__share-button" > - + @@ -102,7 +102,7 @@ export function Demo() { appId="521270401588372" className="Demo__some-network__share-button" > - + @@ -112,7 +112,7 @@ export function Demo() { url={shareUrl} className="Demo__some-network__share-button" > - + @@ -123,7 +123,7 @@ export function Demo() { text={title} className="Demo__some-network__share-button" > - + @@ -136,7 +136,7 @@ export function Demo() { windowHeight={460} className="Demo__some-network__share-button" > - + @@ -147,7 +147,7 @@ export function Demo() { media={`${String(window.location)}/${exampleImage}`} className="Demo__some-network__share-button" > - + @@ -158,7 +158,7 @@ export function Demo() { text={title} className="Demo__some-network__share-button" > - + @@ -170,7 +170,7 @@ export function Demo() { image={`${String(window.location)}/${exampleImage}`} className="Demo__some-network__share-button" > - + @@ -181,7 +181,7 @@ export function Demo() { text={title} className="Demo__some-network__share-button" > - + @@ -194,7 +194,7 @@ export function Demo() { windowHeight={460} className="Demo__some-network__share-button" > - + @@ -205,7 +205,7 @@ export function Demo() { message={title} className="Demo__some-network__share-button" > - + @@ -217,7 +217,7 @@ export function Demo() { body="body" className="Demo__some-network__share-button" > - + @@ -228,7 +228,7 @@ export function Demo() { url={shareUrl} className="Demo__some-network__share-button" > - + @@ -239,7 +239,7 @@ export function Demo() { shareTitle={title} className="Demo__some-network__share-button" > - + @@ -250,7 +250,7 @@ export function Demo() { shareTitle={title} className="Demo__some-network__share-button" > - + @@ -261,7 +261,7 @@ export function Demo() { postTitle={title} className="Demo__some-network__share-button" > - + @@ -272,7 +272,7 @@ export function Demo() { image={`${String(window.location)}/${exampleImage}`} className="Demo__some-network__share-button" > - + @@ -283,7 +283,7 @@ export function Demo() { image={`${String(window.location)}/${exampleImage}`} className="Demo__some-network__share-button" > - + @@ -294,7 +294,7 @@ export function Demo() { shareTitle={title} className="Demo__some-network__share-button" > - + @@ -306,7 +306,7 @@ export function Demo() { description={shareUrl} className="Demo__some-network__share-button" > - + @@ -319,7 +319,7 @@ export function Demo() { windowHeight={460} className="Demo__some-network__share-button" > - + @@ -330,7 +330,7 @@ export function Demo() { quote={title} className="Demo__some-network__share-button" > - + @@ -343,7 +343,7 @@ export function Demo() { windowHeight={640} className="Demo__some-network__share-button" > - + diff --git a/docs/migrate-v5-to-v6.md b/docs/migrate-v5-to-v6.md index a22c1e7d..c99c854e 100644 --- a/docs/migrate-v5-to-v6.md +++ b/docs/migrate-v5-to-v6.md @@ -205,10 +205,34 @@ import { NativeShareButton, NativeShareIcon } from 'react-share'; url={shareUrl} aria-label="Share with your device" > - + ; ``` +## Icons now have raw and legacy families + +In v5, `*Icon` exports were the badge-style icons with background props such as `round`, `bgStyle`, and `iconFillColor`. + +In v6: + +- `*Icon` now means the raw, background-free icon. +- `*LegacyIcon` preserves the old badge-style visual and props. + +If you want the old icon look without changing visuals, rename your imports: + +- `FacebookIcon` -> `FacebookLegacyIcon` +- `XIcon` -> `XLegacyIcon` +- `NativeShareIcon` -> `NativeShareLegacyIcon` + +Example: + +```jsx +import { FacebookLegacyIcon, XIcon } from 'react-share'; + +; +; +``` + ## New mobile app handoff option These buttons now support `mobileBehavior?: 'web' | 'app' | 'auto'`: @@ -238,5 +262,7 @@ Use `mobileBehavior="app"` and `mobileBehavior="auto"` only when best-effort app 4. Rename every old share-button `title` prop to the correct v6 payload prop. 5. Remove `hashtags`, `related`, and `via` from `ThreadsShareButton`. 6. Remove `shareTitle`, `summary`, and `source` from `LinkedinShareButton`. -7. Decide whether any messaging buttons should opt into `mobileBehavior`. -8. For mobile/PWA or file-sharing flows, prefer `NativeShareButton` or `shareNative(...)`. +7. Rename any old badge-style icon usage from `*Icon` to `*LegacyIcon`. +8. Remove `round`, `borderRadius`, `bgStyle`, and `iconFillColor` from raw `*Icon` usage. +9. Decide whether any messaging buttons should opt into `mobileBehavior`. +10. For mobile/PWA or file-sharing flows, prefer `NativeShareButton` or `shareNative(...)`. diff --git a/docs/native-sharing.md b/docs/native-sharing.md index de1d2d32..1f7e56ae 100644 --- a/docs/native-sharing.md +++ b/docs/native-sharing.md @@ -65,7 +65,7 @@ This is the preferred path for: mobileBehavior="app" url={shareUrl} > - + ``` @@ -123,7 +123,7 @@ import { NativeShareButton, NativeShareIcon } from 'react-share'; url={shareUrl} aria-label="Share with your device" > - + ; ``` diff --git a/src/BlueskyIcon.tsx b/src/BlueskyIcon.tsx index 8383ff55..3a4c3245 100644 --- a/src/BlueskyIcon.tsx +++ b/src/BlueskyIcon.tsx @@ -1,9 +1,9 @@ -import { IconBase, type IconProps } from './icons/IconBase'; +import { RawIconBase, type RawIconProps } from './icons/RawIconBase'; -export function BlueskyIcon(props: IconProps) { +export function BlueskyIcon(props: RawIconProps) { return ( - - - + + + ); } diff --git a/src/BlueskyLegacyIcon.tsx b/src/BlueskyLegacyIcon.tsx new file mode 100644 index 00000000..92a130c1 --- /dev/null +++ b/src/BlueskyLegacyIcon.tsx @@ -0,0 +1,9 @@ +import { LegacyIconBase, type LegacyIconProps } from './icons/LegacyIconBase'; + +export function BlueskyLegacyIcon(props: LegacyIconProps) { + return ( + + + + ); +} diff --git a/src/EmailIcon.tsx b/src/EmailIcon.tsx index 600baf93..2a642ce0 100644 --- a/src/EmailIcon.tsx +++ b/src/EmailIcon.tsx @@ -1,9 +1,9 @@ -import { IconBase, type IconProps } from './icons/IconBase'; +import { RawIconBase, type RawIconProps } from './icons/RawIconBase'; -export function EmailIcon(props: IconProps) { +export function EmailIcon(props: RawIconProps) { return ( - + - + ); } diff --git a/src/EmailLegacyIcon.tsx b/src/EmailLegacyIcon.tsx new file mode 100644 index 00000000..92d4045f --- /dev/null +++ b/src/EmailLegacyIcon.tsx @@ -0,0 +1,9 @@ +import { LegacyIconBase, type LegacyIconProps } from './icons/LegacyIconBase'; + +export function EmailLegacyIcon(props: LegacyIconProps) { + return ( + + + + ); +} diff --git a/src/FacebookIcon.tsx b/src/FacebookIcon.tsx index 210c707f..15aa9a3f 100644 --- a/src/FacebookIcon.tsx +++ b/src/FacebookIcon.tsx @@ -1,9 +1,9 @@ -import { IconBase, type IconProps } from './icons/IconBase'; +import { RawIconBase, type RawIconProps } from './icons/RawIconBase'; -export function FacebookIcon(props: IconProps) { +export function FacebookIcon(props: RawIconProps) { return ( - - - + + + ); } diff --git a/src/FacebookLegacyIcon.tsx b/src/FacebookLegacyIcon.tsx new file mode 100644 index 00000000..c8ac2546 --- /dev/null +++ b/src/FacebookLegacyIcon.tsx @@ -0,0 +1,9 @@ +import { LegacyIconBase, type LegacyIconProps } from './icons/LegacyIconBase'; + +export function FacebookLegacyIcon(props: LegacyIconProps) { + return ( + + + + ); +} diff --git a/src/FacebookMessengerIcon.tsx b/src/FacebookMessengerIcon.tsx index fade8b81..1a002ada 100644 --- a/src/FacebookMessengerIcon.tsx +++ b/src/FacebookMessengerIcon.tsx @@ -1,9 +1,9 @@ -import { IconBase, type IconProps } from './icons/IconBase'; +import { RawIconBase, type RawIconProps } from './icons/RawIconBase'; -export function FacebookMessengerIcon(props: IconProps) { +export function FacebookMessengerIcon(props: RawIconProps) { return ( - - - + + + ); } diff --git a/src/FacebookMessengerLegacyIcon.tsx b/src/FacebookMessengerLegacyIcon.tsx new file mode 100644 index 00000000..b4c52d5c --- /dev/null +++ b/src/FacebookMessengerLegacyIcon.tsx @@ -0,0 +1,9 @@ +import { LegacyIconBase, type LegacyIconProps } from './icons/LegacyIconBase'; + +export function FacebookMessengerLegacyIcon(props: LegacyIconProps) { + return ( + + + + ); +} diff --git a/src/GabIcon.tsx b/src/GabIcon.tsx index 217898bb..2f87544f 100644 --- a/src/GabIcon.tsx +++ b/src/GabIcon.tsx @@ -1,9 +1,9 @@ -import { IconBase, type IconProps } from './icons/IconBase'; +import { RawIconBase, type RawIconProps } from './icons/RawIconBase'; -export function GabIcon(props: IconProps) { +export function GabIcon(props: RawIconProps) { return ( - + - + ); } diff --git a/src/GabLegacyIcon.tsx b/src/GabLegacyIcon.tsx new file mode 100644 index 00000000..2da52fe5 --- /dev/null +++ b/src/GabLegacyIcon.tsx @@ -0,0 +1,9 @@ +import { LegacyIconBase, type LegacyIconProps } from './icons/LegacyIconBase'; + +export function GabLegacyIcon(props: LegacyIconProps) { + return ( + + + + ); +} diff --git a/src/HatenaIcon.tsx b/src/HatenaIcon.tsx index da3ca7e8..74ded795 100644 --- a/src/HatenaIcon.tsx +++ b/src/HatenaIcon.tsx @@ -1,9 +1,9 @@ -import { IconBase, type IconProps } from './icons/IconBase'; +import { RawIconBase, type RawIconProps } from './icons/RawIconBase'; -export function HatenaIcon(props: IconProps) { +export function HatenaIcon(props: RawIconProps) { return ( - - - + + + ); } diff --git a/src/HatenaLegacyIcon.tsx b/src/HatenaLegacyIcon.tsx new file mode 100644 index 00000000..42515596 --- /dev/null +++ b/src/HatenaLegacyIcon.tsx @@ -0,0 +1,9 @@ +import { LegacyIconBase, type LegacyIconProps } from './icons/LegacyIconBase'; + +export function HatenaLegacyIcon(props: LegacyIconProps) { + return ( + + + + ); +} diff --git a/src/InstapaperIcon.tsx b/src/InstapaperIcon.tsx index 002de608..4c549391 100644 --- a/src/InstapaperIcon.tsx +++ b/src/InstapaperIcon.tsx @@ -1,9 +1,9 @@ -import { IconBase, type IconProps } from './icons/IconBase'; +import { RawIconBase, type RawIconProps } from './icons/RawIconBase'; -export function InstapaperIcon(props: IconProps) { +export function InstapaperIcon(props: RawIconProps) { return ( - - - + + + ); } diff --git a/src/InstapaperLegacyIcon.tsx b/src/InstapaperLegacyIcon.tsx new file mode 100644 index 00000000..43e56fda --- /dev/null +++ b/src/InstapaperLegacyIcon.tsx @@ -0,0 +1,9 @@ +import { LegacyIconBase, type LegacyIconProps } from './icons/LegacyIconBase'; + +export function InstapaperLegacyIcon(props: LegacyIconProps) { + return ( + + + + ); +} diff --git a/src/LineIcon.tsx b/src/LineIcon.tsx index 84fd27ce..cbd6ab3f 100644 --- a/src/LineIcon.tsx +++ b/src/LineIcon.tsx @@ -1,9 +1,9 @@ -import { IconBase, type IconProps } from './icons/IconBase'; +import { RawIconBase, type RawIconProps } from './icons/RawIconBase'; -export function LineIcon(props: IconProps) { +export function LineIcon(props: RawIconProps) { return ( - - - + + + ); } diff --git a/src/LineLegacyIcon.tsx b/src/LineLegacyIcon.tsx new file mode 100644 index 00000000..2ab75525 --- /dev/null +++ b/src/LineLegacyIcon.tsx @@ -0,0 +1,9 @@ +import { LegacyIconBase, type LegacyIconProps } from './icons/LegacyIconBase'; + +export function LineLegacyIcon(props: LegacyIconProps) { + return ( + + + + ); +} diff --git a/src/LinkedinIcon.tsx b/src/LinkedinIcon.tsx index 4e074199..c16e10a5 100644 --- a/src/LinkedinIcon.tsx +++ b/src/LinkedinIcon.tsx @@ -1,9 +1,9 @@ -import { IconBase, type IconProps } from './icons/IconBase'; +import { RawIconBase, type RawIconProps } from './icons/RawIconBase'; -export function LinkedinIcon(props: IconProps) { +export function LinkedinIcon(props: RawIconProps) { return ( - + - + ); } diff --git a/src/LinkedinLegacyIcon.tsx b/src/LinkedinLegacyIcon.tsx new file mode 100644 index 00000000..a97ddbf6 --- /dev/null +++ b/src/LinkedinLegacyIcon.tsx @@ -0,0 +1,9 @@ +import { LegacyIconBase, type LegacyIconProps } from './icons/LegacyIconBase'; + +export function LinkedinLegacyIcon(props: LegacyIconProps) { + return ( + + + + ); +} diff --git a/src/LivejournalIcon.tsx b/src/LivejournalIcon.tsx index 26058f44..24fa750d 100644 --- a/src/LivejournalIcon.tsx +++ b/src/LivejournalIcon.tsx @@ -1,9 +1,9 @@ -import { IconBase, type IconProps } from './icons/IconBase'; +import { RawIconBase, type RawIconProps } from './icons/RawIconBase'; -export function LivejournalIcon(props: IconProps) { +export function LivejournalIcon(props: RawIconProps) { return ( - - - + + + ); } diff --git a/src/LivejournalLegacyIcon.tsx b/src/LivejournalLegacyIcon.tsx new file mode 100644 index 00000000..0959d22d --- /dev/null +++ b/src/LivejournalLegacyIcon.tsx @@ -0,0 +1,9 @@ +import { LegacyIconBase, type LegacyIconProps } from './icons/LegacyIconBase'; + +export function LivejournalLegacyIcon(props: LegacyIconProps) { + return ( + + + + ); +} diff --git a/src/MailruIcon.tsx b/src/MailruIcon.tsx index d2d3ea7c..0900be99 100644 --- a/src/MailruIcon.tsx +++ b/src/MailruIcon.tsx @@ -1,9 +1,9 @@ -import { IconBase, type IconProps } from './icons/IconBase'; +import { RawIconBase, type RawIconProps } from './icons/RawIconBase'; -export function MailruIcon(props: IconProps) { +export function MailruIcon(props: RawIconProps) { return ( - - - + + + ); } diff --git a/src/MailruLegacyIcon.tsx b/src/MailruLegacyIcon.tsx new file mode 100644 index 00000000..3f4feff8 --- /dev/null +++ b/src/MailruLegacyIcon.tsx @@ -0,0 +1,9 @@ +import { LegacyIconBase, type LegacyIconProps } from './icons/LegacyIconBase'; + +export function MailruLegacyIcon(props: LegacyIconProps) { + return ( + + + + ); +} diff --git a/src/NativeShareIcon.tsx b/src/NativeShareIcon.tsx index 4f2edc85..c334887b 100644 --- a/src/NativeShareIcon.tsx +++ b/src/NativeShareIcon.tsx @@ -1,9 +1,9 @@ -import { IconBase, type IconProps } from './icons/IconBase'; +import { RawIconBase, type RawIconProps } from './icons/RawIconBase'; -export function NativeShareIcon(props: IconProps) { +export function NativeShareIcon(props: RawIconProps) { return ( - + - + ); } diff --git a/src/NativeShareLegacyIcon.tsx b/src/NativeShareLegacyIcon.tsx new file mode 100644 index 00000000..6670cb2d --- /dev/null +++ b/src/NativeShareLegacyIcon.tsx @@ -0,0 +1,9 @@ +import { LegacyIconBase, type LegacyIconProps } from './icons/LegacyIconBase'; + +export function NativeShareLegacyIcon(props: LegacyIconProps) { + return ( + + + + ); +} diff --git a/src/OKIcon.tsx b/src/OKIcon.tsx index f0101c5b..bbc11021 100644 --- a/src/OKIcon.tsx +++ b/src/OKIcon.tsx @@ -1,9 +1,9 @@ -import { IconBase, type IconProps } from './icons/IconBase'; +import { RawIconBase, type RawIconProps } from './icons/RawIconBase'; -export function OKIcon(props: IconProps) { +export function OKIcon(props: RawIconProps) { return ( - - - + + + ); } diff --git a/src/OKLegacyIcon.tsx b/src/OKLegacyIcon.tsx new file mode 100644 index 00000000..49f1ffc7 --- /dev/null +++ b/src/OKLegacyIcon.tsx @@ -0,0 +1,9 @@ +import { LegacyIconBase, type LegacyIconProps } from './icons/LegacyIconBase'; + +export function OKLegacyIcon(props: LegacyIconProps) { + return ( + + + + ); +} diff --git a/src/PinterestIcon.tsx b/src/PinterestIcon.tsx index bf5b175e..15a4787d 100644 --- a/src/PinterestIcon.tsx +++ b/src/PinterestIcon.tsx @@ -1,9 +1,9 @@ -import { IconBase, type IconProps } from './icons/IconBase'; +import { RawIconBase, type RawIconProps } from './icons/RawIconBase'; -export function PinterestIcon(props: IconProps) { +export function PinterestIcon(props: RawIconProps) { return ( - - - + + + ); } diff --git a/src/PinterestLegacyIcon.tsx b/src/PinterestLegacyIcon.tsx new file mode 100644 index 00000000..e7d76cef --- /dev/null +++ b/src/PinterestLegacyIcon.tsx @@ -0,0 +1,9 @@ +import { LegacyIconBase, type LegacyIconProps } from './icons/LegacyIconBase'; + +export function PinterestLegacyIcon(props: LegacyIconProps) { + return ( + + + + ); +} diff --git a/src/PocketIcon.tsx b/src/PocketIcon.tsx index d17bcc8a..88078593 100644 --- a/src/PocketIcon.tsx +++ b/src/PocketIcon.tsx @@ -1,9 +1,9 @@ -import { IconBase, type IconProps } from './icons/IconBase'; +import { RawIconBase, type RawIconProps } from './icons/RawIconBase'; -export function PocketIcon(props: IconProps) { +export function PocketIcon(props: RawIconProps) { return ( - + - + ); } diff --git a/src/PocketLegacyIcon.tsx b/src/PocketLegacyIcon.tsx new file mode 100644 index 00000000..ee687a37 --- /dev/null +++ b/src/PocketLegacyIcon.tsx @@ -0,0 +1,9 @@ +import { LegacyIconBase, type LegacyIconProps } from './icons/LegacyIconBase'; + +export function PocketLegacyIcon(props: LegacyIconProps) { + return ( + + + + ); +} diff --git a/src/RedditIcon.tsx b/src/RedditIcon.tsx index 4542cc8d..a93ab9c5 100644 --- a/src/RedditIcon.tsx +++ b/src/RedditIcon.tsx @@ -1,9 +1,9 @@ -import { IconBase, type IconProps } from './icons/IconBase'; +import { RawIconBase, type RawIconProps } from './icons/RawIconBase'; -export function RedditIcon(props: IconProps) { +export function RedditIcon(props: RawIconProps) { return ( - - - + + + ); } diff --git a/src/RedditLegacyIcon.tsx b/src/RedditLegacyIcon.tsx new file mode 100644 index 00000000..ef342448 --- /dev/null +++ b/src/RedditLegacyIcon.tsx @@ -0,0 +1,9 @@ +import { LegacyIconBase, type LegacyIconProps } from './icons/LegacyIconBase'; + +export function RedditLegacyIcon(props: LegacyIconProps) { + return ( + + + + ); +} diff --git a/src/TelegramIcon.tsx b/src/TelegramIcon.tsx index 658546ba..c2bab837 100644 --- a/src/TelegramIcon.tsx +++ b/src/TelegramIcon.tsx @@ -1,9 +1,9 @@ -import { IconBase, type IconProps } from './icons/IconBase'; +import { RawIconBase, type RawIconProps } from './icons/RawIconBase'; -export function TelegramIcon(props: IconProps) { +export function TelegramIcon(props: RawIconProps) { return ( - - - + + + ); } diff --git a/src/TelegramLegacyIcon.tsx b/src/TelegramLegacyIcon.tsx new file mode 100644 index 00000000..f505a502 --- /dev/null +++ b/src/TelegramLegacyIcon.tsx @@ -0,0 +1,9 @@ +import { LegacyIconBase, type LegacyIconProps } from './icons/LegacyIconBase'; + +export function TelegramLegacyIcon(props: LegacyIconProps) { + return ( + + + + ); +} diff --git a/src/ThreadsIcon.tsx b/src/ThreadsIcon.tsx index 42c8ac25..b6f2832e 100644 --- a/src/ThreadsIcon.tsx +++ b/src/ThreadsIcon.tsx @@ -1,9 +1,9 @@ -import { IconBase, type IconProps } from './icons/IconBase'; +import { RawIconBase, type RawIconProps } from './icons/RawIconBase'; -export function ThreadsIcon(props: IconProps) { +export function ThreadsIcon(props: RawIconProps) { return ( - - - + + + ); } diff --git a/src/ThreadsLegacyIcon.tsx b/src/ThreadsLegacyIcon.tsx new file mode 100644 index 00000000..c4be13b6 --- /dev/null +++ b/src/ThreadsLegacyIcon.tsx @@ -0,0 +1,9 @@ +import { LegacyIconBase, type LegacyIconProps } from './icons/LegacyIconBase'; + +export function ThreadsLegacyIcon(props: LegacyIconProps) { + return ( + + + + ); +} diff --git a/src/TumblrIcon.tsx b/src/TumblrIcon.tsx index 55fad26d..33c76d05 100644 --- a/src/TumblrIcon.tsx +++ b/src/TumblrIcon.tsx @@ -1,9 +1,9 @@ -import { IconBase, type IconProps } from './icons/IconBase'; +import { RawIconBase, type RawIconProps } from './icons/RawIconBase'; -export function TumblrIcon(props: IconProps) { +export function TumblrIcon(props: RawIconProps) { return ( - - - + + + ); } diff --git a/src/TumblrLegacyIcon.tsx b/src/TumblrLegacyIcon.tsx new file mode 100644 index 00000000..a484ec49 --- /dev/null +++ b/src/TumblrLegacyIcon.tsx @@ -0,0 +1,9 @@ +import { LegacyIconBase, type LegacyIconProps } from './icons/LegacyIconBase'; + +export function TumblrLegacyIcon(props: LegacyIconProps) { + return ( + + + + ); +} diff --git a/src/VKIcon.tsx b/src/VKIcon.tsx index d3a417ba..91969fee 100644 --- a/src/VKIcon.tsx +++ b/src/VKIcon.tsx @@ -1,9 +1,9 @@ -import { IconBase, type IconProps } from './icons/IconBase'; +import { RawIconBase, type RawIconProps } from './icons/RawIconBase'; -export function VKIcon(props: IconProps) { +export function VKIcon(props: RawIconProps) { return ( - - - + + + ); } diff --git a/src/VKLegacyIcon.tsx b/src/VKLegacyIcon.tsx new file mode 100644 index 00000000..35e49d45 --- /dev/null +++ b/src/VKLegacyIcon.tsx @@ -0,0 +1,9 @@ +import { LegacyIconBase, type LegacyIconProps } from './icons/LegacyIconBase'; + +export function VKLegacyIcon(props: LegacyIconProps) { + return ( + + + + ); +} diff --git a/src/ViberIcon.tsx b/src/ViberIcon.tsx index 0112da7a..5ef1a802 100644 --- a/src/ViberIcon.tsx +++ b/src/ViberIcon.tsx @@ -1,9 +1,9 @@ -import { IconBase, type IconProps } from './icons/IconBase'; +import { RawIconBase, type RawIconProps } from './icons/RawIconBase'; -export function ViberIcon(props: IconProps) { +export function ViberIcon(props: RawIconProps) { return ( - - - + + + ); } diff --git a/src/ViberLegacyIcon.tsx b/src/ViberLegacyIcon.tsx new file mode 100644 index 00000000..95f19292 --- /dev/null +++ b/src/ViberLegacyIcon.tsx @@ -0,0 +1,9 @@ +import { LegacyIconBase, type LegacyIconProps } from './icons/LegacyIconBase'; + +export function ViberLegacyIcon(props: LegacyIconProps) { + return ( + + + + ); +} diff --git a/src/WeiboIcon.tsx b/src/WeiboIcon.tsx index 2bf2cd5e..4778755c 100644 --- a/src/WeiboIcon.tsx +++ b/src/WeiboIcon.tsx @@ -1,9 +1,9 @@ -import { IconBase, type IconProps } from './icons/IconBase'; +import { RawIconBase, type RawIconProps } from './icons/RawIconBase'; -export function WeiboIcon(props: IconProps) { +export function WeiboIcon(props: RawIconProps) { return ( - - - + + + ); } diff --git a/src/WeiboLegacyIcon.tsx b/src/WeiboLegacyIcon.tsx new file mode 100644 index 00000000..41432df1 --- /dev/null +++ b/src/WeiboLegacyIcon.tsx @@ -0,0 +1,9 @@ +import { LegacyIconBase, type LegacyIconProps } from './icons/LegacyIconBase'; + +export function WeiboLegacyIcon(props: LegacyIconProps) { + return ( + + + + ); +} diff --git a/src/WhatsappIcon.tsx b/src/WhatsappIcon.tsx index 56357bd3..a0ff7179 100644 --- a/src/WhatsappIcon.tsx +++ b/src/WhatsappIcon.tsx @@ -1,9 +1,9 @@ -import { IconBase, type IconProps } from './icons/IconBase'; +import { RawIconBase, type RawIconProps } from './icons/RawIconBase'; -export function WhatsappIcon(props: IconProps) { +export function WhatsappIcon(props: RawIconProps) { return ( - - - + + + ); } diff --git a/src/WhatsappLegacyIcon.tsx b/src/WhatsappLegacyIcon.tsx new file mode 100644 index 00000000..5553f260 --- /dev/null +++ b/src/WhatsappLegacyIcon.tsx @@ -0,0 +1,9 @@ +import { LegacyIconBase, type LegacyIconProps } from './icons/LegacyIconBase'; + +export function WhatsappLegacyIcon(props: LegacyIconProps) { + return ( + + + + ); +} diff --git a/src/WorkplaceIcon.tsx b/src/WorkplaceIcon.tsx index 965e201f..3d4930fd 100644 --- a/src/WorkplaceIcon.tsx +++ b/src/WorkplaceIcon.tsx @@ -1,9 +1,9 @@ -import { IconBase, type IconProps } from './icons/IconBase'; +import { RawIconBase, type RawIconProps } from './icons/RawIconBase'; -export function WorkplaceIcon(props: IconProps) { +export function WorkplaceIcon(props: RawIconProps) { return ( - - - + + + ); } diff --git a/src/WorkplaceLegacyIcon.tsx b/src/WorkplaceLegacyIcon.tsx new file mode 100644 index 00000000..fed2c094 --- /dev/null +++ b/src/WorkplaceLegacyIcon.tsx @@ -0,0 +1,9 @@ +import { LegacyIconBase, type LegacyIconProps } from './icons/LegacyIconBase'; + +export function WorkplaceLegacyIcon(props: LegacyIconProps) { + return ( + + + + ); +} diff --git a/src/XIcon.tsx b/src/XIcon.tsx index b52d2ad0..2262e4c9 100644 --- a/src/XIcon.tsx +++ b/src/XIcon.tsx @@ -1,9 +1,9 @@ -import { IconBase, type IconProps } from './icons/IconBase'; +import { RawIconBase, type RawIconProps } from './icons/RawIconBase'; -export function XIcon(props: IconProps) { +export function XIcon(props: RawIconProps) { return ( - - - + + + ); } diff --git a/src/XLegacyIcon.tsx b/src/XLegacyIcon.tsx new file mode 100644 index 00000000..0696b8a6 --- /dev/null +++ b/src/XLegacyIcon.tsx @@ -0,0 +1,9 @@ +import { LegacyIconBase, type LegacyIconProps } from './icons/LegacyIconBase'; + +export function XLegacyIcon(props: LegacyIconProps) { + return ( + + + + ); +} diff --git a/src/icons/IconBase.tsx b/src/icons/LegacyIconBase.tsx similarity index 89% rename from src/icons/IconBase.tsx rename to src/icons/LegacyIconBase.tsx index f7d4df74..43b34922 100644 --- a/src/icons/IconBase.tsx +++ b/src/icons/LegacyIconBase.tsx @@ -3,7 +3,7 @@ import { Children, cloneElement, isValidElement } from 'react'; type IconSvgProps = Omit, 'width' | 'height'>; -export type IconProps = IconSvgProps & { +export type LegacyIconProps = IconSvgProps & { bgStyle?: CSSProperties; borderRadius?: number; iconFillColor?: string; @@ -11,7 +11,7 @@ export type IconProps = IconSvgProps & { size?: number | string; }; -type IconBaseProps = IconProps & { +type LegacyIconBaseProps = LegacyIconProps & { children: ReactNode; color: string; }; @@ -26,7 +26,7 @@ function withDefaultFill(children: ReactNode, iconFillColor: string) { }); } -export function IconBase({ +export function LegacyIconBase({ bgStyle = {}, borderRadius = 0, children, @@ -35,7 +35,7 @@ export function IconBase({ round = false, size = 64, ...rest -}: IconBaseProps) { +}: LegacyIconBaseProps) { const content = withDefaultFill(children, iconFillColor); return ( diff --git a/src/icons/RawIconBase.tsx b/src/icons/RawIconBase.tsx new file mode 100644 index 00000000..3ddf1a2a --- /dev/null +++ b/src/icons/RawIconBase.tsx @@ -0,0 +1,26 @@ +import type { ReactNode, SVGProps } from 'react'; + +export type RawIconProps = Omit, 'width' | 'height'> & { + size?: number | string; +}; + +type RawIconBaseProps = RawIconProps & { + children: ReactNode; + color?: string; + viewBox?: string; +}; + +export function RawIconBase({ + children, + color, + fill, + size = 24, + viewBox = '0 0 24 24', + ...rest +}: RawIconBaseProps) { + return ( + + {children} + + ); +} diff --git a/src/index.ts b/src/index.ts index 7fe65b47..b3b6524e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,78 +1,105 @@ export { BlueskyIcon } from './BlueskyIcon'; +export { BlueskyLegacyIcon } from './BlueskyLegacyIcon'; export { BlueskyShareButton } from './BlueskyShareButton'; export { buildBlueskyShareUrl } from './BlueskyShareButton'; export { EmailIcon } from './EmailIcon'; +export { EmailLegacyIcon } from './EmailLegacyIcon'; export { EmailShareButton } from './EmailShareButton'; export { buildEmailShareUrl } from './EmailShareButton'; export { FacebookIcon } from './FacebookIcon'; +export { FacebookLegacyIcon } from './FacebookLegacyIcon'; export { FacebookMessengerIcon } from './FacebookMessengerIcon'; +export { FacebookMessengerLegacyIcon } from './FacebookMessengerLegacyIcon'; export { FacebookMessengerShareButton } from './FacebookMessengerShareButton'; export { buildFacebookMessengerShareUrl } from './FacebookMessengerShareButton'; export { FacebookShareButton } from './FacebookShareButton'; export { buildFacebookShareUrl } from './FacebookShareButton'; +export { GabIcon } from './GabIcon'; +export { GabLegacyIcon } from './GabLegacyIcon'; +export { GabShareButton } from './GabShareButton'; +export { buildGabShareUrl } from './GabShareButton'; export { HatenaIcon } from './HatenaIcon'; +export { HatenaLegacyIcon } from './HatenaLegacyIcon'; export { HatenaShareButton } from './HatenaShareButton'; export { buildHatenaShareUrl } from './HatenaShareButton'; export { InstapaperIcon } from './InstapaperIcon'; +export { InstapaperLegacyIcon } from './InstapaperLegacyIcon'; export { InstapaperShareButton } from './InstapaperShareButton'; export { buildInstapaperShareUrl } from './InstapaperShareButton'; export { LineIcon } from './LineIcon'; +export { LineLegacyIcon } from './LineLegacyIcon'; export { LineShareButton } from './LineShareButton'; export { buildLineShareUrl } from './LineShareButton'; export { LinkedinIcon } from './LinkedinIcon'; +export { LinkedinLegacyIcon } from './LinkedinLegacyIcon'; export { LinkedinShareButton } from './LinkedinShareButton'; export { buildLinkedinShareUrl } from './LinkedinShareButton'; export { LivejournalIcon } from './LivejournalIcon'; +export { LivejournalLegacyIcon } from './LivejournalLegacyIcon'; export { LivejournalShareButton } from './LivejournalShareButton'; export { buildLivejournalShareUrl } from './LivejournalShareButton'; export { MailruIcon } from './MailruIcon'; +export { MailruLegacyIcon } from './MailruLegacyIcon'; export { MailruShareButton } from './MailruShareButton'; export { buildMailruShareUrl } from './MailruShareButton'; export { NativeShareButton } from './NativeShareButton'; export { NativeShareIcon } from './NativeShareIcon'; +export { NativeShareLegacyIcon } from './NativeShareLegacyIcon'; export { canNativeShare } from './nativeShare'; export { shareNative } from './nativeShare'; export type { NativeShareData } from './nativeShare'; export { OKIcon } from './OKIcon'; +export { OKLegacyIcon } from './OKLegacyIcon'; export { OKShareButton } from './OKShareButton'; export { buildOKShareUrl } from './OKShareButton'; export { PinterestIcon } from './PinterestIcon'; +export { PinterestLegacyIcon } from './PinterestLegacyIcon'; export { PinterestShareButton } from './PinterestShareButton'; export { buildPinterestShareUrl } from './PinterestShareButton'; export { PocketIcon } from './PocketIcon'; +export { PocketLegacyIcon } from './PocketLegacyIcon'; export { PocketShareButton } from './PocketShareButton'; export { buildPocketShareUrl } from './PocketShareButton'; export { RedditIcon } from './RedditIcon'; +export { RedditLegacyIcon } from './RedditLegacyIcon'; export { RedditShareButton } from './RedditShareButton'; export { buildRedditShareUrl } from './RedditShareButton'; -export { GabShareButton } from './GabShareButton'; -export { GabIcon } from './GabIcon'; -export { buildGabShareUrl } from './GabShareButton'; export { TelegramIcon } from './TelegramIcon'; +export { TelegramLegacyIcon } from './TelegramLegacyIcon'; export { TelegramShareButton } from './TelegramShareButton'; export { buildTelegramShareUrl } from './TelegramShareButton'; export { ThreadsIcon } from './ThreadsIcon'; +export { ThreadsLegacyIcon } from './ThreadsLegacyIcon'; export { ThreadsShareButton } from './ThreadsShareButton'; export { buildThreadsShareUrl } from './ThreadsShareButton'; export { TumblrIcon } from './TumblrIcon'; +export { TumblrLegacyIcon } from './TumblrLegacyIcon'; export { TumblrShareButton } from './TumblrShareButton'; export { buildTumblrShareUrl } from './TumblrShareButton'; export { XShareButton } from './XShareButton'; export { buildXShareUrl } from './XShareButton'; export { ViberIcon } from './ViberIcon'; +export { ViberLegacyIcon } from './ViberLegacyIcon'; export { ViberShareButton } from './ViberShareButton'; export { buildViberShareUrl } from './ViberShareButton'; export { VKIcon } from './VKIcon'; +export { VKLegacyIcon } from './VKLegacyIcon'; export { VKShareButton } from './VKShareButton'; export { buildVKShareUrl } from './VKShareButton'; export { WeiboIcon } from './WeiboIcon'; +export { WeiboLegacyIcon } from './WeiboLegacyIcon'; export { WeiboShareButton } from './WeiboShareButton'; export { buildWeiboShareUrl } from './WeiboShareButton'; export { WhatsappIcon } from './WhatsappIcon'; +export { WhatsappLegacyIcon } from './WhatsappLegacyIcon'; export { WhatsappShareButton } from './WhatsappShareButton'; export { buildWhatsappShareUrl } from './WhatsappShareButton'; export { WorkplaceIcon } from './WorkplaceIcon'; +export { WorkplaceLegacyIcon } from './WorkplaceLegacyIcon'; export { WorkplaceShareButton } from './WorkplaceShareButton'; export { buildWorkplaceShareUrl } from './WorkplaceShareButton'; export { XIcon } from './XIcon'; -export { openShareWindow, type ShareWindowOptions, type WindowPosition } from './shareWindow'; +export { XLegacyIcon } from './XLegacyIcon'; +export { openShareWindow } from './shareWindow'; +export type { ShareWindowOptions } from './shareWindow'; +export type { WindowPosition } from './shareWindow'; diff --git a/test/IconBase.test.tsx b/test/IconBase.test.tsx deleted file mode 100644 index 42f2a62c..00000000 --- a/test/IconBase.test.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import { describe, expect, it } from 'vitest'; - -import { FacebookIcon } from '../src/FacebookIcon'; -import { FacebookMessengerIcon } from '../src/FacebookMessengerIcon'; -import { HatenaIcon } from '../src/HatenaIcon'; -import { IconBase } from '../src/icons/IconBase'; -import { LineIcon } from '../src/LineIcon'; -import { LivejournalIcon } from '../src/LivejournalIcon'; -import { MailruIcon } from '../src/MailruIcon'; -import { NativeShareIcon } from '../src/NativeShareIcon'; -import { OKIcon } from '../src/OKIcon'; -import { PinterestIcon } from '../src/PinterestIcon'; -import { RedditIcon } from '../src/RedditIcon'; -import { TelegramIcon } from '../src/TelegramIcon'; -import { TumblrIcon } from '../src/TumblrIcon'; -import { VKIcon } from '../src/VKIcon'; -import { ViberIcon } from '../src/ViberIcon'; -import { WeiboIcon } from '../src/WeiboIcon'; -import { WorkplaceIcon } from '../src/WorkplaceIcon'; - -describe('IconBase', () => { - it('renders exported icons with the shared rectangular background by default', () => { - render(); - - const svg = screen.getByTestId('icon'); - const rect = svg.querySelector('rect'); - const path = svg.querySelector('path'); - - expect(svg).toHaveAttribute('width', '64'); - expect(svg).toHaveAttribute('height', '64'); - expect(rect).toHaveAttribute('fill', '#0866FF'); - expect(path).toHaveAttribute('fill', 'white'); - }); - - it('keeps the reviewed brand background colors for current v5 icon badges', () => { - const cases = [ - ['facebook-messenger', FacebookMessengerIcon, '#0866FF'], - ['hatena', HatenaIcon, '#00A4DE'], - ['line', LineIcon, '#06C755'], - ['livejournal', LivejournalIcon, '#00B0EA'], - ['mailru', MailruIcon, '#005FF9'], - ['native-share', NativeShareIcon, '#0A84FF'], - ['ok', OKIcon, '#FF7700'], - ['pinterest', PinterestIcon, '#E60023'], - ['reddit', RedditIcon, '#FF4500'], - ['telegram', TelegramIcon, '#26A5E4'], - ['tumblr', TumblrIcon, '#36465D'], - ['vk', VKIcon, '#0077FF'], - ['viber', ViberIcon, '#7360F2'], - ['weibo', WeiboIcon, '#E6162D'], - ['workplace', WorkplaceIcon, '#4526CE'], - ] as const; - - for (const [name, Component, color] of cases) { - render(); - - const rect = screen.getByTestId(name).querySelector('rect'); - - expect(rect).toHaveAttribute('fill', color); - } - }); - - it('renders a round icon with custom style props', () => { - render( - - - , - ); - - const svg = screen.getByLabelText('round icon'); - const circle = svg.querySelector('circle'); - const rect = svg.querySelector('rect'); - const path = svg.querySelector('path'); - - expect(svg).toHaveAttribute('width', '32'); - expect(svg).toHaveAttribute('height', '32'); - expect(circle).toHaveStyle({ opacity: '0.5' }); - expect(rect).toBeNull(); - expect(path).toHaveAttribute('fill', 'black'); - }); - - it('applies borderRadius when rendering a non-round icon', () => { - render( - - - , - ); - - const rect = screen.getByLabelText('rounded rect').querySelector('rect'); - - expect(rect).toHaveAttribute('rx', '12'); - expect(rect).toHaveAttribute('ry', '12'); - }); -}); diff --git a/test/IconFamilies.test.tsx b/test/IconFamilies.test.tsx new file mode 100644 index 00000000..b02ded63 --- /dev/null +++ b/test/IconFamilies.test.tsx @@ -0,0 +1,120 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { describe, expect, it } from 'vitest'; + +import { FacebookIcon } from '../src/FacebookIcon'; +import { FacebookLegacyIcon } from '../src/FacebookLegacyIcon'; +import { LinkedinIcon } from '../src/LinkedinIcon'; +import { NativeShareIcon } from '../src/NativeShareIcon'; +import { NativeShareLegacyIcon } from '../src/NativeShareLegacyIcon'; +import { RawIconBase } from '../src/icons/RawIconBase'; + +describe('raw icon family', () => { + it('renders background-free icons with their default brand color', () => { + render(); + + const svg = screen.getByTestId('facebook-icon'); + + expect(svg).toHaveAttribute('width', '24'); + expect(svg).toHaveAttribute('height', '24'); + expect(svg).toHaveAttribute('fill', '#0866FF'); + expect(svg.querySelector('rect')).toBeNull(); + expect(svg.querySelector('circle')).toBeNull(); + }); + + it('keeps repo-local raw icons background-free', () => { + render(); + + const svg = screen.getByTestId('linkedin-icon'); + + expect(svg).toHaveAttribute('width', '32'); + expect(svg).toHaveAttribute('height', '32'); + expect(svg).toHaveAttribute('fill', '#0077B5'); + expect(svg.querySelector('rect')).toBeNull(); + expect(svg.querySelector('circle')).toBeNull(); + }); + + it('uses the current text color for the native share glyph', () => { + render(); + + expect(screen.getByTestId('native-share-icon')).toHaveAttribute('fill', 'currentColor'); + }); + + it('passes standard svg props through the raw icon base', () => { + render( + + + , + ); + + const svg = screen.getByTestId('raw-icon'); + + expect(svg).toHaveAttribute('width', '32'); + expect(svg).toHaveAttribute('height', '32'); + expect(svg).toHaveAttribute('fill', '#123456'); + expect(svg).toHaveAttribute('viewBox', '0 0 32 32'); + }); +}); + +describe('legacy icon family', () => { + it('renders the existing badge background by default', () => { + render( + , + ); + + const svg = screen.getByTestId('facebook-legacy-icon'); + const rect = svg.querySelector('rect'); + const path = svg.querySelector('path'); + + expect(svg).toHaveAttribute('width', '64'); + expect(svg).toHaveAttribute('height', '64'); + expect(rect).toHaveAttribute('fill', '#0866FF'); + expect(path).toHaveAttribute('fill', 'white'); + }); + + it('preserves round, bgStyle, and iconFillColor support on legacy icons', () => { + render( + , + ); + + const svg = screen.getByTestId('native-share-legacy-icon'); + const circle = svg.querySelector('circle'); + const rect = svg.querySelector('rect'); + const path = svg.querySelector('path'); + + expect(svg).toHaveAttribute('width', '32'); + expect(svg).toHaveAttribute('height', '32'); + expect(circle).toHaveStyle({ opacity: '0.5' }); + expect(rect).toBeNull(); + expect(path).toHaveAttribute('fill', 'black'); + }); + + it('applies borderRadius when rendering a legacy rectangular icon', () => { + render( + , + ); + + const rect = screen.getByTestId('rounded-legacy-icon').querySelector('rect'); + + expect(rect).toHaveAttribute('rx', '12'); + expect(rect).toHaveAttribute('ry', '12'); + }); +}); diff --git a/test/PackageBuilds.test.ts b/test/PackageBuilds.test.ts index 28105835..0804c91a 100644 --- a/test/PackageBuilds.test.ts +++ b/test/PackageBuilds.test.ts @@ -31,7 +31,9 @@ const isUsableExport = (value: unknown) => const representativeExportNames = [ 'FacebookShareButton', + 'FacebookLegacyIcon', 'NativeShareIcon', + 'NativeShareLegacyIcon', 'NativeShareButton', 'XShareButton', 'canNativeShare', diff --git a/test/PackageExports.test.tsx b/test/PackageExports.test.tsx index ce5a0a56..e33e8e21 100644 --- a/test/PackageExports.test.tsx +++ b/test/PackageExports.test.tsx @@ -10,7 +10,9 @@ const iconExportNames = Object.keys(ReactShare) const representativeExportNames = [ 'FacebookShareButton', + 'FacebookLegacyIcon', 'NativeShareIcon', + 'NativeShareLegacyIcon', 'NativeShareButton', 'XShareButton', 'canNativeShare', diff --git a/test/ShareButton.test.tsx b/test/ShareButton.test.tsx index 4046d7a1..f3780532 100644 --- a/test/ShareButton.test.tsx +++ b/test/ShareButton.test.tsx @@ -3,6 +3,7 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { FacebookIcon } from '../src/FacebookIcon'; +import { FacebookLegacyIcon } from '../src/FacebookLegacyIcon'; import { ShareButton } from '../src/ShareButton'; describe('ShareButton', () => { @@ -328,7 +329,7 @@ describe('ShareButton', () => { opts={{}} url="https://example.com" > - + , ); @@ -343,7 +344,7 @@ describe('ShareButton', () => { opts={{}} url="https://example.com" > - + , ); @@ -362,7 +363,7 @@ describe('ShareButton', () => { opts={{}} url="https://example.com" > - + , ); @@ -383,7 +384,7 @@ describe('ShareButton', () => { opts={{}} url="https://example.com" > - + , ); @@ -417,7 +418,7 @@ describe('ShareButton', () => { opts={{}} url="https://example.com" > - + , ); @@ -433,7 +434,7 @@ describe('ShareButton', () => { style={{ borderRadius: 4, display: 'inline-block', outlineOffset: 3 }} url="https://example.com" > - + , ); diff --git a/test/ShareButtonComponents.test.tsx b/test/ShareButtonComponents.test.tsx index e114b669..3fef20f5 100644 --- a/test/ShareButtonComponents.test.tsx +++ b/test/ShareButtonComponents.test.tsx @@ -66,7 +66,7 @@ describe('share button components', () => { it('adds the email fallback label for icon-only buttons', () => { render( - + , ); @@ -76,7 +76,7 @@ describe('share button components', () => { it('adds the Pinterest fallback label for icon-only buttons', () => { render( - + , ); From 19d2977b47333b58747b8ee04b056753cd535765 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20Nyga=CC=8Ard?= Date: Thu, 12 Mar 2026 17:06:50 +0200 Subject: [PATCH 14/36] chore(deps): upgrade vite to v8 Upgrade Vite to 8.0.0 and move the React plugin to 6.0.0 so the bundler stack stays on the supported peer range. Vitest and @vitest/coverage-v8 are updated to 4.1.0 at the same time to keep the test runner aligned with Vite 8 and avoid split peer ranges. No library runtime API changes are included in this commit. --- package-lock.json | 1084 +++++++++++++++++++++++++++++++++++---------- package.json | 8 +- 2 files changed, 854 insertions(+), 238 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3ba91cf9..8ec4d666 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,8 +21,8 @@ "@types/react-dom": "^19.2.3", "@typescript-eslint/eslint-plugin": "^8.56.1", "@typescript-eslint/parser": "^8.56.1", - "@vitejs/plugin-react": "^5.1.4", - "@vitest/coverage-v8": "^4.0.18", + "@vitejs/plugin-react": "^6.0.0", + "@vitest/coverage-v8": "^4.1.0", "eslint": "^9.39.4", "eslint-config-prettier": "^10.1.8", "eslint-plugin-babel": "^5.3.1", @@ -37,9 +37,9 @@ "react": "^19.2.4", "react-dom": "^19.2.4", "typescript": "^5.9.3", - "vite": "^7.3.1", + "vite": "^8.0.0", "vite-plugin-dts": "^4.5.4", - "vitest": "^4.0.18" + "vitest": "^4.1.0" }, "peerDependencies": { "react": "^18 || ^19" @@ -278,16 +278,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", @@ -348,38 +338,6 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", - "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", - "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/runtime": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", @@ -867,6 +825,40 @@ "node": ">=20.19.0" } }, + "node_modules/@emnapi/core": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.0.tgz", + "integrity": "sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.0.tgz", + "integrity": "sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", + "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", @@ -880,6 +872,7 @@ "os": [ "aix" ], + "peer": true, "engines": { "node": ">=18" } @@ -897,6 +890,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": ">=18" } @@ -914,6 +908,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": ">=18" } @@ -931,6 +926,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": ">=18" } @@ -948,6 +944,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">=18" } @@ -965,6 +962,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">=18" } @@ -982,6 +980,7 @@ "os": [ "freebsd" ], + "peer": true, "engines": { "node": ">=18" } @@ -999,6 +998,7 @@ "os": [ "freebsd" ], + "peer": true, "engines": { "node": ">=18" } @@ -1016,6 +1016,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=18" } @@ -1033,6 +1034,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=18" } @@ -1050,6 +1052,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=18" } @@ -1067,6 +1070,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=18" } @@ -1084,6 +1088,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=18" } @@ -1101,6 +1106,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=18" } @@ -1118,6 +1124,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=18" } @@ -1135,6 +1142,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=18" } @@ -1152,6 +1160,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=18" } @@ -1169,6 +1178,7 @@ "os": [ "netbsd" ], + "peer": true, "engines": { "node": ">=18" } @@ -1186,6 +1196,7 @@ "os": [ "netbsd" ], + "peer": true, "engines": { "node": ">=18" } @@ -1203,6 +1214,7 @@ "os": [ "openbsd" ], + "peer": true, "engines": { "node": ">=18" } @@ -1220,6 +1232,7 @@ "os": [ "openbsd" ], + "peer": true, "engines": { "node": ">=18" } @@ -1237,6 +1250,7 @@ "os": [ "openharmony" ], + "peer": true, "engines": { "node": ">=18" } @@ -1254,6 +1268,7 @@ "os": [ "sunos" ], + "peer": true, "engines": { "node": ">=18" } @@ -1271,6 +1286,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=18" } @@ -1288,6 +1304,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=18" } @@ -1305,6 +1322,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=18" } @@ -1862,6 +1880,23 @@ "dev": true, "license": "MIT" }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz", + "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1900,6 +1935,26 @@ "node": ">= 8" } }, + "node_modules/@oxc-project/runtime": { + "version": "0.115.0", + "resolved": "https://registry.npmjs.org/@oxc-project/runtime/-/runtime-0.115.0.tgz", + "integrity": "sha512-Rg8Wlt5dCbXhQnsXPrkOjL1DTSvXLgb2R/KYfnf1/K+R0k6UMLEmbQXPM+kwrWqSmWA2t0B1EtHy2/3zikQpvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.115.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.115.0.tgz", + "integrity": "sha512-4n91DKnebUS4yjUHl2g3/b2T+IUdCfmoZGhmwsovZCDaJSs+QkVAM+0AqqTxHSsHfeiMuueT75cZaZcT/m0pSw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, "node_modules/@pkgr/core": { "version": "0.2.9", "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", @@ -1913,10 +1968,265 @@ "url": "https://opencollective.com/pkgr" } }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.9.tgz", + "integrity": "sha512-lcJL0bN5hpgJfSIz/8PIf02irmyL43P+j1pTCfbD1DbLkmGRuFIA4DD3B3ZOvGqG0XiVvRznbKtN0COQVaKUTg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.9.tgz", + "integrity": "sha512-J7Zk3kLYFsLtuH6U+F4pS2sYVzac0qkjcO5QxHS7OS7yZu2LRs+IXo+uvJ/mvpyUljDJ3LROZPoQfgBIpCMhdQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.9.tgz", + "integrity": "sha512-iwtmmghy8nhfRGeNAIltcNXzD0QMNaaA5U/NyZc1Ia4bxrzFByNMDoppoC+hl7cDiUq5/1CnFthpT9n+UtfFyg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.9.tgz", + "integrity": "sha512-DLFYI78SCiZr5VvdEplsVC2Vx53lnA4/Ga5C65iyldMVaErr86aiqCoNBLl92PXPfDtUYjUh+xFFor40ueNs4Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.9.tgz", + "integrity": "sha512-CsjTmTwd0Hri6iTw/DRMK7kOZ7FwAkrO4h8YWKoX/kcj833e4coqo2wzIFywtch/8Eb5enQ/lwLM7w6JX1W5RQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.9.tgz", + "integrity": "sha512-2x9O2JbSPxpxMDhP9Z74mahAStibTlrBMW0520+epJH5sac7/LwZW5Bmg/E6CXuEF53JJFW509uP+lSedaUNxg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.9.tgz", + "integrity": "sha512-JA1QRW31ogheAIRhIg9tjMfsYbglXXYGNPLdPEYrwFxdbkQCAzvpSCSHCDWNl4hTtrol8WeboCSEpjdZK8qrCg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.9.tgz", + "integrity": "sha512-aOKU9dJheda8Kj8Y3w9gnt9QFOO+qKPAl8SWd7JPHP+Cu0EuDAE5wokQubLzIDQWg2myXq2XhTpOVS07qqvT+w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.9.tgz", + "integrity": "sha512-OalO94fqj7IWRn3VdXWty75jC5dk4C197AWEuMhIpvVv2lw9fiPhud0+bW2ctCxb3YoBZor71QHbY+9/WToadA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.9.tgz", + "integrity": "sha512-cVEl1vZtBsBZna3YMjGXNvnYYrOJ7RzuWvZU0ffvJUexWkukMaDuGhUXn0rjnV0ptzGVkvc+vW9Yqy6h8YX4pg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.9.tgz", + "integrity": "sha512-UzYnKCIIc4heAKgI4PZ3dfBGUZefGCJ1TPDuLHoCzgrMYPb5Rv6TLFuYtyM4rWyHM7hymNdsg5ik2C+UD9VDbA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.9.tgz", + "integrity": "sha512-+6zoiF+RRyf5cdlFQP7nm58mq7+/2PFaY2DNQeD4B87N36JzfF/l9mdBkkmTvSYcYPE8tMh/o3cRlsx1ldLfog==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.9.tgz", + "integrity": "sha512-rgFN6sA/dyebil3YTlL2evvi/M+ivhfnyxec7AccTpRPccno/rPoNlqybEZQBkcbZu8Hy+eqNJCqfBR8P7Pg8g==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.1.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.9.tgz", + "integrity": "sha512-lHVNUG/8nlF1IQk1C0Ci574qKYyty2goMiPlRqkC5R+3LkXDkL5Dhx8ytbxq35m+pkHVIvIxviD+TWLdfeuadA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.9.tgz", + "integrity": "sha512-G0oA4+w1iY5AGi5HcDTxWsoxF509hrFIPB2rduV5aDqS9FtDg1CAfa7V34qImbjfhIcA8C+RekocJZA96EarwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.3", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", - "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", + "version": "1.0.0-rc.7", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.7.tgz", + "integrity": "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==", "dev": true, "license": "MIT" }, @@ -1968,7 +2278,8 @@ "optional": true, "os": [ "android" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-android-arm64": { "version": "4.59.0", @@ -1982,7 +2293,8 @@ "optional": true, "os": [ "android" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-darwin-arm64": { "version": "4.59.0", @@ -1996,7 +2308,8 @@ "optional": true, "os": [ "darwin" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-darwin-x64": { "version": "4.59.0", @@ -2010,7 +2323,8 @@ "optional": true, "os": [ "darwin" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-freebsd-arm64": { "version": "4.59.0", @@ -2024,7 +2338,8 @@ "optional": true, "os": [ "freebsd" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-freebsd-x64": { "version": "4.59.0", @@ -2038,7 +2353,8 @@ "optional": true, "os": [ "freebsd" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { "version": "4.59.0", @@ -2052,7 +2368,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { "version": "4.59.0", @@ -2066,7 +2383,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-arm64-gnu": { "version": "4.59.0", @@ -2080,7 +2398,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-arm64-musl": { "version": "4.59.0", @@ -2094,7 +2413,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-loong64-gnu": { "version": "4.59.0", @@ -2108,7 +2428,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-loong64-musl": { "version": "4.59.0", @@ -2122,7 +2443,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { "version": "4.59.0", @@ -2136,7 +2458,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-ppc64-musl": { "version": "4.59.0", @@ -2150,7 +2473,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { "version": "4.59.0", @@ -2164,7 +2488,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-riscv64-musl": { "version": "4.59.0", @@ -2178,7 +2503,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-s390x-gnu": { "version": "4.59.0", @@ -2192,7 +2518,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-x64-gnu": { "version": "4.59.0", @@ -2206,7 +2533,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-x64-musl": { "version": "4.59.0", @@ -2220,7 +2548,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-openbsd-x64": { "version": "4.59.0", @@ -2234,7 +2563,8 @@ "optional": true, "os": [ "openbsd" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-openharmony-arm64": { "version": "4.59.0", @@ -2248,7 +2578,8 @@ "optional": true, "os": [ "openharmony" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-win32-arm64-msvc": { "version": "4.59.0", @@ -2262,7 +2593,8 @@ "optional": true, "os": [ "win32" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-win32-ia32-msvc": { "version": "4.59.0", @@ -2276,7 +2608,8 @@ "optional": true, "os": [ "win32" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-win32-x64-gnu": { "version": "4.59.0", @@ -2290,7 +2623,8 @@ "optional": true, "os": [ "win32" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-win32-x64-msvc": { "version": "4.59.0", @@ -2304,7 +2638,8 @@ "optional": true, "os": [ "win32" - ] + ], + "peer": true }, "node_modules/@rtsao/scc": { "version": "1.1.0", @@ -2604,6 +2939,17 @@ } } }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@types/argparse": { "version": "1.0.38", "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", @@ -2626,47 +2972,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", - "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.7" - } - }, "node_modules/@types/chai": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", @@ -3018,50 +3323,55 @@ } }, "node_modules/@vitejs/plugin-react": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.4.tgz", - "integrity": "sha512-VIcFLdRi/VYRU8OL/puL7QXMYafHmqOnwTZY50U1JPlCNj30PxCMx65c494b1K9be9hX83KVt0+gTEwTWLqToA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.0.tgz", + "integrity": "sha512-Bu5/eP6td3WI654+tRq+ryW1PbgA90y5pqMKpb3U7UpNk6VjI53P/ncPUd192U9dSrepLy7DHnq1XEMDz5H++w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.29.0", - "@babel/plugin-transform-react-jsx-self": "^7.27.1", - "@babel/plugin-transform-react-jsx-source": "^7.27.1", - "@rolldown/pluginutils": "1.0.0-rc.3", - "@types/babel__core": "^7.20.5", - "react-refresh": "^0.18.0" + "@rolldown/pluginutils": "1.0.0-rc.7" }, "engines": { "node": "^20.19.0 || >=22.12.0" }, "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + "@rolldown/plugin-babel": "^0.1.7", + "babel-plugin-react-compiler": "^1.0.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + } } }, "node_modules/@vitest/coverage-v8": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.0.18.tgz", - "integrity": "sha512-7i+N2i0+ME+2JFZhfuz7Tg/FqKtilHjGyGvoHYQ6iLV0zahbsJ9sljC9OcFcPDbhYKCet+sG8SsVqlyGvPflZg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.0.tgz", + "integrity": "sha512-nDWulKeik2bL2Va/Wl4x7DLuTKAXa906iRFooIRPR+huHkcvp9QDkPQ2RJdmjOFrqOqvNfoSQLF68deE3xC3CQ==", "dev": true, "license": "MIT", "dependencies": { "@bcoe/v8-coverage": "^1.0.2", - "@vitest/utils": "4.0.18", - "ast-v8-to-istanbul": "^0.3.10", + "@vitest/utils": "4.1.0", + "ast-v8-to-istanbul": "^1.0.0", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", "istanbul-reports": "^3.2.0", - "magicast": "^0.5.1", + "magicast": "^0.5.2", "obug": "^2.1.1", - "std-env": "^3.10.0", + "std-env": "^4.0.0-rc.1", "tinyrainbow": "^3.0.3" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "4.0.18", - "vitest": "4.0.18" + "@vitest/browser": "4.1.0", + "vitest": "4.1.0" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -3070,17 +3380,17 @@ } }, "node_modules/@vitest/expect": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz", - "integrity": "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.0.tgz", + "integrity": "sha512-EIxG7k4wlWweuCLG9Y5InKFwpMEOyrMb6ZJ1ihYu02LVj/bzUwn2VMU+13PinsjRW75XnITeFrQBMH5+dLvCDA==", "dev": true, "license": "MIT", "dependencies": { - "@standard-schema/spec": "^1.0.0", + "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.0.18", - "@vitest/utils": "4.0.18", - "chai": "^6.2.1", + "@vitest/spy": "4.1.0", + "@vitest/utils": "4.1.0", + "chai": "^6.2.2", "tinyrainbow": "^3.0.3" }, "funding": { @@ -3088,13 +3398,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.18.tgz", - "integrity": "sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.0.tgz", + "integrity": "sha512-evxREh+Hork43+Y4IOhTo+h5lGmVRyjqI739Rz4RlUPqwrkFFDF6EMvOOYjTx4E8Tl6gyCLRL8Mu7Ry12a13Tw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.0.18", + "@vitest/spy": "4.1.0", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -3103,7 +3413,7 @@ }, "peerDependencies": { "msw": "^2.4.9", - "vite": "^6.0.0 || ^7.0.0-0" + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0" }, "peerDependenciesMeta": { "msw": { @@ -3125,9 +3435,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.18.tgz", - "integrity": "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.0.tgz", + "integrity": "sha512-3RZLZlh88Ib0J7NQTRATfc/3ZPOnSUn2uDBUoGNn5T36+bALixmzphN26OUD3LRXWkJu4H0s5vvUeqBiw+kS0A==", "dev": true, "license": "MIT", "dependencies": { @@ -3138,13 +3448,13 @@ } }, "node_modules/@vitest/runner": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.18.tgz", - "integrity": "sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.0.tgz", + "integrity": "sha512-Duvx2OzQ7d6OjchL+trw+aSrb9idh7pnNfxrklo14p3zmNL4qPCDeIJAK+eBKYjkIwG96Bc6vYuxhqDXQOWpoQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.0.18", + "@vitest/utils": "4.1.0", "pathe": "^2.0.3" }, "funding": { @@ -3152,13 +3462,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.18.tgz", - "integrity": "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.0.tgz", + "integrity": "sha512-0Vy9euT1kgsnj1CHttwi9i9o+4rRLEaPRSOJ5gyv579GJkNpgJK+B4HSv/rAWixx2wdAFci1X4CEPjiu2bXIMg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.0.18", + "@vitest/pretty-format": "4.1.0", + "@vitest/utils": "4.1.0", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -3167,9 +3478,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.18.tgz", - "integrity": "sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.0.tgz", + "integrity": "sha512-pz77k+PgNpyMDv2FV6qmk5ZVau6c3R8HC8v342T2xlFxQKTrSeYw9waIJG8KgV9fFwAtTu4ceRzMivPTH6wSxw==", "dev": true, "license": "MIT", "funding": { @@ -3177,13 +3488,14 @@ } }, "node_modules/@vitest/utils": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.18.tgz", - "integrity": "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.0.tgz", + "integrity": "sha512-XfPXT6a8TZY3dcGY8EdwsBulFCIw+BeeX0RZn2x/BtiY/75YGh8FeWGG8QISN/WhaqSrE2OrlDgtF8q5uhOTmw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.0.18", + "@vitest/pretty-format": "4.1.0", + "convert-source-map": "^2.0.0", "tinyrainbow": "^3.0.3" }, "funding": { @@ -3651,9 +3963,9 @@ "dev": true }, "node_modules/ast-v8-to-istanbul": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.12.tgz", - "integrity": "sha512-BRRC8VRZY2R4Z4lFIL35MwNXmwVqBityvOIwETtsCSwvjl0IdgFsy9NhdaA6j74nUdtJJlIypeRhpDam19Wq3g==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.0.tgz", + "integrity": "sha512-1fSfIwuDICFA4LKkCzRPO7F0hzFf0B7+Xqrl27ynQaa+Rh0e1Es0v6kWHPott3lU10AyAr7oKHa65OppjLn3Rg==", "dev": true, "license": "MIT", "dependencies": { @@ -4199,6 +4511,16 @@ "node": ">=8" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/diff": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz", @@ -4402,9 +4724,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", + "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", "dev": true, "license": "MIT" }, @@ -4475,6 +4797,8 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "optional": true, + "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -6530,6 +6854,267 @@ "node": ">= 0.8.0" } }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/local-pkg": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.2.tgz", @@ -7374,16 +7959,6 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, - "node_modules/react-refresh": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", - "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/read-yaml-file": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/read-yaml-file/-/read-yaml-file-1.1.0.tgz", @@ -7520,12 +8095,55 @@ "node": ">=0.10.0" } }, + "node_modules/rolldown": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.9.tgz", + "integrity": "sha512-9EbgWge7ZH+yqb4d2EnELAntgPTWbfL8ajiTW+SyhJEC4qhBbkCKbqFV4Ge4zmu5ziQuVbWxb/XwLZ+RIO7E8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.115.0", + "@rolldown/pluginutils": "1.0.0-rc.9" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.9", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.9", + "@rolldown/binding-darwin-x64": "1.0.0-rc.9", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.9", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.9", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.9", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.9", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.9", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.9", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.9", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.9", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.9", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.9", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.9", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.9" + } + }, + "node_modules/rolldown/node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.9.tgz", + "integrity": "sha512-w6oiRWgEBl04QkFZgmW+jnU1EC9b57Oihi2ot3HNWIQRqgHp5PnYDia5iZ5FF7rpa4EQdiqMDXjlqKGXBhsoXw==", + "dev": true, + "license": "MIT" + }, "node_modules/rollup": { "version": "4.59.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -7919,9 +8537,9 @@ "license": "MIT" }, "node_modules/std-env": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", - "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.0.0.tgz", + "integrity": "sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==", "dev": true, "license": "MIT" }, @@ -8266,9 +8884,9 @@ } }, "node_modules/tinyrainbow": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz", - "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", "dev": true, "license": "MIT", "engines": { @@ -8371,6 +8989,14 @@ "json5": "lib/cli.js" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -8568,17 +9194,17 @@ } }, "node_modules/vite": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", - "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.0.tgz", + "integrity": "sha512-fPGaRNj9Zytaf8LEiBhY7Z6ijnFKdzU/+mL8EFBaKr7Vw1/FWcTBAMW0wLPJAGMPX38ZPVCVgLceWiEqeoqL2Q==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "^0.27.0", - "fdir": "^6.5.0", + "@oxc-project/runtime": "0.115.0", + "lightningcss": "^1.32.0", "picomatch": "^4.0.3", - "postcss": "^8.5.6", - "rollup": "^4.43.0", + "postcss": "^8.5.8", + "rolldown": "1.0.0-rc.9", "tinyglobby": "^0.2.15" }, "bin": { @@ -8595,9 +9221,10 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.0.0-alpha.31", + "esbuild": "^0.27.0", "jiti": ">=1.21.0", "less": "^4.0.0", - "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", @@ -8610,13 +9237,16 @@ "@types/node": { "optional": true }, - "jiti": { + "@vitejs/devtools": { "optional": true }, - "less": { + "esbuild": { "optional": true }, - "lightningcss": { + "jiti": { + "optional": true + }, + "less": { "optional": true }, "sass": { @@ -8669,24 +9299,6 @@ } } }, - "node_modules/vite/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, "node_modules/vite/node_modules/picomatch": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", @@ -8701,31 +9313,31 @@ } }, "node_modules/vitest": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.18.tgz", - "integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.0.tgz", + "integrity": "sha512-YbDrMF9jM2Lqc++2530UourxZHmkKLxrs4+mYhEwqWS97WJ7wOYEkcr+QfRgJ3PW9wz3odRijLZjHEaRLTNbqw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.0.18", - "@vitest/mocker": "4.0.18", - "@vitest/pretty-format": "4.0.18", - "@vitest/runner": "4.0.18", - "@vitest/snapshot": "4.0.18", - "@vitest/spy": "4.0.18", - "@vitest/utils": "4.0.18", - "es-module-lexer": "^1.7.0", - "expect-type": "^1.2.2", + "@vitest/expect": "4.1.0", + "@vitest/mocker": "4.1.0", + "@vitest/pretty-format": "4.1.0", + "@vitest/runner": "4.1.0", + "@vitest/snapshot": "4.1.0", + "@vitest/spy": "4.1.0", + "@vitest/utils": "4.1.0", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", - "std-env": "^3.10.0", + "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.0.3", - "vite": "^6.0.0 || ^7.0.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0", "why-is-node-running": "^2.3.0" }, "bin": { @@ -8741,12 +9353,13 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.0.18", - "@vitest/browser-preview": "4.0.18", - "@vitest/browser-webdriverio": "4.0.18", - "@vitest/ui": "4.0.18", + "@vitest/browser-playwright": "4.1.0", + "@vitest/browser-preview": "4.1.0", + "@vitest/browser-webdriverio": "4.1.0", + "@vitest/ui": "4.1.0", "happy-dom": "*", - "jsdom": "*" + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0" }, "peerDependenciesMeta": { "@edge-runtime/vm": { @@ -8775,6 +9388,9 @@ }, "jsdom": { "optional": true + }, + "vite": { + "optional": false } } }, diff --git a/package.json b/package.json index d8f00f66..95e10dac 100644 --- a/package.json +++ b/package.json @@ -64,8 +64,8 @@ "@types/react-dom": "^19.2.3", "@typescript-eslint/eslint-plugin": "^8.56.1", "@typescript-eslint/parser": "^8.56.1", - "@vitejs/plugin-react": "^5.1.4", - "@vitest/coverage-v8": "^4.0.18", + "@vitejs/plugin-react": "^6.0.0", + "@vitest/coverage-v8": "^4.1.0", "eslint": "^9.39.4", "eslint-config-prettier": "^10.1.8", "eslint-plugin-babel": "^5.3.1", @@ -80,9 +80,9 @@ "react": "^19.2.4", "react-dom": "^19.2.4", "typescript": "^5.9.3", - "vite": "^7.3.1", + "vite": "^8.0.0", "vite-plugin-dts": "^4.5.4", - "vitest": "^4.0.18" + "vitest": "^4.1.0" }, "dependencies": { "classnames": "^2.3.2" From ce091845b449301e012d979f00fd828a39096e86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20Nyga=CC=8Ard?= Date: Fri, 13 Mar 2026 10:50:01 +0200 Subject: [PATCH 15/36] chore(eslint): upgrade to v10 Upgrade ESLint and @eslint/js to the v10 line and keep the existing flat-config rule set working with ESLint's v10 rule API changes. Wrap legacy React, hooks, and jsx-a11y plugins with @eslint/compat, replace the crashing import/no-default-export rule with an equivalent core selector rule, and add a local npm legacy peer deps setting while upstream plugin peer ranges catch up. Also add @testing-library/dom explicitly so clean installs keep tests runnable, ignore demo/dist in lint, and refresh formatting in files touched by the stricter lint pass. --- .npmrc | 3 + eslint.config.mjs | 23 +- package-lock.json | 2738 ++++++++-------------------- package.json | 10 +- src/BlueskyShareButton.tsx | 1 - src/EmailShareButton.tsx | 1 - src/FacebookShareButton.tsx | 1 - src/GabShareButton.tsx | 1 - src/HatenaShareButton.tsx | 1 - src/InstapaperShareButton.tsx | 1 - src/LineShareButton.tsx | 1 - src/LivejournalShareButton.tsx | 1 - src/MailruShareButton.tsx | 1 - src/OKShareButton.tsx | 7 +- src/PinterestShareButton.tsx | 1 - src/PocketShareButton.tsx | 1 - src/RedditShareButton.tsx | 1 - src/ThreadsShareButton.tsx | 1 - src/TumblrShareButton.tsx | 1 - src/VKShareButton.tsx | 1 - src/WeiboShareButton.tsx | 6 +- src/WorkplaceShareButton.tsx | 1 - src/XShareButton.tsx | 1 - src/shareWindow.ts | 7 +- src/utils/objectToGetParams.ts | 4 +- test/EmailShareButton.test.tsx | 4 +- test/NetworkShareButtons.test.tsx | 23 +- test/PinterestShareButton.test.tsx | 6 +- 28 files changed, 798 insertions(+), 2050 deletions(-) create mode 100644 .npmrc diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..76dfe126 --- /dev/null +++ b/.npmrc @@ -0,0 +1,3 @@ +# eslint-plugin-react, eslint-plugin-import, and eslint-plugin-jsx-a11y +# have not published ESLint 10 peer ranges yet. +legacy-peer-deps=true diff --git a/eslint.config.mjs b/eslint.config.mjs index efd14c46..894d2f8b 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,18 +1,23 @@ +import { fixupPluginRules } from "@eslint/compat"; import js from "@eslint/js"; import ts from "@typescript-eslint/eslint-plugin"; import tsParser from "@typescript-eslint/parser"; import react from "eslint-plugin-react"; import reactHooks from "eslint-plugin-react-hooks"; import jsxA11y from "eslint-plugin-jsx-a11y"; -import importPlugin from "eslint-plugin-import"; import prettier from "eslint-plugin-prettier"; import prettierConfig from "eslint-config-prettier"; import globals from "globals"; +const reactCompat = fixupPluginRules(react); +const reactHooksCompat = fixupPluginRules(reactHooks); +const jsxA11yCompat = fixupPluginRules(jsxA11y); + export default [ { ignores: [ "demo/assets/**", + "demo/dist/**", "dist/**", "**/.DS_Store", "**/*.css", @@ -34,7 +39,6 @@ export default [ }, plugins: { "@typescript-eslint": ts, - import: importPlugin, }, rules: { ...ts.configs.strict.rules, @@ -45,21 +49,28 @@ export default [ disallowTypeAnnotations: false, }, ], + "no-undef": "off", }, }, { files: ["src/**/*.{ts,tsx}", "test/**/*.{ts,tsx}", "demo/**/*.{ts,tsx}"], ignores: ["demo/declarations.d.ts"], rules: { - "import/no-default-export": "error", + "no-restricted-syntax": [ + "error", + { + selector: "ExportDefaultDeclaration", + message: "Default exports are not allowed.", + }, + ], }, }, { files: ["**/*.jsx", "**/*.tsx"], plugins: { - react, - "react-hooks": reactHooks, - "jsx-a11y": jsxA11y, + react: reactCompat, + "react-hooks": reactHooksCompat, + "jsx-a11y": jsxA11yCompat, }, settings: { react: { version: "detect" } }, rules: { diff --git a/package-lock.json b/package-lock.json index 8ec4d666..ea74139a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,20 +13,22 @@ }, "devDependencies": { "@changesets/cli": "^2.30.0", + "@eslint/compat": "^2.0.3", + "@eslint/js": "^10.0.1", + "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@types/assert": "^1.5.11", "@types/node": "^24.12.0", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", - "@typescript-eslint/eslint-plugin": "^8.56.1", - "@typescript-eslint/parser": "^8.56.1", + "@typescript-eslint/eslint-plugin": "^8.57.0", + "@typescript-eslint/parser": "^8.57.0", "@vitejs/plugin-react": "^6.0.0", "@vitest/coverage-v8": "^4.1.0", - "eslint": "^9.39.4", + "eslint": "^10.0.3", "eslint-config-prettier": "^10.1.8", "eslint-plugin-babel": "^5.3.1", - "eslint-plugin-import": "^2.32.0", "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-prettier": "^5.5.5", "eslint-plugin-react": "^7.37.5", @@ -859,1537 +861,670 @@ "tslib": "^2.4.0" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", - "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", - "cpu": [ - "ppc64" - ], + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "peer": true, + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, "engines": { - "node": ">=18" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@esbuild/android-arm": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", - "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", - "cpu": [ - "arm" - ], + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "peer": true, "engines": { - "node": ">=18" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@esbuild/android-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", - "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint/compat": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-2.0.3.tgz", + "integrity": "sha512-SjIJhGigp8hmd1YGIBwh7Ovri7Kisl42GYFjrOyHhtfYGGoLW6teYi/5p8W50KSsawUPpuLOSmsq1bD0NGQLBw==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "peer": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.1.1" + }, "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "peerDependencies": { + "eslint": "^8.40 || 9 || 10" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } } }, - "node_modules/@esbuild/android-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", - "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", - "cpu": [ - "x64" - ], + "node_modules/@eslint/config-array": { + "version": "0.23.3", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.3.tgz", + "integrity": "sha512-j+eEWmB6YYLwcNOdlwQ6L2OsptI/LO6lNBuLIqe5R7RetD658HLoF+Mn7LzYmAWWNNzdC6cqP+L6r8ujeYXWLw==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "peer": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.3", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", - "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint/config-array/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, "engines": { - "node": ">=18" + "node": "18 || 20 || >=22" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", - "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", - "cpu": [ - "x64" - ], + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, + "dependencies": { + "balanced-match": "^4.0.2" + }, "engines": { - "node": ">=18" + "node": "18 || 20 || >=22" } }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", - "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "peer": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, "engines": { - "node": ">=18" + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", - "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", - "cpu": [ - "x64" - ], + "node_modules/@eslint/config-helpers": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.3.tgz", + "integrity": "sha512-lzGN0onllOZCGroKJmRwY6QcEHxbjBw1gwB8SgRSqK8YbbtEXMvKynsXc3553ckIEBxsbMBU7oOZXKIPGZNeZw==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "peer": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.1.1" + }, "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@esbuild/linux-arm": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", - "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", - "cpu": [ - "arm" - ], + "node_modules/@eslint/core": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.1.1.tgz", + "integrity": "sha512-QUPblTtE51/7/Zhfv8BDwO0qkkzQL7P/aWWbqcf4xWLEYn1oKjdO0gglQBB4GAsu7u6wjijbCmzsUTy6mnk6oQ==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", - "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } } }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", - "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", - "cpu": [ - "ia32" - ], + "node_modules/@eslint/object-schema": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.3.tgz", + "integrity": "sha512-iM869Pugn9Nsxbh/YHRqYiqd23AmIbxJOcpUMOuWCVNdoQJ5ZtwL6h3t0bcZzJUlC3Dq9jCFCESBZnX0GTv7iQ==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, + "license": "Apache-2.0", "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", - "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", - "cpu": [ - "loong64" - ], + "node_modules/@eslint/plugin-kit": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.6.1.tgz", + "integrity": "sha512-iH1B076HoAshH1mLpHMgwdGeTs0CYwL0SPMkGuSebZrwBp16v415e9NZXg2jtrqPVQjf6IANe2Vtlr5KswtcZQ==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.1.1", + "levn": "^0.4.1" + }, "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", - "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", - "cpu": [ - "mips64el" - ], + "node_modules/@exodus/bytes": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.0.tgz", + "integrity": "sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, "engines": { - "node": ">=18" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } } }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", - "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", - "cpu": [ - "ppc64" - ], + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, + "license": "Apache-2.0", "engines": { - "node": ">=18" + "node": ">=18.18.0" } }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", - "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", - "cpu": [ - "riscv64" - ], + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, "engines": { - "node": ">=18" + "node": ">=18.18.0" } }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", - "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", - "cpu": [ - "s390x" - ], + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, + "license": "Apache-2.0", "engines": { - "node": ">=18" + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@esbuild/linux-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", - "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", - "cpu": [ - "x64" - ], + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, "engines": { - "node": ">=18" + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", - "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", - "cpu": [ - "arm64" - ], + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "peer": true, + "license": "Apache-2.0", "engines": { - "node": ">=18" + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", - "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", - "cpu": [ - "x64" - ], + "node_modules/@inquirer/external-editor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "peer": true, + "dependencies": { + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" + }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", - "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", - "cpu": [ - "arm64" - ], + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "peer": true, - "engines": { - "node": ">=18" + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", - "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", - "cpu": [ - "x64" - ], + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "peer": true, - "engines": { - "node": ">=18" + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", - "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", - "cpu": [ - "arm64" - ], + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "peer": true, "engines": { - "node": ">=18" + "node": ">=6.0.0" } }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", - "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", - "cpu": [ - "x64" - ], + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "peer": true, - "engines": { - "node": ">=18" - } + "license": "MIT" }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", - "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", - "cpu": [ - "arm64" - ], + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">=18" + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", - "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", - "cpu": [ - "ia32" - ], + "node_modules/@manypkg/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@manypkg/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">=18" + "dependencies": { + "@babel/runtime": "^7.5.5", + "@types/node": "^12.7.1", + "find-up": "^4.1.0", + "fs-extra": "^8.1.0" } }, - "node_modules/@esbuild/win32-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", - "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", - "cpu": [ - "x64" - ], + "node_modules/@manypkg/find-root/node_modules/@types/node": { + "version": "12.20.55", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", + "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">=18" - } + "license": "MIT" }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", - "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "node_modules/@manypkg/find-root/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", "dev": true, "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.4.3" + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "node": ">=6 <7 || >=8" } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "node_modules/@manypkg/get-packages": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@manypkg/get-packages/-/get-packages-1.1.3.tgz", + "integrity": "sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==", "dev": true, "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.21.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", - "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", - "dev": true, - "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.7", - "debug": "^4.3.1", - "minimatch": "^3.1.5" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "@babel/runtime": "^7.5.5", + "@changesets/types": "^4.0.1", + "@manypkg/find-root": "^1.1.0", + "fs-extra": "^8.1.0", + "globby": "^11.0.0", + "read-yaml-file": "^1.1.0" } }, - "node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "node_modules/@manypkg/get-packages/node_modules/@changesets/types": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@changesets/types/-/types-4.1.0.tgz", + "integrity": "sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } + "license": "MIT" }, - "node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "node_modules/@manypkg/get-packages/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.15" + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=6 <7 || >=8" } }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", - "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "node_modules/@microsoft/api-extractor": { + "version": "7.57.6", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.57.6.tgz", + "integrity": "sha512-0rFv/D8Grzw1Mjs2+8NGUR+o4h9LVm5zKRtMeWnpdB5IMJF4TeHCL1zR5LMCIudkOvyvjbhMG5Wjs0B5nqsrRQ==", "dev": true, "license": "MIT", "dependencies": { - "ajv": "^6.14.0", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", - "minimatch": "^3.1.5", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "@microsoft/api-extractor-model": "7.33.4", + "@microsoft/tsdoc": "~0.16.0", + "@microsoft/tsdoc-config": "~0.18.1", + "@rushstack/node-core-library": "5.20.3", + "@rushstack/rig-package": "0.7.2", + "@rushstack/terminal": "0.22.3", + "@rushstack/ts-command-line": "5.3.3", + "diff": "~8.0.2", + "lodash": "~4.17.23", + "minimatch": "10.2.1", + "resolve": "~1.22.1", + "semver": "~7.5.4", + "source-map": "~0.6.1", + "typescript": "5.8.2" }, - "funding": { - "url": "https://opencollective.com/eslint" + "bin": { + "api-extractor": "bin/api-extractor" } }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "node_modules/@microsoft/api-extractor-model": { + "version": "7.33.4", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.33.4.tgz", + "integrity": "sha512-u1LTaNTikZAQ9uK6KG1Ms7nvNedsnODnspq/gH2dcyETWvH4hVNGNDvRAEutH66kAmxA4/necElqGNs1FggC8w==", "dev": true, "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "dependencies": { + "@microsoft/tsdoc": "~0.16.0", + "@microsoft/tsdoc-config": "~0.18.1", + "@rushstack/node-core-library": "5.20.3" } }, - "node_modules/@eslint/js": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", - "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "node_modules/@microsoft/api-extractor/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" + "node": "18 || 20 || >=22" } }, - "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "node_modules/@microsoft/api-extractor/node_modules/brace-expansion": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "18 || 20 || >=22" } }, - "node_modules/@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "node_modules/@microsoft/api-extractor/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, - "license": "Apache-2.0", + "license": "ISC", "dependencies": { - "@eslint/core": "^0.17.0", - "levn": "^0.4.1" + "yallist": "^4.0.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=10" } }, - "node_modules/@exodus/bytes": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.0.tgz", - "integrity": "sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==", + "node_modules/@microsoft/api-extractor/node_modules/minimatch": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.1.tgz", + "integrity": "sha512-MClCe8IL5nRRmawL6ib/eT4oLyeKMGCghibcDWK+J0hh0Q8kqSdia6BvbRMVk6mPa6WqUa5uR2oxt6C5jd533A==", "dev": true, - "license": "MIT", - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" }, - "peerDependencies": { - "@noble/hashes": "^1.8.0 || ^2.0.0" + "engines": { + "node": "20 || >=22" }, - "peerDependenciesMeta": { - "@noble/hashes": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "node_modules/@microsoft/api-extractor/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, - "license": "Apache-2.0", + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, "engines": { - "node": ">=18.18.0" + "node": ">=10" } }, - "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "node_modules/@microsoft/api-extractor/node_modules/typescript": { + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", + "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", "dev": true, "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": ">=18.18.0" + "node": ">=14.17" } }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "node_modules/@microsoft/api-extractor/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "license": "ISC" + }, + "node_modules/@microsoft/tsdoc": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.16.0.tgz", + "integrity": "sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@microsoft/tsdoc-config": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.18.1.tgz", + "integrity": "sha512-9brPoVdfN9k9g0dcWkFeA7IH9bbcttzDJlXvkf8b2OBzd5MueR1V2wkKBL0abn0otvmkHJC6aapBOTJDDeMCZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@microsoft/tsdoc": "0.16.0", + "ajv": "~8.18.0", + "jju": "~1.4.0", + "resolve": "~1.22.2" } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "node_modules/@microsoft/tsdoc-config/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, - "engines": { - "node": ">=12.22" + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" }, "funding": { "type": "github", - "url": "https://github.com/sponsors/nzakas" + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "node_modules/@microsoft/tsdoc-config/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" + "license": "MIT" + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz", + "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", + "@tybys/wasm-util": "^0.10.1" }, "funding": { "type": "github", - "url": "https://github.com/sponsors/nzakas" + "url": "https://github.com/sponsors/Brooooooklyn" } }, - "node_modules/@inquirer/external-editor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", - "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "license": "MIT", "dependencies": { - "chardet": "^2.1.1", - "iconv-lite": "^0.7.0" + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" }, "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } + "node": ">= 8" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" + "engines": { + "node": ">= 8" } }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", - "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@manypkg/find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@manypkg/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.5.5", - "@types/node": "^12.7.1", - "find-up": "^4.1.0", - "fs-extra": "^8.1.0" - } - }, - "node_modules/@manypkg/find-root/node_modules/@types/node": { - "version": "12.20.55", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", - "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@manypkg/find-root/node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/@manypkg/get-packages": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@manypkg/get-packages/-/get-packages-1.1.3.tgz", - "integrity": "sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.5.5", - "@changesets/types": "^4.0.1", - "@manypkg/find-root": "^1.1.0", - "fs-extra": "^8.1.0", - "globby": "^11.0.0", - "read-yaml-file": "^1.1.0" - } - }, - "node_modules/@manypkg/get-packages/node_modules/@changesets/types": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@changesets/types/-/types-4.1.0.tgz", - "integrity": "sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@manypkg/get-packages/node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/@microsoft/api-extractor": { - "version": "7.57.6", - "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.57.6.tgz", - "integrity": "sha512-0rFv/D8Grzw1Mjs2+8NGUR+o4h9LVm5zKRtMeWnpdB5IMJF4TeHCL1zR5LMCIudkOvyvjbhMG5Wjs0B5nqsrRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@microsoft/api-extractor-model": "7.33.4", - "@microsoft/tsdoc": "~0.16.0", - "@microsoft/tsdoc-config": "~0.18.1", - "@rushstack/node-core-library": "5.20.3", - "@rushstack/rig-package": "0.7.2", - "@rushstack/terminal": "0.22.3", - "@rushstack/ts-command-line": "5.3.3", - "diff": "~8.0.2", - "lodash": "~4.17.23", - "minimatch": "10.2.1", - "resolve": "~1.22.1", - "semver": "~7.5.4", - "source-map": "~0.6.1", - "typescript": "5.8.2" - }, - "bin": { - "api-extractor": "bin/api-extractor" - } - }, - "node_modules/@microsoft/api-extractor-model": { - "version": "7.33.4", - "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.33.4.tgz", - "integrity": "sha512-u1LTaNTikZAQ9uK6KG1Ms7nvNedsnODnspq/gH2dcyETWvH4hVNGNDvRAEutH66kAmxA4/necElqGNs1FggC8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@microsoft/tsdoc": "~0.16.0", - "@microsoft/tsdoc-config": "~0.18.1", - "@rushstack/node-core-library": "5.20.3" - } - }, - "node_modules/@microsoft/api-extractor/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@microsoft/api-extractor/node_modules/brace-expansion": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", - "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@microsoft/api-extractor/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@microsoft/api-extractor/node_modules/minimatch": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.1.tgz", - "integrity": "sha512-MClCe8IL5nRRmawL6ib/eT4oLyeKMGCghibcDWK+J0hh0Q8kqSdia6BvbRMVk6mPa6WqUa5uR2oxt6C5jd533A==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@microsoft/api-extractor/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@microsoft/api-extractor/node_modules/typescript": { - "version": "5.8.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", - "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/@microsoft/api-extractor/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "license": "ISC" - }, - "node_modules/@microsoft/tsdoc": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.16.0.tgz", - "integrity": "sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@microsoft/tsdoc-config": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.18.1.tgz", - "integrity": "sha512-9brPoVdfN9k9g0dcWkFeA7IH9bbcttzDJlXvkf8b2OBzd5MueR1V2wkKBL0abn0otvmkHJC6aapBOTJDDeMCZg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@microsoft/tsdoc": "0.16.0", - "ajv": "~8.18.0", - "jju": "~1.4.0", - "resolve": "~1.22.2" - } - }, - "node_modules/@microsoft/tsdoc-config/node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@microsoft/tsdoc-config/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz", - "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1", - "@tybys/wasm-util": "^0.10.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@oxc-project/runtime": { - "version": "0.115.0", - "resolved": "https://registry.npmjs.org/@oxc-project/runtime/-/runtime-0.115.0.tgz", - "integrity": "sha512-Rg8Wlt5dCbXhQnsXPrkOjL1DTSvXLgb2R/KYfnf1/K+R0k6UMLEmbQXPM+kwrWqSmWA2t0B1EtHy2/3zikQpvQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@oxc-project/types": { - "version": "0.115.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.115.0.tgz", - "integrity": "sha512-4n91DKnebUS4yjUHl2g3/b2T+IUdCfmoZGhmwsovZCDaJSs+QkVAM+0AqqTxHSsHfeiMuueT75cZaZcT/m0pSw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Boshen" - } - }, - "node_modules/@pkgr/core": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", - "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/pkgr" - } - }, - "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.9.tgz", - "integrity": "sha512-lcJL0bN5hpgJfSIz/8PIf02irmyL43P+j1pTCfbD1DbLkmGRuFIA4DD3B3ZOvGqG0XiVvRznbKtN0COQVaKUTg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.9.tgz", - "integrity": "sha512-J7Zk3kLYFsLtuH6U+F4pS2sYVzac0qkjcO5QxHS7OS7yZu2LRs+IXo+uvJ/mvpyUljDJ3LROZPoQfgBIpCMhdQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.9.tgz", - "integrity": "sha512-iwtmmghy8nhfRGeNAIltcNXzD0QMNaaA5U/NyZc1Ia4bxrzFByNMDoppoC+hl7cDiUq5/1CnFthpT9n+UtfFyg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.9.tgz", - "integrity": "sha512-DLFYI78SCiZr5VvdEplsVC2Vx53lnA4/Ga5C65iyldMVaErr86aiqCoNBLl92PXPfDtUYjUh+xFFor40ueNs4Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.9.tgz", - "integrity": "sha512-CsjTmTwd0Hri6iTw/DRMK7kOZ7FwAkrO4h8YWKoX/kcj833e4coqo2wzIFywtch/8Eb5enQ/lwLM7w6JX1W5RQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.9.tgz", - "integrity": "sha512-2x9O2JbSPxpxMDhP9Z74mahAStibTlrBMW0520+epJH5sac7/LwZW5Bmg/E6CXuEF53JJFW509uP+lSedaUNxg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.9.tgz", - "integrity": "sha512-JA1QRW31ogheAIRhIg9tjMfsYbglXXYGNPLdPEYrwFxdbkQCAzvpSCSHCDWNl4hTtrol8WeboCSEpjdZK8qrCg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.9.tgz", - "integrity": "sha512-aOKU9dJheda8Kj8Y3w9gnt9QFOO+qKPAl8SWd7JPHP+Cu0EuDAE5wokQubLzIDQWg2myXq2XhTpOVS07qqvT+w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.9.tgz", - "integrity": "sha512-OalO94fqj7IWRn3VdXWty75jC5dk4C197AWEuMhIpvVv2lw9fiPhud0+bW2ctCxb3YoBZor71QHbY+9/WToadA==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.9.tgz", - "integrity": "sha512-cVEl1vZtBsBZna3YMjGXNvnYYrOJ7RzuWvZU0ffvJUexWkukMaDuGhUXn0rjnV0ptzGVkvc+vW9Yqy6h8YX4pg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.9.tgz", - "integrity": "sha512-UzYnKCIIc4heAKgI4PZ3dfBGUZefGCJ1TPDuLHoCzgrMYPb5Rv6TLFuYtyM4rWyHM7hymNdsg5ik2C+UD9VDbA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.9.tgz", - "integrity": "sha512-+6zoiF+RRyf5cdlFQP7nm58mq7+/2PFaY2DNQeD4B87N36JzfF/l9mdBkkmTvSYcYPE8tMh/o3cRlsx1ldLfog==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.9.tgz", - "integrity": "sha512-rgFN6sA/dyebil3YTlL2evvi/M+ivhfnyxec7AccTpRPccno/rPoNlqybEZQBkcbZu8Hy+eqNJCqfBR8P7Pg8g==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^1.1.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.9.tgz", - "integrity": "sha512-lHVNUG/8nlF1IQk1C0Ci574qKYyty2goMiPlRqkC5R+3LkXDkL5Dhx8ytbxq35m+pkHVIvIxviD+TWLdfeuadA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.9.tgz", - "integrity": "sha512-G0oA4+w1iY5AGi5HcDTxWsoxF509hrFIPB2rduV5aDqS9FtDg1CAfa7V34qImbjfhIcA8C+RekocJZA96EarwQ==", - "cpu": [ - "x64" - ], + "node_modules/@oxc-project/runtime": { + "version": "0.115.0", + "resolved": "https://registry.npmjs.org/@oxc-project/runtime/-/runtime-0.115.0.tgz", + "integrity": "sha512-Rg8Wlt5dCbXhQnsXPrkOjL1DTSvXLgb2R/KYfnf1/K+R0k6UMLEmbQXPM+kwrWqSmWA2t0B1EtHy2/3zikQpvQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], "engines": { "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.7", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.7.tgz", - "integrity": "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rollup/pluginutils": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", - "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/pluginutils/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", - "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "peer": true - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", - "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "peer": true - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", - "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", - "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", - "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "peer": true - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", - "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "peer": true - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", - "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", - "cpu": [ - "arm" - ], + } + }, + "node_modules/@oxc-project/types": { + "version": "0.115.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.115.0.tgz", + "integrity": "sha512-4n91DKnebUS4yjUHl2g3/b2T+IUdCfmoZGhmwsovZCDaJSs+QkVAM+0AqqTxHSsHfeiMuueT75cZaZcT/m0pSw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true + "funding": { + "url": "https://github.com/sponsors/Boshen" + } }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", - "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", - "cpu": [ - "arm" - ], + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", - "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.9.tgz", + "integrity": "sha512-lcJL0bN5hpgJfSIz/8PIf02irmyL43P+j1pTCfbD1DbLkmGRuFIA4DD3B3ZOvGqG0XiVvRznbKtN0COQVaKUTg==", "cpu": [ "arm64" ], @@ -2397,14 +1532,16 @@ "license": "MIT", "optional": true, "os": [ - "linux" + "android" ], - "peer": true + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", - "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.9.tgz", + "integrity": "sha512-J7Zk3kLYFsLtuH6U+F4pS2sYVzac0qkjcO5QxHS7OS7yZu2LRs+IXo+uvJ/mvpyUljDJ3LROZPoQfgBIpCMhdQ==", "cpu": [ "arm64" ], @@ -2412,46 +1549,52 @@ "license": "MIT", "optional": true, "os": [ - "linux" + "darwin" ], - "peer": true + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", - "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.9.tgz", + "integrity": "sha512-iwtmmghy8nhfRGeNAIltcNXzD0QMNaaA5U/NyZc1Ia4bxrzFByNMDoppoC+hl7cDiUq5/1CnFthpT9n+UtfFyg==", "cpu": [ - "loong64" + "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" + "darwin" ], - "peer": true + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", - "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.9.tgz", + "integrity": "sha512-DLFYI78SCiZr5VvdEplsVC2Vx53lnA4/Ga5C65iyldMVaErr86aiqCoNBLl92PXPfDtUYjUh+xFFor40ueNs4Q==", "cpu": [ - "loong64" + "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" + "freebsd" ], - "peer": true + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", - "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.9.tgz", + "integrity": "sha512-CsjTmTwd0Hri6iTw/DRMK7kOZ7FwAkrO4h8YWKoX/kcj833e4coqo2wzIFywtch/8Eb5enQ/lwLM7w6JX1W5RQ==", "cpu": [ - "ppc64" + "arm" ], "dev": true, "license": "MIT", @@ -2459,14 +1602,16 @@ "os": [ "linux" ], - "peer": true + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", - "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.9.tgz", + "integrity": "sha512-2x9O2JbSPxpxMDhP9Z74mahAStibTlrBMW0520+epJH5sac7/LwZW5Bmg/E6CXuEF53JJFW509uP+lSedaUNxg==", "cpu": [ - "ppc64" + "arm64" ], "dev": true, "license": "MIT", @@ -2474,14 +1619,16 @@ "os": [ "linux" ], - "peer": true + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", - "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.9.tgz", + "integrity": "sha512-JA1QRW31ogheAIRhIg9tjMfsYbglXXYGNPLdPEYrwFxdbkQCAzvpSCSHCDWNl4hTtrol8WeboCSEpjdZK8qrCg==", "cpu": [ - "riscv64" + "arm64" ], "dev": true, "license": "MIT", @@ -2489,14 +1636,16 @@ "os": [ "linux" ], - "peer": true + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", - "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.9.tgz", + "integrity": "sha512-aOKU9dJheda8Kj8Y3w9gnt9QFOO+qKPAl8SWd7JPHP+Cu0EuDAE5wokQubLzIDQWg2myXq2XhTpOVS07qqvT+w==", "cpu": [ - "riscv64" + "ppc64" ], "dev": true, "license": "MIT", @@ -2504,12 +1653,14 @@ "os": [ "linux" ], - "peer": true + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", - "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.9.tgz", + "integrity": "sha512-OalO94fqj7IWRn3VdXWty75jC5dk4C197AWEuMhIpvVv2lw9fiPhud0+bW2ctCxb3YoBZor71QHbY+9/WToadA==", "cpu": [ "s390x" ], @@ -2519,12 +1670,14 @@ "os": [ "linux" ], - "peer": true + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", - "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.9.tgz", + "integrity": "sha512-cVEl1vZtBsBZna3YMjGXNvnYYrOJ7RzuWvZU0ffvJUexWkukMaDuGhUXn0rjnV0ptzGVkvc+vW9Yqy6h8YX4pg==", "cpu": [ "x64" ], @@ -2534,12 +1687,14 @@ "os": [ "linux" ], - "peer": true + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", - "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.9.tgz", + "integrity": "sha512-UzYnKCIIc4heAKgI4PZ3dfBGUZefGCJ1TPDuLHoCzgrMYPb5Rv6TLFuYtyM4rWyHM7hymNdsg5ik2C+UD9VDbA==", "cpu": [ "x64" ], @@ -2549,27 +1704,14 @@ "os": [ "linux" ], - "peer": true - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", - "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "peer": true + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", - "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.9.tgz", + "integrity": "sha512-+6zoiF+RRyf5cdlFQP7nm58mq7+/2PFaY2DNQeD4B87N36JzfF/l9mdBkkmTvSYcYPE8tMh/o3cRlsx1ldLfog==", "cpu": [ "arm64" ], @@ -2579,29 +1721,33 @@ "os": [ "openharmony" ], - "peer": true + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", - "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.9.tgz", + "integrity": "sha512-rgFN6sA/dyebil3YTlL2evvi/M+ivhfnyxec7AccTpRPccno/rPoNlqybEZQBkcbZu8Hy+eqNJCqfBR8P7Pg8g==", "cpu": [ - "arm64" + "wasm32" ], "dev": true, "license": "MIT", "optional": true, - "os": [ - "win32" - ], - "peer": true + "dependencies": { + "@napi-rs/wasm-runtime": "^1.1.1" + }, + "engines": { + "node": ">=14.0.0" + } }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", - "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.9.tgz", + "integrity": "sha512-lHVNUG/8nlF1IQk1C0Ci574qKYyty2goMiPlRqkC5R+3LkXDkL5Dhx8ytbxq35m+pkHVIvIxviD+TWLdfeuadA==", "cpu": [ - "ia32" + "arm64" ], "dev": true, "license": "MIT", @@ -2609,12 +1755,14 @@ "os": [ "win32" ], - "peer": true + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", - "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.9", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.9.tgz", + "integrity": "sha512-G0oA4+w1iY5AGi5HcDTxWsoxF509hrFIPB2rduV5aDqS9FtDg1CAfa7V34qImbjfhIcA8C+RekocJZA96EarwQ==", "cpu": [ "x64" ], @@ -2624,29 +1772,52 @@ "os": [ "win32" ], - "peer": true + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", - "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", - "cpu": [ - "x64" - ], + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.7", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.7.tgz", + "integrity": "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/pluginutils": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", + "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } }, - "node_modules/@rtsao/scc": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", - "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "node_modules/@rollup/pluginutils/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, "node_modules/@rushstack/node-core-library": { "version": "5.20.3", @@ -2858,7 +2029,6 @@ "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -2879,7 +2049,6 @@ "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "dequal": "^2.0.3" } @@ -2962,8 +2131,7 @@ "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/assert": { "version": "1.5.11", @@ -2990,6 +2158,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -3004,12 +2179,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, "node_modules/@types/node": { "version": "24.12.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.0.tgz", @@ -3041,17 +2210,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.1.tgz", - "integrity": "sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.0.tgz", + "integrity": "sha512-qeu4rTHR3/IaFORbD16gmjq9+rEs9fGKdX0kF6BKSfi+gCuG3RCKLlSBYzn/bGsY9Tj7KE/DAQStbp8AHJGHEQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.56.1", - "@typescript-eslint/type-utils": "8.56.1", - "@typescript-eslint/utils": "8.56.1", - "@typescript-eslint/visitor-keys": "8.56.1", + "@typescript-eslint/scope-manager": "8.57.0", + "@typescript-eslint/type-utils": "8.57.0", + "@typescript-eslint/utils": "8.57.0", + "@typescript-eslint/visitor-keys": "8.57.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" @@ -3064,7 +2233,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.56.1", + "@typescript-eslint/parser": "^8.57.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } @@ -3080,16 +2249,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.1.tgz", - "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.0.tgz", + "integrity": "sha512-XZzOmihLIr8AD1b9hL9ccNMzEMWt/dE2u7NyTY9jJG6YNiNthaD5XtUHVF2uCXZ15ng+z2hT3MVuxnUYhq6k1g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.56.1", - "@typescript-eslint/types": "8.56.1", - "@typescript-eslint/typescript-estree": "8.56.1", - "@typescript-eslint/visitor-keys": "8.56.1", + "@typescript-eslint/scope-manager": "8.57.0", + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/typescript-estree": "8.57.0", + "@typescript-eslint/visitor-keys": "8.57.0", "debug": "^4.4.3" }, "engines": { @@ -3105,14 +2274,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.1.tgz", - "integrity": "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.0.tgz", + "integrity": "sha512-pR+dK0BlxCLxtWfaKQWtYr7MhKmzqZxuii+ZjuFlZlIGRZm22HnXFqa2eY+90MUz8/i80YJmzFGDUsi8dMOV5w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.56.1", - "@typescript-eslint/types": "^8.56.1", + "@typescript-eslint/tsconfig-utils": "^8.57.0", + "@typescript-eslint/types": "^8.57.0", "debug": "^4.4.3" }, "engines": { @@ -3127,14 +2296,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.1.tgz", - "integrity": "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.0.tgz", + "integrity": "sha512-nvExQqAHF01lUM66MskSaZulpPL5pgy5hI5RfrxviLgzZVffB5yYzw27uK/ft8QnKXI2X0LBrHJFr1TaZtAibw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.56.1", - "@typescript-eslint/visitor-keys": "8.56.1" + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/visitor-keys": "8.57.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3145,9 +2314,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.1.tgz", - "integrity": "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.0.tgz", + "integrity": "sha512-LtXRihc5ytjJIQEH+xqjB0+YgsV4/tW35XKX3GTZHpWtcC8SPkT/d4tqdf1cKtesryHm2bgp6l555NYcT2NLvA==", "dev": true, "license": "MIT", "engines": { @@ -3162,15 +2331,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.56.1.tgz", - "integrity": "sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.0.tgz", + "integrity": "sha512-yjgh7gmDcJ1+TcEg8x3uWQmn8ifvSupnPfjP21twPKrDP/pTHlEQgmKcitzF/rzPSmv7QjJ90vRpN4U+zoUjwQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.56.1", - "@typescript-eslint/typescript-estree": "8.56.1", - "@typescript-eslint/utils": "8.56.1", + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/typescript-estree": "8.57.0", + "@typescript-eslint/utils": "8.57.0", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, @@ -3187,9 +2356,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.1.tgz", - "integrity": "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.0.tgz", + "integrity": "sha512-dTLI8PEXhjUC7B9Kre+u0XznO696BhXcTlOn0/6kf1fHaQW8+VjJAVHJ3eTI14ZapTxdkOmc80HblPQLaEeJdg==", "dev": true, "license": "MIT", "engines": { @@ -3201,16 +2370,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.1.tgz", - "integrity": "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.0.tgz", + "integrity": "sha512-m7faHcyVg0BT3VdYTlX8GdJEM7COexXxS6KqGopxdtkQRvBanK377QDHr4W/vIPAR+ah9+B/RclSW5ldVniO1Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.56.1", - "@typescript-eslint/tsconfig-utils": "8.56.1", - "@typescript-eslint/types": "8.56.1", - "@typescript-eslint/visitor-keys": "8.56.1", + "@typescript-eslint/project-service": "8.57.0", + "@typescript-eslint/tsconfig-utils": "8.57.0", + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/visitor-keys": "8.57.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -3268,16 +2437,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.1.tgz", - "integrity": "sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.0.tgz", + "integrity": "sha512-5iIHvpD3CZe06riAsbNxxreP+MuYgVUsV0n4bwLH//VJmgtt54sQeY2GszntJ4BjYCpMzrfVh2SBnUQTtys2lQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.56.1", - "@typescript-eslint/types": "8.56.1", - "@typescript-eslint/typescript-estree": "8.56.1" + "@typescript-eslint/scope-manager": "8.57.0", + "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/typescript-estree": "8.57.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3292,13 +2461,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.56.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.1.tgz", - "integrity": "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.0.tgz", + "integrity": "sha512-zm6xx8UT/Xy2oSr2ZXD0pZo7Jx2XsCoID2IUh9YSTFRu7z+WdwYTRk6LhUftm1crwqbuoF6I8zAFeCMw0YjwDg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/types": "8.57.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -3748,7 +2917,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -3847,28 +3015,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", - "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.9", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "es-shim-unscopables": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/array.prototype.flat": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", @@ -4136,14 +3282,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, - "optional": true, - "peer": true - }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -4188,20 +3326,10 @@ "get-intrinsic": "^1.3.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/caniuse-lite": { @@ -4496,7 +3624,6 @@ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -4549,8 +3676,7 @@ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/dunder-proto": { "version": "1.0.1", @@ -4790,50 +3916,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/esbuild": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", - "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "peer": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.3", - "@esbuild/android-arm": "0.27.3", - "@esbuild/android-arm64": "0.27.3", - "@esbuild/android-x64": "0.27.3", - "@esbuild/darwin-arm64": "0.27.3", - "@esbuild/darwin-x64": "0.27.3", - "@esbuild/freebsd-arm64": "0.27.3", - "@esbuild/freebsd-x64": "0.27.3", - "@esbuild/linux-arm": "0.27.3", - "@esbuild/linux-arm64": "0.27.3", - "@esbuild/linux-ia32": "0.27.3", - "@esbuild/linux-loong64": "0.27.3", - "@esbuild/linux-mips64el": "0.27.3", - "@esbuild/linux-ppc64": "0.27.3", - "@esbuild/linux-riscv64": "0.27.3", - "@esbuild/linux-s390x": "0.27.3", - "@esbuild/linux-x64": "0.27.3", - "@esbuild/netbsd-arm64": "0.27.3", - "@esbuild/netbsd-x64": "0.27.3", - "@esbuild/openbsd-arm64": "0.27.3", - "@esbuild/openbsd-x64": "0.27.3", - "@esbuild/openharmony-arm64": "0.27.3", - "@esbuild/sunos-x64": "0.27.3", - "@esbuild/win32-arm64": "0.27.3", - "@esbuild/win32-ia32": "0.27.3", - "@esbuild/win32-x64": "0.27.3" - } - }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -4845,33 +3927,30 @@ } }, "node_modules/eslint": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", - "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.0.3.tgz", + "integrity": "sha512-COV33RzXZkqhG9P2rZCFl9ZmJ7WL+gQSCRzE7RhkbclbQPtLAWReL7ysA0Sh4c8Im2U9ynybdR56PV0XcKvqaQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.2", - "@eslint/config-helpers": "^0.4.2", - "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.5", - "@eslint/js": "9.39.4", - "@eslint/plugin-kit": "^0.4.1", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.3", + "@eslint/config-helpers": "^0.5.2", + "@eslint/core": "^1.1.1", + "@eslint/plugin-kit": "^0.6.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", - "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.1.1", + "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", @@ -4881,8 +3960,7 @@ "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.5", + "minimatch": "^10.2.4", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, @@ -4890,7 +3968,7 @@ "eslint": "bin/eslint.js" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://eslint.org/donate" @@ -4920,54 +3998,6 @@ "eslint": ">=7.0.0" } }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", - "dev": true, - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-module-utils": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", - "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, "node_modules/eslint-plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/eslint-plugin-babel/-/eslint-plugin-babel-5.3.1.tgz", @@ -4984,70 +4014,6 @@ "eslint": ">=4.0.0" } }, - "node_modules/eslint-plugin-import": { - "version": "2.32.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", - "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@rtsao/scc": "^1.1.0", - "array-includes": "^3.1.9", - "array.prototype.findlastindex": "^1.2.6", - "array.prototype.flat": "^1.3.3", - "array.prototype.flatmap": "^1.3.3", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.12.1", - "hasown": "^2.0.2", - "is-core-module": "^2.16.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.8", - "object.groupby": "^1.0.3", - "object.values": "^1.2.1", - "semver": "^6.3.1", - "string.prototype.trimend": "^1.0.9", - "tsconfig-paths": "^3.15.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/eslint-plugin-jsx-a11y": { "version": "6.10.2", "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", @@ -5210,17 +4176,19 @@ } }, "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" @@ -5239,55 +4207,29 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/eslint/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": "18 || 20 || >=22" } }, - "node_modules/eslint/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/eslint/node_modules/brace-expansion": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", "dev": true, + "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "balanced-match": "^4.0.2" }, "engines": { - "node": ">=7.0.0" + "node": "18 || 20 || >=22" } }, - "node_modules/eslint/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "node_modules/eslint/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -5301,13 +4243,13 @@ } }, "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, "license": "Apache-2.0", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" @@ -5344,6 +4286,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eslint/node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/eslint/node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -5374,44 +4332,32 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/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==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.15.0", + "acorn": "^8.16.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" + "eslint-visitor-keys": "^5.0.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, "license": "Apache-2.0", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" @@ -5432,10 +4378,11 @@ } }, "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" }, @@ -6088,33 +5035,6 @@ "node": ">= 4" } }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/import-lazy": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", @@ -7153,12 +6073,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, "node_modules/lodash.startcase": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", @@ -7194,7 +6108,6 @@ "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -7301,15 +6214,6 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/mlly": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.1.tgz", @@ -7485,21 +6389,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object.groupby": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", - "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/object.values": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", @@ -7641,19 +6530,6 @@ "dev": true, "license": "MIT" }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/parse5": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", @@ -7854,7 +6730,6 @@ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -7869,8 +6744,7 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/prop-types": { "version": "15.8.1", @@ -8136,53 +7010,6 @@ "dev": true, "license": "MIT" }, - "node_modules/rollup": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", - "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@types/estree": "1.0.8" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.59.0", - "@rollup/rollup-android-arm64": "4.59.0", - "@rollup/rollup-darwin-arm64": "4.59.0", - "@rollup/rollup-darwin-x64": "4.59.0", - "@rollup/rollup-freebsd-arm64": "4.59.0", - "@rollup/rollup-freebsd-x64": "4.59.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", - "@rollup/rollup-linux-arm-musleabihf": "4.59.0", - "@rollup/rollup-linux-arm64-gnu": "4.59.0", - "@rollup/rollup-linux-arm64-musl": "4.59.0", - "@rollup/rollup-linux-loong64-gnu": "4.59.0", - "@rollup/rollup-linux-loong64-musl": "4.59.0", - "@rollup/rollup-linux-ppc64-gnu": "4.59.0", - "@rollup/rollup-linux-ppc64-musl": "4.59.0", - "@rollup/rollup-linux-riscv64-gnu": "4.59.0", - "@rollup/rollup-linux-riscv64-musl": "4.59.0", - "@rollup/rollup-linux-s390x-gnu": "4.59.0", - "@rollup/rollup-linux-x64-gnu": "4.59.0", - "@rollup/rollup-linux-x64-musl": "4.59.0", - "@rollup/rollup-openbsd-x64": "4.59.0", - "@rollup/rollup-openharmony-arm64": "4.59.0", - "@rollup/rollup-win32-arm64-msvc": "4.59.0", - "@rollup/rollup-win32-ia32-msvc": "4.59.0", - "@rollup/rollup-win32-x64-gnu": "4.59.0", - "@rollup/rollup-win32-x64-msvc": "4.59.0", - "fsevents": "~2.3.2" - } - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -8499,18 +7326,6 @@ "node": ">=0.10.0" } }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, "node_modules/spawndamnit": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spawndamnit/-/spawndamnit-3.0.1.tgz", @@ -8790,34 +7605,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/terser": { - "version": "5.24.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.24.0.tgz", - "integrity": "sha512-ZpGR4Hy3+wBEzVEnHvstMvqpD/nABNelQn/z2r0fjVWGQsN3bpOLzQlqDxmb4CDZnXq5lpjnQ+mHQLAOpfM5iw==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "optional": true, - "peer": true - }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -8965,30 +7752,6 @@ "typescript": ">=4.8.4" } }, - "node_modules/tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", - "dev": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -9189,6 +7952,7 @@ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } diff --git a/package.json b/package.json index 95e10dac..cfe981cc 100644 --- a/package.json +++ b/package.json @@ -55,21 +55,23 @@ ], "license": "MIT", "devDependencies": { + "@eslint/js": "^10.0.1", "@changesets/cli": "^2.30.0", + "@eslint/compat": "^2.0.3", + "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@types/assert": "^1.5.11", "@types/node": "^24.12.0", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", - "@typescript-eslint/eslint-plugin": "^8.56.1", - "@typescript-eslint/parser": "^8.56.1", + "@typescript-eslint/eslint-plugin": "^8.57.0", + "@typescript-eslint/parser": "^8.57.0", "@vitejs/plugin-react": "^6.0.0", "@vitest/coverage-v8": "^4.1.0", - "eslint": "^9.39.4", + "eslint": "^10.0.3", "eslint-config-prettier": "^10.1.8", "eslint-plugin-babel": "^5.3.1", - "eslint-plugin-import": "^2.32.0", "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-prettier": "^5.5.5", "eslint-plugin-react": "^7.37.5", diff --git a/src/BlueskyShareButton.tsx b/src/BlueskyShareButton.tsx index cc2946b0..7f4e77ce 100644 --- a/src/BlueskyShareButton.tsx +++ b/src/BlueskyShareButton.tsx @@ -43,4 +43,3 @@ export const BlueskyShareButton = forwardRef ); GabShareButton.displayName = 'GabShareButton'; - diff --git a/src/HatenaShareButton.tsx b/src/HatenaShareButton.tsx index c92ff214..5a4f4a39 100644 --- a/src/HatenaShareButton.tsx +++ b/src/HatenaShareButton.tsx @@ -30,4 +30,3 @@ export const HatenaShareButton = forwardRef( ); OKShareButton.displayName = 'OKShareButton'; - diff --git a/src/PinterestShareButton.tsx b/src/PinterestShareButton.tsx index a7f1fe9b..3b7d5b3a 100644 --- a/src/PinterestShareButton.tsx +++ b/src/PinterestShareButton.tsx @@ -50,4 +50,3 @@ export const PinterestShareButton = forwardRef( ); VKShareButton.displayName = 'VKShareButton'; - diff --git a/src/WeiboShareButton.tsx b/src/WeiboShareButton.tsx index 318b44cc..54b5bf9c 100644 --- a/src/WeiboShareButton.tsx +++ b/src/WeiboShareButton.tsx @@ -11,10 +11,7 @@ type WeiboShareButtonProps = Omit< shareTitle?: string; }; -function weiboLink( - url: string, - { shareTitle, image }: { shareTitle?: string; image?: string }, -) { +function weiboLink(url: string, { shareTitle, image }: { shareTitle?: string; image?: string }) { assert(url, 'weibo.url'); return ( @@ -48,4 +45,3 @@ export const WeiboShareButton = forwardRef( ); XShareButton.displayName = 'XShareButton'; - diff --git a/src/shareWindow.ts b/src/shareWindow.ts index f87ca872..121819f1 100644 --- a/src/shareWindow.ts +++ b/src/shareWindow.ts @@ -20,12 +20,7 @@ const getBoxPositionOnScreenCenter = (width: number, height: number) => ({ export function openShareWindow( url: string, - { - height = 400, - onClose, - position = 'windowCenter', - width = 550, - }: ShareWindowOptions = {}, + { height = 400, onClose, position = 'windowCenter', width = 550 }: ShareWindowOptions = {}, ) { const config: { [key: string]: string | number } = { height, diff --git a/src/utils/objectToGetParams.ts b/src/utils/objectToGetParams.ts index 6288f39f..ba5f55dd 100644 --- a/src/utils/objectToGetParams.ts +++ b/src/utils/objectToGetParams.ts @@ -1,6 +1,4 @@ -export function objectToGetParams(object: { - [key: string]: string | number | undefined | null; -}) { +export function objectToGetParams(object: { [key: string]: string | number | undefined | null }) { const params = Object.entries(object) .filter(([, value]) => value !== undefined && value !== null) .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`); diff --git a/test/EmailShareButton.test.tsx b/test/EmailShareButton.test.tsx index c76bafe6..c7615982 100644 --- a/test/EmailShareButton.test.tsx +++ b/test/EmailShareButton.test.tsx @@ -30,9 +30,7 @@ describe('EmailShareButton', () => { fireEvent.click(screen.getByRole('button', { name: 'Share by email' })); expect(openSpy).not.toHaveBeenCalled(); - expect(hrefState.value).toBe( - 'mailto:?subject=Subject&body=Body%20https%3A%2F%2Fexample.com', - ); + expect(hrefState.value).toBe('mailto:?subject=Subject&body=Body%20https%3A%2F%2Fexample.com'); }); it('uses the shared url as the body when no body is provided', () => { diff --git a/test/NetworkShareButtons.test.tsx b/test/NetworkShareButtons.test.tsx index 93ae40ce..480f5d19 100644 --- a/test/NetworkShareButtons.test.tsx +++ b/test/NetworkShareButtons.test.tsx @@ -151,7 +151,8 @@ const shareButtonCases: ShareButtonCase[] = [ { children: 'Share on Threads', Component: ThreadsShareButton, - expectedUrl: 'https://threads.net/intent/post?url=https%3A%2F%2Fexample.com&text=Example%20title', + expectedUrl: + 'https://threads.net/intent/post?url=https%3A%2F%2Fexample.com&text=Example%20title', props: { text: 'Example title', url: 'https://example.com', @@ -216,18 +217,16 @@ const shareButtonCases: ShareButtonCase[] = [ ]; describe('network share buttons', () => { - it.each(shareButtonCases)('builds the expected share URL for $children', ({ - Component, - children, - expectedUrl, - props, - }) => { - const openSpy = vi.spyOn(window, 'open').mockReturnValue(null); + it.each(shareButtonCases)( + 'builds the expected share URL for $children', + ({ Component, children, expectedUrl, props }) => { + const openSpy = vi.spyOn(window, 'open').mockReturnValue(null); - render({children}); + render({children}); - fireEvent.click(screen.getByRole('button', { name: children })); + fireEvent.click(screen.getByRole('button', { name: children })); - expect(openSpy).toHaveBeenCalledWith(expectedUrl, '', expect.any(String)); - }); + expect(openSpy).toHaveBeenCalledWith(expectedUrl, '', expect.any(String)); + }, + ); }); diff --git a/test/PinterestShareButton.test.tsx b/test/PinterestShareButton.test.tsx index ce9bb5a5..363b1e0c 100644 --- a/test/PinterestShareButton.test.tsx +++ b/test/PinterestShareButton.test.tsx @@ -31,7 +31,11 @@ describe('PinterestShareButton', () => { const openSpy = vi.spyOn(window, 'open').mockReturnValue(null); render( - + Pin it , ); From 346b3450c016ac1cc8338da42a8b65a6b587e643 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20Nyga=CC=8Ard?= Date: Mon, 12 Oct 2026 09:41:28 +0300 Subject: [PATCH 16/36] chore(deps): remove unused eslint-plugin-babel The flat ESLint config no longer references the Babel plugin since the TypeScript and Vite migration, so it was only adding install weight and peer-dependency noise. Co-Authored-By: Claude Opus 5.5 --- package-lock.json | 26 -------------------------- package.json | 3 +-- 2 files changed, 1 insertion(+), 28 deletions(-) diff --git a/package-lock.json b/package-lock.json index ea74139a..8e4828c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,7 +28,6 @@ "@vitest/coverage-v8": "^4.1.0", "eslint": "^10.0.3", "eslint-config-prettier": "^10.1.8", - "eslint-plugin-babel": "^5.3.1", "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-prettier": "^5.5.5", "eslint-plugin-react": "^7.37.5", @@ -3998,22 +3997,6 @@ "eslint": ">=7.0.0" } }, - "node_modules/eslint-plugin-babel": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-babel/-/eslint-plugin-babel-5.3.1.tgz", - "integrity": "sha512-VsQEr6NH3dj664+EyxJwO4FCYm/00JhYb3Sk3ft8o+fpKuIfQ9TaW6uVUfvwMXHcf/lsnRIoyFPsLMyiWCSL/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-rule-composer": "^0.3.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": ">=4.0.0" - } - }, "node_modules/eslint-plugin-jsx-a11y": { "version": "6.10.2", "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", @@ -4166,15 +4149,6 @@ "semver": "bin/semver.js" } }, - "node_modules/eslint-rule-composer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz", - "integrity": "sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==", - "dev": true, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/eslint-scope": { "version": "9.1.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", diff --git a/package.json b/package.json index cfe981cc..ae642afe 100644 --- a/package.json +++ b/package.json @@ -55,9 +55,9 @@ ], "license": "MIT", "devDependencies": { - "@eslint/js": "^10.0.1", "@changesets/cli": "^2.30.0", "@eslint/compat": "^2.0.3", + "@eslint/js": "^10.0.1", "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", @@ -71,7 +71,6 @@ "@vitest/coverage-v8": "^4.1.0", "eslint": "^10.0.3", "eslint-config-prettier": "^10.1.8", - "eslint-plugin-babel": "^5.3.1", "eslint-plugin-jsx-a11y": "^6.10.2", "eslint-plugin-prettier": "^5.5.5", "eslint-plugin-react": "^7.37.5", From 81842ae21453882d8313a4c3eac5d3981c41f1d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20Nyga=CC=8Ard?= Date: Mon, 12 Oct 2026 09:43:19 +0300 Subject: [PATCH 17/36] refactor(x): use "x" as the internal network name XShareButton still identified itself as "twitter" internally, a leftover from the TwitterShareButton era. With the Twitter aliases gone in v6, key the default aria-label on "x" instead. No public API change; a test now covers the default aria-label for icon-only X buttons. Co-Authored-By: Claude Opus 5.5 --- src/ShareButton.tsx | 2 +- src/XShareButton.tsx | 2 +- test/XShareButton.test.tsx | 11 +++++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/ShareButton.tsx b/src/ShareButton.tsx index 6b388de4..5b5554d4 100644 --- a/src/ShareButton.tsx +++ b/src/ShareButton.tsx @@ -26,12 +26,12 @@ const DEFAULT_ARIA_LABELS = { telegram: 'Share on Telegram', threads: 'Share on Threads', tumblr: 'Share on Tumblr', - twitter: 'Share on X', viber: 'Share on Viber', vk: 'Share on VK', weibo: 'Share on Weibo', whatsapp: 'Share on WhatsApp', workplace: 'Share on Workplace', + x: 'Share on X', } as const; export interface Props extends NativeButtonProps { diff --git a/src/XShareButton.tsx b/src/XShareButton.tsx index 57bd0bc4..5b15999c 100644 --- a/src/XShareButton.tsx +++ b/src/XShareButton.tsx @@ -50,7 +50,7 @@ export const XShareButton = forwardRef( { it('builds a share url with hashtags and related accounts', () => { @@ -74,4 +75,14 @@ describe('XShareButton', () => { expect.any(String), ); }); + + it('applies the default X aria-label to icon-only buttons', () => { + render( + + + , + ); + + expect(screen.getByRole('button', { name: 'Share on X' })).toBeInTheDocument(); + }); }); From 05c499726036462132d2634d5714975a18526485 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20Nyga=CC=8Ard?= Date: Mon, 12 Oct 2026 09:44:21 +0300 Subject: [PATCH 18/36] refactor(networks): remove Pocket and Workplace share buttons Pocket shut down in July 2025 and Workplace from Meta shut down in June 2026. Their share endpoints no longer accept shares, so keeping the buttons only shipped broken UI. v6 is the breaking release, so remove the share buttons, raw and legacy icons, and URL builders for both networks. Tests now assert the removed symbols stay out of the source and built package surfaces, and the migration guide lists the removals. Co-Authored-By: Claude Opus 5.5 --- .changeset/quiet-pockets-close.md | 7 +++++ .changeset/wise-years-rest.md | 2 +- README.md | 38 --------------------------- demo/Demo.tsx | 26 ------------------- docs/migrate-v5-to-v6.md | 25 ++++++++++++------ src/PocketIcon.tsx | 9 ------- src/PocketLegacyIcon.tsx | 9 ------- src/PocketShareButton.tsx | 38 --------------------------- src/ShareButton.tsx | 2 -- src/WorkplaceIcon.tsx | 9 ------- src/WorkplaceLegacyIcon.tsx | 9 ------- src/WorkplaceShareButton.tsx | 43 ------------------------------- src/index.ts | 8 ------ test/NetworkShareButtons.test.tsx | 22 ---------------- test/PackageBuilds.test.ts | 4 ++- test/PackageExports.test.tsx | 18 +++++++++++++ 16 files changed, 46 insertions(+), 223 deletions(-) create mode 100644 .changeset/quiet-pockets-close.md delete mode 100644 src/PocketIcon.tsx delete mode 100644 src/PocketLegacyIcon.tsx delete mode 100644 src/PocketShareButton.tsx delete mode 100644 src/WorkplaceIcon.tsx delete mode 100644 src/WorkplaceLegacyIcon.tsx delete mode 100644 src/WorkplaceShareButton.tsx diff --git a/.changeset/quiet-pockets-close.md b/.changeset/quiet-pockets-close.md new file mode 100644 index 00000000..0edca288 --- /dev/null +++ b/.changeset/quiet-pockets-close.md @@ -0,0 +1,7 @@ +--- +'react-share': major +--- + +Remove `PocketShareButton`, `PocketIcon`, `PocketLegacyIcon`, and `buildPocketShareUrl`, as well as `WorkplaceShareButton`, `WorkplaceIcon`, `WorkplaceLegacyIcon`, and `buildWorkplaceShareUrl`. + +Pocket shut down in July 2025 and Workplace from Meta shut down in June 2026, so these buttons could only open dead share pages. diff --git a/.changeset/wise-years-rest.md b/.changeset/wise-years-rest.md index d9f23421..af9aa0ec 100644 --- a/.changeset/wise-years-rest.md +++ b/.changeset/wise-years-rest.md @@ -6,7 +6,7 @@ Replace the share button `title` prop with explicit v6 payload names: - `text` for Bluesky, Gab, Line, Telegram, Threads, and X - `message` for WhatsApp and Viber -- `shareTitle` for Hatena, Instapaper, LiveJournal, Mail.ru, OK, Pocket, Reddit, VK, and Weibo +- `shareTitle` for Hatena, Instapaper, LiveJournal, Mail.ru, OK, Reddit, VK, and Weibo - `postTitle` for Tumblr `htmlTitle` remains the native button tooltip/title attribute. diff --git a/README.md b/README.md index ee5c68db..905abf50 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,6 @@ npm install react-share - [Mail.Ru](#mailrusharebutton) - [Odnoklassniki](#oksharebutton) - [Pinterest](#pinterestsharebutton) - - [Pocket](#pocketsharebutton) - [Reddit](#redditsharebutton) - [Telegram](#telegramsharebutton) - [Threads](#threadssharebutton) @@ -42,7 +41,6 @@ npm install react-share - [VK](#vksharebutton) - [Weibo](#weibosharebutton) - [Whatsapp](#whatsappsharebutton) - - [Workplace](#workplacesharebutton) - [X](#xsharebutton) - social media icons included in the library - supports also custom icons @@ -405,22 +403,6 @@ import { PinterestShareButton, PinterestIcon } from "react-share"; ; ``` -#### PocketShareButton - -Optional props: - -- `shareTitle` (`string`): Title sent in the share payload. -- Notes: - - If Pocket detects a title on the target page, it may ignore this prop and use page metadata instead. - -```jsx -import { PocketShareButton, PocketIcon } from "react-share"; - - - -; -``` - #### RedditShareButton Optional props: @@ -604,26 +586,6 @@ import { WhatsappShareButton, WhatsappIcon } from "react-share"; ; ``` -#### WorkplaceShareButton - -Optional props: - -- `quote` (`string`): Quote to include with the share. -- `hashtag` (`string`): Hashtag to append to the share. Include the leading `#`. - -```jsx -import { WorkplaceShareButton, WorkplaceIcon } from "react-share"; - - - -; -``` - ### Icons v6 exports two icon families: diff --git a/demo/Demo.tsx b/demo/Demo.tsx index d7f458b7..e45daee4 100644 --- a/demo/Demo.tsx +++ b/demo/Demo.tsx @@ -28,8 +28,6 @@ import { OKShareButton, PinterestIcon, PinterestShareButton, - PocketIcon, - PocketShareButton, RedditIcon, RedditShareButton, TelegramIcon, @@ -46,8 +44,6 @@ import { WeiboShareButton, WhatsappIcon, WhatsappShareButton, - WorkplaceIcon, - WorkplaceShareButton, XIcon, XShareButton, } from '../src'; @@ -232,17 +228,6 @@ export function Demo() { -
- - - -
-
-
- - - -
-
- - - ); -} diff --git a/src/PocketLegacyIcon.tsx b/src/PocketLegacyIcon.tsx deleted file mode 100644 index ee687a37..00000000 --- a/src/PocketLegacyIcon.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { LegacyIconBase, type LegacyIconProps } from './icons/LegacyIconBase'; - -export function PocketLegacyIcon(props: LegacyIconProps) { - return ( - - - - ); -} diff --git a/src/PocketShareButton.tsx b/src/PocketShareButton.tsx deleted file mode 100644 index 7efe2a5b..00000000 --- a/src/PocketShareButton.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { forwardRef } from 'react'; -import { assert } from './utils/assert'; -import { objectToGetParams } from './utils/objectToGetParams'; -import { ShareButton, type ShareButtonProps } from './ShareButton'; - -type PocketShareButtonProps = Omit, 'title'> & { - shareTitle?: string; -}; - -function pocketLink(url: string, { shareTitle }: { shareTitle?: string }) { - assert(url, 'pocket.url'); - - return ( - 'https://getpocket.com/save' + - objectToGetParams({ - url, - title: shareTitle, - }) - ); -} - -export const buildPocketShareUrl = pocketLink; - -export const PocketShareButton = forwardRef( - ({ shareTitle, ...props }, ref) => ( - - ), -); - -PocketShareButton.displayName = 'PocketShareButton'; diff --git a/src/ShareButton.tsx b/src/ShareButton.tsx index 5b5554d4..3e9ea11b 100644 --- a/src/ShareButton.tsx +++ b/src/ShareButton.tsx @@ -21,7 +21,6 @@ const DEFAULT_ARIA_LABELS = { mailru: 'Share on Mail.ru', ok: 'Share on OK', pinterest: 'Pin on Pinterest', - pocket: 'Save to Pocket', reddit: 'Share on Reddit', telegram: 'Share on Telegram', threads: 'Share on Threads', @@ -30,7 +29,6 @@ const DEFAULT_ARIA_LABELS = { vk: 'Share on VK', weibo: 'Share on Weibo', whatsapp: 'Share on WhatsApp', - workplace: 'Share on Workplace', x: 'Share on X', } as const; diff --git a/src/WorkplaceIcon.tsx b/src/WorkplaceIcon.tsx deleted file mode 100644 index 3d4930fd..00000000 --- a/src/WorkplaceIcon.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { RawIconBase, type RawIconProps } from './icons/RawIconBase'; - -export function WorkplaceIcon(props: RawIconProps) { - return ( - - - - ); -} diff --git a/src/WorkplaceLegacyIcon.tsx b/src/WorkplaceLegacyIcon.tsx deleted file mode 100644 index fed2c094..00000000 --- a/src/WorkplaceLegacyIcon.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { LegacyIconBase, type LegacyIconProps } from './icons/LegacyIconBase'; - -export function WorkplaceLegacyIcon(props: LegacyIconProps) { - return ( - - - - ); -} diff --git a/src/WorkplaceShareButton.tsx b/src/WorkplaceShareButton.tsx deleted file mode 100644 index 56ffc20d..00000000 --- a/src/WorkplaceShareButton.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { forwardRef } from 'react'; -import { assert } from './utils/assert'; -import { objectToGetParams } from './utils/objectToGetParams'; -import { ShareButton, type ShareButtonProps } from './ShareButton'; - -type WorkplaceShareButtonProps = ShareButtonProps<{ quote?: string; hashtag?: string }> & { - hashtag?: string; - quote?: string; -}; - -function workplaceLink(url: string, { quote, hashtag }: { quote?: string; hashtag?: string }) { - assert(url, 'workplace.url'); - - return ( - 'https://work.facebook.com/sharer.php' + - objectToGetParams({ - u: url, - quote, - hashtag, - }) - ); -} - -export const buildWorkplaceShareUrl = workplaceLink; - -export const WorkplaceShareButton = forwardRef( - ({ hashtag, quote, ...props }, ref) => ( - - ), -); - -WorkplaceShareButton.displayName = 'WorkplaceShareButton'; diff --git a/src/index.ts b/src/index.ts index b3b6524e..6ef3ad05 100644 --- a/src/index.ts +++ b/src/index.ts @@ -56,10 +56,6 @@ export { PinterestIcon } from './PinterestIcon'; export { PinterestLegacyIcon } from './PinterestLegacyIcon'; export { PinterestShareButton } from './PinterestShareButton'; export { buildPinterestShareUrl } from './PinterestShareButton'; -export { PocketIcon } from './PocketIcon'; -export { PocketLegacyIcon } from './PocketLegacyIcon'; -export { PocketShareButton } from './PocketShareButton'; -export { buildPocketShareUrl } from './PocketShareButton'; export { RedditIcon } from './RedditIcon'; export { RedditLegacyIcon } from './RedditLegacyIcon'; export { RedditShareButton } from './RedditShareButton'; @@ -94,10 +90,6 @@ export { WhatsappIcon } from './WhatsappIcon'; export { WhatsappLegacyIcon } from './WhatsappLegacyIcon'; export { WhatsappShareButton } from './WhatsappShareButton'; export { buildWhatsappShareUrl } from './WhatsappShareButton'; -export { WorkplaceIcon } from './WorkplaceIcon'; -export { WorkplaceLegacyIcon } from './WorkplaceLegacyIcon'; -export { WorkplaceShareButton } from './WorkplaceShareButton'; -export { buildWorkplaceShareUrl } from './WorkplaceShareButton'; export { XIcon } from './XIcon'; export { XLegacyIcon } from './XLegacyIcon'; export { openShareWindow } from './shareWindow'; diff --git a/test/NetworkShareButtons.test.tsx b/test/NetworkShareButtons.test.tsx index 480f5d19..71148a31 100644 --- a/test/NetworkShareButtons.test.tsx +++ b/test/NetworkShareButtons.test.tsx @@ -12,14 +12,12 @@ import { LinkedinShareButton } from '../src/LinkedinShareButton'; import { LivejournalShareButton } from '../src/LivejournalShareButton'; import { MailruShareButton } from '../src/MailruShareButton'; import { OKShareButton } from '../src/OKShareButton'; -import { PocketShareButton } from '../src/PocketShareButton'; import { TelegramShareButton } from '../src/TelegramShareButton'; import { ThreadsShareButton } from '../src/ThreadsShareButton'; import { TumblrShareButton } from '../src/TumblrShareButton'; import { ViberShareButton } from '../src/ViberShareButton'; import { VKShareButton } from '../src/VKShareButton'; import { WeiboShareButton } from '../src/WeiboShareButton'; -import { WorkplaceShareButton } from '../src/WorkplaceShareButton'; type ShareButtonCase = { children: string; @@ -130,15 +128,6 @@ const shareButtonCases: ShareButtonCase[] = [ url: 'https://example.com', }, }, - { - children: 'Share on Pocket', - Component: PocketShareButton, - expectedUrl: 'https://getpocket.com/save?url=https%3A%2F%2Fexample.com&title=Example%20title', - props: { - shareTitle: 'Example title', - url: 'https://example.com', - }, - }, { children: 'Share on Telegram', Component: TelegramShareButton, @@ -203,17 +192,6 @@ const shareButtonCases: ShareButtonCase[] = [ url: 'https://example.com', }, }, - { - children: 'Share on Workplace', - Component: WorkplaceShareButton, - expectedUrl: - 'https://work.facebook.com/sharer.php?u=https%3A%2F%2Fexample.com"e=Example%20quote&hashtag=%23reactshare', - props: { - hashtag: '#reactshare', - quote: 'Example quote', - url: 'https://example.com', - }, - }, ]; describe('network share buttons', () => { diff --git a/test/PackageBuilds.test.ts b/test/PackageBuilds.test.ts index 0804c91a..5147ade3 100644 --- a/test/PackageBuilds.test.ts +++ b/test/PackageBuilds.test.ts @@ -66,13 +66,15 @@ describe('package builds', () => { await expect(access(distPaths.typesCjs)).resolves.toBeUndefined(); }); - it('does not emit removed share count or Twitter alias exports in the built type declarations', async () => { + it('does not emit removed v6 exports in the built type declarations', async () => { const declarationBundle = await readFile(distPaths.types, 'utf8'); expect(declarationBundle).not.toContain('FacebookShareCount'); expect(declarationBundle).not.toContain('PinterestShareCount'); expect(declarationBundle).not.toContain('TwitterShareButton'); expect(declarationBundle).not.toContain('TwitterIcon'); + expect(declarationBundle).not.toContain('PocketShareButton'); + expect(declarationBundle).not.toContain('WorkplaceShareButton'); }); it('exports the same public surface from the ESM and CJS builds', async () => { diff --git a/test/PackageExports.test.tsx b/test/PackageExports.test.tsx index e33e8e21..8f0534ef 100644 --- a/test/PackageExports.test.tsx +++ b/test/PackageExports.test.tsx @@ -24,6 +24,20 @@ const representativeExportNames = [ 'shareNative', ] as const; +const removedExportNames = [ + 'TwitterShareButton', + 'TwitterIcon', + 'FacebookShareCount', + 'PocketShareButton', + 'PocketIcon', + 'PocketLegacyIcon', + 'buildPocketShareUrl', + 'WorkplaceShareButton', + 'WorkplaceIcon', + 'WorkplaceLegacyIcon', + 'buildWorkplaceShareUrl', +]; + describe('package exports', () => { it('exports representative public symbols', () => { for (const exportName of representativeExportNames) { @@ -31,6 +45,10 @@ describe('package exports', () => { } }); + it.each(removedExportNames)('does not export removed symbol %s', exportName => { + expect(ReactShare).not.toHaveProperty(exportName); + }); + it('exports icon components', () => { expect(iconExportNames.length).toBeGreaterThan(0); }); From 4aa9052c979298e61b6ad21b230db899022fdbdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20Nyga=CC=8Ard?= Date: Mon, 12 Oct 2026 09:45:03 +0300 Subject: [PATCH 19/36] fix(networks): use current X, Threads, and Telegram share endpoints X, Threads, and Telegram moved their canonical hosts to x.com, threads.com, and t.me. The old twitter.com, threads.net, and telegram.me URLs still work only through redirects, which add a hop and can drop the share intent if they are retired. Point the builders straight at the current endpoints. Requested in #536. Co-Authored-By: Claude Opus 5.5 --- .changeset/fresh-hosts-point.md | 7 +++++++ docs/native-sharing.md | 2 +- src/TelegramShareButton.tsx | 2 +- src/ThreadsShareButton.tsx | 2 +- src/XShareButton.tsx | 2 +- test/MobileShareButtons.test.tsx | 2 +- test/NetworkShareButtons.test.tsx | 4 ++-- test/ShareHelpers.test.ts | 2 +- test/XShareButton.test.tsx | 6 +++--- 9 files changed, 18 insertions(+), 11 deletions(-) create mode 100644 .changeset/fresh-hosts-point.md diff --git a/.changeset/fresh-hosts-point.md b/.changeset/fresh-hosts-point.md new file mode 100644 index 00000000..155eccda --- /dev/null +++ b/.changeset/fresh-hosts-point.md @@ -0,0 +1,7 @@ +--- +'react-share': patch +--- + +Use the current share endpoints for X (`https://x.com/intent/post`), Threads (`https://www.threads.com/intent/post`), and Telegram (`https://t.me/share/url`). + +The old `twitter.com`, `threads.net`, and `telegram.me` hosts only keep working through redirects, which add an extra hop and can drop the share intent if those redirects are retired. Requested in #536. diff --git a/docs/native-sharing.md b/docs/native-sharing.md index 1f7e56ae..9e9c65cd 100644 --- a/docs/native-sharing.md +++ b/docs/native-sharing.md @@ -71,7 +71,7 @@ This is the preferred path for: ### `TelegramShareButton` -- `mobileBehavior="web"`: uses `https://telegram.me/share/url?...` in a popup +- `mobileBehavior="web"`: uses `https://t.me/share/url?...` in a popup - `mobileBehavior="app"`: navigates to `tg://msg_url?...` - `mobileBehavior="auto"`: uses the app link on detected mobile environments and the popup URL elsewhere diff --git a/src/TelegramShareButton.tsx b/src/TelegramShareButton.tsx index 95e78e85..3a82c9d3 100644 --- a/src/TelegramShareButton.tsx +++ b/src/TelegramShareButton.tsx @@ -13,7 +13,7 @@ function telegramLink(url: string, { text }: { text?: string }) { assert(url, 'telegram.url'); return ( - 'https://telegram.me/share/url' + + 'https://t.me/share/url' + objectToGetParams({ url, text, diff --git a/src/ThreadsShareButton.tsx b/src/ThreadsShareButton.tsx index 2d243f35..24e26727 100644 --- a/src/ThreadsShareButton.tsx +++ b/src/ThreadsShareButton.tsx @@ -7,7 +7,7 @@ function threadsLink(url: string, { text }: { text?: string }) { assert(url, 'threads.url'); return ( - 'https://threads.net/intent/post' + + 'https://www.threads.com/intent/post' + objectToGetParams({ url, text, diff --git a/src/XShareButton.tsx b/src/XShareButton.tsx index 5b15999c..d44e5ca3 100644 --- a/src/XShareButton.tsx +++ b/src/XShareButton.tsx @@ -17,7 +17,7 @@ function xLink( assert(Array.isArray(related), 'x.related is not an array'); return ( - 'https://twitter.com/intent/tweet' + + 'https://x.com/intent/post' + objectToGetParams({ url, text, diff --git a/test/MobileShareButtons.test.tsx b/test/MobileShareButtons.test.tsx index bfc34471..fd610029 100644 --- a/test/MobileShareButtons.test.tsx +++ b/test/MobileShareButtons.test.tsx @@ -64,7 +64,7 @@ describe('mobile share buttons', () => { fireEvent.click(screen.getByRole('button', { name: 'Share on Telegram' })); expect(openSpy).toHaveBeenCalledWith( - 'https://telegram.me/share/url?url=https%3A%2F%2Fexample.com&text=Example', + 'https://t.me/share/url?url=https%3A%2F%2Fexample.com&text=Example', '', expect.any(String), ); diff --git a/test/NetworkShareButtons.test.tsx b/test/NetworkShareButtons.test.tsx index 71148a31..646feed9 100644 --- a/test/NetworkShareButtons.test.tsx +++ b/test/NetworkShareButtons.test.tsx @@ -131,7 +131,7 @@ const shareButtonCases: ShareButtonCase[] = [ { children: 'Share on Telegram', Component: TelegramShareButton, - expectedUrl: 'https://telegram.me/share/url?url=https%3A%2F%2Fexample.com&text=Example%20title', + expectedUrl: 'https://t.me/share/url?url=https%3A%2F%2Fexample.com&text=Example%20title', props: { text: 'Example title', url: 'https://example.com', @@ -141,7 +141,7 @@ const shareButtonCases: ShareButtonCase[] = [ children: 'Share on Threads', Component: ThreadsShareButton, expectedUrl: - 'https://threads.net/intent/post?url=https%3A%2F%2Fexample.com&text=Example%20title', + 'https://www.threads.com/intent/post?url=https%3A%2F%2Fexample.com&text=Example%20title', props: { text: 'Example title', url: 'https://example.com', diff --git a/test/ShareHelpers.test.ts b/test/ShareHelpers.test.ts index 9091e18c..54da51bb 100644 --- a/test/ShareHelpers.test.ts +++ b/test/ShareHelpers.test.ts @@ -23,7 +23,7 @@ describe('share helpers', () => { via: 'reactshare', }), ).toBe( - 'https://twitter.com/intent/tweet?url=https%3A%2F%2Fexample.com&text=Example%20title&via=reactshare&hashtags=react%2Cshare&related=openai', + 'https://x.com/intent/post?url=https%3A%2F%2Fexample.com&text=Example%20title&via=reactshare&hashtags=react%2Cshare&related=openai', ); }); diff --git a/test/XShareButton.test.tsx b/test/XShareButton.test.tsx index c71457d2..2cee7294 100644 --- a/test/XShareButton.test.tsx +++ b/test/XShareButton.test.tsx @@ -23,7 +23,7 @@ describe('XShareButton', () => { fireEvent.click(screen.getByRole('button', { name: 'Share on X' })); expect(openSpy).toHaveBeenCalledWith( - 'https://twitter.com/intent/tweet?url=https%3A%2F%2Fexample.com&text=Example%20title&via=reactshare&hashtags=react%2Cshare&related=openai%2Cvitejs', + 'https://x.com/intent/post?url=https%3A%2F%2Fexample.com&text=Example%20title&via=reactshare&hashtags=react%2Cshare&related=openai%2Cvitejs', '', expect.any(String), ); @@ -52,7 +52,7 @@ describe('XShareButton', () => { expect(button).not.toHaveAttribute('hashtags'); expect(button).not.toHaveAttribute('related'); expect(openSpy).toHaveBeenCalledWith( - 'https://twitter.com/intent/tweet?url=https%3A%2F%2Fexample.com&text=Share%20title&hashtags=react&related=openai', + 'https://x.com/intent/post?url=https%3A%2F%2Fexample.com&text=Share%20title&hashtags=react&related=openai', '', expect.any(String), ); @@ -70,7 +70,7 @@ describe('XShareButton', () => { fireEvent.click(screen.getByRole('button', { name: 'Share on X' })); expect(openSpy).toHaveBeenCalledWith( - 'https://twitter.com/intent/tweet?url=https%3A%2F%2Fexample.com&text=Example%20title', + 'https://x.com/intent/post?url=https%3A%2F%2Fexample.com&text=Example%20title', '', expect.any(String), ); From 5bb43a5efff41633900a9eeefd2eb4e874ab2aba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20Nyga=CC=8Ard?= Date: Mon, 12 Oct 2026 09:48:08 +0300 Subject: [PATCH 20/36] fix(share-buttons): open the popup before awaiting beforeOnClick When beforeOnClick returned a promise, the share popup was opened only after awaiting it. By then the click's user activation had expired, so Safari and other strict popup blockers silently blocked the share window (#525, #506, #529). Open a blank popup synchronously during the click and load the share URL into it once beforeOnClick resolves. A rejected promise now cancels the share and closes the popup instead of surfacing as an unhandled rejection. The synchronous path is unchanged. Co-Authored-By: Claude Opus 5.5 --- .changeset/early-popups-open.md | 7 ++++ README.md | 4 +-- docs/migrate-v5-to-v6.md | 4 +++ src/ShareButton.tsx | 38 +++++++++++++------- test/ShareButton.test.tsx | 61 +++++++++++++++++++++++++++++++++ 5 files changed, 100 insertions(+), 14 deletions(-) create mode 100644 .changeset/early-popups-open.md diff --git a/.changeset/early-popups-open.md b/.changeset/early-popups-open.md new file mode 100644 index 00000000..0adda01d --- /dev/null +++ b/.changeset/early-popups-open.md @@ -0,0 +1,7 @@ +--- +'react-share': major +--- + +Open the share popup synchronously when `beforeOnClick` returns a promise, then load the share URL into it once the promise resolves. If the promise rejects, sharing is cancelled and the popup is closed. + +Previously the popup was opened only after awaiting `beforeOnClick`. By then the click's user activation had expired, so Safari and other browsers with strict popup blockers silently blocked the share window (#525, #506, #529). Rejections from `beforeOnClick` also used to surface as unhandled promise rejections. diff --git a/README.md b/README.md index 905abf50..86931269 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ Required: - `children` (`ReactNode`): Button contents. - `url` (`string`): URL of the shared page. Optional on all share buttons: -- `beforeOnClick` (`() => Promise | void`): Runs before the share action. +- `beforeOnClick` (`() => Promise | void`): Runs before the share action. If it returns a promise, popup-based buttons open the popup immediately (so popup blockers allow it) and load the share URL once the promise resolves. Reject the promise to cancel sharing and close the popup. The share URL is built from the props at click time, so state updated inside `beforeOnClick` is not picked up. - `disabled` (`boolean`): Disables click handling and adds a `disabled` class. - `disabledStyle` (`CSSProperties`, default `{ opacity: 0.6 }`): Applied when `disabled` is true. - `htmlTitle` (`string`): Sets the native `title` attribute on the underlying button. @@ -631,7 +631,7 @@ Standalone icons are raw SVG elements. Mark decorative icons with `aria-hidden=" ## Troubleshooting -- Popup blockers: share buttons use `window.open` from the click handler. If your app does async work in `beforeOnClick`, some browsers may treat the eventual popup as script-triggered and block it. In those cases, set `openShareDialogOnClick={false}` and manage the resulting URL yourself. +- Popup blockers: share buttons call `window.open` synchronously from the click handler, even when `beforeOnClick` is async, so browsers treat the popup as user-initiated. `NativeShareButton` cannot pre-open the system share sheet, so long async work in its `beforeOnClick` may cause the browser to reject `navigator.share()`. - Accessibility: icon-only buttons should always have an accessible name. Use `aria-label` or `aria-labelledby` on the share button; `htmlTitle` is only a tooltip. - Mobile app handoff: some platforms open the browser, an in-app browser, or an extra tab before handing off to a native app. This behavior is controlled by the target platform and browser, not by `react-share`. - Mobile app strategies: `mobileBehavior="app"` and `mobileBehavior="auto"` are best-effort only. The library does not try to detect whether an app-open succeeded or guarantee a browser fallback path. diff --git a/docs/migrate-v5-to-v6.md b/docs/migrate-v5-to-v6.md index 0c1553aa..2f3a9530 100644 --- a/docs/migrate-v5-to-v6.md +++ b/docs/migrate-v5-to-v6.md @@ -171,6 +171,10 @@ Used by `TumblrShareButton`. - `related` - `via` +## Behavior changes + +- Popup-based buttons now open the share popup immediately when `beforeOnClick` returns a promise, and load the share URL once it resolves. Rejecting the promise cancels sharing and closes the popup. Previously the popup opened after the promise resolved and was often blocked by the browser. + ## New helper exports v6 adds low-level helpers from the package root: diff --git a/src/ShareButton.tsx b/src/ShareButton.tsx index 3e9ea11b..161c6926 100644 --- a/src/ShareButton.tsx +++ b/src/ShareButton.tsx @@ -36,6 +36,9 @@ export interface Props extends NativeButtonProps { /** * Takes a function that returns a Promise to be fulfilled before calling * `onClick`. If you do not return promise, `onClick` is called immediately. + * When a Promise is returned, the share popup opens immediately and loads the + * share URL once the Promise resolves. Rejecting the Promise cancels sharing + * and closes the popup. */ beforeOnClick?: () => Promise | void; children: React.ReactNode; @@ -123,21 +126,32 @@ export function ShareButton>({ event.preventDefault(); - if (beforeOnClick) { - const returnVal = beforeOnClick(); - - if (isPromise(returnVal)) { + const returnVal = beforeOnClick?.(); + const opensPopup = openShareDialogOnClick && linkBehavior === 'popup'; + const windowOptions = { + height: windowHeight, + onClose: onShareWindowClose, + position: windowPosition, + width: windowWidth, + }; + + if (isPromise(returnVal)) { + // Popup blockers only allow window.open during the click's user activation, + // which is gone after an await, so open a blank popup now and load the link later. + const shareWindow = opensPopup ? openShareWindow('about:blank', windowOptions) : null; + + try { await returnVal; + } catch { + shareWindow?.close(); + return; } - } - if (openShareDialogOnClick && linkBehavior === 'popup') { - openShareWindow(link, { - height: windowHeight, - onClose: onShareWindowClose, - position: windowPosition, - width: windowWidth, - }); + if (shareWindow && !shareWindow.closed) { + shareWindow.location.replace(link); + } + } else if (opensPopup) { + openShareWindow(link, windowOptions); } if (openShareDialogOnClick && linkBehavior === 'navigate') { diff --git a/test/ShareButton.test.tsx b/test/ShareButton.test.tsx index f3780532..b26b8e6d 100644 --- a/test/ShareButton.test.tsx +++ b/test/ShareButton.test.tsx @@ -68,6 +68,67 @@ describe('ShareButton', () => { expect(steps).toEqual(['before:start', 'before:end', 'click']); }); + it('opens the popup synchronously when beforeOnClick is async', async () => { + let resolveBeforeOnClick = () => {}; + const shareWindow = { close: vi.fn(), closed: false, location: { replace: vi.fn() } }; + const openSpy = vi.spyOn(window, 'open').mockReturnValue(shareWindow as unknown as Window); + const onClick = vi.fn(); + + render( + + new Promise(resolve => { + resolveBeforeOnClick = resolve; + }) + } + networkLink={(url: string) => `${url}/share`} + networkName="test" + onClick={onClick} + opts={{}} + url="https://example.com" + > + Share + , + ); + + fireEvent.click(screen.getByRole('button', { name: 'Share' })); + + // Popup blockers only allow window.open during the click's user activation. + expect(openSpy).toHaveBeenCalledWith('about:blank', '', expect.stringContaining('height=400')); + expect(shareWindow.location.replace).not.toHaveBeenCalled(); + + resolveBeforeOnClick(); + + await waitFor(() => expect(onClick).toHaveBeenCalledTimes(1)); + expect(openSpy).toHaveBeenCalledTimes(1); + expect(shareWindow.location.replace).toHaveBeenCalledWith('https://example.com/share'); + }); + + it('cancels sharing and closes the popup when async beforeOnClick rejects', async () => { + const shareWindow = { close: vi.fn(), closed: false, location: { replace: vi.fn() } }; + vi.spyOn(window, 'open').mockReturnValue(shareWindow as unknown as Window); + const onClick = vi.fn(); + + render( + Promise.reject(new Error('cancelled'))} + networkLink={(url: string) => url} + networkName="test" + onClick={onClick} + opts={{}} + url="https://example.com" + > + Share + , + ); + + fireEvent.click(screen.getByRole('button', { name: 'Share' })); + + await waitFor(() => expect(shareWindow.close).toHaveBeenCalledTimes(1)); + expect(shareWindow.location.replace).not.toHaveBeenCalled(); + expect(onClick).not.toHaveBeenCalled(); + }); + it('skips window.open when openShareDialogOnClick is false', () => { const openSpy = vi.spyOn(window, 'open').mockReturnValue(null); From 7996b59fbeccc6e6bcfd9bced3c6260a370dee2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20Nyga=CC=8Ard?= Date: Mon, 12 Oct 2026 09:48:26 +0300 Subject: [PATCH 21/36] feat(share-buttons): add onShareWindowOpen callback Apps had no way to tell whether the share popup actually opened, so a popup blocker made the share button silently do nothing (#525). Popup- based buttons now call onShareWindowOpen with the opened window, or null when the popup was blocked, so apps can offer a fallback. Co-Authored-By: Claude Opus 5.5 --- .changeset/bright-windows-wave.md | 5 ++++ README.md | 1 + src/ShareButton.tsx | 14 +++++++++- test/ShareButton.test.tsx | 44 +++++++++++++++++++++++++++++++ 4 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 .changeset/bright-windows-wave.md diff --git a/.changeset/bright-windows-wave.md b/.changeset/bright-windows-wave.md new file mode 100644 index 00000000..fbf62b74 --- /dev/null +++ b/.changeset/bright-windows-wave.md @@ -0,0 +1,5 @@ +--- +'react-share': minor +--- + +Add an `onShareWindowOpen` callback to popup-based share buttons. It receives the opened share window, or `null` when a popup blocker prevented it from opening, so apps can detect blocked popups and offer a fallback, as requested in #525. diff --git a/README.md b/README.md index 86931269..9f4aa3c8 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,7 @@ Required: - `htmlTitle` (`string`): Sets the native `title` attribute on the underlying button. - `onClick` (`(event, link) => void`): Called with the click event and generated share URL after link generation. Not exposed by `EmailShareButton`. - `onShareWindowClose` (`() => void`): Called after the popup closes on popup-based buttons. +- `onShareWindowOpen` (`(shareWindow: Window | null) => void`): Called after the popup opens on popup-based buttons. Receives `null` when a popup blocker prevented the window from opening, so you can fall back to another sharing flow. - `resetButtonStyle` (`boolean`, default `true`): Resets native button styles unless disabled. - `windowHeight`, `windowWidth` (`number`): Override popup dimensions on popup-based buttons. - `windowPosition` (`"windowCenter" | "screenCenter"`): Controls popup positioning on popup-based buttons. diff --git a/src/ShareButton.tsx b/src/ShareButton.tsx index 161c6926..db7d4843 100644 --- a/src/ShareButton.tsx +++ b/src/ShareButton.tsx @@ -65,6 +65,11 @@ export interface Props extends NativeButtonProps { * Takes a function to be called after closing share dialog. */ onShareWindowClose?: () => void; + /** + * Takes a function to be called with the opened share window, or `null` + * when a popup blocker prevented it from opening. + */ + onShareWindowOpen?: (shareWindow: Window | null) => void; openShareDialogOnClick?: boolean; opts: LinkOptions; resetButtonStyle?: boolean; @@ -98,6 +103,7 @@ export function ShareButton>({ networkName, // deconstructed from ...rest to prevent passing it to the button element onClick, onShareWindowClose, + onShareWindowOpen, openShareDialogOnClick = true, opts, resetButtonStyle = true, @@ -150,8 +156,14 @@ export function ShareButton>({ if (shareWindow && !shareWindow.closed) { shareWindow.location.replace(link); } + + if (opensPopup) { + onShareWindowOpen?.(shareWindow); + } } else if (opensPopup) { - openShareWindow(link, windowOptions); + const shareWindow = openShareWindow(link, windowOptions); + + onShareWindowOpen?.(shareWindow); } if (openShareDialogOnClick && linkBehavior === 'navigate') { diff --git a/test/ShareButton.test.tsx b/test/ShareButton.test.tsx index b26b8e6d..9feebe3d 100644 --- a/test/ShareButton.test.tsx +++ b/test/ShareButton.test.tsx @@ -129,6 +129,50 @@ describe('ShareButton', () => { expect(onClick).not.toHaveBeenCalled(); }); + it('passes the opened share window to onShareWindowOpen', () => { + const shareWindow = { closed: false }; + vi.spyOn(window, 'open').mockReturnValue(shareWindow as unknown as Window); + const onShareWindowOpen = vi.fn(); + + render( + url} + networkName="test" + onShareWindowOpen={onShareWindowOpen} + opts={{}} + url="https://example.com" + > + Share + , + ); + + fireEvent.click(screen.getByRole('button', { name: 'Share' })); + + expect(onShareWindowOpen).toHaveBeenCalledWith(shareWindow); + }); + + it('passes null to onShareWindowOpen when the popup is blocked', async () => { + vi.spyOn(window, 'open').mockReturnValue(null); + const onShareWindowOpen = vi.fn(); + + render( + Promise.resolve()} + networkLink={(url: string) => url} + networkName="test" + onShareWindowOpen={onShareWindowOpen} + opts={{}} + url="https://example.com" + > + Share + , + ); + + fireEvent.click(screen.getByRole('button', { name: 'Share' })); + + await waitFor(() => expect(onShareWindowOpen).toHaveBeenCalledWith(null)); + }); + it('skips window.open when openShareDialogOnClick is false', () => { const openSpy = vi.spyOn(window, 'open').mockReturnValue(null); From 5a481f2ff6ceffe1d6d0654530b997be6e8a2c9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20Nyga=CC=8Ard?= Date: Mon, 12 Oct 2026 09:49:21 +0300 Subject: [PATCH 22/36] fix(helpers): default separators in share URL builders The Bluesky, Email, Viber, and WhatsApp builders relied on their button components to supply the default " " separator. Calling the newly exported build*ShareUrl helpers directly without a separator produced URLs such as "Helloundefinedhttps://...". Default the separator in the builders themselves. The builders are unreleased v6 API, so the existing helpers changeset covers this. Co-Authored-By: Claude Opus 5.5 --- src/BlueskyShareButton.tsx | 5 ++++- src/EmailShareButton.tsx | 2 +- src/ViberShareButton.tsx | 5 ++++- src/WhatsappShareButton.tsx | 4 ++-- test/ShareHelpers.test.ts | 29 +++++++++++++++++++++++++++++ 5 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/BlueskyShareButton.tsx b/src/BlueskyShareButton.tsx index 7f4e77ce..ee9c4843 100644 --- a/src/BlueskyShareButton.tsx +++ b/src/BlueskyShareButton.tsx @@ -11,7 +11,10 @@ type BlueskyShareButtonProps = Omit< text?: string; }; -function blueskyLink(url: string, { text, separator }: { text?: string; separator?: string }) { +function blueskyLink( + url: string, + { text, separator = ' ' }: { text?: string; separator?: string }, +) { assert(url, 'bluesky.url'); return ( diff --git a/src/EmailShareButton.tsx b/src/EmailShareButton.tsx index a0597596..1688976d 100644 --- a/src/EmailShareButton.tsx +++ b/src/EmailShareButton.tsx @@ -11,7 +11,7 @@ type Options = { type EmailShareButtonProps = Omit, 'onClick' | 'openShareDialogOnClick'> & Options; -function emailLink(url: string, { subject, body, separator }: Options) { +function emailLink(url: string, { subject, body, separator = ' ' }: Options) { return 'mailto:' + objectToGetParams({ subject, body: body ? body + separator + url : url }); } diff --git a/src/ViberShareButton.tsx b/src/ViberShareButton.tsx index cb00d689..85ac6695 100644 --- a/src/ViberShareButton.tsx +++ b/src/ViberShareButton.tsx @@ -13,7 +13,10 @@ type ViberShareButtonProps = Omit< separator?: string; }; -function viberLink(url: string, { message, separator }: { message?: string; separator?: string }) { +function viberLink( + url: string, + { message, separator = ' ' }: { message?: string; separator?: string }, +) { assert(url, 'viber.url'); return ( 'viber://forward' + diff --git a/src/WhatsappShareButton.tsx b/src/WhatsappShareButton.tsx index 780e59cc..04d961c1 100644 --- a/src/WhatsappShareButton.tsx +++ b/src/WhatsappShareButton.tsx @@ -15,7 +15,7 @@ type WhatsappShareButtonProps = Omit< function whatsappLink( url: string, - { message, separator }: { message?: string; separator?: string }, + { message, separator = ' ' }: { message?: string; separator?: string }, ) { assert(url, 'whatsapp.url'); return ( @@ -30,7 +30,7 @@ export const buildWhatsappShareUrl = whatsappLink; function whatsappAppLink( url: string, - { message, separator }: { message?: string; separator?: string }, + { message, separator = ' ' }: { message?: string; separator?: string }, ) { assert(url, 'whatsapp.url'); return ( diff --git a/test/ShareHelpers.test.ts b/test/ShareHelpers.test.ts index 54da51bb..eaaecab0 100644 --- a/test/ShareHelpers.test.ts +++ b/test/ShareHelpers.test.ts @@ -1,8 +1,12 @@ import { describe, expect, it, vi } from 'vitest'; import { + buildBlueskyShareUrl, + buildEmailShareUrl, buildFacebookShareUrl, buildLinkedinShareUrl, + buildViberShareUrl, + buildWhatsappShareUrl, buildXShareUrl, openShareWindow, } from '../src'; @@ -33,6 +37,31 @@ describe('share helpers', () => { ); }); + it.each([ + [ + 'Bluesky', + () => buildBlueskyShareUrl('https://example.com', { text: 'Hello' }), + 'https://bsky.app/intent/compose?text=Hello%20https%3A%2F%2Fexample.com', + ], + [ + 'Email', + () => buildEmailShareUrl('https://example.com', { body: 'Hello' }), + 'mailto:?body=Hello%20https%3A%2F%2Fexample.com', + ], + [ + 'Viber', + () => buildViberShareUrl('https://example.com', { message: 'Hello' }), + 'viber://forward?text=Hello%20https%3A%2F%2Fexample.com', + ], + [ + 'WhatsApp', + () => buildWhatsappShareUrl('https://example.com', { message: 'Hello' }), + 'https://api.whatsapp.com/send?text=Hello%20https%3A%2F%2Fexample.com', + ], + ])('defaults the separator when building a %s share URL directly', (_, build, expected) => { + expect(build()).toBe(expected); + }); + it('opens a centered share window with the requested size', () => { const openSpy = vi.spyOn(window, 'open').mockReturnValue(null); From a811bc3852ed39850e586ea9adbaefb2c3f9f2ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20Nyga=CC=8Ard?= Date: Mon, 12 Oct 2026 09:50:04 +0300 Subject: [PATCH 23/36] fix(email): put the shared URL on its own line by default EmailShareButton joined body and URL with a space, so the link ran into the body text in the composed email (#582). Default the separator to a newline in both the button and buildEmailShareUrl. This changes the generated mailto: body, so it ships in the v6 major; pass separator=" " to keep the old formatting. The blank lines above the body shown in #582 come from the mail client's composer, not from the mailto: URL. Co-Authored-By: Claude Opus 5.5 --- .changeset/calm-letters-break.md | 7 +++++++ README.md | 2 +- docs/migrate-v5-to-v6.md | 1 + src/EmailShareButton.tsx | 4 ++-- test/EmailShareButton.test.tsx | 2 +- test/ShareHelpers.test.ts | 2 +- 6 files changed, 13 insertions(+), 5 deletions(-) create mode 100644 .changeset/calm-letters-break.md diff --git a/.changeset/calm-letters-break.md b/.changeset/calm-letters-break.md new file mode 100644 index 00000000..7544c403 --- /dev/null +++ b/.changeset/calm-letters-break.md @@ -0,0 +1,7 @@ +--- +'react-share': major +--- + +Change the default `EmailShareButton` `separator` from `" "` to `"\n"` so the shared URL appears on its own line below the email body. The new `buildEmailShareUrl` helper uses the same default. + +Reported in #582, where the URL was run together with the body text. Pass `separator=" "` to keep the previous formatting. diff --git a/README.md b/README.md index 9f4aa3c8..98dc73e4 100644 --- a/README.md +++ b/README.md @@ -185,7 +185,7 @@ Optional props: - `subject` (`string`): Email subject line. - `body` (`string`): Text prepended before the shared URL. -- `separator` (`string`, default `" "`): Separator between the body and URL. +- `separator` (`string`, default `"\n"`): Separator between the body and URL. The default puts the URL on its own line. - Notes: - Uses `mailto:` navigation instead of opening a popup. - Does not expose `openShareDialogOnClick` or `onClick`. diff --git a/docs/migrate-v5-to-v6.md b/docs/migrate-v5-to-v6.md index 2f3a9530..6dc17fc5 100644 --- a/docs/migrate-v5-to-v6.md +++ b/docs/migrate-v5-to-v6.md @@ -174,6 +174,7 @@ Used by `TumblrShareButton`. ## Behavior changes - Popup-based buttons now open the share popup immediately when `beforeOnClick` returns a promise, and load the share URL once it resolves. Rejecting the promise cancels sharing and closes the popup. Previously the popup opened after the promise resolved and was often blocked by the browser. +- `EmailShareButton` and `buildEmailShareUrl` now separate `body` and the URL with a newline (`"\n"`) instead of a space, so the URL appears on its own line. Pass `separator=" "` to keep the v5 formatting. ## New helper exports diff --git a/src/EmailShareButton.tsx b/src/EmailShareButton.tsx index 1688976d..a3cf3c59 100644 --- a/src/EmailShareButton.tsx +++ b/src/EmailShareButton.tsx @@ -11,7 +11,7 @@ type Options = { type EmailShareButtonProps = Omit, 'onClick' | 'openShareDialogOnClick'> & Options; -function emailLink(url: string, { subject, body, separator = ' ' }: Options) { +function emailLink(url: string, { subject, body, separator = '\n' }: Options) { return 'mailto:' + objectToGetParams({ subject, body: body ? body + separator + url : url }); } @@ -31,7 +31,7 @@ export const EmailShareButton = forwardRef ), diff --git a/test/EmailShareButton.test.tsx b/test/EmailShareButton.test.tsx index c7615982..08d89d61 100644 --- a/test/EmailShareButton.test.tsx +++ b/test/EmailShareButton.test.tsx @@ -30,7 +30,7 @@ describe('EmailShareButton', () => { fireEvent.click(screen.getByRole('button', { name: 'Share by email' })); expect(openSpy).not.toHaveBeenCalled(); - expect(hrefState.value).toBe('mailto:?subject=Subject&body=Body%20https%3A%2F%2Fexample.com'); + expect(hrefState.value).toBe('mailto:?subject=Subject&body=Body%0Ahttps%3A%2F%2Fexample.com'); }); it('uses the shared url as the body when no body is provided', () => { diff --git a/test/ShareHelpers.test.ts b/test/ShareHelpers.test.ts index eaaecab0..c652db86 100644 --- a/test/ShareHelpers.test.ts +++ b/test/ShareHelpers.test.ts @@ -46,7 +46,7 @@ describe('share helpers', () => { [ 'Email', () => buildEmailShareUrl('https://example.com', { body: 'Hello' }), - 'mailto:?body=Hello%20https%3A%2F%2Fexample.com', + 'mailto:?body=Hello%0Ahttps%3A%2F%2Fexample.com', ], [ 'Viber', From 98bb14070a0a66fa8f679ef2aa86d9c53451b9ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20Nyga=CC=8Ard?= Date: Mon, 12 Oct 2026 09:50:39 +0300 Subject: [PATCH 24/36] feat(build): mark the bundle as a React Server Components client module Share buttons attach click handlers, so importing them from a Server Component (for example a Next.js App Router page) failed unless the app added its own "use client" wrapper (#491). Emit a "use client" banner on both the ESM and CJS bundles so frameworks treat the package as a client module, and assert it in the dist tests. Co-Authored-By: Claude Opus 5.5 --- .changeset/clear-servers-meet.md | 5 +++++ README.md | 1 + test/PackageBuilds.test.ts | 9 +++++++++ vite.config.js | 5 +++++ 4 files changed, 20 insertions(+) create mode 100644 .changeset/clear-servers-meet.md diff --git a/.changeset/clear-servers-meet.md b/.changeset/clear-servers-meet.md new file mode 100644 index 00000000..622da8a3 --- /dev/null +++ b/.changeset/clear-servers-meet.md @@ -0,0 +1,5 @@ +--- +'react-share': minor +--- + +Mark the published ESM and CJS bundles with a `"use client"` directive so share buttons can be imported directly from React Server Components, such as Next.js App Router pages, without a local client wrapper. Requested in #491. diff --git a/README.md b/README.md index 98dc73e4..8d78b5ce 100644 --- a/README.md +++ b/README.md @@ -636,6 +636,7 @@ Standalone icons are raw SVG elements. Mark decorative icons with `aria-hidden=" - Accessibility: icon-only buttons should always have an accessible name. Use `aria-label` or `aria-labelledby` on the share button; `htmlTitle` is only a tooltip. - Mobile app handoff: some platforms open the browser, an in-app browser, or an extra tab before handing off to a native app. This behavior is controlled by the target platform and browser, not by `react-share`. - Mobile app strategies: `mobileBehavior="app"` and `mobileBehavior="auto"` are best-effort only. The library does not try to detect whether an app-open succeeded or guarantee a browser fallback path. +- React Server Components: the package is marked with `"use client"`, so share buttons and icons can be imported directly from Server Components in frameworks such as Next.js. They render as client components. - iOS PWAs: standalone mode can show a blank intermediary page for schemes such as `mailto:` or native-app handoff URLs. This is a platform limitation. ### About semantic versioning diff --git a/test/PackageBuilds.test.ts b/test/PackageBuilds.test.ts index 5147ade3..b7042ce0 100644 --- a/test/PackageBuilds.test.ts +++ b/test/PackageBuilds.test.ts @@ -66,6 +66,15 @@ describe('package builds', () => { await expect(access(distPaths.typesCjs)).resolves.toBeUndefined(); }); + it.each([ + ['ESM', distPaths.esm], + ['CJS', distPaths.cjs], + ])('marks the %s build as a client module for React Server Components', async (_, distPath) => { + const bundle = await readFile(distPath, 'utf8'); + + expect(bundle.startsWith('"use client";')).toBe(true); + }); + it('does not emit removed v6 exports in the built type declarations', async () => { const declarationBundle = await readFile(distPaths.types, 'utf8'); diff --git a/vite.config.js b/vite.config.js index 8d3df208..43c2d7f7 100644 --- a/vite.config.js +++ b/vite.config.js @@ -35,6 +35,11 @@ export default defineConfig({ minify: false, rollupOptions: { external: externalDeps, + output: { + // Share buttons attach click handlers, so React Server Components must + // treat the package as a client module. + banner: '"use client";', + }, }, }, }); From 59bbdda3d7396d249c69605f7d5b2c2d18881fc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20Nyga=CC=8Ard?= Date: Mon, 12 Oct 2026 11:11:46 +0300 Subject: [PATCH 25/36] docs(migration): correct and complete the v5 to v6 guide The v5 to v6 guide predated onShareWindowOpen, the "use client" banner, and the share endpoint updates, and its checklist did not mention the new beforeOnClick rejection and email separator behavior. Add an "Other improvements" section and the missing checklist steps. Fix two facts carried over from intermediate v6 states: v5's LinkedinShareButton took `title` (not `shareTitle`), and NativeShareIcon is new in v6, so it is not a v5 rename. Split the checklist into required, behavior, and optional steps, merge the two icon steps into one either/or step, and warn that plain JavaScript silently ignores the old `title` prop. Also point v5 users at the guide right under the install instructions, since the Guides section is easy to miss further down the README. Co-Authored-By: Claude Opus 5.5 --- README.md | 2 ++ docs/migrate-v5-to-v6.md | 37 +++++++++++++++++++++++++++---------- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 8d78b5ce..004fa6ee 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ npm install react-share ``` +Upgrading from v5? Follow the [v5 to v6 migration guide](./docs/migrate-v5-to-v6.md). + ## Features - no external script loading, i.e. no dependencies on SDKs diff --git a/docs/migrate-v5-to-v6.md b/docs/migrate-v5-to-v6.md index 6dc17fc5..af9c4813 100644 --- a/docs/migrate-v5-to-v6.md +++ b/docs/migrate-v5-to-v6.md @@ -101,7 +101,6 @@ Used by: - `HatenaShareButton` - `InstapaperShareButton` - `LivejournalShareButton` -- `LinkedinShareButton` in v5 only - `MailruShareButton` - `OKShareButton` - `RedditShareButton` @@ -140,7 +139,7 @@ Used by `TumblrShareButton`. `LinkedinShareButton` no longer accepts: -- `shareTitle` +- `title` - `summary` - `source` @@ -150,7 +149,7 @@ Used by `TumblrShareButton`. ```jsx `FacebookLegacyIcon` - `XIcon` -> `XLegacyIcon` -- `NativeShareIcon` -> `NativeShareLegacyIcon` +- `WhatsappIcon` -> `WhatsappLegacyIcon` Example: @@ -267,16 +266,34 @@ Default behavior is still `mobileBehavior="web"`. Use `mobileBehavior="app"` and `mobileBehavior="auto"` only when best-effort app handoff is acceptable. The library does not try to detect whether the native app actually opened. +## Other improvements + +These need no code changes, but are worth knowing about: + +- `onShareWindowOpen` is a new callback on popup-based buttons. It receives the opened share window, or `null` when a popup blocker prevented it from opening, so you can offer a fallback. +- The package is marked with `"use client"`, so share buttons can be imported directly from React Server Components, such as Next.js App Router pages. You can remove any local `"use client"` wrapper you added only for `react-share`. +- X, Threads, and Telegram buttons now use the current `x.com`, `threads.com`, and `t.me` share endpoints instead of relying on redirects from the old hosts. + ## Upgrade checklist +Required: + 1. Upgrade your app to React 18 or 19. 2. Replace `TwitterShareButton` and `TwitterIcon` with `XShareButton` and `XIcon`. 3. Remove all share count usage. 4. Remove `PocketShareButton` and `WorkplaceShareButton` usage. -5. Rename every old share-button `title` prop to the correct v6 payload prop. +5. Rename every share-button `title` prop to the network's v6 payload prop (`text`, `message`, `shareTitle`, or `postTitle`). TypeScript reports the old prop, but plain JavaScript silently ignores it, so search your code for `title=` on share buttons. Keep using `htmlTitle` for the native tooltip. 6. Remove `hashtags`, `related`, and `via` from `ThreadsShareButton`. -7. Remove `shareTitle`, `summary`, and `source` from `LinkedinShareButton`. -8. Rename any old badge-style icon usage from `*Icon` to `*LegacyIcon`. -9. Remove `round`, `borderRadius`, `bgStyle`, and `iconFillColor` from raw `*Icon` usage. -10. Decide whether any messaging buttons should opt into `mobileBehavior`. -11. For mobile/PWA or file-sharing flows, prefer `NativeShareButton` or `shareNative(...)`. +7. Remove `title`, `summary`, and `source` from `LinkedinShareButton`. +8. For each `*Icon`, either rename it to `*LegacyIcon` to keep the v5 badge look and props, or keep `*Icon` and remove `round`, `borderRadius`, `bgStyle`, and `iconFillColor`. + +Check behavior: + +9. If `beforeOnClick` returns a promise, the popup now opens immediately, and rejecting the promise cancels the share and closes the popup. +10. `EmailShareButton` now puts the URL on its own line. Pass `separator=" "` to keep the v5 formatting. + +Optional: + +11. Opt messaging buttons into `mobileBehavior` where app handoff matters. +12. For mobile/PWA or file-sharing flows, use `NativeShareButton` or `shareNative(...)`. +13. Use `onShareWindowOpen` to detect blocked popups, and remove any local `"use client"` wrappers you added only for `react-share`. From 0d1255c71728f398f2f2f802aad9fb0bb8e31427 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20Nyga=CC=8Ard?= Date: Mon, 12 Oct 2026 11:34:22 +0300 Subject: [PATCH 26/36] claude launch --- .claude/launch.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .claude/launch.json diff --git a/.claude/launch.json b/.claude/launch.json new file mode 100644 index 00000000..1fb54810 --- /dev/null +++ b/.claude/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.0.1", + "configurations": [ + { + "name": "demo", + "runtimeExecutable": "npx", + "runtimeArgs": ["vite", "--config", "vite.demo.config.js", "--port", "5199", "--strictPort"], + "port": 5199 + } + ] +} From 71e21b3559ce47002be71be32e28dcab64f7db44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20Nyga=CC=8Ard?= Date: Mon, 12 Oct 2026 11:54:25 +0300 Subject: [PATCH 27/36] fix(icons): draw LinkedIn and Gab raw icons at full size LinkedinIcon and GabIcon still reused the 64x64 glyphs from the badge icons without their backgrounds, so they only filled 40-55% of the icon box. Next to the full-bleed Simple Icons marks they looked tiny and inconsistent. LinkedIn now uses its full rounded-square "in" mark in the current #0A66C2 brand blue. Gab knocks the wordmark out of a rounded green square, matching the VK and Hatena marks. Both are drawn on a 24x24 viewBox like the other raw icons. Co-Authored-By: Claude Opus 5.5 --- .changeset/full-marks-grow.md | 5 +++++ src/GabIcon.tsx | 7 +++++-- src/LinkedinIcon.tsx | 4 ++-- test/IconFamilies.test.tsx | 5 +++-- 4 files changed, 15 insertions(+), 6 deletions(-) create mode 100644 .changeset/full-marks-grow.md diff --git a/.changeset/full-marks-grow.md b/.changeset/full-marks-grow.md new file mode 100644 index 00000000..ccf5d0fc --- /dev/null +++ b/.changeset/full-marks-grow.md @@ -0,0 +1,5 @@ +--- +'react-share': patch +--- + +Draw the raw `LinkedinIcon` and `GabIcon` at full size. They reused the small glyphs from the badge icons without their backgrounds, so they filled less than half of the icon box and looked tiny next to the other raw icons. `LinkedinIcon` now uses the full rounded-square "in" mark in LinkedIn's current `#0A66C2` blue, and `GabIcon` puts the wordmark on a rounded green square. diff --git a/src/GabIcon.tsx b/src/GabIcon.tsx index 2f87544f..5750c356 100644 --- a/src/GabIcon.tsx +++ b/src/GabIcon.tsx @@ -2,8 +2,11 @@ import { RawIconBase, type RawIconProps } from './icons/RawIconBase'; export function GabIcon(props: RawIconProps) { return ( - - + + ); } diff --git a/src/LinkedinIcon.tsx b/src/LinkedinIcon.tsx index c16e10a5..7515d113 100644 --- a/src/LinkedinIcon.tsx +++ b/src/LinkedinIcon.tsx @@ -2,8 +2,8 @@ import { RawIconBase, type RawIconProps } from './icons/RawIconBase'; export function LinkedinIcon(props: RawIconProps) { return ( - - + + ); } diff --git a/test/IconFamilies.test.tsx b/test/IconFamilies.test.tsx index b02ded63..0f41d247 100644 --- a/test/IconFamilies.test.tsx +++ b/test/IconFamilies.test.tsx @@ -22,14 +22,15 @@ describe('raw icon family', () => { expect(svg.querySelector('circle')).toBeNull(); }); - it('keeps repo-local raw icons background-free', () => { + it('draws LinkedIn as a single-color full-size mark', () => { render(); const svg = screen.getByTestId('linkedin-icon'); expect(svg).toHaveAttribute('width', '32'); expect(svg).toHaveAttribute('height', '32'); - expect(svg).toHaveAttribute('fill', '#0077B5'); + expect(svg).toHaveAttribute('fill', '#0A66C2'); + expect(svg).toHaveAttribute('viewBox', '0 0 24 24'); expect(svg.querySelector('rect')).toBeNull(); expect(svg.querySelector('circle')).toBeNull(); }); From 17a51d2cd6ce3653a38d8b995cbc220a92b5e45f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20Nyga=CC=8Ard?= Date: Mon, 12 Oct 2026 11:33:21 +0300 Subject: [PATCH 28/36] feat(icons): use Lucide outline glyphs for email and native share The raw EmailIcon and NativeShareIcon reused small badge glyphs on a 64x64 viewBox and looked undersized next to the other raw icons. Swap them for the Lucide "mail" and "share" icons, which fill the 24x24 box like the brand marks do. Lucide icons are stroke-based, so RawIconBase gains an internal `outlined` mode that renders fill="none" with a round 2px stroke. The `color` (or `fill`) prop drives the stroke in that mode, so every raw icon can be recolored the same way. The README now documents `color` and credits Lucide. Co-Authored-By: Claude Opus 5.5 --- .changeset/plain-glyphs-swap.md | 5 +++++ README.md | 5 +++-- src/EmailIcon.tsx | 6 +++-- src/NativeShareIcon.tsx | 7 ++++-- src/icons/RawIconBase.tsx | 15 ++++++++++++- test/IconFamilies.test.tsx | 39 ++++++++++++++++++++++++++++++++- 6 files changed, 69 insertions(+), 8 deletions(-) create mode 100644 .changeset/plain-glyphs-swap.md diff --git a/.changeset/plain-glyphs-swap.md b/.changeset/plain-glyphs-swap.md new file mode 100644 index 00000000..89804d68 --- /dev/null +++ b/.changeset/plain-glyphs-swap.md @@ -0,0 +1,5 @@ +--- +'react-share': minor +--- + +Use the Lucide mail and share glyphs for the raw `EmailIcon` and `NativeShareIcon`. The previous glyphs were small badge glyphs on a 64x64 viewBox and looked undersized next to the other raw icons. diff --git a/README.md b/README.md index 004fa6ee..1113e4ba 100644 --- a/README.md +++ b/README.md @@ -601,6 +601,7 @@ Raw icons use a smaller, simpler prop surface: | Prop | Type | Default | Description | | ------------- | ------------------------- | ------- | ---------------------------------------------------------------------- | | `size` | `number \| string` | `24` | Icon size. | +| `color` | `string` | brand | Icon color. Applied as the fill, or as the stroke on outline icons. | | `...svgProps` | `SVGProps` | - | Standard SVG attributes such as `className`, `aria-hidden`, `aria-label`, or `role`. | Example: @@ -611,7 +612,7 @@ import { XIcon } from "react-share";