UNPKG

nuqs

Version:

Type-safe search params state manager for React - Like useState, but stored in the URL query string

132 lines (126 loc) 4.13 kB
import { createContext, createElement, useContext } from 'react'; // src/errors.ts var errors = { 303: "Multiple adapter contexts detected. This might happen in monorepos.", 404: "nuqs requires an adapter to work with your framework.", 409: "Multiple versions of the library are loaded. This may lead to unexpected behavior. Currently using `%s`, but `%s` (via the %s adapter) was about to load on top.", 414: "Max safe URL length exceeded. Some browsers may not be able to accept this URL. Consider limiting the amount of state stored in the URL.", 429: "URL update rate-limited by the browser. Consider increasing `throttleMs` for key(s) `%s`. %O", 500: "Empty search params cache. Search params can't be accessed in Layouts.", 501: "Search params cache already populated. Have you called `parse` twice?" }; function error(code) { return `[nuqs] ${errors[code]} See https://err.47ng.com/NUQS-${code}`; } // src/url-encoding.ts function renderQueryString(search) { if (search.size === 0) { return ""; } const query = []; for (const [key, value] of search.entries()) { const safeKey = key.replace(/#/g, "%23").replace(/&/g, "%26").replace(/\+/g, "%2B").replace(/=/g, "%3D").replace(/\?/g, "%3F"); query.push(`${safeKey}=${encodeQueryValue(value)}`); } const queryString = "?" + query.join("&"); warnIfURLIsTooLong(queryString); return queryString; } function encodeQueryValue(input) { return input.replace(/%/g, "%25").replace(/\+/g, "%2B").replace(/ /g, "+").replace(/#/g, "%23").replace(/&/g, "%26").replace(/"/g, "%22").replace(/'/g, "%27").replace(/`/g, "%60").replace(/</g, "%3C").replace(/>/g, "%3E").replace(/[\x00-\x1F]/g, (char) => encodeURIComponent(char)); } var URL_MAX_LENGTH = 2e3; function warnIfURLIsTooLong(queryString) { if (process.env.NODE_ENV === "production") { return; } if (typeof location === "undefined") { return; } const url = new URL(location.href); url.search = queryString; if (url.href.length > URL_MAX_LENGTH) { console.warn(error(414)); } } // src/debug.ts var debugEnabled = isDebugEnabled(); function debug(message, ...args) { if (!debugEnabled) { return; } const msg = sprintf(message, ...args); performance.mark(msg); try { console.log(message, ...args); } catch (error2) { console.log(msg); } } function warn(message, ...args) { if (!debugEnabled) { return; } console.warn(message, ...args); } function sprintf(base, ...args) { return base.replace(/%[sfdO]/g, (match) => { const arg = args.shift(); if (match === "%O" && arg) { return JSON.stringify(arg).replace(/"([^"]+)":/g, "$1:"); } else { return String(arg); } }); } function isDebugEnabled() { try { if (typeof localStorage === "undefined") { return false; } const test = "nuqs-localStorage-test"; localStorage.setItem(test, test); const isStorageAvailable = localStorage.getItem(test) === test; localStorage.removeItem(test); if (!isStorageAvailable) { return false; } } catch (error2) { console.error( "[nuqs]: debug mode is disabled (localStorage unavailable).", error2 ); return false; } const debug2 = localStorage.getItem("debug") ?? ""; return debug2.includes("nuqs"); } // src/adapters/lib/context.ts var context = createContext({ useAdapter() { throw new Error(error(404)); } }); context.displayName = "NuqsAdapterContext"; if (debugEnabled && typeof window !== "undefined") { if (window.__NuqsAdapterContext && window.__NuqsAdapterContext !== context) { console.error(error(303)); } window.__NuqsAdapterContext = context; } function createAdapterProvider(useAdapter2) { return ({ children, ...props }) => createElement( context.Provider, { ...props, value: { useAdapter: useAdapter2 } }, children ); } function useAdapter() { const value = useContext(context); if (!("useAdapter" in value)) { throw new Error(error(404)); } return value.useAdapter(); } export { context, createAdapterProvider, debug, error, renderQueryString, useAdapter, warn };