From 63cbce7d78fbcde2de9eb2fdab25c7ea332e44b5 Mon Sep 17 00:00:00 2001 From: 777genius Date: Mon, 25 May 2026 22:32:44 +0300 Subject: [PATCH] fix(build): verify radix dismissable layer patch --- package.json | 3 +- ...x-ui__react-dismissable-layer@1.1.11.patch | 70 +++++++++++++++++++ scripts/ci/verify-radix-presence-patch.mjs | 45 +++++++++--- 3 files changed, 107 insertions(+), 11 deletions(-) create mode 100644 patches/@radix-ui__react-dismissable-layer@1.1.11.patch diff --git a/package.json b/package.json index c2ca558d..401273f4 100644 --- a/package.json +++ b/package.json @@ -439,7 +439,8 @@ ], "patchedDependencies": { "@radix-ui/react-presence@1.1.5": "patches/@radix-ui__react-presence@1.1.5.patch", - "@radix-ui/react-focus-scope@1.1.7": "patches/@radix-ui__react-focus-scope@1.1.7.patch" + "@radix-ui/react-focus-scope@1.1.7": "patches/@radix-ui__react-focus-scope@1.1.7.patch", + "@radix-ui/react-dismissable-layer@1.1.11": "patches/@radix-ui__react-dismissable-layer@1.1.11.patch" } }, "knip": { diff --git a/patches/@radix-ui__react-dismissable-layer@1.1.11.patch b/patches/@radix-ui__react-dismissable-layer@1.1.11.patch new file mode 100644 index 00000000..572ae84a --- /dev/null +++ b/patches/@radix-ui__react-dismissable-layer@1.1.11.patch @@ -0,0 +1,70 @@ +diff --git a/dist/index.js b/dist/index.js +--- a/dist/index.js ++++ b/dist/index.js +@@ -71,9 +71,30 @@ var DismissableLayer = React.forwardRef( + } = props; + const context = React.useContext(DismissableLayerContext); + const [node, setNode] = React.useState(null); ++ const nodeRef = React.useRef(null); ++ const nodeCleanupGenerationRef = React.useRef(0); + const ownerDocument = node?.ownerDocument ?? globalThis?.document; + const [, force] = React.useState({}); +- const composedRefs = (0, import_react_compose_refs.useComposedRefs)(forwardedRef, (node2) => setNode(node2)); ++ const setNodeRef = React.useCallback((node2) => { ++ const syncNode = (nextNode) => { ++ if (nodeRef.current === nextNode) return; ++ nodeRef.current = nextNode; ++ setNode(nextNode); ++ }; ++ nodeCleanupGenerationRef.current += 1; ++ const cleanupGeneration = nodeCleanupGenerationRef.current; ++ if (node2) { ++ syncNode(node2); ++ return; ++ } ++ queueMicrotask(() => { ++ if (nodeCleanupGenerationRef.current !== cleanupGeneration) { ++ return; ++ } ++ syncNode(null); ++ }); ++ }, []); ++ const composedRefs = (0, import_react_compose_refs.useComposedRefs)(forwardedRef, setNodeRef); + const layers = Array.from(context.layers); + const [highestLayerWithOutsidePointerEventsDisabled] = [...context.layersWithOutsidePointerEventsDisabled].slice(-1); + const highestLayerWithOutsidePointerEventsDisabledIndex = layers.indexOf(highestLayerWithOutsidePointerEventsDisabled); +diff --git a/dist/index.mjs b/dist/index.mjs +--- a/dist/index.mjs ++++ b/dist/index.mjs +@@ -33,9 +33,30 @@ var DismissableLayer = React.forwardRef( + } = props; + const context = React.useContext(DismissableLayerContext); + const [node, setNode] = React.useState(null); ++ const nodeRef = React.useRef(null); ++ const nodeCleanupGenerationRef = React.useRef(0); + const ownerDocument = node?.ownerDocument ?? globalThis?.document; + const [, force] = React.useState({}); +- const composedRefs = useComposedRefs(forwardedRef, (node2) => setNode(node2)); ++ const setNodeRef = React.useCallback((node2) => { ++ const syncNode = (nextNode) => { ++ if (nodeRef.current === nextNode) return; ++ nodeRef.current = nextNode; ++ setNode(nextNode); ++ }; ++ nodeCleanupGenerationRef.current += 1; ++ const cleanupGeneration = nodeCleanupGenerationRef.current; ++ if (node2) { ++ syncNode(node2); ++ return; ++ } ++ queueMicrotask(() => { ++ if (nodeCleanupGenerationRef.current !== cleanupGeneration) { ++ return; ++ } ++ syncNode(null); ++ }); ++ }, []); ++ const composedRefs = useComposedRefs(forwardedRef, setNodeRef); + const layers = Array.from(context.layers); + const [highestLayerWithOutsidePointerEventsDisabled] = [...context.layersWithOutsidePointerEventsDisabled].slice(-1); + const highestLayerWithOutsidePointerEventsDisabledIndex = layers.indexOf(highestLayerWithOutsidePointerEventsDisabled); diff --git a/scripts/ci/verify-radix-presence-patch.mjs b/scripts/ci/verify-radix-presence-patch.mjs index 36cf3f21..b66d4f29 100644 --- a/scripts/ci/verify-radix-presence-patch.mjs +++ b/scripts/ci/verify-radix-presence-patch.mjs @@ -4,26 +4,51 @@ import { dirname, join } from 'node:path'; const require = createRequire(import.meta.url); -const entrypointPath = require.resolve('@radix-ui/react-presence'); -const packageRoot = dirname(dirname(entrypointPath)); const filesToCheck = ['dist/index.js', 'dist/index.mjs']; +const patchChecks = [ + { + packageName: '@radix-ui/react-presence', + requiredMarkers: ['nodeCleanupGenerationRef', 'syncNode(null)'], + }, + { + packageName: '@radix-ui/react-focus-scope', + resolverFromPackage: '@radix-ui/react-dialog', + requiredMarkers: ['containerCleanupGenerationRef', 'syncContainer(null)'], + }, + { + packageName: '@radix-ui/react-dismissable-layer', + resolverFromPackage: '@radix-ui/react-dialog', + requiredMarkers: ['nodeCleanupGenerationRef', 'syncNode(null)'], + }, +]; + +function resolvePackageRoot({ packageName, resolverFromPackage }) { + const packageRequire = resolverFromPackage + ? createRequire(require.resolve(resolverFromPackage)) + : require; + const entrypointPath = packageRequire.resolve(packageName); + return dirname(dirname(entrypointPath)); +} -const requiredMarkers = ['nodeCleanupGenerationRef', 'syncNode(null)']; const missing = []; -for (const relativePath of filesToCheck) { - const filePath = join(packageRoot, relativePath); - const source = readFileSync(filePath, 'utf8'); - const missingMarkers = requiredMarkers.filter((marker) => !source.includes(marker)); - if (missingMarkers.length > 0) { - missing.push(`${relativePath}: ${missingMarkers.join(', ')}`); +for (const check of patchChecks) { + const packageRoot = resolvePackageRoot(check); + + for (const relativePath of filesToCheck) { + const filePath = join(packageRoot, relativePath); + const source = readFileSync(filePath, 'utf8'); + const missingMarkers = check.requiredMarkers.filter((marker) => !source.includes(marker)); + if (missingMarkers.length > 0) { + missing.push(`${check.packageName}/${relativePath}: ${missingMarkers.join(', ')}`); + } } } if (missing.length > 0) { console.error( [ - '@radix-ui/react-presence is installed without the local React 19 Presence patch.', + 'Radix is installed without one or more local React 19 ref-cleanup patches.', 'Run `pnpm install --force` before building production artifacts.', '', ...missing,