fix(docs): repair canonical URLs, sitemap coverage and robots.txt (#6735)

* fix(docs): canonicalize pages to the URLs that are actually served

Every docs page declared `https://www.heroui.com/docs/<slug>` as its
canonical, but that URL answers with two permanent redirects: `www` ->
apex, then `/docs/...` -> `/<lang>/docs/...`. Google therefore dropped
the crawled page in favour of a redirect, and both `/en` and `/cn`
variants pointed at the same English URL.

The root layout also declared the home page as canonical, which every
page without its own `alternates` inherited, self-excluding `/cn`,
`/<lang>/themes` and `/<lang>/showcase` from the index.

Canonicals now use the served locale-prefixed URL on the apex domain and
carry an hreflang cluster (en / zh-Hans / x-default). Untranslated blog
posts point at the default locale instead of self-canonicalizing a
duplicate.

* fix(docs): build the sitemap from the docs source instead of the prerender manifest

`next-sitemap` derives its URL list from Next's prerender manifest, and
every docs, blog and themes route is server-rendered on demand. The
published sitemap therefore listed 19 URLs — no documentation pages at
all — while advertising `llms-*.txt`, `manifest.webmanifest` and
`rss.xml` as indexable pages.

The App Router `sitemap.ts` enumerates the Fumadocs loader, the blog
collection and the showcase registry instead, producing 501 canonical
URLs with hreflang alternates, all of which answer 200 without a
redirect.

* fix(docs): serve robots.txt from the app and keep non-production hosts out of the index

The committed `public/robots.txt` was the artefact of an earlier
`next-sitemap` run, so its `Sitemap` and `Host` lines were pinned to
whichever host generated them — canary still advertised
`www.heroui.com`. Serving it from the app derives the sitemap URL from
the deployed host and keeps the Content Signals directive that
`MetadataRoute.Robots` cannot express.

Every route stays allowed for every crawler; nothing indexable is
blocked. Preview and development deployments opt out through
`X-Robots-Tag` instead, which is host-scoped and cannot de-index
production the way a stray `Disallow` would.

* fix(docs): exclude untranslated blog fallbacks from sitemap

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: WK Wong <wingkwong.code@gmail.com>
This commit is contained in:
Junior Garcia
2026-07-29 07:10:37 -03:00
committed by GitHub
parent f341db2766
commit 6146a08a58
18 changed files with 313 additions and 114 deletions
-42
View File
@@ -1,42 +0,0 @@
/* eslint-disable import/no-anonymous-default-export */
const normalizeSiteUrl = (siteUrl) => {
const url = new URL(siteUrl);
if (url.hostname === "v3.heroui.com") {
url.hostname = "heroui.com";
}
return url.toString().replace(/\/$/, "");
};
const contentSignalDirective = "Content-Signal: ai-train=yes, search=yes, ai-input=yes";
/** @type {import('next-sitemap').IConfig} */
export default {
autoLastmod: true,
changefreq: "daily",
exclude: ["/api/*", "/llms.mdx/*", "/llms.txt", "/llms-full.txt", "/og/*"],
generateIndexSitemap: true,
generateRobotsTxt: true,
priority: 0.7,
robotsTxtOptions: {
additionalSitemaps: [],
policies: [
{
allow: "/",
userAgent: "*",
},
],
transformRobotsTxt: async (_, robotsTxt) => {
if (robotsTxt.includes(contentSignalDirective)) {
return robotsTxt;
}
return robotsTxt.replace("Allow: /\n", `Allow: /\n${contentSignalDirective}\n`);
},
},
siteUrl: normalizeSiteUrl(
process.env.NEXT_PUBLIC_SITE_URL || process.env.SITE_URL || "https://heroui.com",
),
sitemapSize: 5000,
};
+8 -1
View File
@@ -10,6 +10,13 @@ import {getRedirects} from "./next-redirects";
const withMDX = createMDX();
// Preview and development deployments serve the same content as production on a
// different host, so they must opt out of indexing. Anything other than an
// explicit non-production value stays indexable: a missing env var must never
// silently de-index the production site.
const appEnv = process.env["NEXT_PUBLIC_APP_ENV"];
const isIndexable = appEnv !== "preview" && appEnv !== "development";
const config: NextConfig = {
compress: true,
experimental: {
@@ -30,7 +37,7 @@ const config: NextConfig = {
headers: [
{
key: "X-Robots-Tag",
value: "index, follow",
value: isIndexable ? "index, follow" : "noindex, nofollow",
},
],
source: "/:path*",
-2
View File
@@ -8,7 +8,6 @@
"dev": "next dev",
"prebuild": "node scripts/build-skills.mjs && node scripts/build-theme-presets.mjs",
"build": "next build",
"postbuild": "next-sitemap",
"start": "next start",
"lint": "eslint",
"lint:fix": "eslint --fix",
@@ -51,7 +50,6 @@
"motion": "12.23.26",
"next": "16.2.6",
"next-mdx-remote": "6.0.0",
"next-sitemap": "4.2.3",
"next-themes": "0.4.6",
"nuqs": "2.8.6",
"posthog-node": "5.21.0",
-10
View File
@@ -1,10 +0,0 @@
# *
User-agent: *
Allow: /
Content-Signal: ai-train=yes, search=yes, ai-input=yes
# Host
Host: https://heroui.com
# Sitemaps
Sitemap: https://heroui.com/sitemap.xml
+15
View File
@@ -1,3 +1,5 @@
import type {Metadata} from "next";
import {buttonVariants} from "@heroui/react";
import LinkRoot from "fumadocs-core/link";
import {notFound} from "next/navigation";
@@ -7,6 +9,7 @@ import {StarsCount} from "@/components/github-link";
import {GitHubIcon} from "@/icons/github";
import {getDictionary, hasLocale} from "@/lib/dictionaries";
import {i18n} from "@/lib/i18n";
import {getLocalizedAlternates} from "@/lib/seo";
import {DemoShowcase} from "./components/demo-showcase";
import {ProBanner} from "./components/pro-banner";
@@ -19,6 +22,18 @@ export function generateStaticParams() {
return i18n.languages.map((lang) => ({lang}));
}
export async function generateMetadata({
params,
}: {
params: Promise<{lang: string}>;
}): Promise<Metadata> {
const {lang} = await params;
return {
alternates: getLocalizedAlternates({locale: lang}),
};
}
export default async function HomePage({params}: {params: Promise<{lang: string}>}) {
const {lang} = await params;
@@ -1,8 +1,27 @@
import type {Metadata} from "next";
import type {ReactNode} from "react";
import {notFound} from "next/navigation";
import {getDictionary, hasLocale} from "@/lib/dictionaries";
import {getLocalizedAlternates} from "@/lib/seo";
// The showcase index itself is a client component, so its metadata is declared
// here — without it the page would inherit the root layout's canonical.
export async function generateMetadata({
params,
}: {
params: Promise<{lang: string}>;
}): Promise<Metadata> {
const {lang} = await params;
const dict = hasLocale(lang) ? await getDictionary(lang) : await getDictionary("en");
return {
alternates: getLocalizedAlternates({locale: lang, path: "/showcase"}),
description: dict.showcase.description,
title: dict.showcase.heading,
};
}
export default async function ShowcaseLayout({
children,
+10 -4
View File
@@ -15,6 +15,7 @@ import {getAllBlogPosts, getBlogPost, getRelatedPosts} from "@/lib/blog";
import {getDictionary, hasLocale} from "@/lib/dictionaries";
import {i18n} from "@/lib/i18n";
import {getTechArticleJsonLd} from "@/lib/json-ld";
import {LOCALES, getLocalizedAlternates} from "@/lib/seo";
import {getMDXComponents} from "@/mdx-components";
import {PostCard} from "../post-card";
@@ -40,12 +41,17 @@ export async function generateMetadata({params}: BlogPostPageProps): Promise<Met
if (!post) return {};
const url = `/${lang}/blog/${slug}`;
const path = `/blog/${slug}`;
// Untranslated posts are served in every locale from the default-locale file,
// so only locales with a real translation belong in the hreflang cluster.
const translatedLocales = LOCALES.filter(
(locale) => getBlogPost(slug, locale)?.locale === locale,
);
const alternates = getLocalizedAlternates({locale: lang, locales: translatedLocales, path});
const url = alternates.canonical;
return {
alternates: {
canonical: url,
},
alternates,
description: post.description,
openGraph: {
authors: [post.author],
+2 -3
View File
@@ -8,6 +8,7 @@ import {getAllBlogPosts} from "@/lib/blog";
import {getDictionary, hasLocale} from "@/lib/dictionaries";
import {i18n} from "@/lib/i18n";
import {getBlogJsonLd} from "@/lib/json-ld";
import {getLocalizedAlternates} from "@/lib/seo";
import {BlogContent} from "./blog-content";
@@ -25,9 +26,7 @@ export async function generateMetadata({params}: BlogPageProps): Promise<Metadat
const {blog} = dict;
return {
alternates: {
canonical: `/${lang}/blog`,
},
alternates: getLocalizedAlternates({locale: lang, path: "/blog"}),
description: blog.metaDescription,
openGraph: {
description: blog.metaDescription,
@@ -27,6 +27,7 @@ import StatusChip from "@/components/status-chip";
import {siteConfig} from "@/config/site";
import {getComponentCount, getExampleCount} from "@/demos";
import {getBreadcrumbJsonLd, getTechArticleJsonLd} from "@/lib/json-ld";
import {getLocalizedAlternates, stripLocale} from "@/lib/seo";
import {source} from "@/lib/source";
import {getMDXComponents} from "@/mdx-components";
import {
@@ -190,12 +191,13 @@ export async function generateMetadata(props: {
// Ensure absolute URL for Open Graph
const imageUrl = image.startsWith("http") ? image : new URL(image, siteConfig.siteUrl).toString();
const url = `/docs/${(params.slug ?? []).join("/")}`;
// `page.url` already carries the locale prefix (`/en/docs/...`), which is the
// URL actually served — the unprefixed `/docs/...` form permanently redirects.
const url = page.url;
const alternates = getLocalizedAlternates({locale: params.lang, path: stripLocale(url)});
return {
alternates: {
canonical: url,
},
alternates,
description: page.data.description,
openGraph: {
description: page.data.description,
+4 -1
View File
@@ -65,8 +65,11 @@ export default async function Layout({
}
export const metadata: Metadata = {
// No `canonical` here on purpose: layout metadata is inherited by every page
// below it, so a canonical defined at this level would point localized and
// non-home routes at the home page and exclude them from the index. Each page
// declares its own canonical through `getLocalizedAlternates`.
alternates: {
canonical: __BASE_URL__.toString(),
types: {
"application/rss+xml": [
{
@@ -8,6 +8,7 @@ import {notFound} from "next/navigation";
import {ShowcaseSource} from "@/components/showcase-source";
import {hasLocale} from "@/lib/dictionaries";
import {i18n} from "@/lib/i18n";
import {getLocalizedAlternates} from "@/lib/seo";
import {getAllShowcases, getShowcase} from "@/showcases";
import {ShowcaseCodePanel} from "./showcase-code-panel";
@@ -30,15 +31,15 @@ export async function generateMetadata({params}: ShowcasePageProps): Promise<Met
if (!showcase) return {};
const alternates = getLocalizedAlternates({locale: lang, path: `/showcase/${id}`});
return {
alternates: {
canonical: `/${lang}/showcase/${id}`,
},
alternates,
description: `Interactive demo of ${showcase.name} built with HeroUI components.`,
openGraph: {
description: `Interactive demo of ${showcase.name} built with HeroUI components.`,
title: `${showcase.name} - HeroUI Showcase`,
url: `/${lang}/showcase/${id}`,
url: alternates.canonical,
},
title: `${showcase.name} - HeroUI Showcase`,
};
+15
View File
@@ -1,9 +1,12 @@
import type {Metadata} from "next";
import {Suspense} from "react";
import {ProBanner} from "@/app/[lang]/(home)/components/pro-banner";
import {CodePanelProvider} from "@/hooks/use-code-panel";
import {DictionaryProvider} from "@/hooks/use-dictionary";
import {getDictionary, hasLocale} from "@/lib/dictionaries";
import {getLocalizedAlternates} from "@/lib/seo";
import {
AccentColorSelector,
@@ -18,6 +21,18 @@ import {Onboarding} from "./components/onboarding";
import {ThemeBuilderContent} from "./components/theme-builder-content";
import {THEME_BUILDER_PAGE_ID, formRadiusOptions, radiusOptions} from "./constants";
export async function generateMetadata({
params,
}: {
params: Promise<{lang: string}>;
}): Promise<Metadata> {
const {lang} = await params;
return {
alternates: getLocalizedAlternates({locale: lang, path: "/themes"}),
};
}
export default async function ThemeBuilderPage({params}: {params: Promise<{lang: string}>}) {
const {lang} = await params;
const dict = hasLocale(lang) ? await getDictionary(lang) : await getDictionary("en");
+32
View File
@@ -0,0 +1,32 @@
import {absoluteUrl} from "@/lib/seo";
export const revalidate = false;
/**
* Content Signals (https://contentsignals.org) is not expressible through
* Next.js' `MetadataRoute.Robots` type, so `robots.txt` is served as a route
* instead of a static file. Keeping it in the app means the `Sitemap` line can
* never drift from the deployed canonical host.
*/
const CONTENT_SIGNAL = "Content-Signal: ai-train=yes, search=yes, ai-input=yes";
export const GET = () => {
const body = [
"# Every crawlable route is open to every crawler. Non-indexable endpoints",
"# (preview deployments, machine-only routes) opt out via X-Robots-Tag or",
"# page metadata instead, so nothing indexable is ever blocked here.",
"User-agent: *",
"Allow: /",
CONTENT_SIGNAL,
"",
`Sitemap: ${absoluteUrl("/sitemap.xml")}`,
"",
].join("\n");
return new Response(body, {
headers: {
"Cache-Control": "public, max-age=0, s-maxage=3600, stale-while-revalidate=86400",
"Content-Type": "text/plain; charset=utf-8",
},
});
};
+79
View File
@@ -0,0 +1,79 @@
import type {MetadataRoute} from "next";
import {getAllBlogPosts, getBlogPost} from "@/lib/blog";
import {filterExcludedPages} from "@/lib/llms-utils";
import {LOCALES, absoluteUrl, getSitemapAlternates, localizedPath, stripLocale} from "@/lib/seo";
import {source} from "@/lib/source";
import {getAllShowcases} from "@/showcases";
type SitemapEntry = MetadataRoute.Sitemap[number];
/**
* Locale-agnostic paths mapped to the locales they are published in. Docs pages
* come from the Fumadocs loader (they are rendered dynamically, so they are
* invisible to any sitemap generator that only reads the prerender manifest).
*/
type LocalizedPaths = Map<string, {locales: string[]; lastModified?: Date}>;
function addPath(paths: LocalizedPaths, path: string, locale: string, lastModified?: Date): void {
const existing = paths.get(path);
if (!existing) {
paths.set(path, {lastModified, locales: [locale]});
return;
}
if (!existing.locales.includes(locale)) existing.locales.push(locale);
if (lastModified && (!existing.lastModified || lastModified > existing.lastModified)) {
existing.lastModified = lastModified;
}
}
function toEntries(paths: LocalizedPaths): SitemapEntry[] {
return [...paths].flatMap(([path, {lastModified, locales}]) =>
locales.map((locale) => ({
alternates: {languages: getSitemapAlternates(path, locales)},
url: absoluteUrl(localizedPath(locale, path)),
...(lastModified ? {lastModified} : {}),
})),
);
}
function parseDate(value: string | undefined): Date | undefined {
if (!value) return undefined;
const date = new Date(value);
return Number.isNaN(date.getTime()) ? undefined : date;
}
export default function sitemap(): MetadataRoute.Sitemap {
const paths: LocalizedPaths = new Map();
for (const locale of LOCALES) {
addPath(paths, "/", locale);
addPath(paths, "/blog", locale);
addPath(paths, "/showcase", locale);
addPath(paths, "/themes", locale);
for (const page of filterExcludedPages(source.getPages(locale))) {
addPath(paths, stripLocale(page.url), locale);
}
for (const post of getAllBlogPosts(locale)) {
if (getBlogPost(post.slug, locale)?.locale !== locale) continue;
addPath(paths, `/blog/${post.slug}`, locale, parseDate(post.date));
}
for (const showcase of getAllShowcases()) {
addPath(paths, `/showcase/${showcase.name}`, locale);
}
}
// Served outside `app/[lang]`, so it has no locale variants.
const unlocalized: SitemapEntry[] = [{url: absoluteUrl("/docs/native-showcase/privacy-policy")}];
return [...toEntries(paths), ...unlocalized];
}
+114
View File
@@ -0,0 +1,114 @@
import {siteConfig} from "@/config/site";
import {i18n} from "@/lib/i18n";
export interface LocalizedAlternates {
canonical: string;
languages: Record<string, string>;
}
/**
* `hreflang` values must be BCP 47 language tags. The content directories use
* `cn` for Simplified Chinese, which is a region subtag rather than a language,
* so it has to be translated before it reaches the markup.
*/
const HREFLANG_BY_LOCALE: Record<string, string> = {
cn: "zh-Hans",
en: "en",
};
export const DEFAULT_LOCALE = i18n.defaultLanguage;
export const LOCALES: readonly string[] = i18n.languages;
const LOCALE_PREFIX_PATTERN = new RegExp(`^/(${LOCALES.join("|")})(?=/|$)`);
export function normalizeLocale(locale: string | undefined): string {
return locale && LOCALES.includes(locale) ? locale : DEFAULT_LOCALE;
}
/** Turns `/en/docs/react/button` into the locale-agnostic `/docs/react/button`. */
export function stripLocale(path: string): string {
return path.replace(LOCALE_PREFIX_PATTERN, "") || "/";
}
/**
* Resolves the URL actually served for `path` in `locale`.
*
* The default-locale home page is served at `/` — `/en` only exists as the
* internal rewrite target of `proxy.ts`, so it must never be advertised as a
* canonical URL. Every other route keeps its locale prefix because unprefixed
* paths redirect (`/docs/...` -> `/en/docs/...`).
*/
export function localizedPath(locale: string, path = "/"): string {
const normalizedPath = path === "/" ? "" : path.startsWith("/") ? path : `/${path}`;
if (!normalizedPath && normalizeLocale(locale) === DEFAULT_LOCALE) return "/";
return `/${normalizeLocale(locale)}${normalizedPath}`;
}
export function absoluteUrl(path: string): string {
return new URL(path, siteConfig.siteUrl).toString();
}
/**
* Builds `<link rel="canonical">` plus the `hreflang` cluster for a document
* that exists in `locales`, from its locale-agnostic `path`.
*
* When the requested `locale` has no translation of its own, the route still
* renders (fallback content), so it points at the default locale instead of
* self-canonicalising untranslated duplicates.
*/
export function getLocalizedAlternates({
locale,
locales = LOCALES,
path = "/",
}: {
locale: string;
locales?: readonly string[];
path?: string;
}): LocalizedAlternates {
const languages: Record<string, string> = {};
for (const candidate of locales) {
const hreflang = HREFLANG_BY_LOCALE[candidate];
if (!hreflang) continue;
languages[hreflang] = localizedPath(candidate, path);
}
if (locales.includes(DEFAULT_LOCALE)) {
languages["x-default"] = localizedPath(DEFAULT_LOCALE, path);
}
const canonicalLocale = locales.includes(normalizeLocale(locale))
? normalizeLocale(locale)
: DEFAULT_LOCALE;
return {
canonical: localizedPath(canonicalLocale, path),
languages,
};
}
/** Same as `getLocalizedAlternates`, but with absolute URLs for `sitemap.xml`. */
export function getSitemapAlternates(
path: string,
locales: readonly string[] = LOCALES,
): Record<string, string> {
const languages: Record<string, string> = {};
for (const candidate of locales) {
const hreflang = HREFLANG_BY_LOCALE[candidate];
if (!hreflang) continue;
languages[hreflang] = absoluteUrl(localizedPath(candidate, path));
}
if (locales.includes(DEFAULT_LOCALE)) {
languages["x-default"] = absoluteUrl(localizedPath(DEFAULT_LOCALE, path));
}
return languages;
}
+3 -2
View File
@@ -12,8 +12,9 @@ const getBaseURL = (): URL => {
// preview
if (__PREVIEW__) host = "v3.heroui.com";
// production
if (__PROD__) host = "www.heroui.com";
// production — the apex domain is canonical; `www` permanently redirects to it,
// so pointing metadata at `www` would make every canonical URL a redirect.
if (__PROD__) host = "heroui.com";
// protocol
const protocol = host.startsWith("localhost") ? "http" : "https";
-1
View File
@@ -38,7 +38,6 @@ const config = defineConfig([
"!.*.mjs",
"!.*.ts",
"!contentlayer.config.ts",
"!next-sitemap.config.ts",
],
},
...baseConfig,
+1 -40
View File
@@ -264,9 +264,6 @@ importers:
next-mdx-remote:
specifier: 6.0.0
version: 6.0.0(@types/react@19.2.14)(react@19.2.6)
next-sitemap:
specifier: 4.2.3
version: 4.2.3(next@16.2.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))
next-themes:
specifier: 0.4.6
version: 0.4.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
@@ -860,9 +857,6 @@ packages:
conventional-commits-parser:
optional: true
'@corex/deepmerge@4.0.43':
resolution: {integrity: sha512-N8uEMrMPL0cu/bdboEWpQYb/0i2K5Qn8eCsxzOmxSggJbbQte7ljMRoXm917AbntqTGOzdTu+vP3KOOzoC70HQ==}
'@emnapi/core@1.10.0':
resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==}
@@ -1587,9 +1581,6 @@ packages:
'@emnapi/core': ^1.7.1
'@emnapi/runtime': ^1.7.1
'@next/env@13.5.11':
resolution: {integrity: sha512-fbb2C7HChgM7CemdCY+y3N1n8pcTKdqtQLbC7/EQtPdLvlMUT9JX/dBYl8MMZAtYG4uVMyPFHXckb68q/NRwqg==}
'@next/env@16.2.6':
resolution: {integrity: sha512-gd8HoHN4ufj73WmR3JmVolrpJR47ILK6LouP5xElPglaVxir6e1a7VzvTvDWkOoPXT9rkkTzyCxBu4yeZfZwcw==}
@@ -4575,10 +4566,6 @@ packages:
resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==}
engines: {node: '>=8.6.0'}
fast-glob@3.3.3:
resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
engines: {node: '>=8.6.0'}
fast-json-stable-stringify@2.1.0:
resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
@@ -4830,6 +4817,7 @@ packages:
git-raw-commits@5.0.1:
resolution: {integrity: sha512-Y+csSm2GD/PCSh6Isd/WiMjNAydu0VBiG9J7EdQsNA5P9uXvLayqjmTsNlK5Gs9IhblFZqOU0yid5Il5JPoLiQ==}
engines: {node: '>=18'}
deprecated: Deprecated and no longer maintained. Use @conventional-changelog/git-client instead.
hasBin: true
github-slugger@2.0.0:
@@ -5722,13 +5710,6 @@ packages:
peerDependencies:
react: '>=16'
next-sitemap@4.2.3:
resolution: {integrity: sha512-vjdCxeDuWDzldhCnyFCQipw5bfpl4HmZA7uoo3GAaYGjGgfL4Cxb1CiztPuWGmS+auYs7/8OekRS8C2cjdAsjQ==}
engines: {node: '>=14.18'}
hasBin: true
peerDependencies:
next: '*'
next-themes@0.4.6:
resolution: {integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==}
peerDependencies:
@@ -7835,8 +7816,6 @@ snapshots:
optionalDependencies:
conventional-commits-parser: 6.4.0
'@corex/deepmerge@4.0.43': {}
'@emnapi/core@1.10.0':
dependencies:
'@emnapi/wasi-threads': 1.2.1
@@ -8381,8 +8360,6 @@ snapshots:
'@tybys/wasm-util': 0.10.2
optional: true
'@next/env@13.5.11': {}
'@next/env@16.2.6': {}
'@next/eslint-plugin-next@16.1.1':
@@ -11732,14 +11709,6 @@ snapshots:
merge2: 1.4.1
micromatch: 4.0.8
fast-glob@3.3.3:
dependencies:
'@nodelib/fs.stat': 2.0.5
'@nodelib/fs.walk': 1.2.8
glob-parent: 5.1.2
merge2: 1.4.1
micromatch: 4.0.8
fast-json-stable-stringify@2.1.0: {}
fast-levenshtein@2.0.6: {}
@@ -13168,14 +13137,6 @@ snapshots:
- '@types/react'
- supports-color
next-sitemap@4.2.3(next@16.2.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)):
dependencies:
'@corex/deepmerge': 4.0.43
'@next/env': 13.5.11
fast-glob: 3.3.3
minimist: 1.2.8
next: 16.2.6(@babel/core@7.28.5)(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
next-themes@0.4.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6):
dependencies:
react: 19.2.6