/** * 判断当前是否存在 DOM 环境(同时存在 window 与 document)。 * 用于命令式挂载、URL 监听等 Web API;与 Hooks 是否可用无关(React Native 等无 DOM 环境仍可使用 Hooks)。 * @returns 是否可安全访问 DOM / History API */ function isBrowser(): boolean { return typeof window !== 'undefined' && typeof document !== 'undefined'; } /** * 构造「需要 DOM 环境」的错误对象。 * @param apiName - 调用的 API 名称 * @returns Error 实例 */ function createBrowserRequiredError(apiName: string): Error { return new Error( `[use-modal-ref] ${apiName} requires a DOM environment (window & document). ` + 'It is not available in React Native — use useModalRef / useCommonRef with in-tree modal components instead. ' + 'On the web, call it from an event handler or useEffect inside a Client Component.', ); } /** * 在非浏览器环境下抛出明确错误。 * @param apiName - 调用的 API 名称 */ function assertBrowser(apiName: string): void { if (!isBrowser()) { throw createBrowserRequiredError(apiName); } } /** * 在非浏览器环境调用 URL 监听 API 时的 dev 警告(仅提示一次)。 */ function warnNoopUrlListenerOnce(): void { if (process.env.NODE_ENV === 'production') { return; } if ((warnNoopUrlListenerOnce as { warned?: boolean }).warned) { return; } (warnNoopUrlListenerOnce as { warned?: boolean }).warned = true; // eslint-disable-next-line no-console console.warn( '[use-modal-ref] createUrlListener() / getUrlListener() called without a browser environment; ' + 'using a noop listener. URL change callbacks will not run until mounted in the browser.', ); } export { isBrowser, createBrowserRequiredError, assertBrowser, warnNoopUrlListenerOnce, };