"use strict"; 'use client'; var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default; Object.defineProperty(exports, "__esModule", { value: true }); exports.useDisposable = void 0; var _index = _interopRequireDefault(require("../formatErrorMessage/index.js")); var React = _interopRequireWildcard(require("react")); var _useOnMount = require("@base-ui/utils/useOnMount"); var _safeReact = require("@base-ui/utils/safeReact"); var _disposable = require("../disposable"); // React 19 exposes shared internals under `__CLIENT_INTERNALS_…` with the // fiber under `A.getOwner()`; React 18 exposes them under `__SECRET_INTERNALS_…` // with the fiber under `ReactCurrentOwner.current`. `mode` is a bitfield on // the fiber. We key on StrictEffectsMode (16) — the bit a concurrent-root // `` sets on React 18/19, and the bit that actually drives the // mount→unmount→mount effect replay this hook guards against. Setups that // don't replay effects (legacy-root StrictMode, React 17, Preact) don't set // it, so they fall through to a single, normal dispose. Bit values are // React-version-specific; if a future React renumbers them and detection // misfires, the mount counter throws rather than silently leaking. const STRICT_MODE_BITS = 0b10000; // Read the internals off Base UI's `SafeReact` — a plain clone of the React // namespace — rather than `React` directly. Bundlers rewrite a static // `React.__SECRET_INTERNALS_…` access into a named import of `react`, which // errors at build time when the key isn't exported (React 19 drops // `__SECRET_INTERNALS_…`, React 18 drops `__CLIENT_INTERNALS_…`). Reading off // the clone keeps it a plain property access the bundler leaves alone. const SafeReactInternals = _safeReact.SafeReact; const ReactInternals = // eslint-disable-next-line no-underscore-dangle SafeReactInternals.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE ?? // eslint-disable-next-line no-underscore-dangle SafeReactInternals.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; function isInStrictMode() { try { const owner = ReactInternals?.A?.getOwner?.() ?? ReactInternals?.ReactCurrentOwner?.current ?? null; if (owner == null || typeof owner.mode !== 'number') { return false; } // eslint-disable-next-line no-bitwise return (owner.mode & STRICT_MODE_BITS) !== 0; } catch { return false; } } // Module-private symbol used to stash the `useOnMount` callback on the // instance during the first render so subsequent renders can pass the same // reference into `useOnMount` without allocating a fresh closure. const MOUNT = Symbol('useDisposable.mount'); // Module-private symbol holding the dev-only StrictMode bookkeeping that lets // the mount callback distinguish StrictMode's simulated unmount from a real // one and assert our render-time detection was correct. const DEV_STATE = Symbol('useDisposable.devState'); const UNINITIALIZED = Symbol(); /** * Production variant: lazily creates the instance on first render and stashes * a single mount/cleanup pair on it under a module-private symbol so render- * pass allocations stay at zero after the first render. No StrictMode * detection is needed because StrictMode's double-mount only happens in dev. * @returns {T} the lazily-created instance. */ function useDisposableProduction(factory) { const ref = React.useRef(UNINITIALIZED); if (ref.current === UNINITIALIZED) { const inst = factory(); const cleanup = () => { inst[_disposable.disposeSymbol](); // Reset so a fiber-preserving remount (e.g. `` reveal, which // re-renders before re-running effects) rebuilds a fresh instance instead // of reusing the disposed one. ref.current = UNINITIALIZED; }; inst[MOUNT] = () => cleanup; ref.current = inst; } (0, _useOnMount.useOnMount)(ref.current[MOUNT]); return ref.current; } /** * Development variant: detects StrictMode by reading the currently-rendering * fiber's `mode` bits from React shared internals so the same instance * survives StrictMode's mount→unmount→mount cycle. * * The mount callback counts mounts to keep the detection honest: * - it skips dispose on StrictMode's *first* (simulated) unmount only, and * disposes on every real unmount — including the final one inside * ``, so no instance is leaked in dev; * - if the instance is mounted a second time but was already disposed, our * render-time detection produced a false negative. Rather than silently * hand back a dead instance (which is the failure that motivated this hook), * it throws so the bug surfaces immediately. * @returns {T} the lazily-created instance. */ function useDisposableDevelopment(factory) { const ref = React.useRef(UNINITIALIZED); if (ref.current === UNINITIALIZED) { const inst = factory(); // Captured during render because the owner fiber is only set while React // is rendering — by the time the effect runs it's already null. const state = { detectedStrict: isInStrictMode(), mountCount: 0, disposed: false }; inst[DEV_STATE] = state; inst[MOUNT] = () => { state.mountCount += 1; if (state.mountCount > 1 && state.disposed) { throw new Error(process.env.NODE_ENV !== "production" ? 'MUI X: useDisposable failed to detect React StrictMode.\n' + "The instance was disposed on StrictMode's simulated unmount and is about to be reused while torn down.\n" + 'This is an internal invariant violation — please report it at https://github.com/mui/mui-x/issues.' : (0, _index.default)(288)); } return () => { // Skip StrictMode's simulated unmount (the first unmount, when strict // mode was detected); dispose on every real unmount. if (state.detectedStrict && state.mountCount < 2) { return; } state.disposed = true; inst[_disposable.disposeSymbol](); // Reset so a fiber-preserving remount (e.g. `` reveal, which // re-renders before re-running effects) rebuilds a fresh instance // instead of reusing the disposed one. ref.current = UNINITIALIZED; }; }; ref.current = inst; } (0, _useOnMount.useOnMount)(ref.current[MOUNT]); return ref.current; } /** * Lazily creates an instance on first render and runs its `[disposeSymbol]` * once on unmount. The cleanup runs synchronously; in development StrictMode's * simulated unmount is detected and skipped so the same instance survives the * double mount. The development variant degrades to production behaviour on * runtimes that don't replay the double mount (Preact, React 17) because no * readable StrictEffectsMode bit is found, so no runtime branching is needed. */ const useDisposable = exports.useDisposable = process.env.NODE_ENV === 'production' ? useDisposableProduction : useDisposableDevelopment;