diff --git a/packages/docusaurus-theme-classic/package.json b/packages/docusaurus-theme-classic/package.json index 63fcff4e0a3d..afde057843f5 100644 --- a/packages/docusaurus-theme-classic/package.json +++ b/packages/docusaurus-theme-classic/package.json @@ -35,6 +35,7 @@ "@docusaurus/utils-validation": "3.9.2", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", + "copy-text-to-clipboard": "3.2.2", "infima": "0.2.0-alpha.45", "lodash": "^4.17.21", "nprogress": "^0.2.0", diff --git a/packages/docusaurus-theme-classic/src/theme/CodeBlock/Buttons/CopyButton/index.tsx b/packages/docusaurus-theme-classic/src/theme/CodeBlock/Buttons/CopyButton/index.tsx index a09400029890..e94f14acd3e8 100644 --- a/packages/docusaurus-theme-classic/src/theme/CodeBlock/Buttons/CopyButton/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/CodeBlock/Buttons/CopyButton/index.tsx @@ -44,6 +44,22 @@ function ariaLabel(isCopied: boolean) { }); } +async function copyCodeToClipboard(code: string): Promise { + if (typeof navigator.clipboard?.writeText === 'function') { + try { + await navigator.clipboard.writeText(code); + return true; + } catch {} + } + + try { + const copy = (await import('copy-text-to-clipboard')).default; + return copy(code); + } catch { + return false; + } +} + function useCopyButton() { const { metadata: {code}, @@ -52,7 +68,11 @@ function useCopyButton() { const copyTimeout = useRef(undefined); const copyCode = useCallback(() => { - navigator.clipboard.writeText(code).then(() => { + void copyCodeToClipboard(code).then((didCopy) => { + if (!didCopy) { + return; + } + setIsCopied(true); copyTimeout.current = window.setTimeout(() => { setIsCopied(false); diff --git a/packages/docusaurus/src/webpack/base.ts b/packages/docusaurus/src/webpack/base.ts index 6cf165db4f47..4ec1be2c0184 100644 --- a/packages/docusaurus/src/webpack/base.ts +++ b/packages/docusaurus/src/webpack/base.ts @@ -27,6 +27,14 @@ const CSS_REGEX = /\.css$/i; const CSS_MODULE_REGEX = /\.module\.css$/i; export const clientDir = path.join(__dirname, '..', 'client'); +const LibrariesToTranspile = [ + 'copy-text-to-clipboard', // Contains optional catch binding, incompatible with recent versions of Edge +]; + +const LibrariesToTranspileRegex = new RegExp( + LibrariesToTranspile.map((libName) => `(node_modules/${libName})`).join('|'), +); + function getReactAliases(siteDir: string): Record { // Escape hatch if (process.env.DOCUSAURUS_NO_REACT_ALIASES) { @@ -49,7 +57,8 @@ export function excludeJS(modulePath: string): boolean { // Don't transpile node_modules except any docusaurus npm package return ( modulePath.includes('node_modules') && - !/docusaurus(?:(?!node_modules).)*\.jsx?$/.test(modulePath) + !/docusaurus(?:(?!node_modules).)*\.jsx?$/.test(modulePath) && + !LibrariesToTranspileRegex.test(modulePath) ); } diff --git a/yarn.lock b/yarn.lock index 7f9b9a8df2e8..f5810d93ae3f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6907,6 +6907,11 @@ cookie@~0.4.1: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== +copy-text-to-clipboard@3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/copy-text-to-clipboard/-/copy-text-to-clipboard-3.2.2.tgz#99bc79db3f2d355ec33a08d573aff6804491ddb9" + integrity sha512-T6SqyLd1iLuqPA90J5N4cTalrtovCySh58iiZDGJ6FGznbclKh4UI+FGacQSgFzwKG77W7XT5gwbVEbd9cIH1A== + copy-webpack-plugin@^11.0.0: version "11.0.0" resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz#96d4dbdb5f73d02dd72d0528d1958721ab72e04a"