@pas7/nextjs-sitemap-hreflang 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/assertHreflang.ts","../src/lib/url.ts","../src/lib/buildLanguagesMap.ts","../src/lib/withHreflang.ts","../src/lib/routing.ts","../src/lib/expandLocales.ts","../src/lib/fromManifest.ts","../src/xml/xml.ts","../src/xml/inject.ts","../src/xml/check.ts"],"sourcesContent":["import type { HreflangIssue, HreflangReport, SitemapEntryLike } from \"./types.js\";\n\nexport type AssertHreflangOptions = {\n requireAbsolute?: boolean;\n requireXDefaultWhenMultiple?: boolean;\n requireSelf?: boolean;\n canonicalLocale?: string;\n};\n\nexport function assertHreflang(\n entries: readonly SitemapEntryLike[],\n options: AssertHreflangOptions,\n): HreflangReport {\n const requireAbsolute = options.requireAbsolute ?? true;\n const requireXDefaultWhenMultiple = options.requireXDefaultWhenMultiple ?? true;\n const requireSelf = options.requireSelf ?? false;\n\n const issues: HreflangIssue[] = [];\n\n for (const entry of entries) {\n const languages = entry.alternates?.languages;\n if (!languages) continue;\n\n const pairs = Object.entries(languages);\n\n if (pairs.length === 0) {\n issues.push({\n code: \"MISSING_LANGUAGES\",\n entryUrl: entry.url,\n message: \"alternates.languages is empty\",\n });\n continue;\n }\n\n if (requireXDefaultWhenMultiple && pairs.length > 1 && !languages[\"x-default\"]) {\n issues.push({\n code: \"MISSING_XDEFAULT\",\n entryUrl: entry.url,\n message: \"Missing x-default hreflang for a multilingual entry\",\n });\n }\n\n if (requireSelf && options.canonicalLocale) {\n if (!languages[options.canonicalLocale]) {\n issues.push({\n code: \"MISSING_SELF\",\n entryUrl: entry.url,\n message: `Missing self hreflang for canonicalLocale=${options.canonicalLocale}`,\n });\n }\n }\n\n const hrefSeen = new Set<string>();\n for (const [locale, href] of pairs) {\n if (!isValidLocaleKey(locale)) {\n issues.push({\n code: \"INVALID_LOCALE_KEY\",\n entryUrl: entry.url,\n message: `Invalid locale key: ${locale}`,\n });\n }\n if (requireAbsolute && !isAbsoluteUrl(href)) {\n issues.push({\n code: \"NON_ABSOLUTE_URL\",\n entryUrl: entry.url,\n message: `Non-absolute hreflang href for ${locale}: ${href}`,\n });\n }\n if (hrefSeen.has(href)) {\n issues.push({\n code: \"DUPLICATE_HREF\",\n entryUrl: entry.url,\n message: `Duplicate hreflang href detected: ${href}`,\n });\n }\n hrefSeen.add(href);\n }\n }\n\n return { ok: issues.length === 0, issues };\n}\n\nfunction isAbsoluteUrl(url: string): boolean {\n return url.startsWith(\"http://\") || url.startsWith(\"https://\");\n}\n\nfunction isValidLocaleKey(key: string): boolean {\n if (key === \"x-default\") return true;\n return /^[a-z]{2}(-[A-Z]{2})?$/.test(key);\n}\n","export type TrailingSlashPolicy = \"preserve\" | \"always\" | \"never\";\n\nexport function normalizeUrl(url: string, trailingSlash: TrailingSlashPolicy): string {\n const u = new URL(url);\n if (trailingSlash === \"preserve\") return u.toString();\n if (trailingSlash === \"always\") {\n if (!u.pathname.endsWith(\"/\")) u.pathname = `${u.pathname}/`;\n return u.toString();\n }\n if (u.pathname !== \"/\" && u.pathname.endsWith(\"/\")) u.pathname = u.pathname.slice(0, -1);\n return u.toString();\n}\n\nexport function resolveAbsoluteUrl(input: string, baseUrl: string): string {\n if (input.startsWith(\"http://\") || input.startsWith(\"https://\")) return input;\n return new URL(input.startsWith(\"/\") ? input : `/${input}`, baseUrl).toString();\n}\n\nexport function getOriginFromAbsoluteUrl(absoluteUrl: string): string {\n const u = new URL(absoluteUrl);\n return `${u.protocol}//${u.host}`;\n}\n","import type { XDefaultStrategy } from \"./types.js\";\nimport { normalizeUrl, resolveAbsoluteUrl } from \"./url.js\";\n\nexport type BuildLanguagesMapInput = {\n baseUrl: string;\n locales: readonly string[];\n canonicalLocale: string;\n resolveHref: (locale: string) => string;\n includeXDefault: boolean;\n xDefaultStrategy?: XDefaultStrategy;\n trailingSlash?: \"preserve\" | \"always\" | \"never\";\n};\n\nexport type BuildLanguagesMapOutput = {\n canonical: string;\n languages: Record<string, string>;\n};\n\nexport function buildLanguagesMap(input: BuildLanguagesMapInput): BuildLanguagesMapOutput {\n const trailingSlash = input.trailingSlash ?? \"preserve\";\n const languages: Record<string, string> = {};\n\n for (const locale of input.locales) {\n const href = input.resolveHref(locale);\n const abs = resolveAbsoluteUrl(href, input.baseUrl);\n languages[locale] = normalizeUrl(abs, trailingSlash);\n }\n\n const canonical = languages[input.canonicalLocale] ?? normalizeUrl(input.baseUrl, trailingSlash);\n\n if (input.includeXDefault) {\n const xDefault = resolveXDefaultForLanguages({\n canonical,\n languages,\n baseUrl: input.baseUrl,\n strategy: input.xDefaultStrategy ?? { type: \"loc\" },\n trailingSlash,\n });\n languages[\"x-default\"] = xDefault;\n }\n\n return { canonical, languages };\n}\n\nexport function resolveXDefaultForLanguages(args: {\n canonical: string;\n languages: Record<string, string>;\n baseUrl: string;\n strategy: XDefaultStrategy;\n trailingSlash: \"preserve\" | \"always\" | \"never\";\n}): string {\n const { canonical, languages, baseUrl, strategy, trailingSlash } = args;\n\n if (strategy.type === \"loc\") return normalizeUrl(canonical, trailingSlash);\n if (strategy.type === \"root\") return normalizeUrl(resolveAbsoluteUrl(\"/\", baseUrl), trailingSlash);\n if (strategy.type === \"custom\") return normalizeUrl(resolveAbsoluteUrl(strategy.url, baseUrl), trailingSlash);\n if (strategy.type === \"locale\") {\n const href = languages[strategy.locale];\n if (href) return normalizeUrl(href, trailingSlash);\n return normalizeUrl(canonical, trailingSlash);\n }\n const href = strategy.resolve({ url: canonical });\n return normalizeUrl(resolveAbsoluteUrl(href, baseUrl), trailingSlash);\n}\n","import type {\n HreflangReport,\n SitemapEntryLike,\n XDefaultStrategy,\n RoutingStrategy,\n} from \"./types.js\";\nimport { assertHreflang as assertHreflangImpl } from \"./assertHreflang.js\";\nimport { buildLanguagesMap, resolveXDefaultForLanguages } from \"./buildLanguagesMap.js\";\nimport { normalizeUrl, resolveAbsoluteUrl } from \"./url.js\";\n\nexport { normalizeUrl, resolveAbsoluteUrl };\nexport { buildLanguagesMap };\n\nexport type WithHreflangOptions = {\n baseUrl?: string;\n trailingSlash?: \"preserve\" | \"always\" | \"never\";\n ensureAbsolute?: boolean;\n ensureXDefault?: boolean;\n ensureSelf?: boolean;\n canonicalLocale?: string;\n xDefaultStrategy?: XDefaultStrategy;\n shouldApply?: (entry: SitemapEntryLike) => boolean;\n};\n\nexport function withHreflang<T extends SitemapEntryLike>(\n entries: readonly T[],\n options: WithHreflangOptions,\n): T[] {\n const trailingSlash = options.trailingSlash ?? \"preserve\";\n const ensureAbsolute = options.ensureAbsolute ?? true;\n const ensureXDefault = options.ensureXDefault ?? true;\n const ensureSelf = options.ensureSelf ?? false;\n\n return entries.map((entry) => {\n if (options.shouldApply && !options.shouldApply(entry)) return entry;\n\n const url = normalizeEntryUrl(entry.url, options.baseUrl, ensureAbsolute, trailingSlash);\n\n const languages = entry.alternates?.languages\n ? normalizeLanguages(entry.alternates.languages, options.baseUrl, ensureAbsolute, trailingSlash)\n : undefined;\n\n if (!languages) {\n return { ...entry, url } as T;\n }\n\n const nextLanguages = { ...languages };\n\n if (ensureSelf && options.canonicalLocale) {\n if (!nextLanguages[options.canonicalLocale]) nextLanguages[options.canonicalLocale] = url;\n }\n\n if (ensureXDefault) {\n if (!nextLanguages[\"x-default\"]) {\n const xDefault = resolveXDefaultForLanguages({\n canonical: url,\n languages: nextLanguages,\n baseUrl: options.baseUrl ?? url,\n strategy: options.xDefaultStrategy ?? { type: \"loc\" },\n trailingSlash,\n });\n nextLanguages[\"x-default\"] = xDefault;\n }\n }\n\n const alternates = { ...(entry.alternates ?? {}), languages: nextLanguages };\n\n return { ...entry, url, alternates } as T;\n });\n}\n\nfunction normalizeEntryUrl(\n url: string,\n baseUrl: string | undefined,\n ensureAbsolute: boolean,\n trailingSlash: \"preserve\" | \"always\" | \"never\",\n): string {\n const abs = ensureAbsolute ? resolveAbsoluteUrl(url, baseUrl ?? url) : url;\n return normalizeUrl(abs, trailingSlash);\n}\n\nfunction normalizeLanguages(\n languages: Record<string, string>,\n baseUrl: string | undefined,\n ensureAbsolute: boolean,\n trailingSlash: \"preserve\" | \"always\" | \"never\",\n): Record<string, string> {\n const out: Record<string, string> = {};\n for (const [k, v] of Object.entries(languages)) {\n const abs = ensureAbsolute ? resolveAbsoluteUrl(v, baseUrl ?? v) : v;\n out[k] = normalizeUrl(abs, trailingSlash);\n }\n return out;\n}\n\nexport type AssertHreflangOptions = {\n requireAbsolute?: boolean;\n requireXDefaultWhenMultiple?: boolean;\n requireSelf?: boolean;\n canonicalLocale?: string;\n};\n\nexport function assertHreflangExport(entries: readonly SitemapEntryLike[], options: AssertHreflangOptions): HreflangReport {\n return assertHreflangImpl(entries, options);\n}\n\n/**\n * Build hreflang entries using a routing strategy.\n * This is the recommended way to use withHreflang with i18n routing.\n */\nexport function withHreflangFromRouting<T extends SitemapEntryLike>(\n entries: readonly T[],\n strategy: RoutingStrategy,\n options: {\n baseUrl: string;\n trailingSlash?: \"preserve\" | \"always\" | \"never\";\n ensureAbsolute?: boolean;\n ensureXDefault?: boolean;\n ensureSelf?: boolean;\n shouldApply?: (entry: SitemapEntryLike) => boolean;\n },\n): T[] {\n const trailingSlash = options.trailingSlash ?? \"preserve\";\n const ensureAbsolute = options.ensureAbsolute ?? true;\n const ensureXDefault = options.ensureXDefault ?? true;\n const ensureSelf = options.ensureSelf ?? false;\n\n return entries.map((entry) => {\n if (options.shouldApply && !options.shouldApply(entry)) return entry;\n\n // Extract pathname from URL\n let pathname: string;\n try {\n const url = new URL(entry.url, options.baseUrl);\n pathname = url.pathname;\n } catch {\n pathname = entry.url;\n }\n\n // Build languages map from routing strategy\n const languages: Record<string, string> = {};\n for (const locale of strategy.locales) {\n const href = strategy.hrefFor({ pathname, locale });\n const abs = ensureAbsolute ? resolveAbsoluteUrl(href, options.baseUrl) : href;\n languages[locale] = normalizeUrl(abs, trailingSlash);\n }\n\n // Add x-default if needed\n if (ensureXDefault && !languages[\"x-default\"]) {\n const canonical =\n languages[strategy.canonicalLocale] ?? normalizeUrl(entry.url, trailingSlash);\n const xDefault = resolveXDefaultForLanguages({\n canonical,\n languages,\n baseUrl: options.baseUrl,\n strategy: strategy.xDefault ?? { type: \"loc\" },\n trailingSlash,\n });\n languages[\"x-default\"] = xDefault;\n }\n\n // Ensure self for canonical locale\n if (ensureSelf && !languages[strategy.canonicalLocale]) {\n const url = normalizeEntryUrl(entry.url, options.baseUrl, ensureAbsolute, trailingSlash);\n languages[strategy.canonicalLocale] = url;\n }\n\n const alternates = { ...(entry.alternates ?? {}), languages };\n\n return { ...entry, alternates } as T;\n });\n}\n","import type {\n RoutingStrategy,\n RoutingPrefixAsNeededOptions,\n RoutingPrefixAlwaysOptions,\n RoutingDomainBasedOptions,\n RoutingSuffixLocaleOptions,\n RoutingPAS7Options,\n} from \"./types.js\";\n\n/**\n * Prefix-as-needed routing: default locale has no prefix, others have /locale\n * Example: /about (en), /uk/about (uk), /de/about (de)\n *\n * Compatible with next-intl's prefix-as-needed strategy.\n */\nexport function routingPrefixAsNeeded(\n options: RoutingPrefixAsNeededOptions,\n): RoutingStrategy {\n const { defaultLocale, locales, basePath = \"\" } = options;\n\n return {\n locales,\n canonicalLocale: defaultLocale,\n hrefFor: ({ pathname, locale }) => {\n const normalizedPath = pathname.startsWith(\"/\") ? pathname : `/${pathname}`;\n const base = basePath || \"\";\n\n if (locale === defaultLocale) {\n // Default locale - no prefix\n return `${base}${normalizedPath}`;\n }\n // Other locales - add prefix\n return `${base}/${locale}${normalizedPath}`;\n },\n };\n}\n\n/**\n * Prefix-always routing: all locales have /locale prefix\n * Example: /en/about, /uk/about, /de/about\n *\n * Compatible with next-intl's prefix-always strategy.\n */\nexport function routingPrefixAlways(\n options: RoutingPrefixAlwaysOptions,\n): RoutingStrategy {\n const { defaultLocale, locales, basePath = \"\" } = options;\n\n return {\n locales,\n canonicalLocale: defaultLocale,\n hrefFor: ({ pathname, locale }) => {\n const normalizedPath = pathname.startsWith(\"/\") ? pathname : `/${pathname}`;\n const base = basePath || \"\";\n return `${base}/${locale}${normalizedPath}`;\n },\n };\n}\n\n/**\n * Domain-based routing: each locale has its own domain\n * Example: example.com (en), example.de (de), example.ua (uk)\n *\n * Compatible with next-intl's domain-based routing.\n */\nexport function routingDomainBased(\n options: RoutingDomainBasedOptions,\n): RoutingStrategy {\n const { defaultLocale, locales, localeToDomain } = options;\n\n return {\n locales,\n canonicalLocale: defaultLocale,\n hrefFor: ({ pathname, locale }) => {\n const domain = localeToDomain[locale];\n if (!domain) {\n // Fallback to default domain with prefix\n const defaultDomain = localeToDomain[defaultLocale] || \"\";\n const normalizedPath = pathname.startsWith(\"/\") ? pathname : `/${pathname}`;\n return `${defaultDomain}/${locale}${normalizedPath}`;\n }\n const normalizedPath = pathname.startsWith(\"/\") ? pathname : `/${pathname}`;\n return `${domain}${normalizedPath}`;\n },\n };\n}\n\n/**\n * Suffix-locale routing: locale added as suffix to path\n * Example: /blog (en), /blog/uk (uk), /blog/de (de)\n *\n * Common for blogs and content sections.\n */\nexport function routingSuffixLocale(\n options: RoutingSuffixLocaleOptions,\n): RoutingStrategy {\n const { defaultLocale, locales, basePath = \"\" } = options;\n\n return {\n locales,\n canonicalLocale: defaultLocale,\n hrefFor: ({ pathname, locale }) => {\n const normalizedPath = pathname.startsWith(\"/\") ? pathname : `/${pathname}`;\n // Remove trailing slash for consistent handling\n const path =\n normalizedPath.endsWith(\"/\") && normalizedPath !== \"/\"\n ? normalizedPath.slice(0, -1)\n : normalizedPath;\n const base = basePath || \"\";\n\n if (locale === defaultLocale) {\n // Default locale - no suffix\n return `${base}${path}`;\n }\n // Other locales - add suffix\n return `${base}${path}/${locale}`;\n },\n };\n}\n\n/**\n * Custom routing with user-defined function\n */\nexport function routingCustom(options: {\n locales: readonly string[];\n canonicalLocale: string;\n hrefFor: (args: { pathname: string; locale: string }) => string;\n xDefault?: RoutingStrategy[\"xDefault\"];\n}): RoutingStrategy {\n if (options.xDefault !== undefined) {\n return {\n locales: options.locales,\n canonicalLocale: options.canonicalLocale,\n hrefFor: options.hrefFor,\n xDefault: options.xDefault,\n };\n }\n return {\n locales: options.locales,\n canonicalLocale: options.canonicalLocale,\n hrefFor: options.hrefFor,\n };\n}\n\n/**\n * PAS7 custom routing strategy:\n * - Home: / (en), /uk, /de, /it, /hr\n * - Hubs: /blog (en), /blog/uk, /blog/de\n * - Details: /blog/en/<slug>, /blog/uk/<slug>\n *\n * This is a hybrid routing scheme where:\n * - Home pages use prefix pattern for non-default locales\n * - Hub pages (listing pages) use suffix pattern for non-default locales\n * - Detail pages have locale as a segment after the section\n */\nexport function routingPAS7(options: RoutingPAS7Options): RoutingStrategy {\n const {\n defaultLocale,\n locales,\n hubPaths = [\"/blog\", \"/projects\", \"/services\", \"/cases\"],\n detailPathPattern = /^\\/(blog|projects|services|cases)\\//,\n } = options;\n\n return {\n locales,\n canonicalLocale: defaultLocale,\n hrefFor: ({ pathname, locale }) => {\n // Normalize pathname\n const normalizedPath = pathname.startsWith(\"/\") ? pathname : `/${pathname}`;\n\n // Home page\n if (normalizedPath === \"/\" || normalizedPath === \"\") {\n if (locale === defaultLocale) return \"/\";\n return `/${locale}`;\n }\n\n // Detail pages - locale is segment after section\n // /blog/en/slug → /blog/uk/slug\n if (detailPathPattern.test(normalizedPath)) {\n const parts = normalizedPath.split(\"/\").filter(Boolean);\n if (parts.length >= 3) {\n // parts[0] = section, parts[1] = locale, parts[2+] = slug\n const section = parts[0];\n const slug = parts.slice(2).join(\"/\");\n return `/${section}/${locale}/${slug}`;\n }\n }\n\n // Hub pages - locale as suffix\n // /blog → /blog/uk\n const matchingHub = hubPaths.find((hub) => {\n const normalizedHub = hub.startsWith(\"/\") ? hub : `/${hub}`;\n return normalizedPath === normalizedHub || normalizedPath === `${normalizedHub}/`;\n });\n if (matchingHub) {\n const normalizedHub = matchingHub.startsWith(\"/\")\n ? matchingHub.replace(/\\/$/, \"\")\n : `/${matchingHub.replace(/\\/$/, \"\")}`;\n if (locale === defaultLocale) return normalizedHub;\n return `${normalizedHub}/${locale}`;\n }\n\n // Fallback: prefix-as-needed style\n if (locale === defaultLocale) return normalizedPath;\n return `/${locale}${normalizedPath}`;\n },\n };\n}\n","import type { SitemapEntryLike } from \"./types.js\";\n\nexport type ExpandLocalesOptions = {\n mode: \"cluster-only\" | \"expanded\";\n includeXDefault?: boolean;\n locales?: readonly string[];\n};\n\nexport function expandLocaleEntries<T extends SitemapEntryLike>(\n entries: readonly T[],\n options: ExpandLocalesOptions,\n): T[] {\n if (options.mode === \"cluster-only\") {\n return [...entries];\n }\n\n const result: T[] = [];\n\n for (const entry of entries) {\n const languages = entry.alternates?.languages;\n if (!languages || Object.keys(languages).length === 0) {\n result.push(entry);\n continue;\n }\n\n // Create a separate entry for each locale\n for (const [locale, href] of Object.entries(languages)) {\n if (locale === \"x-default\" && !options.includeXDefault) continue;\n\n // Filter by specific locales if provided\n if (options.locales && !options.locales.includes(locale)) continue;\n\n const expandedEntry: T = {\n ...entry,\n url: href,\n // alternates.languages remains the full cluster\n };\n result.push(expandedEntry);\n }\n }\n\n // Dedupe by url\n const seen = new Set<string>();\n return result.filter((e) => {\n if (seen.has(e.url)) return false;\n seen.add(e.url);\n return true;\n });\n}\n","import type { ChangeFrequency, SitemapEntry, XDefaultStrategy } from \"./types.js\";\nimport { withHreflang } from \"./withHreflang.js\";\nimport { normalizeUrl, resolveAbsoluteUrl } from \"./url.js\";\n\nexport type ManifestRouteStyle =\n | \"prefix-as-needed\"\n | \"prefix-always\"\n | \"suffix-locale\"\n | \"locale-segment\";\n\nexport type LocalizedManifestItem = {\n slug: string;\n locales: readonly string[];\n updatedAt?: string | Date;\n publishedAt?: string | Date;\n lastModified?: string | Date;\n date?: string | Date;\n changeFrequency?: ChangeFrequency;\n priority?: number;\n images?: string[];\n};\n\nexport type CreateSitemapEntriesFromManifestOptions = {\n baseUrl: string;\n sectionPath: string;\n defaultLocale: string;\n canonicalLocale?: string;\n routeStyle?: ManifestRouteStyle;\n trailingSlash?: \"preserve\" | \"always\" | \"never\";\n ensureXDefault?: boolean;\n xDefaultStrategy?: XDefaultStrategy;\n includeLocales?: readonly string[];\n pathnameFor?: (args: {\n slug: string;\n locale: string;\n defaultLocale: string;\n sectionPath: string;\n routeStyle: ManifestRouteStyle;\n }) => string;\n};\n\nexport function createSitemapEntriesFromManifest<T extends LocalizedManifestItem>(\n items: readonly T[],\n options: CreateSitemapEntriesFromManifestOptions,\n): SitemapEntry[] {\n const routeStyle = options.routeStyle ?? \"locale-segment\";\n const includeXDefault = options.ensureXDefault ?? true;\n const trailingSlash = options.trailingSlash ?? \"preserve\";\n const includeLocales = options.includeLocales ? new Set(options.includeLocales) : null;\n\n const entries: SitemapEntry[] = [];\n\n for (const item of items) {\n const slug = normalizeSlug(item.slug);\n if (!slug) continue;\n\n const locales = unique(item.locales).filter((locale) => (includeLocales ? includeLocales.has(locale) : true));\n if (locales.length === 0) continue;\n\n const configuredCanonical = options.canonicalLocale ?? options.defaultLocale;\n const effectiveCanonicalLocale =\n locales.includes(configuredCanonical) ? configuredCanonical : locales[0] ?? configuredCanonical;\n\n const languages: Record<string, string> = {};\n for (const locale of locales) {\n const pathname =\n options.pathnameFor?.({\n slug,\n locale,\n defaultLocale: options.defaultLocale,\n sectionPath: options.sectionPath,\n routeStyle,\n }) ??\n buildPathname({\n slug,\n locale,\n defaultLocale: options.defaultLocale,\n sectionPath: options.sectionPath,\n routeStyle,\n });\n\n const href = normalizeUrl(resolveAbsoluteUrl(pathname, options.baseUrl), trailingSlash);\n languages[locale] = href;\n }\n\n const canonicalUrl =\n languages[effectiveCanonicalLocale] ??\n normalizeUrl(resolveAbsoluteUrl(\"/\", options.baseUrl), trailingSlash);\n\n const lastModified = pickLastModified(item);\n\n const baseEntry: SitemapEntry = {\n url: canonicalUrl,\n alternates: { languages },\n ...(lastModified ? { lastModified } : {}),\n ...(item.changeFrequency ? { changeFrequency: item.changeFrequency } : {}),\n ...(typeof item.priority === \"number\" ? { priority: item.priority } : {}),\n ...(item.images ? { images: [...item.images] } : {}),\n };\n\n const [entryWithHreflang] = withHreflang([baseEntry], {\n baseUrl: options.baseUrl,\n trailingSlash,\n ensureAbsolute: true,\n ensureXDefault: includeXDefault,\n ensureSelf: true,\n canonicalLocale: effectiveCanonicalLocale,\n ...(options.xDefaultStrategy ? { xDefaultStrategy: options.xDefaultStrategy } : {}),\n });\n\n if (entryWithHreflang) entries.push(entryWithHreflang);\n }\n\n return entries;\n}\n\nfunction buildPathname(args: {\n slug: string;\n locale: string;\n defaultLocale: string;\n sectionPath: string;\n routeStyle: ManifestRouteStyle;\n}): string {\n const section = normalizeSection(args.sectionPath);\n\n if (args.routeStyle === \"prefix-as-needed\") {\n if (args.locale === args.defaultLocale) return joinPath(section, args.slug);\n return joinPath(`/${args.locale}`, section, args.slug);\n }\n\n if (args.routeStyle === \"prefix-always\") {\n return joinPath(`/${args.locale}`, section, args.slug);\n }\n\n if (args.routeStyle === \"suffix-locale\") {\n if (args.locale === args.defaultLocale) return joinPath(section, args.slug);\n return joinPath(section, args.slug, args.locale);\n }\n\n return joinPath(section, args.locale, args.slug);\n}\n\nfunction normalizeSlug(slug: string): string {\n return slug.trim().replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n}\n\nfunction normalizeSection(sectionPath: string): string {\n const normalized = sectionPath.trim();\n if (!normalized || normalized === \"/\") return \"\";\n return `/${normalized.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\")}`;\n}\n\nfunction joinPath(...parts: string[]): string {\n const cleaned = parts\n .map((part) => part.trim())\n .filter(Boolean)\n .map((part) => part.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\"));\n\n if (cleaned.length === 0) return \"/\";\n return `/${cleaned.join(\"/\")}`;\n}\n\nfunction unique(values: readonly string[]): string[] {\n return [...new Set(values)];\n}\n\nfunction pickLastModified(item: LocalizedManifestItem): string | Date | undefined {\n return item.lastModified ?? item.updatedAt ?? item.publishedAt ?? item.date;\n}\n","export function xmlEscape(input: string): string {\n return input\n .replaceAll(\"&\", \"&amp;\")\n .replaceAll(\"<\", \"&lt;\")\n .replaceAll(\">\", \"&gt;\")\n .replaceAll('\"', \"&quot;\")\n .replaceAll(\"'\", \"&apos;\");\n}\n\nexport function hasXhtmlNamespace(xml: string): boolean {\n return /<urlset[^>]*\\sxmlns:xhtml=\"http:\\/\\/www\\.w3\\.org\\/1999\\/xhtml\"[^>]*>/.test(xml);\n}\n\nexport function ensureXhtmlNamespace(xml: string): string {\n if (hasXhtmlNamespace(xml)) return xml;\n return xml.replace(\n /<urlset(\\s[^>]*?)?>/m,\n (m) => (m.includes(\"xmlns:xhtml=\") ? m : m.replace(\"<urlset\", '<urlset xmlns:xhtml=\"http://www.w3.org/1999/xhtml\"')),\n );\n}\n\nexport function extractUrlBlocks(xml: string): string[] {\n return xml.match(/<url>[\\s\\S]*?<\\/url>/g) ?? [];\n}\n\nexport function extractLoc(urlBlock: string): string | null {\n return urlBlock.match(/<loc>([^<]+)<\\/loc>/)?.[1]?.trim() ?? null;\n}\n\nexport function extractXhtmlLinks(urlBlock: string): Array<{ hreflang: string; href: string }> {\n const out: Array<{ hreflang: string; href: string }> = [];\n const re = /<xhtml:link[^>]*\\shreflang=\"([^\"]+)\"[^>]*\\shref=\"([^\"]+)\"[^>]*\\/>/g;\n for (const m of urlBlock.matchAll(re)) {\n if (m[1] !== undefined && m[2] !== undefined) {\n out.push({ hreflang: m[1], href: m[2] });\n }\n }\n return out;\n}\n\nexport function hasXDefault(urlBlock: string): boolean {\n return /<xhtml:link[^>]*\\shreflang=\"x-default\"[^>]*\\/>/.test(urlBlock);\n}\n\nexport function insertXhtmlLink(urlBlock: string, linkXml: string): string {\n const lastLinkIdx = urlBlock.lastIndexOf(\"<xhtml:link\");\n if (lastLinkIdx === -1) {\n const locEnd = urlBlock.indexOf(\"</loc>\");\n if (locEnd === -1) return urlBlock;\n const insertPos = locEnd + \"</loc>\".length;\n return `${urlBlock.slice(0, insertPos)}\\n ${linkXml}${urlBlock.slice(insertPos)}`;\n }\n const lineEnd = urlBlock.indexOf(\"\\n\", lastLinkIdx);\n if (lineEnd === -1) return `${urlBlock}\\n ${linkXml}`;\n return `${urlBlock.slice(0, lineEnd)}\\n ${linkXml}${urlBlock.slice(lineEnd)}`;\n}\n\nexport type ReorderOptions = {\n canonicalLocale?: string;\n order: \"canonical-first\" | \"preserve\";\n};\n\nexport function reorderXhtmlLinks(urlBlock: string, options: ReorderOptions): string {\n if (options.order === \"preserve\") return urlBlock;\n\n const links = extractXhtmlLinks(urlBlock);\n if (links.length === 0) return urlBlock;\n\n const loc = extractLoc(urlBlock);\n\n // Determine canonical\n let canonical: { hreflang: string; href: string } | undefined;\n if (options.canonicalLocale) {\n canonical = links.find((l) => l.hreflang === options.canonicalLocale);\n }\n if (!canonical && loc) {\n canonical = links.find((l) => l.href === loc);\n }\n\n // Split into groups\n const canonicalLinks = canonical ? [canonical] : [];\n const otherLinks = links.filter((l) => l !== canonical && l.hreflang !== \"x-default\");\n const xDefaultLinks = links.filter((l) => l.hreflang === \"x-default\");\n\n // Build new order\n const orderedLinks = [...canonicalLinks, ...otherLinks, ...xDefaultLinks];\n\n // Remove all existing xhtml:link and insert in new order\n const newBlock = urlBlock.replace(/<xhtml:link[^>]*\\/>\\s*/g, \"\");\n\n // Find position after </loc>\n const locEnd = newBlock.indexOf(\"</loc>\");\n if (locEnd === -1) return urlBlock;\n\n const insertPos = locEnd + \"</loc>\".length;\n const linksXml = orderedLinks\n .map((l) => `\\n <xhtml:link rel=\"alternate\" hreflang=\"${xmlEscape(l.hreflang)}\" href=\"${xmlEscape(l.href)}\" />`)\n .join(\"\");\n\n return `${newBlock.slice(0, insertPos)}${linksXml}\\n ${newBlock.slice(insertPos).trimStart()}`;\n}\n\nexport function normalizeTrailingSlashInBlock(\n urlBlock: string,\n policy: \"preserve\" | \"always\" | \"never\",\n): string {\n if (policy === \"preserve\") return urlBlock;\n\n // Normalize <loc>\n let result = urlBlock.replace(/<loc>([^<]+)<\\/loc>/g, (_, url: string) => {\n return `<loc>${applyTrailingSlashPolicy(url, policy)}</loc>`;\n });\n\n // Normalize href in xhtml:link\n result = result.replace(\n /(<xhtml:link[^>]*href=\")([^\"]+)(\"[^>]*\\/>)/g,\n (_, prefix: string, url: string, suffix: string) => {\n return `${prefix}${applyTrailingSlashPolicy(url, policy)}${suffix}`;\n },\n );\n\n return result;\n}\n\nfunction applyTrailingSlashPolicy(url: string, policy: \"always\" | \"never\"): string {\n try {\n const u = new URL(url);\n if (policy === \"always\" && !u.pathname.endsWith(\"/\")) {\n u.pathname += \"/\";\n } else if (policy === \"never\" && u.pathname !== \"/\" && u.pathname.endsWith(\"/\")) {\n u.pathname = u.pathname.slice(0, -1);\n }\n return u.toString();\n } catch {\n return url;\n }\n}\n","import { getOriginFromAbsoluteUrl, resolveAbsoluteUrl } from \"../lib/url.js\";\nimport type { XDefaultStrategy } from \"../lib/types.js\";\nimport {\n ensureXhtmlNamespace,\n extractLoc,\n extractUrlBlocks,\n extractXhtmlLinks,\n hasXDefault,\n insertXhtmlLink,\n normalizeTrailingSlashInBlock,\n reorderXhtmlLinks,\n xmlEscape,\n} from \"./xml.js\";\n\nexport type InjectOptions = {\n baseUrl?: string;\n xDefaultStrategy?: XDefaultStrategy;\n ensureNamespace?: boolean;\n canonicalLocale?: string;\n order?: \"canonical-first\" | \"preserve\";\n trailingSlash?: \"preserve\" | \"always\" | \"never\";\n};\n\nexport function injectXDefaultIntoSitemapXml(xml: string, options: InjectOptions): string {\n const ensureNamespace = options.ensureNamespace ?? true;\n const xDefaultStrategy = options.xDefaultStrategy ?? { type: \"loc\" };\n const order = options.order ?? \"preserve\";\n const trailingSlash = options.trailingSlash ?? \"preserve\";\n\n const xmlWithNs = ensureNamespace ? ensureXhtmlNamespace(xml) : xml;\n\n const blocks = extractUrlBlocks(xmlWithNs);\n if (blocks.length === 0) return xmlWithNs;\n\n let out = xmlWithNs;\n\n for (const block of blocks) {\n const loc = extractLoc(block);\n if (!loc) continue;\n\n const links = extractXhtmlLinks(block);\n if (links.length === 0) continue;\n\n let nextBlock = block;\n\n if (!hasXDefault(block)) {\n const href = resolveXDefaultHref({\n loc,\n links,\n ...(options.baseUrl ? { baseUrl: options.baseUrl } : {}),\n strategy: xDefaultStrategy,\n });\n\n const linkXml = `<xhtml:link rel=\"alternate\" hreflang=\"x-default\" href=\"${xmlEscape(href)}\" />`;\n nextBlock = insertXhtmlLink(nextBlock, linkXml);\n }\n\n // Apply reordering if needed\n if (order === \"canonical-first\") {\n nextBlock = reorderXhtmlLinks(nextBlock, {\n ...(options.canonicalLocale ? { canonicalLocale: options.canonicalLocale } : {}),\n order: \"canonical-first\",\n });\n }\n\n // Apply trailing slash normalization\n if (trailingSlash !== \"preserve\") {\n nextBlock = normalizeTrailingSlashInBlock(nextBlock, trailingSlash);\n }\n\n out = out.replace(block, nextBlock);\n }\n\n return out;\n}\n\nfunction resolveXDefaultHref(args: {\n loc: string;\n links: Array<{ hreflang: string; href: string }>;\n baseUrl?: string;\n strategy: XDefaultStrategy;\n}): string {\n const { loc, links, baseUrl, strategy } = args;\n\n const locAbs = baseUrl ? resolveAbsoluteUrl(loc, baseUrl) : loc;\n const origin = isAbsolute(locAbs) ? getOriginFromAbsoluteUrl(locAbs) : baseUrl;\n\n if (strategy.type === \"loc\") return locAbs;\n if (strategy.type === \"root\") {\n if (!origin) return locAbs;\n return resolveAbsoluteUrl(\"/\", origin);\n }\n if (strategy.type === \"custom\") {\n if (!origin) return strategy.url;\n return resolveAbsoluteUrl(strategy.url, origin);\n }\n if (strategy.type === \"locale\") {\n const found = links.find((l) => l.hreflang === strategy.locale)?.href;\n if (found) return baseUrl ? resolveAbsoluteUrl(found, baseUrl) : found;\n return locAbs;\n }\n const computed = strategy.resolve({ url: locAbs });\n if (!origin) return computed;\n return resolveAbsoluteUrl(computed, origin);\n}\n\nfunction isAbsolute(u: string): boolean {\n return u.startsWith(\"http://\") || u.startsWith(\"https://\");\n}\n","import type { HreflangIssue, HreflangReport } from \"../lib/types.js\";\nimport { extractLoc, extractUrlBlocks, extractXhtmlLinks, hasXhtmlNamespace } from \"./xml.js\";\n\nexport type CheckXmlOptions = {\n requireNamespace?: boolean;\n requireXDefaultWhenMultiple?: boolean;\n requireAbsolute?: boolean;\n checkDuplicateKeys?: boolean;\n checkDuplicateHrefs?: boolean;\n checkHreflangCasing?: boolean;\n originPolicy?: \"same\" | \"allowlist\" | \"off\";\n allowedOrigins?: string[];\n};\n\nexport function checkSitemapXmlHreflang(xml: string, options: CheckXmlOptions): HreflangReport {\n const requireNamespace = options.requireNamespace ?? true;\n const requireXDefaultWhenMultiple = options.requireXDefaultWhenMultiple ?? true;\n const requireAbsolute = options.requireAbsolute ?? true;\n const checkDuplicateKeys = options.checkDuplicateKeys ?? true;\n const checkDuplicateHrefs = options.checkDuplicateHrefs ?? true;\n const checkHreflangCasing = options.checkHreflangCasing ?? true;\n const originPolicy = options.originPolicy ?? \"off\";\n\n const issues: HreflangIssue[] = [];\n\n const blocks = extractUrlBlocks(xml);\n\n if (requireNamespace) {\n const usesXhtml = /<xhtml:link\\b/.test(xml);\n if (usesXhtml && !hasXhtmlNamespace(xml)) {\n issues.push({\n code: \"MISSING_LANGUAGES\",\n entryUrl: \"sitemap.xml\",\n message: 'Missing xmlns:xhtml=\"http://www.w3.org/1999/xhtml\" in <urlset>',\n });\n }\n }\n\n for (const block of blocks) {\n const loc = extractLoc(block);\n if (!loc) continue;\n\n const links = extractXhtmlLinks(block);\n if (links.length === 0) continue;\n\n const hasXDefault = links.some((l) => l.hreflang === \"x-default\");\n if (requireXDefaultWhenMultiple && links.length > 1 && !hasXDefault) {\n issues.push({\n code: \"MISSING_XDEFAULT\",\n entryUrl: loc,\n message: \"Missing x-default hreflang in sitemap url block\",\n });\n }\n\n // Check for duplicate hreflang keys\n if (checkDuplicateKeys) {\n const seenKeys = new Set<string>();\n for (const link of links) {\n if (seenKeys.has(link.hreflang)) {\n issues.push({\n code: \"DUPLICATE_HREFLANG_KEY\",\n entryUrl: loc,\n message: `Duplicate hreflang key: ${link.hreflang}`,\n });\n }\n seenKeys.add(link.hreflang);\n }\n }\n\n // Check for duplicate hrefs (excluding x-default which can share href with another locale)\n if (checkDuplicateHrefs) {\n const hrefToLocales = new Map<string, string[]>();\n for (const link of links) {\n const locales = hrefToLocales.get(link.href) ?? [];\n locales.push(link.hreflang);\n hrefToLocales.set(link.href, locales);\n }\n for (const [href, locales] of hrefToLocales) {\n // Filter out x-default - it's allowed to share href with another locale\n const nonXDefaultLocales = locales.filter((l) => l !== \"x-default\");\n if (nonXDefaultLocales.length > 1) {\n issues.push({\n code: \"DUPLICATE_HREF\",\n entryUrl: loc,\n message: `Duplicate hreflang href detected: ${href} (locales: ${nonXDefaultLocales.join(\", \")})`,\n });\n }\n }\n }\n\n // Check hreflang casing\n if (checkHreflangCasing) {\n for (const link of links) {\n if (!isValidHreflangCasing(link.hreflang)) {\n issues.push({\n code: \"INVALID_HREFLANG_CASING\",\n entryUrl: loc,\n message: `Invalid hreflang casing: ${link.hreflang}. Expected format: en, pt-BR, or x-default`,\n });\n }\n }\n }\n\n // Check origin policy\n if (originPolicy === \"same\") {\n const locOrigin = getOrigin(loc);\n if (locOrigin) {\n for (const link of links) {\n const linkOrigin = getOrigin(link.href);\n if (linkOrigin && linkOrigin !== locOrigin) {\n issues.push({\n code: \"INCONSISTENT_ORIGIN\",\n entryUrl: loc,\n message: `Inconsistent origin for ${link.hreflang}: expected ${locOrigin}, got ${linkOrigin}`,\n });\n }\n }\n }\n } else if (originPolicy === \"allowlist\") {\n const allowedOrigins = options.allowedOrigins ?? [];\n for (const link of links) {\n const linkOrigin = getOrigin(link.href);\n if (linkOrigin && !allowedOrigins.includes(linkOrigin)) {\n issues.push({\n code: \"INCONSISTENT_ORIGIN\",\n entryUrl: loc,\n message: `Origin not in allowlist for ${link.hreflang}: ${linkOrigin}`,\n });\n }\n }\n const locOrigin = getOrigin(loc);\n if (locOrigin && !allowedOrigins.includes(locOrigin)) {\n issues.push({\n code: \"INCONSISTENT_ORIGIN\",\n entryUrl: loc,\n message: `Origin not in allowlist for loc: ${locOrigin}`,\n });\n }\n }\n\n if (requireAbsolute) {\n for (const link of links) {\n if (!isAbsolute(link.href)) {\n issues.push({\n code: \"NON_ABSOLUTE_URL\",\n entryUrl: loc,\n message: `Non-absolute hreflang href for ${link.hreflang}: ${link.href}`,\n });\n }\n }\n if (!isAbsolute(loc)) {\n issues.push({\n code: \"NON_ABSOLUTE_URL\",\n entryUrl: loc,\n message: `Non-absolute <loc>: ${loc}`,\n });\n }\n }\n }\n\n return { ok: issues.length === 0, issues };\n}\n\nfunction isAbsolute(u: string): boolean {\n return u.startsWith(\"http://\") || u.startsWith(\"https://\");\n}\n\nfunction getOrigin(url: string): string | null {\n try {\n const u = new URL(url);\n return u.origin;\n } catch {\n return null;\n }\n}\n\nfunction isValidHreflangCasing(key: string): boolean {\n if (key === \"x-default\") return true;\n // en, de, uk - only lowercase\n if (/^[a-z]{2}$/.test(key)) return true;\n // pt-BR, en-US - lowercase-UPPERCASE\n if (/^[a-z]{2}-[A-Z]{2}$/.test(key)) return true;\n return false;\n}\n"],"mappings":"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","names":["href","normalizedPath","hasXDefault","isAbsolute"]}
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "@pas7/nextjs-sitemap-hreflang",
3
+ "version": "0.3.1",
4
+ "description": "Add and validate hreflang alternates + x-default for Next.js sitemaps (App Router / MetadataRoute) with a tiny library + CLI postbuild fixer.",
5
+ "homepage": "https://github.com/pas7-studio/nextjs-sitemap-hreflang#readme",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/pas7-studio/nextjs-sitemap-hreflang.git"
9
+ },
10
+ "bugs": {
11
+ "url": "https://github.com/pas7-studio/nextjs-sitemap-hreflang/issues"
12
+ },
13
+ "license": "MIT",
14
+ "type": "module",
15
+ "sideEffects": false,
16
+ "publishConfig": {
17
+ "access": "public",
18
+ "provenance": true
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "README.md",
23
+ "LICENSE"
24
+ ],
25
+ "exports": {
26
+ ".": {
27
+ "types": "./dist/index.d.ts",
28
+ "import": "./dist/index.js"
29
+ }
30
+ },
31
+ "bin": {
32
+ "nextjs-sitemap-hreflang": "./dist/cli.js"
33
+ },
34
+ "scripts": {
35
+ "lint": "eslint .",
36
+ "format": "prettier -w .",
37
+ "typecheck": "tsc -p tsconfig.json --noEmit",
38
+ "test": "vitest run",
39
+ "test:watch": "vitest",
40
+ "build": "tsup",
41
+ "changeset": "changeset",
42
+ "version-packages": "changeset version",
43
+ "release": "npm publish --provenance --access public",
44
+ "prepublishOnly": "npm run lint && npm run typecheck && npm run test && npm run build"
45
+ },
46
+ "keywords": [
47
+ "nextjs",
48
+ "next.js",
49
+ "app-router",
50
+ "metadata-route",
51
+ "sitemap",
52
+ "sitemap.xml",
53
+ "hreflang",
54
+ "x-default",
55
+ "alternates",
56
+ "languages",
57
+ "i18n",
58
+ "international seo",
59
+ "google search console",
60
+ "seo",
61
+ "next-intl",
62
+ "next-i18next"
63
+ ],
64
+ "devDependencies": {
65
+ "@changesets/cli": "^2.29.7",
66
+ "@eslint/js": "^9.0.0",
67
+ "@types/node": "^20.0.0",
68
+ "eslint": "^9.0.0",
69
+ "prettier": "^3.0.0",
70
+ "tsup": "^8.0.0",
71
+ "typescript": "^5.0.0",
72
+ "typescript-eslint": "^8.0.0",
73
+ "vitest": "^2.0.0"
74
+ }
75
+ }