@onruntime/next-sitemap 0.2.1 → 0.3.0

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
@@ -118,6 +118,7 @@ export default nextConfig;
118
118
  | `priority` | `number`, `"auto"`, or `function` | `"auto"` | Priority calculation (auto = depth-based) |
119
119
  | `changeFreq` | `ChangeFrequency` or `function` | `"weekly"` | Change frequency for entries |
120
120
  | `additionalSitemaps` | `string[]` | `[]` | Additional sitemaps to include in index |
121
+ | `debug` | `boolean` | `false` | Enable debug logging to diagnose route discovery issues |
121
122
 
122
123
  #### Exclude Routes
123
124
 
@@ -266,6 +267,78 @@ export async function GET() {
266
267
  </urlset>
267
268
  ```
268
269
 
270
+ ## Troubleshooting
271
+
272
+ ### Dynamic routes not included in sitemap
273
+
274
+ If your dynamic routes (e.g., `/articles/[slug]`) are not appearing in the sitemap, enable debug mode to diagnose:
275
+
276
+ ```typescript
277
+ const { GET } = createSitemapIndexHandler({
278
+ baseUrl: "https://example.com",
279
+ pagesContext,
280
+ debug: true, // Enable debug logging
281
+ });
282
+ ```
283
+
284
+ This will log:
285
+ - All discovered routes and whether `generateStaticParams` was found
286
+ - Number of params returned by each `generateStaticParams` call
287
+ - Any errors that occur during param generation
288
+
289
+ **Common issues:**
290
+
291
+ 1. **`generateStaticParams` not detected**: Make sure it's exported as a named export at the top level of your page file:
292
+ ```typescript
293
+ // ✅ Correct
294
+ export async function generateStaticParams() { ... }
295
+
296
+ // ❌ Wrong - not exported
297
+ async function generateStaticParams() { ... }
298
+ ```
299
+
300
+ 2. **Database/API errors**: If `generateStaticParams` fetches data from a database or API, errors are caught and logged. Check the console for error messages.
301
+
302
+ 3. **Empty params returned**: If `generateStaticParams` returns an empty array, no dynamic paths will be generated.
303
+
304
+ ### Recommended approach for API/Database routes
305
+
306
+ For routes that fetch data from external sources (APIs, databases like Payload CMS), we recommend using `additionalSitemaps` with a custom sitemap route:
307
+
308
+ ```typescript
309
+ // app/sitemap.xml/route.ts
310
+ const { GET } = createSitemapIndexHandler({
311
+ baseUrl: "https://example.com",
312
+ pagesContext,
313
+ additionalSitemaps: ["/articles-sitemap.xml"],
314
+ });
315
+
316
+ // app/articles-sitemap.xml/route.ts
317
+ import { generateSitemapXml } from "@onruntime/next-sitemap";
318
+ import { getPayload } from "payload";
319
+
320
+ export async function GET() {
321
+ const payload = await getPayload({ config: configPromise });
322
+ const articles = await payload.find({
323
+ collection: "articles",
324
+ limit: 1000,
325
+ select: { slug: true, updatedAt: true },
326
+ });
327
+
328
+ const entries = articles.docs.map((article) => ({
329
+ url: `https://example.com/articles/${article.slug}`,
330
+ lastModified: article.updatedAt,
331
+ priority: 0.7,
332
+ }));
333
+
334
+ return new Response(generateSitemapXml(entries), {
335
+ headers: { "Content-Type": "application/xml" },
336
+ });
337
+ }
338
+ ```
339
+
340
+ This approach gives you full control over data fetching and error handling.
341
+
269
342
  ## License
270
343
 
271
344
  MIT
@@ -123,7 +123,7 @@ function extractRoutes(pagesContext, localeSegment) {
123
123
  }
124
124
  return routes;
125
125
  }
126
- async function getAllPaths(routes) {
126
+ async function getAllPaths(routes, debug = false) {
127
127
  const allPaths = ["/"];
128
128
  const seenPaths = /* @__PURE__ */ new Set(["/"]);
129
129
  for (const route of routes) {
@@ -133,16 +133,36 @@ async function getAllPaths(routes) {
133
133
  seenPaths.add(route.pathname);
134
134
  }
135
135
  } else if (route.getParams) {
136
- const params = await route.getParams();
137
- for (const param of params) {
138
- let dynamicPath = route.pathname;
139
- for (const segment of route.dynamicSegments) {
140
- dynamicPath = dynamicPath.replace(`[${segment}]`, param[segment]);
136
+ try {
137
+ const params = await route.getParams();
138
+ if (debug) {
139
+ console.log(`[next-sitemap] ${route.pathname}: generateStaticParams returned ${params.length} params`);
141
140
  }
142
- if (!seenPaths.has(dynamicPath)) {
143
- allPaths.push(dynamicPath);
144
- seenPaths.add(dynamicPath);
141
+ for (const param of params) {
142
+ let dynamicPath = route.pathname;
143
+ for (const segment of route.dynamicSegments) {
144
+ const value = param[segment];
145
+ if (value === void 0) {
146
+ if (debug) {
147
+ console.warn(`[next-sitemap] ${route.pathname}: missing param "${segment}" in`, param);
148
+ }
149
+ continue;
150
+ }
151
+ dynamicPath = dynamicPath.replace(`[${segment}]`, value);
152
+ }
153
+ if (!seenPaths.has(dynamicPath)) {
154
+ allPaths.push(dynamicPath);
155
+ seenPaths.add(dynamicPath);
156
+ }
145
157
  }
158
+ } catch (error) {
159
+ console.error(`[next-sitemap] Error calling generateStaticParams for ${route.pathname}:`, error);
160
+ }
161
+ } else if (route.dynamicSegments.length > 0) {
162
+ if (debug) {
163
+ console.warn(
164
+ `[next-sitemap] Skipping dynamic route ${route.pathname}: no generateStaticParams exported. Use additionalSitemaps for routes that fetch data at runtime.`
165
+ );
146
166
  }
147
167
  }
148
168
  }
@@ -172,12 +192,20 @@ function pathsToEntries(paths, config) {
172
192
  });
173
193
  }
174
194
  function createSitemapIndexHandler(options) {
175
- const { urlsPerSitemap = 5e3, locales = [], defaultLocale, additionalSitemaps, exclude } = options;
195
+ const { urlsPerSitemap = 5e3, locales = [], defaultLocale, additionalSitemaps, exclude, debug = false } = options;
176
196
  const localeSegment = options.localeSegment ?? (locales.length > 0 || defaultLocale ? "[locale]" : "");
177
197
  const routes = extractRoutes(options.pagesContext, localeSegment);
198
+ if (debug) {
199
+ console.log(`[next-sitemap] Found ${routes.length} routes:`);
200
+ routes.forEach((r) => {
201
+ const hasParams = r.getParams ? "\u2713 generateStaticParams" : "\u2717 no generateStaticParams";
202
+ const segments = r.dynamicSegments.length > 0 ? ` [${r.dynamicSegments.join(", ")}]` : "";
203
+ console.log(` ${r.pathname}${segments} - ${hasParams}`);
204
+ });
205
+ }
178
206
  return {
179
207
  GET: async () => {
180
- const allPaths = await getAllPaths(routes);
208
+ const allPaths = await getAllPaths(routes, debug);
181
209
  const filteredPaths = allPaths.filter((pathname) => !shouldExclude(pathname, exclude));
182
210
  const sitemapCount = Math.max(1, Math.ceil(filteredPaths.length / urlsPerSitemap));
183
211
  const xml = generateSitemapIndexXml(options.baseUrl, sitemapCount, {
@@ -190,11 +218,11 @@ function createSitemapIndexHandler(options) {
190
218
  };
191
219
  }
192
220
  function createSitemapHandler(options) {
193
- const { urlsPerSitemap = 5e3, locales = [], defaultLocale, exclude } = options;
221
+ const { urlsPerSitemap = 5e3, locales = [], defaultLocale, exclude, debug = false } = options;
194
222
  const localeSegment = options.localeSegment ?? (locales.length > 0 || defaultLocale ? "[locale]" : "");
195
223
  const routes = extractRoutes(options.pagesContext, localeSegment);
196
224
  const getFilteredPaths = async () => {
197
- const allPaths = await getAllPaths(routes);
225
+ const allPaths = await getAllPaths(routes, debug);
198
226
  return allPaths.filter((pathname) => !shouldExclude(pathname, exclude));
199
227
  };
200
228
  return {
@@ -79,6 +79,12 @@ interface CreateSitemapHandlerOptions extends SitemapConfig {
79
79
  keys: () => string[];
80
80
  (key: string): PageModule;
81
81
  };
82
+ /**
83
+ * Enable debug logging to diagnose issues with route discovery
84
+ * Logs info about generateStaticParams calls and skipped routes
85
+ * @default false
86
+ */
87
+ debug?: boolean;
82
88
  }
83
89
  /**
84
90
  * Create handlers for sitemap index route
@@ -79,6 +79,12 @@ interface CreateSitemapHandlerOptions extends SitemapConfig {
79
79
  keys: () => string[];
80
80
  (key: string): PageModule;
81
81
  };
82
+ /**
83
+ * Enable debug logging to diagnose issues with route discovery
84
+ * Logs info about generateStaticParams calls and skipped routes
85
+ * @default false
86
+ */
87
+ debug?: boolean;
82
88
  }
83
89
  /**
84
90
  * Create handlers for sitemap index route
package/dist/app/index.js CHANGED
@@ -121,7 +121,7 @@ function extractRoutes(pagesContext, localeSegment) {
121
121
  }
122
122
  return routes;
123
123
  }
124
- async function getAllPaths(routes) {
124
+ async function getAllPaths(routes, debug = false) {
125
125
  const allPaths = ["/"];
126
126
  const seenPaths = /* @__PURE__ */ new Set(["/"]);
127
127
  for (const route of routes) {
@@ -131,16 +131,36 @@ async function getAllPaths(routes) {
131
131
  seenPaths.add(route.pathname);
132
132
  }
133
133
  } else if (route.getParams) {
134
- const params = await route.getParams();
135
- for (const param of params) {
136
- let dynamicPath = route.pathname;
137
- for (const segment of route.dynamicSegments) {
138
- dynamicPath = dynamicPath.replace(`[${segment}]`, param[segment]);
134
+ try {
135
+ const params = await route.getParams();
136
+ if (debug) {
137
+ console.log(`[next-sitemap] ${route.pathname}: generateStaticParams returned ${params.length} params`);
139
138
  }
140
- if (!seenPaths.has(dynamicPath)) {
141
- allPaths.push(dynamicPath);
142
- seenPaths.add(dynamicPath);
139
+ for (const param of params) {
140
+ let dynamicPath = route.pathname;
141
+ for (const segment of route.dynamicSegments) {
142
+ const value = param[segment];
143
+ if (value === void 0) {
144
+ if (debug) {
145
+ console.warn(`[next-sitemap] ${route.pathname}: missing param "${segment}" in`, param);
146
+ }
147
+ continue;
148
+ }
149
+ dynamicPath = dynamicPath.replace(`[${segment}]`, value);
150
+ }
151
+ if (!seenPaths.has(dynamicPath)) {
152
+ allPaths.push(dynamicPath);
153
+ seenPaths.add(dynamicPath);
154
+ }
143
155
  }
156
+ } catch (error) {
157
+ console.error(`[next-sitemap] Error calling generateStaticParams for ${route.pathname}:`, error);
158
+ }
159
+ } else if (route.dynamicSegments.length > 0) {
160
+ if (debug) {
161
+ console.warn(
162
+ `[next-sitemap] Skipping dynamic route ${route.pathname}: no generateStaticParams exported. Use additionalSitemaps for routes that fetch data at runtime.`
163
+ );
144
164
  }
145
165
  }
146
166
  }
@@ -170,12 +190,20 @@ function pathsToEntries(paths, config) {
170
190
  });
171
191
  }
172
192
  function createSitemapIndexHandler(options) {
173
- const { urlsPerSitemap = 5e3, locales = [], defaultLocale, additionalSitemaps, exclude } = options;
193
+ const { urlsPerSitemap = 5e3, locales = [], defaultLocale, additionalSitemaps, exclude, debug = false } = options;
174
194
  const localeSegment = options.localeSegment ?? (locales.length > 0 || defaultLocale ? "[locale]" : "");
175
195
  const routes = extractRoutes(options.pagesContext, localeSegment);
196
+ if (debug) {
197
+ console.log(`[next-sitemap] Found ${routes.length} routes:`);
198
+ routes.forEach((r) => {
199
+ const hasParams = r.getParams ? "\u2713 generateStaticParams" : "\u2717 no generateStaticParams";
200
+ const segments = r.dynamicSegments.length > 0 ? ` [${r.dynamicSegments.join(", ")}]` : "";
201
+ console.log(` ${r.pathname}${segments} - ${hasParams}`);
202
+ });
203
+ }
176
204
  return {
177
205
  GET: async () => {
178
- const allPaths = await getAllPaths(routes);
206
+ const allPaths = await getAllPaths(routes, debug);
179
207
  const filteredPaths = allPaths.filter((pathname) => !shouldExclude(pathname, exclude));
180
208
  const sitemapCount = Math.max(1, Math.ceil(filteredPaths.length / urlsPerSitemap));
181
209
  const xml = generateSitemapIndexXml(options.baseUrl, sitemapCount, {
@@ -188,11 +216,11 @@ function createSitemapIndexHandler(options) {
188
216
  };
189
217
  }
190
218
  function createSitemapHandler(options) {
191
- const { urlsPerSitemap = 5e3, locales = [], defaultLocale, exclude } = options;
219
+ const { urlsPerSitemap = 5e3, locales = [], defaultLocale, exclude, debug = false } = options;
192
220
  const localeSegment = options.localeSegment ?? (locales.length > 0 || defaultLocale ? "[locale]" : "");
193
221
  const routes = extractRoutes(options.pagesContext, localeSegment);
194
222
  const getFilteredPaths = async () => {
195
- const allPaths = await getAllPaths(routes);
223
+ const allPaths = await getAllPaths(routes, debug);
196
224
  return allPaths.filter((pathname) => !shouldExclude(pathname, exclude));
197
225
  };
198
226
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onruntime/next-sitemap",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Dynamic sitemap generation for Next.js with automatic route discovery",
5
5
  "author": "onRuntime Studio <contact@onruntime.com>",
6
6
  "repository": {