nuqs
Version:
Type-safe search params state manager for React - Like useState, but stored in the URL query string
110 lines (107 loc) • 3.68 kB
JavaScript
import { createAdapterProvider, renderQueryString, debug } from './chunk-5WWTJYGR.js';
import { useRouter } from 'next/compat/router.js';
import { useMemo, useCallback } from 'react';
function isPagesRouter() {
return typeof window.next?.router?.state?.asPath === "string";
}
function useNuqsNextPagesRouterAdapter() {
const router = useRouter();
const searchParams = useMemo(() => {
const searchParams2 = new URLSearchParams();
if (router === null) {
return searchParams2;
}
for (const [key, value] of Object.entries(router.query)) {
if (typeof value === "string") {
searchParams2.set(key, value);
} else if (Array.isArray(value)) {
for (const v of value) {
searchParams2.append(key, v);
}
}
}
return searchParams2;
}, [JSON.stringify(router?.query)]);
const updateUrl = useCallback((search, options) => {
const nextRouter = window.next?.router;
const urlParams = extractDynamicUrlParams(
nextRouter.pathname,
nextRouter.query
);
const asPath = getAsPathPathname(nextRouter.asPath) + renderQueryString(search) + location.hash;
debug("[nuqs queue (pages)] Updating url: %s", asPath);
const method = options.history === "push" ? nextRouter.push : nextRouter.replace;
method.call(
nextRouter,
// This is what makes the URL work (mapping dynamic segments placeholders
// in pathname to their values in query, plus search params in query too).
{
pathname: nextRouter.pathname,
query: {
// Note: we put search params first so that one that conflicts
// with dynamic params will be overwritten.
...urlSearchParamsToObject(search),
...urlParams
}
// For some reason we don't need to pass the hash here,
// it's preserved when passed as part of the asPath.
},
// This is what makes the URL pretty (resolved dynamic segments
// and nuqs-formatted search params).
asPath,
// And these are the options that are passed to the router.
{
scroll: options.scroll,
shallow: options.shallow
}
);
}, []);
return {
searchParams,
updateUrl
};
}
createAdapterProvider(useNuqsNextPagesRouterAdapter);
function getAsPathPathname(asPath) {
return asPath.replace(/#.*$/, "").replace(/\?.*$/, "");
}
function urlSearchParamsToObject(search) {
const out = {};
for (const key of search.keys()) {
const values = search.getAll(key);
if (values.length === 1) {
out[key] = values[0];
} else if (values.length > 1) {
out[key] = values;
}
}
return out;
}
function extractDynamicUrlParams(pathname, values) {
const paramNames = /* @__PURE__ */ new Set();
const dynamicRegex = /\[([^\]]+)\]/g;
const catchAllRegex = /\[\.{3}([^\]]+)\]$/;
const optionalCatchAllRegex = /\[\[\.{3}([^\]]+)\]\]$/;
let match;
while ((match = dynamicRegex.exec(pathname)) !== null) {
const paramName = match[1];
if (paramName) {
paramNames.add(paramName);
}
}
const dynamicValues = Object.fromEntries(
Object.entries(values).filter(([key]) => paramNames.has(key))
);
const matchCatchAll = catchAllRegex.exec(pathname);
if (matchCatchAll && matchCatchAll[1]) {
const key = matchCatchAll[1];
dynamicValues[key] = values[key] ?? [];
}
const matchOptionalCatchAll = optionalCatchAllRegex.exec(pathname);
if (matchOptionalCatchAll && matchOptionalCatchAll[1]) {
const key = matchOptionalCatchAll[1];
dynamicValues[key] = values[key] ?? [];
}
return dynamicValues;
}
export { isPagesRouter, useNuqsNextPagesRouterAdapter };