@power-seo/sitemap 1.0.4 → 1.0.5

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.
package/README.md CHANGED
@@ -1,75 +1,92 @@
1
- # @power-seo/sitemap — XML Sitemap Generator for TypeScript — Streaming, Index Splitting & Image/Video/News Extensions
1
+ # @power-seo/sitemap
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/@power-seo/sitemap)](https://www.npmjs.com/package/@power-seo/sitemap)
4
4
  [![npm downloads](https://img.shields.io/npm/dm/@power-seo/sitemap)](https://www.npmjs.com/package/@power-seo/sitemap)
5
+ [![Socket](https://socket.dev/api/badge/npm/package/@power-seo/sitemap)](https://socket.dev/npm/package/@power-seo/sitemap)
6
+ [![npm provenance](https://img.shields.io/badge/npm-provenance-enabled-blue)](https://github.com/CyberCraftBD/power-seo/actions)
5
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
8
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue)](https://www.typescriptlang.org/)
7
9
  [![tree-shakeable](https://img.shields.io/badge/tree--shakeable-yes-brightgreen)](https://bundlephobia.com/package/@power-seo/sitemap)
8
10
 
9
- ---
10
-
11
- ## Overview
12
-
13
- **@power-seo/sitemap** is a zero-dependency XML sitemap generator for TypeScript that helps you generate standards-compliant sitemaps with image, video, and news extensions — including streaming generation and automatic index splitting for large sites.
14
-
15
- **What it does**
16
-
17
- - ✅ **Generate XML sitemaps** — `generateSitemap()` produces spec-compliant `<urlset>` XML strings
18
- - ✅ **Stream large sitemaps** — `streamSitemap()` yields XML chunks with constant memory usage
19
- - ✅ **Split into multiple files** — `splitSitemap()` auto-chunks at the 50,000-URL limit
20
- - ✅ **Generate sitemap indexes** — `generateSitemapIndex()` creates `<sitemapindex>` files pointing to child sitemaps
21
- - ✅ **Validate URL entries** — `validateSitemapUrl()` checks against Google's sitemap spec requirements
11
+ XML sitemap generation for TypeScript — streaming output, automatic index splitting, image/video/news extensions, and URL validation — works in Next.js, Remix, Express, and edge runtimes with zero runtime dependencies.
22
12
 
23
- **What it is not**
13
+ `@power-seo/sitemap` produces standards-compliant `<urlset>` and `<sitemapindex>` XML from typed URL arrays. Provide a hostname and URL list — get back a valid XML string ready to serve as `Content-Type: application/xml`. For large catalogs, stream chunks with constant memory usage or auto-split at the 50,000-URL spec limit with a generated index file. All five functions are independently importable and tree-shakeable.
24
14
 
25
- - **Not a sitemap crawler** — does not discover URLs by crawling your site
26
- - ❌ **Not a submission client** — use `@power-seo/search-console` to submit sitemaps to GSC
27
-
28
- **Recommended for**
29
-
30
- - **Next.js App Router sites**, **Remix apps**, **Express servers**, **static site generators**, and any Node.js/edge environment that generates sitemaps programmatically
15
+ > **Zero runtime dependencies** — only `@power-seo/core` as a peer.
31
16
 
32
17
  ---
33
18
 
34
- ## Why @power-seo/sitemap Matters
35
-
36
- **The problem**
37
-
38
- - **Sites with 50,000+ URLs** cannot fit in a single sitemap file — the spec mandates splitting
39
- - **Image and video sitemaps** require `<image:image>` and `<video:video>` namespace extensions that most generators don't support
40
- - **Memory spikes** during XML string concatenation cause crashes or timeouts on large e-commerce catalogs
41
-
42
- **Why developers care**
19
+ ## Why @power-seo/sitemap?
43
20
 
44
- - **SEO:** Well-structured sitemaps improve crawl coverage and ensure all pages are discovered
45
- - **Performance:** Streaming generation keeps memory usage constant for million-URL datasets
46
- - **Indexing:** Image sitemaps help Google discover and index product images for Google Images
21
+ | | Without | With |
22
+ | ----------------- | ----------------------------------------- | --------------------------------------------------- |
23
+ | Spec compliance | Hand-built XML, wrong namespaces | Correct `<urlset>` + namespace declarations |
24
+ | Large sites | ❌ Single file breaks at 50,000 URLs | ✅ Auto-split + sitemap index generation |
25
+ | Memory usage | ❌ String concat spikes on large catalogs | ✅ Synchronous generator yields chunks |
26
+ | Image indexing | ❌ Product images undiscoverable | ✅ `<image:image>` extension per URL |
27
+ | Video SEO | ❌ No structured video metadata | ✅ `<video:video>` extension with title, duration |
28
+ | News sitemaps | ❌ Missing publication + date tags | ✅ `<news:news>` extension for Google News |
29
+ | Hostname handling | ❌ Hardcode absolute URLs everywhere | ✅ Pass `hostname` once; use relative `loc` paths |
30
+ | Validation | ❌ Silent bad data reaches Google | ✅ `validateSitemapUrl()` returns errors + warnings |
47
31
 
48
32
  ---
49
33
 
50
- ## Key Features
34
+ ## Features
51
35
 
52
- - **Full sitemap spec support** — `<loc>`, `<lastmod>`, `<changefreq>`, `<priority>`, and all optional elements
53
- - **Image sitemap extension** — `<image:image>` tags with `loc`, `caption`, `title`, `license`
54
- - **Video sitemap extension** — `<video:video>` tags with title, description, thumbnail, duration
36
+ - **Full sitemap spec support** — `<loc>`, `<lastmod>`, `<changefreq>`, `<priority>`, all optional elements
37
+ - **Hostname + relative paths** — pass `hostname` in config; `loc` can be a relative path like `/about`
38
+ - **Image sitemap extension** — `<image:image>` tags with `loc`, `caption`, `title`, `geoLocation`, `license`
39
+ - **Video sitemap extension** — `<video:video>` tags with title, description, thumbnail, duration, rating
55
40
  - **News sitemap extension** — `<news:news>` tags with publication name, language, date
56
- - **Streaming generation** — `streamSitemap()` returns `AsyncIterable<string>` no memory spike on large lists
57
- - **Automatic index splitting** — `splitSitemap()` chunks at `MAX_URLS_PER_SITEMAP` (50,000)
41
+ - **Streaming generation** — `streamSitemap()` is a synchronous generator yielding XML string chunks; no memory spike on large lists
42
+ - **Automatic index splitting** — `splitSitemap()` chunks at `MAX_URLS_PER_SITEMAP` (50,000) and returns both sitemaps and the index XML
58
43
  - **Sitemap index generation** — `generateSitemapIndex()` creates a `<sitemapindex>` pointing to child sitemaps
59
44
  - **URL validation** — `validateSitemapUrl()` returns `{ valid, errors, warnings }` without throwing
60
- - **Constants exported** — `MAX_URLS_PER_SITEMAP` (50,000) and `MAX_SITEMAP_SIZE_BYTES` (50MB)
45
+ - **Next.js App Router adapter** — `toNextSitemap()` (from `@power-seo/sitemap/next`) converts `SitemapURL[]` to the `MetadataRoute.Sitemap[]` format for `app/sitemap.ts`
46
+ - **Constants exported** — `MAX_URLS_PER_SITEMAP` (50,000) and `MAX_SITEMAP_SIZE_BYTES` (52,428,800)
61
47
  - **Framework-agnostic** — works in Next.js API routes, Remix loaders, Express, Fastify, and edge runtimes
62
48
  - **Full TypeScript support** — typed `SitemapURL`, `SitemapImage`, `SitemapVideo`, `SitemapNews`, `SitemapConfig`
63
49
  - **Zero runtime dependencies** — pure TypeScript, no external XML libraries
50
+ - **Tree-shakeable** — import only the functions you use
64
51
 
65
52
  ---
66
53
 
67
- ## Benefits of Using @power-seo/sitemap
54
+ ## Comparison
55
+
56
+ | Feature | @power-seo/sitemap | next-sitemap | sitemap (npm) | xmlbuilder2 |
57
+ | -------------------------------- | :----------------: | :----------: | :-----------: | :---------: |
58
+ | Image sitemap extension | ✅ | ✅ | ✅ | ❌ |
59
+ | Video sitemap extension | ✅ | ❌ | ✅ | ❌ |
60
+ | News sitemap extension | ✅ | ❌ | ✅ | ❌ |
61
+ | Streaming generation | ✅ | ❌ | ❌ | ❌ |
62
+ | Auto index splitting | ✅ | ✅ | ❌ | ❌ |
63
+ | URL validation | ✅ | ❌ | ❌ | ❌ |
64
+ | Hostname + relative loc paths | ✅ | ❌ | ❌ | ❌ |
65
+ | Zero runtime dependencies | ✅ | ❌ | ❌ | ❌ |
66
+ | Edge runtime compatible | ✅ | ❌ | ❌ | ❌ |
67
+ | TypeScript-first | ✅ | Partial | ❌ | ❌ |
68
+ | Tree-shakeable | ✅ | ❌ | ❌ | ❌ |
69
+ | Next.js `app/sitemap.ts` adapter | ✅ | ✅ | ❌ | ❌ |
68
70
 
69
- - **Improved crawl coverage**: Well-structured sitemaps with `lastmod` help Googlebot prioritize fresh pages
70
- - **Better image indexing**: `<image:image>` extensions surface product images in Google Images
71
- - **Safer implementation**: `validateSitemapUrl()` catches out-of-range `priority` values and invalid dates before serving
72
- - **Faster delivery**: Zero-dependency streaming generation works in any runtime without configuration
71
+ ---
72
+
73
+ ## Installation
74
+
75
+ ```bash
76
+ npm install @power-seo/sitemap
77
+ ```
78
+
79
+ ```bash
80
+ yarn add @power-seo/sitemap
81
+ ```
82
+
83
+ ```bash
84
+ pnpm add @power-seo/sitemap
85
+ ```
86
+
87
+ ```bash
88
+ bun add @power-seo/sitemap
89
+ ```
73
90
 
74
91
  ---
75
92
 
@@ -79,200 +96,216 @@
79
96
  import { generateSitemap } from '@power-seo/sitemap';
80
97
 
81
98
  const xml = generateSitemap({
99
+ hostname: 'https://example.com',
82
100
  urls: [
83
- { loc: 'https://example.com/', lastmod: '2026-01-01', changefreq: 'daily', priority: 1.0 },
84
- { loc: 'https://example.com/about', changefreq: 'monthly', priority: 0.8 },
85
- { loc: 'https://example.com/blog/post-1', lastmod: '2026-01-15', priority: 0.6 },
101
+ { loc: '/', lastmod: '2026-01-01', changefreq: 'daily', priority: 1.0 },
102
+ { loc: '/about', changefreq: 'monthly', priority: 0.8 },
103
+ { loc: '/blog/post-1', lastmod: '2026-01-15', priority: 0.6 },
86
104
  ],
87
105
  });
88
106
 
89
107
  // Returns valid XML string:
90
108
  // <?xml version="1.0" encoding="UTF-8"?>
91
- // <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">...
109
+ // <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
110
+ // <url><loc>https://example.com/</loc>...
92
111
  ```
93
112
 
94
- **What you should see**
95
-
96
- - A standards-compliant XML sitemap string ready to serve as `Content-Type: application/xml`
97
- - `<urlset>` containing `<url>` entries with the fields you provided
113
+ `hostname` is required — it is prepended to any `loc` value that is a relative path. Absolute `loc` values (starting with `http`) are used as-is.
98
114
 
99
115
  ---
100
116
 
101
- ## Installation
117
+ ## Usage
102
118
 
103
- ```bash
104
- npm i @power-seo/sitemap
105
- # or
106
- yarn add @power-seo/sitemap
107
- # or
108
- pnpm add @power-seo/sitemap
109
- # or
110
- bun add @power-seo/sitemap
111
- ```
119
+ ### Generating a Sitemap
112
120
 
113
- ---
121
+ `generateSitemap()` accepts a `SitemapConfig` with `hostname` and `urls` and returns a complete XML string.
122
+
123
+ ```ts
124
+ import { generateSitemap } from '@power-seo/sitemap';
114
125
 
115
- ## Framework Compatibility
126
+ const xml = generateSitemap({
127
+ hostname: 'https://example.com',
128
+ urls: [
129
+ { loc: '/', lastmod: '2026-01-01', changefreq: 'daily', priority: 1.0 },
130
+ { loc: '/products', changefreq: 'weekly', priority: 0.9 },
131
+ { loc: '/blog', changefreq: 'daily', priority: 0.8 },
132
+ ],
133
+ });
116
134
 
117
- **Supported**
135
+ // Serve as application/xml
136
+ res.setHeader('Content-Type', 'application/xml');
137
+ res.send(xml);
138
+ ```
118
139
 
119
- - Next.js App Router — use in `app/sitemap.xml/route.ts` API route
120
- - ✅ Next.js Pages Router — use in `pages/api/sitemap.xml.ts`
121
- - ✅ Remix — use in resource routes
122
- - ✅ Node.js 18+ — pure TypeScript, no native bindings
123
- - ✅ Edge runtimes — no `fs`, no `path`, no Node.js-specific APIs (except `streamSitemap` which uses `AsyncIterable`)
124
- - ✅ Fastify / Express — serve the XML string as response body
140
+ ### Streaming a Large Sitemap
125
141
 
126
- **Environment notes**
142
+ `streamSitemap()` is a synchronous generator. It yields XML string chunks one `<url>` at a time — keeping memory usage constant regardless of catalog size.
127
143
 
128
- - **SSR/SSG:** Fully supported — generate at request time or at build time
129
- - **Edge runtime:** `generateSitemap()` and `generateSitemapIndex()` are edge-compatible; `streamSitemap()` requires Node.js streams if writing to disk
130
- - **Browser-only usage:** Not applicable — sitemap generation is a server-side concern
144
+ ```ts
145
+ import { streamSitemap } from '@power-seo/sitemap';
131
146
 
132
- ---
147
+ const urls = fetchAllProductUrls(); // Iterable<SitemapURL>
133
148
 
134
- ## Use Cases
149
+ const stream = streamSitemap('https://example.com', urls);
150
+ for (const chunk of stream) {
151
+ response.write(chunk);
152
+ }
153
+ response.end();
154
+ ```
135
155
 
136
- - **Next.js SSG sites** generate a full sitemap from your CMS at build time
137
- - **E-commerce catalogs** — product image sitemaps with `<image:image>` for every listing
138
- - **News publishers** — `<news:news>` extension for Google News sitemap submission
139
- - **Multi-locale sites** — separate sitemaps per locale with a unified sitemap index
140
- - **Programmatic SEO** — generate sitemaps for thousands of auto-generated pages
141
- - **Large sites** — automatic splitting at 50,000 URLs per file with index generation
142
- - **Video platforms** — `<video:video>` extension for YouTube-style video SEO
156
+ ### Splitting Large Sitemaps with an Index
143
157
 
144
- ---
158
+ `splitSitemap()` chunks a config at the 50,000-URL spec limit and returns all individual sitemap XML strings plus a sitemap index XML string that references them.
145
159
 
146
- ## Example (Before / After)
160
+ ```ts
161
+ import { splitSitemap } from '@power-seo/sitemap';
162
+
163
+ const { index, sitemaps } = splitSitemap({
164
+ hostname: 'https://example.com',
165
+ urls: largeUrlArray, // more than 50,000 entries
166
+ });
147
167
 
148
- ```text
149
- Before:
150
- - Hand-built XML strings: missing namespace declarations, wrong date formats
151
- - Single 80,000-URL sitemap: invalid per spec (max 50,000), Google truncates
152
- - No image sitemap: product images not discovered by Googlebot Images
168
+ // Write each sitemap file
169
+ for (const { filename, xml } of sitemaps) {
170
+ fs.writeFileSync(`./public${filename}`, xml);
171
+ }
153
172
 
154
- After (@power-seo/sitemap):
155
- - generateSitemap({ urls }) → spec-compliant XML with correct namespace
156
- - splitSitemap(allUrls) + generateSitemapIndex() → auto-split + index file
157
- - urls[i].images = [{ loc, caption }] → <image:image> tags for each product
173
+ // Write the index (default filenames: /sitemap-0.xml, /sitemap-1.xml, ...)
174
+ fs.writeFileSync('./public/sitemap.xml', index);
158
175
  ```
159
176
 
160
- ---
177
+ Custom filename pattern:
161
178
 
162
- ## Implementation Best Practices
179
+ ```ts
180
+ const { index, sitemaps } = splitSitemap(
181
+ { hostname: 'https://example.com', urls: largeUrlArray },
182
+ '/sitemaps/part-{index}.xml', // default: '/sitemap-{index}.xml'
183
+ );
184
+ ```
163
185
 
164
- - **Always include `lastmod`** Googlebot uses it to prioritize re-crawling updated pages
165
- - **Keep `priority` values realistic** — setting everything to 1.0 signals ignored priority; reserve 1.0 for the homepage
166
- - **Use `changefreq: 'never'` for permanent content** — signals Googlebot to skip re-crawling
167
- - **Set `sitemap.xml` URL in `robots.txt`** — `Sitemap: https://example.com/sitemap.xml` is required for discovery
168
- - **Submit to Google Search Console** after generating — use `@power-seo/search-console` `submitSitemap()`
186
+ ### Generating a Sitemap Index Manually
169
187
 
170
- ---
188
+ Use `generateSitemapIndex()` when you maintain separate sitemaps per section or locale and want to combine them under a single index file.
171
189
 
172
- ## Architecture Overview
190
+ ```ts
191
+ import { generateSitemapIndex } from '@power-seo/sitemap';
173
192
 
174
- **Where it runs**
193
+ const indexXml = generateSitemapIndex({
194
+ sitemaps: [
195
+ { loc: 'https://example.com/sitemap-pages.xml', lastmod: '2026-01-01' },
196
+ { loc: 'https://example.com/sitemap-products.xml', lastmod: '2026-01-15' },
197
+ { loc: 'https://example.com/sitemap-blog.xml', lastmod: '2026-01-20' },
198
+ ],
199
+ });
200
+ ```
175
201
 
176
- - **Build-time**: Generate static `sitemap.xml` files during `next build` or Remix production builds
177
- - **Runtime**: Serve dynamically from an API route; regenerate on demand or on ISR revalidation
178
- - **CI/CD**: Validate sitemap URL entries as part of pull request checks
202
+ ### Image Sitemaps
179
203
 
180
- **Data flow**
204
+ Add `images` to any `SitemapURL` entry to emit `<image:image>` extension tags:
181
205
 
182
- 1. **Input**: Array of `SitemapURL` objects with `loc`, `lastmod`, optional images/videos/news
183
- 2. **Analysis**: Spec validation, namespace detection, XML serialization
184
- 3. **Output**: Valid XML string or `AsyncIterable<string>` stream
185
- 4. **Action**: Serve as `application/xml`, write to disk, or submit to GSC via `@power-seo/search-console`
206
+ ```ts
207
+ import { generateSitemap } from '@power-seo/sitemap';
186
208
 
187
- ---
209
+ const xml = generateSitemap({
210
+ hostname: 'https://example.com',
211
+ urls: [
212
+ {
213
+ loc: '/products/blue-sneaker',
214
+ lastmod: '2026-01-10',
215
+ images: [
216
+ {
217
+ loc: 'https://cdn.example.com/sneaker-blue.jpg',
218
+ caption: 'Blue sneaker — side view',
219
+ title: 'Blue Running Sneaker',
220
+ },
221
+ {
222
+ loc: 'https://cdn.example.com/sneaker-blue-top.jpg',
223
+ caption: 'Blue sneaker — top view',
224
+ },
225
+ ],
226
+ },
227
+ ],
228
+ });
229
+ ```
188
230
 
189
- ## Features Comparison with Popular Packages
231
+ ### Validating URL Entries
190
232
 
191
- | Capability | next-sitemap | sitemap (npm) | xmlbuilder2 | @power-seo/sitemap |
192
- | ------------------------- | -----------: | ------------: | ----------: | -----------------: |
193
- | Image sitemap extension | ✅ | ✅ | ❌ | ✅ |
194
- | Video sitemap extension | ❌ | ✅ | ❌ | ✅ |
195
- | News sitemap extension | ❌ | ✅ | ❌ | ✅ |
196
- | Streaming generation | ❌ | ❌ | ❌ | ✅ |
197
- | Auto index splitting | ✅ | ❌ | ❌ | ✅ |
198
- | URL validation | ❌ | ❌ | ❌ | ✅ |
199
- | Zero runtime dependencies | ❌ | ❌ | ❌ | ✅ |
200
- | Edge runtime compatible | ❌ | ❌ | ❌ | ✅ |
233
+ `validateSitemapUrl()` checks a `SitemapURL` against the sitemap spec and returns structured errors and warnings — useful in CI or before serving.
201
234
 
202
- ---
235
+ ```ts
236
+ import { validateSitemapUrl } from '@power-seo/sitemap';
203
237
 
204
- ## [@power-seo](https://www.npmjs.com/org/power-seo) Ecosystem
238
+ const result = validateSitemapUrl({
239
+ loc: '/about',
240
+ priority: 1.5, // out of range
241
+ changefreq: 'daily',
242
+ });
205
243
 
206
- All 17 packages are independently installable — use only what you need.
244
+ // result.valid → false
245
+ // result.errors → ['priority must be between 0.0 and 1.0']
246
+ // result.warnings → []
247
+ ```
207
248
 
208
- | Package | Install | Description |
209
- | ------------------------------------------------------------------------------------------ | ----------------------------------- | ----------------------------------------------------------------------- |
210
- | [`@power-seo/core`](https://www.npmjs.com/package/@power-seo/core) | `npm i @power-seo/core` | Framework-agnostic utilities, types, validators, and constants |
211
- | [`@power-seo/react`](https://www.npmjs.com/package/@power-seo/react) | `npm i @power-seo/react` | React SEO components — meta, Open Graph, Twitter Card, breadcrumbs |
212
- | [`@power-seo/meta`](https://www.npmjs.com/package/@power-seo/meta) | `npm i @power-seo/meta` | SSR meta helpers for Next.js App Router, Remix v2, and generic SSR |
213
- | [`@power-seo/schema`](https://www.npmjs.com/package/@power-seo/schema) | `npm i @power-seo/schema` | Type-safe JSON-LD structured data — 20 builders + 18 React components |
214
- | [`@power-seo/content-analysis`](https://www.npmjs.com/package/@power-seo/content-analysis) | `npm i @power-seo/content-analysis` | Yoast-style SEO content scoring engine with React components |
215
- | [`@power-seo/readability`](https://www.npmjs.com/package/@power-seo/readability) | `npm i @power-seo/readability` | Readability scoring — Flesch-Kincaid, Gunning Fog, Coleman-Liau, ARI |
216
- | [`@power-seo/preview`](https://www.npmjs.com/package/@power-seo/preview) | `npm i @power-seo/preview` | SERP, Open Graph, and Twitter/X Card preview generators |
217
- | [`@power-seo/sitemap`](https://www.npmjs.com/package/@power-seo/sitemap) | `npm i @power-seo/sitemap` | XML sitemap generation, streaming, index splitting, and validation |
218
- | [`@power-seo/redirects`](https://www.npmjs.com/package/@power-seo/redirects) | `npm i @power-seo/redirects` | Redirect engine with Next.js, Remix, and Express adapters |
219
- | [`@power-seo/links`](https://www.npmjs.com/package/@power-seo/links) | `npm i @power-seo/links` | Link graph analysis — orphan detection, suggestions, equity scoring |
220
- | [`@power-seo/audit`](https://www.npmjs.com/package/@power-seo/audit) | `npm i @power-seo/audit` | Full SEO audit engine — meta, content, structure, performance rules |
221
- | [`@power-seo/images`](https://www.npmjs.com/package/@power-seo/images) | `npm i @power-seo/images` | Image SEO — alt text, lazy loading, format analysis, image sitemaps |
222
- | [`@power-seo/ai`](https://www.npmjs.com/package/@power-seo/ai) | `npm i @power-seo/ai` | LLM-agnostic AI prompt templates and parsers for SEO tasks |
223
- | [`@power-seo/analytics`](https://www.npmjs.com/package/@power-seo/analytics) | `npm i @power-seo/analytics` | Merge GSC + audit data, trend analysis, ranking insights, dashboard |
224
- | [`@power-seo/search-console`](https://www.npmjs.com/package/@power-seo/search-console) | `npm i @power-seo/search-console` | Google Search Console API — OAuth2, service account, URL inspection |
225
- | [`@power-seo/integrations`](https://www.npmjs.com/package/@power-seo/integrations) | `npm i @power-seo/integrations` | Semrush and Ahrefs API clients with rate limiting and pagination |
226
- | [`@power-seo/tracking`](https://www.npmjs.com/package/@power-seo/tracking) | `npm i @power-seo/tracking` | GA4, Clarity, PostHog, Plausible, Fathom — scripts + consent management |
249
+ ### Next.js App Router — `app/sitemap.ts` Convention
227
250
 
228
- ### Ecosystem vs alternatives
251
+ Next.js App Router has a built-in `app/sitemap.ts` file convention that returns an array of URL objects (not XML). Use `toNextSitemap()` from the `/next` subpath to convert `SitemapURL[]` to the required format:
229
252
 
230
- | Need | Common approach | @power-seo approach |
231
- | ------------------ | ---------------------- | ---------------------------------------------------- |
232
- | Sitemap generation | `next-sitemap` | `@power-seo/sitemap` — streaming, image, video, news |
233
- | Sitemap submission | Manual GSC UI | `@power-seo/search-console` — `submitSitemap()` |
234
- | Image SEO | Manual `<image:image>` | `@power-seo/sitemap` + `@power-seo/images` |
235
- | Structured data | Manual JSON-LD | `@power-seo/schema` — typed builders |
253
+ ```ts
254
+ // app/sitemap.ts
255
+ import { toNextSitemap } from '@power-seo/sitemap/next';
236
256
 
237
- ---
257
+ export default async function sitemap() {
258
+ const urls = await fetchUrlsFromCms();
238
259
 
239
- ## Enterprise Integration
260
+ return toNextSitemap(urls);
261
+ // Returns NextSitemapEntry[] — Next.js renders the XML automatically
262
+ }
263
+ ```
240
264
 
241
- **Multi-tenant SaaS**
265
+ `toNextSitemap()` filters out invalid URLs, converts `lastmod` strings to `Date` objects, and maps `changefreq` to `changeFrequency` as required by Next.js.
242
266
 
243
- - **Per-tenant sitemaps**: Generate separate sitemaps per client domain; serve from tenant-aware API routes
244
- - **Sitemap index**: Use `generateSitemapIndex()` to aggregate tenant sitemaps into a root index
245
- - **Scheduled regeneration**: Rebuild sitemaps nightly as new content is published
267
+ ### Next.js App Router Route Handler (XML)
246
268
 
247
- **ERP / internal portals**
269
+ For full control over the XML output (useful when you need image/video/news extensions), use a route handler instead:
248
270
 
249
- - Generate sitemaps only for public-facing modules (knowledge base, product catalog)
250
- - Use `validateSitemapUrl()` to enforce URL format standards across generated entries
251
- - Pipe large catalogs through `streamSitemap()` to avoid memory limits in serverless environments
271
+ ```ts
272
+ // app/sitemap.xml/route.ts
273
+ import { generateSitemap } from '@power-seo/sitemap';
252
274
 
253
- **Recommended integration pattern**
275
+ export async function GET() {
276
+ const urls = await fetchUrlsFromCms();
254
277
 
255
- - Generate sitemaps in **CI** at build time for static content
256
- - Serve dynamically from **API routes** for frequently updated content
257
- - Submit new sitemaps to GSC via `@power-seo/search-console` after deployment
258
- - Monitor crawl coverage in Google Search Console
278
+ const xml = generateSitemap({
279
+ hostname: 'https://example.com',
280
+ urls,
281
+ });
259
282
 
260
- ---
283
+ return new Response(xml, {
284
+ headers: { 'Content-Type': 'application/xml' },
285
+ });
286
+ }
287
+ ```
261
288
 
262
- ## Scope and Limitations
289
+ ### Remix Resource Route
263
290
 
264
- **This package does**
291
+ ```ts
292
+ // app/routes/sitemap[.xml].ts
293
+ import { generateSitemap } from '@power-seo/sitemap';
294
+ import type { LoaderFunctionArgs } from '@remix-run/node';
265
295
 
266
- - Generate spec-compliant XML sitemaps from URL arrays
267
- - Support image, video, and news sitemap extensions
268
- - ✅ Stream large sitemaps to avoid memory spikes
269
- - ✅ Split sitemaps and generate sitemap index files
296
+ export async function loader({ request }: LoaderFunctionArgs) {
297
+ const urls = await fetchUrlsFromDb();
270
298
 
271
- **This package does not**
299
+ const xml = generateSitemap({
300
+ hostname: 'https://example.com',
301
+ urls,
302
+ });
272
303
 
273
- - Crawl your site to discover URLs — you provide the URL list
274
- - Submit sitemaps to Google — use `@power-seo/search-console` for submission
275
- - ❌ Monitor sitemap status — use Google Search Console for crawl coverage reports
304
+ return new Response(xml, {
305
+ headers: { 'Content-Type': 'application/xml' },
306
+ });
307
+ }
308
+ ```
276
309
 
277
310
  ---
278
311
 
@@ -284,37 +317,51 @@ All 17 packages are independently installable — use only what you need.
284
317
  function generateSitemap(config: SitemapConfig): string;
285
318
  ```
286
319
 
287
- | Prop | Type | Description |
288
- | ------ | -------------- | -------------------- |
289
- | `urls` | `SitemapURL[]` | Array of URL entries |
290
-
291
- #### `SitemapURL`
320
+ | Prop | Type | Required | Description |
321
+ | ------------------- | -------------- | -------- | ------------------------------------------------------------------------- |
322
+ | `hostname` | `string` | | Base URL prepended to relative `loc` paths (e.g. `'https://example.com'`) |
323
+ | `urls` | `SitemapURL[]` | ✅ | Array of URL entries |
324
+ | `maxUrlsPerSitemap` | `number` | — | Override the 50,000-URL chunk size (used by `splitSitemap`) |
325
+ | `outputDir` | `string` | — | Optional output directory hint (informational; does not write files) |
292
326
 
293
- | Prop | Type | Default | Description |
294
- | ------------ | --------------------------------------------------------------------------------- | ------- | ---------------------------------------- |
295
- | `loc` | `string` | — | **Required.** Absolute URL |
296
- | `lastmod` | `string` | — | Last modified (ISO 8601 or `YYYY-MM-DD`) |
297
- | `changefreq` | `'always' \| 'hourly' \| 'daily' \| 'weekly' \| 'monthly' \| 'yearly' \| 'never'` | — | Change frequency |
298
- | `priority` | `number` | `0.5` | Priority 0.0–1.0 |
299
- | `images` | `SitemapImage[]` | — | Image extension entries |
300
- | `videos` | `SitemapVideo[]` | — | Video extension entries |
301
- | `news` | `SitemapNews` | — | News extension entry |
302
-
303
- ### `streamSitemap(config)`
327
+ ### `streamSitemap(hostname, urls)`
304
328
 
305
329
  ```ts
306
- function streamSitemap(config: SitemapConfig): AsyncIterable<string>;
330
+ function streamSitemap(
331
+ hostname: string,
332
+ urls: Iterable<SitemapURL>,
333
+ ): Generator<string, void, undefined>;
307
334
  ```
308
335
 
309
- Returns an async iterable that yields XML chunks. Suitable for piping to a Node.js `Writable`.
336
+ Synchronous generator. Yields XML string chunks — one for the XML declaration and opening tag, one per `<url>` block, and one for the closing tag. Does not buffer the full XML in memory.
337
+
338
+ | Param | Type | Description |
339
+ | ---------- | ---------------------- | ------------------------------------------------------------------ |
340
+ | `hostname` | `string` | Base URL prepended to relative `loc` paths |
341
+ | `urls` | `Iterable<SitemapURL>` | Any iterable of URL entries — arrays, generators, database cursors |
310
342
 
311
- ### `splitSitemap(urls, maxPerFile?)`
343
+ ### `splitSitemap(config, sitemapUrlPattern?)`
312
344
 
313
345
  ```ts
314
- function splitSitemap(urls: SitemapURL[], maxPerFile?: number): SitemapURL[][];
346
+ function splitSitemap(
347
+ config: SitemapConfig,
348
+ sitemapUrlPattern?: string,
349
+ ): { index: string; sitemaps: Array<{ filename: string; xml: string }> };
315
350
  ```
316
351
 
317
- Splits `urls` into chunks of at most `maxPerFile` (default: `MAX_URLS_PER_SITEMAP` = 50,000).
352
+ Splits a large URL set into multiple sitemap files and returns the index XML and all sitemap XMLs. The `sitemapUrlPattern` parameter controls generated filenames using `{index}` as a placeholder.
353
+
354
+ | Param | Type | Default | Description |
355
+ | ------------------- | --------------- | ------------------------ | --------------------------------------- |
356
+ | `config` | `SitemapConfig` | — | Same config as `generateSitemap()` |
357
+ | `sitemapUrlPattern` | `string` | `'/sitemap-{index}.xml'` | Filename pattern for each split sitemap |
358
+
359
+ **Return value:**
360
+
361
+ | Field | Type | Description |
362
+ | ---------- | ------------------------------------------ | ---------------------------------------------------------------- |
363
+ | `index` | `string` | Sitemap index XML (`<sitemapindex>`) referencing all split files |
364
+ | `sitemaps` | `Array<{ filename: string; xml: string }>` | Each split sitemap with its filename and XML string |
318
365
 
319
366
  ### `generateSitemapIndex(config)`
320
367
 
@@ -322,9 +369,9 @@ Splits `urls` into chunks of at most `maxPerFile` (default: `MAX_URLS_PER_SITEMA
322
369
  function generateSitemapIndex(config: SitemapIndexConfig): string;
323
370
  ```
324
371
 
325
- | Prop | Type | Description |
326
- | ---------- | --------------------- | -------------------------------------------- |
327
- | `sitemaps` | `SitemapIndexEntry[]` | Array of `{ loc: string; lastmod?: string }` |
372
+ | Prop | Type | Description |
373
+ | ---------- | --------------------- | ---------------------------------------------------- |
374
+ | `sitemaps` | `SitemapIndexEntry[]` | Array of `{ loc: string; lastmod?: string }` entries |
328
375
 
329
376
  ### `validateSitemapUrl(url)`
330
377
 
@@ -332,47 +379,138 @@ function generateSitemapIndex(config: SitemapIndexConfig): string;
332
379
  function validateSitemapUrl(url: SitemapURL): SitemapValidationResult;
333
380
  ```
334
381
 
335
- Returns `{ valid: boolean; errors: string[]; warnings: string[] }`.
382
+ Returns `{ valid: boolean; errors: string[]; warnings: string[] }`. Never throws.
383
+
384
+ ### `toNextSitemap(urls)` — from `@power-seo/sitemap/next`
385
+
386
+ ```ts
387
+ import { toNextSitemap } from '@power-seo/sitemap/next';
388
+
389
+ function toNextSitemap(urls: SitemapURL[]): NextSitemapEntry[];
390
+ ```
391
+
392
+ Converts a `SitemapURL[]` to the array format expected by Next.js App Router's `app/sitemap.ts` file convention. Invalid URLs (per `validateSitemapUrl`) are filtered out automatically. `lastmod` strings are converted to `Date` objects; `changefreq` is mapped to `changeFrequency`.
393
+
394
+ | Field | Type | Description |
395
+ | ----------------- | ---------------- | ------------------------------------ |
396
+ | `url` | `string` | Absolute URL (`loc`) |
397
+ | `lastModified` | `Date \| string` | From `lastmod` (converted to `Date`) |
398
+ | `changeFrequency` | `string` | From `changefreq` |
399
+ | `priority` | `number` | From `priority` |
336
400
 
337
401
  ---
338
402
 
339
- ## Contributing
403
+ ## Types
404
+
405
+ | Type | Description |
406
+ | ------------------------- | --------------------------------------------------------------------------------------------- |
407
+ | `SitemapConfig` | `{ hostname: string; urls: SitemapURL[]; maxUrlsPerSitemap?: number; outputDir?: string }` |
408
+ | `SitemapURL` | Single URL entry — see field table below |
409
+ | `SitemapImage` | `{ loc: string; caption?: string; geoLocation?: string; title?: string; license?: string }` |
410
+ | `SitemapVideo` | Video extension entry with `thumbnailLoc`, `title`, `description`, and optional fields |
411
+ | `SitemapNews` | `{ publication: { name: string; language: string }; publicationDate: string; title: string }` |
412
+ | `SitemapIndexConfig` | `{ sitemaps: SitemapIndexEntry[] }` |
413
+ | `SitemapIndexEntry` | `{ loc: string; lastmod?: string }` |
414
+ | `SitemapValidationResult` | `{ valid: boolean; errors: string[]; warnings: string[] }` |
415
+
416
+ ### `SitemapURL` Fields
417
+
418
+ | Prop | Type | Default | Description |
419
+ | ------------ | --------------------------------------------------------------------------------- | ---------------- | ------------------------------------------------------------------------------------------------ |
420
+ | `loc` | `string` | — | **Required.** URL path (e.g. `/about`) or absolute URL. Hostname is prepended to relative paths. |
421
+ | `lastmod` | `string` | — | Last modified date — ISO 8601 or `YYYY-MM-DD` |
422
+ | `changefreq` | `'always' \| 'hourly' \| 'daily' \| 'weekly' \| 'monthly' \| 'yearly' \| 'never'` | — | Suggested crawl frequency |
423
+ | `priority` | `number` | (no tag emitted) | Priority 0.0–1.0. When omitted, no `<priority>` tag is written. |
424
+ | `images` | `SitemapImage[]` | — | Image extension entries — emits `<image:image>` blocks |
425
+ | `videos` | `SitemapVideo[]` | — | Video extension entries — emits `<video:video>` blocks |
426
+ | `news` | `SitemapNews` | — | News extension entry — emits `<news:news>` block |
427
+
428
+ ### Constants
429
+
430
+ | Constant | Value | Description |
431
+ | ------------------------ | ------------ | ------------------------------------------------------------- |
432
+ | `MAX_URLS_PER_SITEMAP` | `50_000` | Maximum URLs allowed per sitemap file (spec limit) |
433
+ | `MAX_SITEMAP_SIZE_BYTES` | `52_428_800` | Maximum sitemap file size in bytes (50 MB = 50 × 1024 × 1024) |
340
434
 
341
- - Issues: [github.com/cybercraftbd/power-seo/issues](https://github.com/cybercraftbd/power-seo/issues)
342
- - PRs: [github.com/cybercraftbd/power-seo/pulls](https://github.com/cybercraftbd/power-seo/pulls)
343
- - Development setup:
344
- 1. `pnpm i`
345
- 2. `pnpm build`
346
- 3. `pnpm test`
435
+ ---
347
436
 
348
- **Release workflow**
437
+ ## Use Cases
349
438
 
350
- - `npm version patch|minor|major`
351
- - `npm publish --access public`
439
+ - **Next.js App Router** — generate sitemaps in `app/sitemap.xml/route.ts` at request time or build time
440
+ - **E-commerce catalogs** product image sitemaps with `<image:image>` for every listing; keep Google Images up to date
441
+ - **News publishers** — `<news:news>` extension for Google News sitemap submission
442
+ - **Multi-locale sites** — separate sitemaps per locale with a unified sitemap index
443
+ - **Programmatic SEO** — generate sitemaps for thousands of auto-generated pages with no memory overhead
444
+ - **Large sites** — automatic splitting at 50,000 URLs per file with a generated index
445
+ - **Video platforms** — `<video:video>` extension with title, description, and thumbnail for video SEO
446
+ - **CI/CD pipelines** — validate URL entries with `validateSitemapUrl()` as part of pull request checks
352
447
 
353
448
  ---
354
449
 
355
- ## About [CyberCraft Bangladesh](https://ccbd.dev)
450
+ ## Architecture Overview
451
+
452
+ - **Pure TypeScript** — no compiled binary, no native modules
453
+ - **Zero runtime dependencies** — only `@power-seo/core` as a peer dependency
454
+ - **Framework-agnostic** — works in any JavaScript environment that supports ES2020+
455
+ - **SSR compatible** — safe to run in Next.js Server Components, Remix loaders, or Express handlers
456
+ - **Edge runtime safe** — no `fs`, no `path`, no Node.js-specific APIs; runs in Cloudflare Workers, Vercel Edge, Deno
457
+ - **Synchronous generator streaming** — `streamSitemap()` uses `function*` — no async overhead, no backpressure complexity
458
+ - **Auto namespace detection** — `generateSitemap()` only adds `xmlns:image`, `xmlns:video`, `xmlns:news` declarations when the URL set actually uses those extensions
459
+ - **Tree-shakeable** — `"sideEffects": false` with named exports per function
460
+ - **Dual ESM + CJS** — ships both formats via tsup for any bundler or `require()` usage
461
+
462
+ ---
356
463
 
357
- **[CyberCraft Bangladesh](https://ccbd.dev)** is a Bangladesh-based enterprise-grade software engineering company specializing in ERP system development, AI-powered SaaS and business applications, full-stack SEO services, custom website development, and scalable eCommerce platforms. We design and develop intelligent, automation-driven SaaS and enterprise solutions that help startups, SMEs, NGOs, educational institutes, and large organizations streamline operations, enhance digital visibility, and accelerate growth through modern cloud-native technologies.
464
+ ## Supply Chain Security
358
465
 
359
- | | |
360
- | -------------------- | -------------------------------------------------------------- |
361
- | **Website** | [ccbd.dev](https://ccbd.dev) |
362
- | **GitHub** | [github.com/cybercraftbd](https://github.com/cybercraftbd) |
363
- | **npm Organization** | [npmjs.com/org/power-seo](https://www.npmjs.com/org/power-seo) |
364
- | **Email** | [info@ccbd.dev](mailto:info@ccbd.dev) |
466
+ - No install scripts (`postinstall`, `preinstall`)
467
+ - No runtime network access
468
+ - No `eval` or dynamic code execution
469
+ - npm provenance enabled — every release is signed via Sigstore through GitHub Actions
470
+ - CI-signed builds all releases published via verified `github.com/CyberCraftBD/power-seo` workflow
471
+ - Safe for SSR, Edge, and server environments
365
472
 
366
473
  ---
367
474
 
368
- ## License
475
+ ## The [@power-seo](https://www.npmjs.com/org/power-seo) Ecosystem
369
476
 
370
- **MIT**
477
+ All 17 packages are independently installable — use only what you need.
478
+
479
+ | Package | Install | Description |
480
+ | ------------------------------------------------------------------------------------------ | ----------------------------------- | ----------------------------------------------------------------------- |
481
+ | [`@power-seo/core`](https://www.npmjs.com/package/@power-seo/core) | `npm i @power-seo/core` | Framework-agnostic utilities, types, validators, and constants |
482
+ | [`@power-seo/react`](https://www.npmjs.com/package/@power-seo/react) | `npm i @power-seo/react` | React SEO components — meta, Open Graph, Twitter Card, breadcrumbs |
483
+ | [`@power-seo/meta`](https://www.npmjs.com/package/@power-seo/meta) | `npm i @power-seo/meta` | SSR meta helpers for Next.js App Router, Remix v2, and generic SSR |
484
+ | [`@power-seo/schema`](https://www.npmjs.com/package/@power-seo/schema) | `npm i @power-seo/schema` | Type-safe JSON-LD structured data — 20 builders + 18 React components |
485
+ | [`@power-seo/content-analysis`](https://www.npmjs.com/package/@power-seo/content-analysis) | `npm i @power-seo/content-analysis` | Yoast-style SEO content scoring engine with React components |
486
+ | [`@power-seo/readability`](https://www.npmjs.com/package/@power-seo/readability) | `npm i @power-seo/readability` | Readability scoring — Flesch-Kincaid, Gunning Fog, Coleman-Liau, ARI |
487
+ | [`@power-seo/preview`](https://www.npmjs.com/package/@power-seo/preview) | `npm i @power-seo/preview` | SERP, Open Graph, and Twitter/X Card preview generators |
488
+ | [`@power-seo/sitemap`](https://www.npmjs.com/package/@power-seo/sitemap) | `npm i @power-seo/sitemap` | XML sitemap generation, streaming, index splitting, and validation |
489
+ | [`@power-seo/redirects`](https://www.npmjs.com/package/@power-seo/redirects) | `npm i @power-seo/redirects` | Redirect engine with Next.js, Remix, and Express adapters |
490
+ | [`@power-seo/links`](https://www.npmjs.com/package/@power-seo/links) | `npm i @power-seo/links` | Link graph analysis — orphan detection, suggestions, equity scoring |
491
+ | [`@power-seo/audit`](https://www.npmjs.com/package/@power-seo/audit) | `npm i @power-seo/audit` | Full SEO audit engine — meta, content, structure, performance rules |
492
+ | [`@power-seo/images`](https://www.npmjs.com/package/@power-seo/images) | `npm i @power-seo/images` | Image SEO — alt text, lazy loading, format analysis, image sitemaps |
493
+ | [`@power-seo/ai`](https://www.npmjs.com/package/@power-seo/ai) | `npm i @power-seo/ai` | LLM-agnostic AI prompt templates and parsers for SEO tasks |
494
+ | [`@power-seo/analytics`](https://www.npmjs.com/package/@power-seo/analytics) | `npm i @power-seo/analytics` | Merge GSC + audit data, trend analysis, ranking insights, dashboard |
495
+ | [`@power-seo/search-console`](https://www.npmjs.com/package/@power-seo/search-console) | `npm i @power-seo/search-console` | Google Search Console API — OAuth2, service account, URL inspection |
496
+ | [`@power-seo/integrations`](https://www.npmjs.com/package/@power-seo/integrations) | `npm i @power-seo/integrations` | Semrush and Ahrefs API clients with rate limiting and pagination |
497
+ | [`@power-seo/tracking`](https://www.npmjs.com/package/@power-seo/tracking) | `npm i @power-seo/tracking` | GA4, Clarity, PostHog, Plausible, Fathom — scripts + consent management |
371
498
 
372
499
  ---
373
500
 
374
501
  ## Keywords
375
502
 
376
- ```text
377
- seo, sitemap, xml-sitemap, sitemap-generator, image-sitemap, video-sitemap, news-sitemap, sitemap-index, typescript, nextjs, remix, streaming, crawl-budget, google-indexing
378
- ```
503
+ xml sitemap generator typescript · sitemap npm package · nextjs sitemap · streaming sitemap · sitemap index generator · image sitemap extension · video sitemap extension · news sitemap google · split sitemap 50000 urls · sitemap validation · edge runtime sitemap · remix sitemap · programmatic seo sitemap · sitemap generator zero dependencies · typescript xml sitemap · sitemap url priority · crawl budget optimization · sitemap changelog · google sitemap spec · seo sitemap typescript
504
+
505
+ ---
506
+
507
+ ## About [CyberCraft Bangladesh](https://ccbd.dev)
508
+
509
+ **[CyberCraft Bangladesh](https://ccbd.dev)** is a Bangladesh-based enterprise-grade software development and Full Stack SEO service provider company specializing in ERP system development, AI-powered SaaS and business applications, full-stack SEO services, custom website development, and scalable eCommerce platforms. We design and develop intelligent, automation-driven SaaS and enterprise solutions that help startups, SMEs, NGOs, educational institutes, and large organizations streamline operations, enhance digital visibility, and accelerate growth through modern cloud-native technologies.
510
+
511
+ [![Website](https://img.shields.io/badge/Website-ccbd.dev-blue?style=for-the-badge)](https://ccbd.dev)
512
+ [![GitHub](https://img.shields.io/badge/GitHub-cybercraftbd-black?style=for-the-badge&logo=github)](https://github.com/cybercraftbd)
513
+ [![npm](https://img.shields.io/badge/npm-power--seo-red?style=for-the-badge&logo=npm)](https://www.npmjs.com/org/power-seo)
514
+ [![Email](https://img.shields.io/badge/Email-info@ccbd.dev-green?style=for-the-badge&logo=gmail)](mailto:info@ccbd.dev)
515
+
516
+ © 2026 [CyberCraft Bangladesh](https://ccbd.dev) · Released under the [MIT License](../../LICENSE)