@onruntime/next-sitemap 0.7.0 → 0.9.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
@@ -9,6 +9,14 @@ Dynamic sitemap generation for Next.js with automatic route discovery.
9
9
  - [Next.js Pages Router](https://github.com/onRuntime/onruntime/tree/master/examples/next-sitemap/pages)
10
10
  - [Next.js Pages Router with i18n](https://github.com/onRuntime/onruntime/tree/master/examples/next-sitemap/pages-with-locales)
11
11
 
12
+ ## Used by
13
+
14
+ - [onruntime.com](https://onruntime.com/sitemap.xml) - Creative development studio
15
+ - [trendstack.news](https://trendstack.news/sitemap.xml) - News site
16
+ - [tonightpass.com](https://tonightpass.com/sitemap.xml) - Nightlife platform
17
+
18
+ Want to be listed here? Open a PR! Just make sure `poweredBy` is enabled (default).
19
+
12
20
  ## Features
13
21
 
14
22
  - **App Router** and **Pages Router** support
@@ -185,6 +193,7 @@ export default createSitemapIndexApiHandler({
185
193
  | `changeFreq` | `ChangeFrequency` or `function` | `"weekly"` | Change frequency for entries |
186
194
  | `additionalSitemaps` | `string[]` | `[]` | Additional sitemaps to include in index |
187
195
  | `debug` | `boolean` | `false` | Enable debug logging to diagnose route discovery issues |
196
+ | `poweredBy` | `boolean` | `true` | Include "Powered by @onruntime/next-sitemap" comment in XML |
188
197
 
189
198
  #### Exclude Routes
190
199
 
@@ -303,6 +312,7 @@ export async function GET() {
303
312
  **Sitemap Index** (`/sitemap.xml`):
304
313
  ```xml
305
314
  <?xml version="1.0" encoding="UTF-8"?>
315
+ <!-- Powered by @onruntime/next-sitemap -->
306
316
  <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
307
317
  <sitemap>
308
318
  <loc>https://example.com/sitemap-0.xml</loc>
@@ -314,6 +324,7 @@ export async function GET() {
314
324
  **Individual Sitemap** (`/sitemap-0.xml`):
315
325
  ```xml
316
326
  <?xml version="1.0" encoding="UTF-8"?>
327
+ <!-- Powered by @onruntime/next-sitemap -->
317
328
  <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
318
329
  <url>
319
330
  <loc>https://example.com/</loc>
@@ -345,6 +356,7 @@ export default createRobotsApiHandler({
345
356
  disallow: ["/admin", "/private"],
346
357
  },
347
358
  sitemap: "https://example.com/sitemap.xml",
359
+ // poweredBy: false, // Disable "Powered by" comment
348
360
  });
349
361
  ```
350
362
 
@@ -27,14 +27,55 @@ function _interopNamespace(e) {
27
27
  var childProcess__namespace = /*#__PURE__*/_interopNamespace(childProcess);
28
28
 
29
29
  // src/app/index.ts
30
+ var JS_EXTENSIONS = /* @__PURE__ */ new Set([
31
+ ".js",
32
+ ".cjs",
33
+ ".mjs",
34
+ ".jsx",
35
+ ".ts",
36
+ ".cts",
37
+ ".mts",
38
+ ".tsx",
39
+ ".json",
40
+ ".node"
41
+ ]);
30
42
  var NO_STATIC_PARAMS = "NO_STATIC_PARAMS";
31
43
  var spawnProcess = childProcess__namespace.spawn;
32
44
  var __dirname$1 = path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
33
45
  var paramsCache = /* @__PURE__ */ new Map();
34
46
  var isDev = process.env.NODE_ENV === "development";
47
+ function resolveFilePath(directory, fileKey) {
48
+ if (fileKey.startsWith("./")) {
49
+ return path.join(directory, fileKey.replace("./", ""));
50
+ }
51
+ const basePath = fileKey === "/" ? "/index" : fileKey;
52
+ const jsPath = path.join(directory, basePath + ".js");
53
+ if (fs.existsSync(jsPath)) {
54
+ return jsPath;
55
+ }
56
+ for (const ext of JS_EXTENSIONS) {
57
+ const sourcePath = path.join(directory, basePath + ext);
58
+ if (fs.existsSync(sourcePath)) {
59
+ return sourcePath;
60
+ }
61
+ }
62
+ for (const ext of JS_EXTENSIONS) {
63
+ const indexPath = path.join(directory, basePath, "index" + ext);
64
+ if (fs.existsSync(indexPath)) {
65
+ return indexPath;
66
+ }
67
+ }
68
+ return null;
69
+ }
35
70
  async function executeWorker(directory, fileKey, debug) {
36
- const absolutePath = path.join(directory, fileKey.replace("./", ""));
71
+ const absolutePath = resolveFilePath(directory, fileKey);
37
72
  const projectRoot = process.cwd();
73
+ if (!absolutePath) {
74
+ if (debug) {
75
+ console.warn(`[next-sitemap] Could not resolve file path for ${fileKey} in ${directory}`);
76
+ }
77
+ return null;
78
+ }
38
79
  const distRoot = path.join(__dirname$1, "..");
39
80
  const workerPath = path.join(distRoot, "worker.cjs");
40
81
  const loaderPath = path.join(distRoot, "loader.js");
@@ -234,7 +275,8 @@ function buildUrl(baseUrl, pathname, locale, defaultLocale) {
234
275
  }
235
276
  return `${baseUrl}/${locale}${normalizedPath}`;
236
277
  }
237
- function generateSitemapXml(entries) {
278
+ function generateSitemapXml(entries, options) {
279
+ const { poweredBy = true } = options || {};
238
280
  const hasAlternates = entries.some((e) => e.alternates?.languages);
239
281
  const xmlns = hasAlternates ? 'xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml"' : 'xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"';
240
282
  const urlEntries = entries.map((entry) => {
@@ -258,13 +300,14 @@ function generateSitemapXml(entries) {
258
300
  ${parts.join("\n")}
259
301
  </url>`;
260
302
  }).join("\n");
261
- return `<?xml version="1.0" encoding="UTF-8"?>
303
+ const comment = poweredBy ? "\n<!-- Powered by @onruntime/next-sitemap -->" : "";
304
+ return `<?xml version="1.0" encoding="UTF-8"?>${comment}
262
305
  <urlset ${xmlns}>
263
306
  ${urlEntries}
264
307
  </urlset>`;
265
308
  }
266
309
  function generateSitemapIndexXml(baseUrl, sitemapCount, options) {
267
- const { sitemapPattern = "/sitemap-{id}.xml", additionalSitemaps = [] } = options || {};
310
+ const { sitemapPattern = "/sitemap-{id}.xml", additionalSitemaps = [], poweredBy = true } = options || {};
268
311
  const now = (/* @__PURE__ */ new Date()).toISOString();
269
312
  const paginatedEntries = Array.from({ length: sitemapCount }, (_, i) => {
270
313
  const loc = `${baseUrl}${sitemapPattern.replace("{id}", String(i))}`;
@@ -281,7 +324,8 @@ function generateSitemapIndexXml(baseUrl, sitemapCount, options) {
281
324
  </sitemap>`;
282
325
  });
283
326
  const allEntries = [...paginatedEntries, ...additionalEntries].join("\n");
284
- return `<?xml version="1.0" encoding="UTF-8"?>
327
+ const comment = poweredBy ? "\n<!-- Powered by @onruntime/next-sitemap -->" : "";
328
+ return `<?xml version="1.0" encoding="UTF-8"?>${comment}
285
329
  <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
286
330
  ${allEntries}
287
331
  </sitemapindex>`;
@@ -347,7 +391,7 @@ function createSitemapIndexHandler(options) {
347
391
  const filteredPaths = allPaths.filter((p) => !shouldExclude(p, exclude));
348
392
  const sitemapCount = Math.max(1, Math.ceil(filteredPaths.length / urlsPerSitemap));
349
393
  return new Response(
350
- generateSitemapIndexXml(options.baseUrl, sitemapCount, { additionalSitemaps }),
394
+ generateSitemapIndexXml(options.baseUrl, sitemapCount, { additionalSitemaps, poweredBy: options.poweredBy }),
351
395
  { headers: { "Content-Type": "application/xml" } }
352
396
  );
353
397
  }
@@ -375,7 +419,7 @@ function createSitemapHandler(options) {
375
419
  const filteredPaths = await getFilteredPaths();
376
420
  const paths = filteredPaths.slice(sitemapId * urlsPerSitemap, (sitemapId + 1) * urlsPerSitemap);
377
421
  const entries = pathsToEntries(paths, { ...options, exclude: void 0 });
378
- return new Response(generateSitemapXml(entries), {
422
+ return new Response(generateSitemapXml(entries, { poweredBy: options.poweredBy }), {
379
423
  headers: { "Content-Type": "application/xml" }
380
424
  });
381
425
  }
@@ -57,6 +57,11 @@ interface SitemapConfig {
57
57
  * @default false
58
58
  */
59
59
  debug?: boolean;
60
+ /**
61
+ * Include "Powered by @onruntime/next-sitemap" comment in generated XML
62
+ * @default true
63
+ */
64
+ poweredBy?: boolean;
60
65
  }
61
66
  interface SitemapEntry {
62
67
  url: string;
@@ -57,6 +57,11 @@ interface SitemapConfig {
57
57
  * @default false
58
58
  */
59
59
  debug?: boolean;
60
+ /**
61
+ * Include "Powered by @onruntime/next-sitemap" comment in generated XML
62
+ * @default true
63
+ */
64
+ poweredBy?: boolean;
60
65
  }
61
66
  interface SitemapEntry {
62
67
  url: string;
package/dist/app/index.js CHANGED
@@ -4,14 +4,55 @@ import * as childProcess from 'child_process';
4
4
  import { fileURLToPath } from 'url';
5
5
 
6
6
  // src/app/index.ts
7
+ var JS_EXTENSIONS = /* @__PURE__ */ new Set([
8
+ ".js",
9
+ ".cjs",
10
+ ".mjs",
11
+ ".jsx",
12
+ ".ts",
13
+ ".cts",
14
+ ".mts",
15
+ ".tsx",
16
+ ".json",
17
+ ".node"
18
+ ]);
7
19
  var NO_STATIC_PARAMS = "NO_STATIC_PARAMS";
8
20
  var spawnProcess = childProcess.spawn;
9
21
  var __dirname$1 = dirname(fileURLToPath(import.meta.url));
10
22
  var paramsCache = /* @__PURE__ */ new Map();
11
23
  var isDev = process.env.NODE_ENV === "development";
24
+ function resolveFilePath(directory, fileKey) {
25
+ if (fileKey.startsWith("./")) {
26
+ return join(directory, fileKey.replace("./", ""));
27
+ }
28
+ const basePath = fileKey === "/" ? "/index" : fileKey;
29
+ const jsPath = join(directory, basePath + ".js");
30
+ if (existsSync(jsPath)) {
31
+ return jsPath;
32
+ }
33
+ for (const ext of JS_EXTENSIONS) {
34
+ const sourcePath = join(directory, basePath + ext);
35
+ if (existsSync(sourcePath)) {
36
+ return sourcePath;
37
+ }
38
+ }
39
+ for (const ext of JS_EXTENSIONS) {
40
+ const indexPath = join(directory, basePath, "index" + ext);
41
+ if (existsSync(indexPath)) {
42
+ return indexPath;
43
+ }
44
+ }
45
+ return null;
46
+ }
12
47
  async function executeWorker(directory, fileKey, debug) {
13
- const absolutePath = join(directory, fileKey.replace("./", ""));
48
+ const absolutePath = resolveFilePath(directory, fileKey);
14
49
  const projectRoot = process.cwd();
50
+ if (!absolutePath) {
51
+ if (debug) {
52
+ console.warn(`[next-sitemap] Could not resolve file path for ${fileKey} in ${directory}`);
53
+ }
54
+ return null;
55
+ }
15
56
  const distRoot = join(__dirname$1, "..");
16
57
  const workerPath = join(distRoot, "worker.cjs");
17
58
  const loaderPath = join(distRoot, "loader.js");
@@ -211,7 +252,8 @@ function buildUrl(baseUrl, pathname, locale, defaultLocale) {
211
252
  }
212
253
  return `${baseUrl}/${locale}${normalizedPath}`;
213
254
  }
214
- function generateSitemapXml(entries) {
255
+ function generateSitemapXml(entries, options) {
256
+ const { poweredBy = true } = options || {};
215
257
  const hasAlternates = entries.some((e) => e.alternates?.languages);
216
258
  const xmlns = hasAlternates ? 'xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml"' : 'xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"';
217
259
  const urlEntries = entries.map((entry) => {
@@ -235,13 +277,14 @@ function generateSitemapXml(entries) {
235
277
  ${parts.join("\n")}
236
278
  </url>`;
237
279
  }).join("\n");
238
- return `<?xml version="1.0" encoding="UTF-8"?>
280
+ const comment = poweredBy ? "\n<!-- Powered by @onruntime/next-sitemap -->" : "";
281
+ return `<?xml version="1.0" encoding="UTF-8"?>${comment}
239
282
  <urlset ${xmlns}>
240
283
  ${urlEntries}
241
284
  </urlset>`;
242
285
  }
243
286
  function generateSitemapIndexXml(baseUrl, sitemapCount, options) {
244
- const { sitemapPattern = "/sitemap-{id}.xml", additionalSitemaps = [] } = options || {};
287
+ const { sitemapPattern = "/sitemap-{id}.xml", additionalSitemaps = [], poweredBy = true } = options || {};
245
288
  const now = (/* @__PURE__ */ new Date()).toISOString();
246
289
  const paginatedEntries = Array.from({ length: sitemapCount }, (_, i) => {
247
290
  const loc = `${baseUrl}${sitemapPattern.replace("{id}", String(i))}`;
@@ -258,7 +301,8 @@ function generateSitemapIndexXml(baseUrl, sitemapCount, options) {
258
301
  </sitemap>`;
259
302
  });
260
303
  const allEntries = [...paginatedEntries, ...additionalEntries].join("\n");
261
- return `<?xml version="1.0" encoding="UTF-8"?>
304
+ const comment = poweredBy ? "\n<!-- Powered by @onruntime/next-sitemap -->" : "";
305
+ return `<?xml version="1.0" encoding="UTF-8"?>${comment}
262
306
  <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
263
307
  ${allEntries}
264
308
  </sitemapindex>`;
@@ -324,7 +368,7 @@ function createSitemapIndexHandler(options) {
324
368
  const filteredPaths = allPaths.filter((p) => !shouldExclude(p, exclude));
325
369
  const sitemapCount = Math.max(1, Math.ceil(filteredPaths.length / urlsPerSitemap));
326
370
  return new Response(
327
- generateSitemapIndexXml(options.baseUrl, sitemapCount, { additionalSitemaps }),
371
+ generateSitemapIndexXml(options.baseUrl, sitemapCount, { additionalSitemaps, poweredBy: options.poweredBy }),
328
372
  { headers: { "Content-Type": "application/xml" } }
329
373
  );
330
374
  }
@@ -352,7 +396,7 @@ function createSitemapHandler(options) {
352
396
  const filteredPaths = await getFilteredPaths();
353
397
  const paths = filteredPaths.slice(sitemapId * urlsPerSitemap, (sitemapId + 1) * urlsPerSitemap);
354
398
  const entries = pathsToEntries(paths, { ...options, exclude: void 0 });
355
- return new Response(generateSitemapXml(entries), {
399
+ return new Response(generateSitemapXml(entries, { poweredBy: options.poweredBy }), {
356
400
  headers: { "Content-Type": "application/xml" }
357
401
  });
358
402
  }
package/dist/index.cjs CHANGED
@@ -44,9 +44,38 @@ var spawnProcess = childProcess__namespace.spawn;
44
44
  var __dirname$1 = path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
45
45
  var paramsCache = /* @__PURE__ */ new Map();
46
46
  var isDev = process.env.NODE_ENV === "development";
47
+ function resolveFilePath(directory, fileKey) {
48
+ if (fileKey.startsWith("./")) {
49
+ return path.join(directory, fileKey.replace("./", ""));
50
+ }
51
+ const basePath = fileKey === "/" ? "/index" : fileKey;
52
+ const jsPath = path.join(directory, basePath + ".js");
53
+ if (fs.existsSync(jsPath)) {
54
+ return jsPath;
55
+ }
56
+ for (const ext of JS_EXTENSIONS) {
57
+ const sourcePath = path.join(directory, basePath + ext);
58
+ if (fs.existsSync(sourcePath)) {
59
+ return sourcePath;
60
+ }
61
+ }
62
+ for (const ext of JS_EXTENSIONS) {
63
+ const indexPath = path.join(directory, basePath, "index" + ext);
64
+ if (fs.existsSync(indexPath)) {
65
+ return indexPath;
66
+ }
67
+ }
68
+ return null;
69
+ }
47
70
  async function executeWorker(directory, fileKey, debug) {
48
- const absolutePath = path.join(directory, fileKey.replace("./", ""));
71
+ const absolutePath = resolveFilePath(directory, fileKey);
49
72
  const projectRoot = process.cwd();
73
+ if (!absolutePath) {
74
+ if (debug) {
75
+ console.warn(`[next-sitemap] Could not resolve file path for ${fileKey} in ${directory}`);
76
+ }
77
+ return null;
78
+ }
50
79
  const distRoot = path.join(__dirname$1, "..");
51
80
  const workerPath = path.join(distRoot, "worker.cjs");
52
81
  const loaderPath = path.join(distRoot, "loader.js");
@@ -246,7 +275,8 @@ function buildUrl(baseUrl, pathname, locale, defaultLocale) {
246
275
  }
247
276
  return `${baseUrl}/${locale}${normalizedPath}`;
248
277
  }
249
- function generateSitemapXml(entries) {
278
+ function generateSitemapXml(entries, options) {
279
+ const { poweredBy = true } = options || {};
250
280
  const hasAlternates = entries.some((e) => e.alternates?.languages);
251
281
  const xmlns = hasAlternates ? 'xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml"' : 'xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"';
252
282
  const urlEntries = entries.map((entry) => {
@@ -270,13 +300,14 @@ function generateSitemapXml(entries) {
270
300
  ${parts.join("\n")}
271
301
  </url>`;
272
302
  }).join("\n");
273
- return `<?xml version="1.0" encoding="UTF-8"?>
303
+ const comment = poweredBy ? "\n<!-- Powered by @onruntime/next-sitemap -->" : "";
304
+ return `<?xml version="1.0" encoding="UTF-8"?>${comment}
274
305
  <urlset ${xmlns}>
275
306
  ${urlEntries}
276
307
  </urlset>`;
277
308
  }
278
309
  function generateSitemapIndexXml(baseUrl, sitemapCount, options) {
279
- const { sitemapPattern = "/sitemap-{id}.xml", additionalSitemaps = [] } = options || {};
310
+ const { sitemapPattern = "/sitemap-{id}.xml", additionalSitemaps = [], poweredBy = true } = options || {};
280
311
  const now = (/* @__PURE__ */ new Date()).toISOString();
281
312
  const paginatedEntries = Array.from({ length: sitemapCount }, (_, i) => {
282
313
  const loc = `${baseUrl}${sitemapPattern.replace("{id}", String(i))}`;
@@ -293,7 +324,8 @@ function generateSitemapIndexXml(baseUrl, sitemapCount, options) {
293
324
  </sitemap>`;
294
325
  });
295
326
  const allEntries = [...paginatedEntries, ...additionalEntries].join("\n");
296
- return `<?xml version="1.0" encoding="UTF-8"?>
327
+ const comment = poweredBy ? "\n<!-- Powered by @onruntime/next-sitemap -->" : "";
328
+ return `<?xml version="1.0" encoding="UTF-8"?>${comment}
297
329
  <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
298
330
  ${allEntries}
299
331
  </sitemapindex>`;
package/dist/index.d.cts CHANGED
@@ -106,6 +106,11 @@ interface SitemapConfig {
106
106
  * @default false
107
107
  */
108
108
  debug?: boolean;
109
+ /**
110
+ * Include "Powered by @onruntime/next-sitemap" comment in generated XML
111
+ * @default true
112
+ */
113
+ poweredBy?: boolean;
109
114
  }
110
115
  interface SitemapEntry {
111
116
  url: string;
@@ -153,13 +158,16 @@ declare function buildUrl(baseUrl: string, pathname: string, locale?: string, de
153
158
  /**
154
159
  * Generate sitemap XML from entries
155
160
  */
156
- declare function generateSitemapXml(entries: SitemapEntry[]): string;
161
+ declare function generateSitemapXml(entries: SitemapEntry[], options?: {
162
+ poweredBy?: boolean;
163
+ }): string;
157
164
  /**
158
165
  * Generate sitemap index XML
159
166
  */
160
167
  declare function generateSitemapIndexXml(baseUrl: string, sitemapCount: number, options?: {
161
168
  sitemapPattern?: string;
162
169
  additionalSitemaps?: string[];
170
+ poweredBy?: boolean;
163
171
  }): string;
164
172
 
165
173
  export { type ChangeFrequency, JS_EXTENSIONS, NO_STATIC_PARAMS, type PageModule, type RouteInfo, type SitemapConfig, type SitemapEntry, type WorkerFailure, type WorkerOutput, type WorkerSuccess, buildUrl, calculateDepthPriority, executeWorker, generateAllPaths, generateSitemapIndexXml, generateSitemapXml, getChangeFreq, getPriority, getRouteParams, isDev, normalizePath, paramsCache, pathsToEntries, shouldExclude };
package/dist/index.d.ts CHANGED
@@ -106,6 +106,11 @@ interface SitemapConfig {
106
106
  * @default false
107
107
  */
108
108
  debug?: boolean;
109
+ /**
110
+ * Include "Powered by @onruntime/next-sitemap" comment in generated XML
111
+ * @default true
112
+ */
113
+ poweredBy?: boolean;
109
114
  }
110
115
  interface SitemapEntry {
111
116
  url: string;
@@ -153,13 +158,16 @@ declare function buildUrl(baseUrl: string, pathname: string, locale?: string, de
153
158
  /**
154
159
  * Generate sitemap XML from entries
155
160
  */
156
- declare function generateSitemapXml(entries: SitemapEntry[]): string;
161
+ declare function generateSitemapXml(entries: SitemapEntry[], options?: {
162
+ poweredBy?: boolean;
163
+ }): string;
157
164
  /**
158
165
  * Generate sitemap index XML
159
166
  */
160
167
  declare function generateSitemapIndexXml(baseUrl: string, sitemapCount: number, options?: {
161
168
  sitemapPattern?: string;
162
169
  additionalSitemaps?: string[];
170
+ poweredBy?: boolean;
163
171
  }): string;
164
172
 
165
173
  export { type ChangeFrequency, JS_EXTENSIONS, NO_STATIC_PARAMS, type PageModule, type RouteInfo, type SitemapConfig, type SitemapEntry, type WorkerFailure, type WorkerOutput, type WorkerSuccess, buildUrl, calculateDepthPriority, executeWorker, generateAllPaths, generateSitemapIndexXml, generateSitemapXml, getChangeFreq, getPriority, getRouteParams, isDev, normalizePath, paramsCache, pathsToEntries, shouldExclude };
package/dist/index.js CHANGED
@@ -21,9 +21,38 @@ var spawnProcess = childProcess.spawn;
21
21
  var __dirname$1 = dirname(fileURLToPath(import.meta.url));
22
22
  var paramsCache = /* @__PURE__ */ new Map();
23
23
  var isDev = process.env.NODE_ENV === "development";
24
+ function resolveFilePath(directory, fileKey) {
25
+ if (fileKey.startsWith("./")) {
26
+ return join(directory, fileKey.replace("./", ""));
27
+ }
28
+ const basePath = fileKey === "/" ? "/index" : fileKey;
29
+ const jsPath = join(directory, basePath + ".js");
30
+ if (existsSync(jsPath)) {
31
+ return jsPath;
32
+ }
33
+ for (const ext of JS_EXTENSIONS) {
34
+ const sourcePath = join(directory, basePath + ext);
35
+ if (existsSync(sourcePath)) {
36
+ return sourcePath;
37
+ }
38
+ }
39
+ for (const ext of JS_EXTENSIONS) {
40
+ const indexPath = join(directory, basePath, "index" + ext);
41
+ if (existsSync(indexPath)) {
42
+ return indexPath;
43
+ }
44
+ }
45
+ return null;
46
+ }
24
47
  async function executeWorker(directory, fileKey, debug) {
25
- const absolutePath = join(directory, fileKey.replace("./", ""));
48
+ const absolutePath = resolveFilePath(directory, fileKey);
26
49
  const projectRoot = process.cwd();
50
+ if (!absolutePath) {
51
+ if (debug) {
52
+ console.warn(`[next-sitemap] Could not resolve file path for ${fileKey} in ${directory}`);
53
+ }
54
+ return null;
55
+ }
27
56
  const distRoot = join(__dirname$1, "..");
28
57
  const workerPath = join(distRoot, "worker.cjs");
29
58
  const loaderPath = join(distRoot, "loader.js");
@@ -223,7 +252,8 @@ function buildUrl(baseUrl, pathname, locale, defaultLocale) {
223
252
  }
224
253
  return `${baseUrl}/${locale}${normalizedPath}`;
225
254
  }
226
- function generateSitemapXml(entries) {
255
+ function generateSitemapXml(entries, options) {
256
+ const { poweredBy = true } = options || {};
227
257
  const hasAlternates = entries.some((e) => e.alternates?.languages);
228
258
  const xmlns = hasAlternates ? 'xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml"' : 'xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"';
229
259
  const urlEntries = entries.map((entry) => {
@@ -247,13 +277,14 @@ function generateSitemapXml(entries) {
247
277
  ${parts.join("\n")}
248
278
  </url>`;
249
279
  }).join("\n");
250
- return `<?xml version="1.0" encoding="UTF-8"?>
280
+ const comment = poweredBy ? "\n<!-- Powered by @onruntime/next-sitemap -->" : "";
281
+ return `<?xml version="1.0" encoding="UTF-8"?>${comment}
251
282
  <urlset ${xmlns}>
252
283
  ${urlEntries}
253
284
  </urlset>`;
254
285
  }
255
286
  function generateSitemapIndexXml(baseUrl, sitemapCount, options) {
256
- const { sitemapPattern = "/sitemap-{id}.xml", additionalSitemaps = [] } = options || {};
287
+ const { sitemapPattern = "/sitemap-{id}.xml", additionalSitemaps = [], poweredBy = true } = options || {};
257
288
  const now = (/* @__PURE__ */ new Date()).toISOString();
258
289
  const paginatedEntries = Array.from({ length: sitemapCount }, (_, i) => {
259
290
  const loc = `${baseUrl}${sitemapPattern.replace("{id}", String(i))}`;
@@ -270,7 +301,8 @@ function generateSitemapIndexXml(baseUrl, sitemapCount, options) {
270
301
  </sitemap>`;
271
302
  });
272
303
  const allEntries = [...paginatedEntries, ...additionalEntries].join("\n");
273
- return `<?xml version="1.0" encoding="UTF-8"?>
304
+ const comment = poweredBy ? "\n<!-- Powered by @onruntime/next-sitemap -->" : "";
305
+ return `<?xml version="1.0" encoding="UTF-8"?>${comment}
274
306
  <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
275
307
  ${allEntries}
276
308
  </sitemapindex>`;
@@ -27,14 +27,55 @@ function _interopNamespace(e) {
27
27
  var childProcess__namespace = /*#__PURE__*/_interopNamespace(childProcess);
28
28
 
29
29
  // src/pages/index.ts
30
+ var JS_EXTENSIONS = /* @__PURE__ */ new Set([
31
+ ".js",
32
+ ".cjs",
33
+ ".mjs",
34
+ ".jsx",
35
+ ".ts",
36
+ ".cts",
37
+ ".mts",
38
+ ".tsx",
39
+ ".json",
40
+ ".node"
41
+ ]);
30
42
  var NO_STATIC_PARAMS = "NO_STATIC_PARAMS";
31
43
  var spawnProcess = childProcess__namespace.spawn;
32
44
  var __dirname$1 = path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
33
45
  var paramsCache = /* @__PURE__ */ new Map();
34
46
  var isDev = process.env.NODE_ENV === "development";
47
+ function resolveFilePath(directory, fileKey) {
48
+ if (fileKey.startsWith("./")) {
49
+ return path.join(directory, fileKey.replace("./", ""));
50
+ }
51
+ const basePath = fileKey === "/" ? "/index" : fileKey;
52
+ const jsPath = path.join(directory, basePath + ".js");
53
+ if (fs.existsSync(jsPath)) {
54
+ return jsPath;
55
+ }
56
+ for (const ext of JS_EXTENSIONS) {
57
+ const sourcePath = path.join(directory, basePath + ext);
58
+ if (fs.existsSync(sourcePath)) {
59
+ return sourcePath;
60
+ }
61
+ }
62
+ for (const ext of JS_EXTENSIONS) {
63
+ const indexPath = path.join(directory, basePath, "index" + ext);
64
+ if (fs.existsSync(indexPath)) {
65
+ return indexPath;
66
+ }
67
+ }
68
+ return null;
69
+ }
35
70
  async function executeWorker(directory, fileKey, debug) {
36
- const absolutePath = path.join(directory, fileKey.replace("./", ""));
71
+ const absolutePath = resolveFilePath(directory, fileKey);
37
72
  const projectRoot = process.cwd();
73
+ if (!absolutePath) {
74
+ if (debug) {
75
+ console.warn(`[next-sitemap] Could not resolve file path for ${fileKey} in ${directory}`);
76
+ }
77
+ return null;
78
+ }
38
79
  const distRoot = path.join(__dirname$1, "..");
39
80
  const workerPath = path.join(distRoot, "worker.cjs");
40
81
  const loaderPath = path.join(distRoot, "loader.js");
@@ -234,7 +275,8 @@ function buildUrl(baseUrl, pathname, locale, defaultLocale) {
234
275
  }
235
276
  return `${baseUrl}/${locale}${normalizedPath}`;
236
277
  }
237
- function generateSitemapXml(entries) {
278
+ function generateSitemapXml(entries, options) {
279
+ const { poweredBy = true } = options || {};
238
280
  const hasAlternates = entries.some((e) => e.alternates?.languages);
239
281
  const xmlns = hasAlternates ? 'xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml"' : 'xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"';
240
282
  const urlEntries = entries.map((entry) => {
@@ -258,13 +300,14 @@ function generateSitemapXml(entries) {
258
300
  ${parts.join("\n")}
259
301
  </url>`;
260
302
  }).join("\n");
261
- return `<?xml version="1.0" encoding="UTF-8"?>
303
+ const comment = poweredBy ? "\n<!-- Powered by @onruntime/next-sitemap -->" : "";
304
+ return `<?xml version="1.0" encoding="UTF-8"?>${comment}
262
305
  <urlset ${xmlns}>
263
306
  ${urlEntries}
264
307
  </urlset>`;
265
308
  }
266
309
  function generateSitemapIndexXml(baseUrl, sitemapCount, options) {
267
- const { sitemapPattern = "/sitemap-{id}.xml", additionalSitemaps = [] } = options || {};
310
+ const { sitemapPattern = "/sitemap-{id}.xml", additionalSitemaps = [], poweredBy = true } = options || {};
268
311
  const now = (/* @__PURE__ */ new Date()).toISOString();
269
312
  const paginatedEntries = Array.from({ length: sitemapCount }, (_, i) => {
270
313
  const loc = `${baseUrl}${sitemapPattern.replace("{id}", String(i))}`;
@@ -281,7 +324,8 @@ function generateSitemapIndexXml(baseUrl, sitemapCount, options) {
281
324
  </sitemap>`;
282
325
  });
283
326
  const allEntries = [...paginatedEntries, ...additionalEntries].join("\n");
284
- return `<?xml version="1.0" encoding="UTF-8"?>
327
+ const comment = poweredBy ? "\n<!-- Powered by @onruntime/next-sitemap -->" : "";
328
+ return `<?xml version="1.0" encoding="UTF-8"?>${comment}
285
329
  <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
286
330
  ${allEntries}
287
331
  </sitemapindex>`;
@@ -289,7 +333,12 @@ ${allEntries}
289
333
 
290
334
  // src/pages/robots.ts
291
335
  function generateRobotsTxt(config) {
336
+ const { poweredBy = true } = config;
292
337
  const lines = [];
338
+ if (poweredBy) {
339
+ lines.push("# Powered by @onruntime/next-sitemap");
340
+ lines.push("");
341
+ }
293
342
  const rules = Array.isArray(config.rules) ? config.rules : [config.rules];
294
343
  for (const rule of rules) {
295
344
  const userAgents = Array.isArray(rule.userAgent) ? rule.userAgent : [rule.userAgent];
@@ -326,6 +375,46 @@ function generateRobotsTxt(config) {
326
375
  }
327
376
 
328
377
  // src/pages/index.ts
378
+ function readRoutesManifest(debug) {
379
+ const manifestPath = path.join(process.cwd(), ".next", "routes-manifest.json");
380
+ if (!fs.existsSync(manifestPath)) {
381
+ if (debug) {
382
+ console.log(`[next-sitemap] routes-manifest.json not found at ${manifestPath}`);
383
+ }
384
+ return null;
385
+ }
386
+ try {
387
+ const content = fs.readFileSync(manifestPath, "utf-8");
388
+ return JSON.parse(content);
389
+ } catch (err) {
390
+ if (debug) {
391
+ console.warn(`[next-sitemap] Failed to read routes-manifest.json:`, err);
392
+ }
393
+ return null;
394
+ }
395
+ }
396
+ function extractRoutesFromManifest(manifest) {
397
+ const routes = [];
398
+ for (const route of manifest.staticRoutes) {
399
+ if (route.page.startsWith("/api/")) continue;
400
+ routes.push({
401
+ pathname: route.page,
402
+ dynamicSegments: [],
403
+ fileKey: route.page
404
+ });
405
+ }
406
+ for (const route of manifest.dynamicRoutes) {
407
+ if (route.page.startsWith("/api/")) continue;
408
+ if (route.page.includes("[...")) continue;
409
+ const dynamicSegments = route.page.match(/\[([^\]]+)\]/g)?.map((s) => s.slice(1, -1)) ?? [];
410
+ routes.push({
411
+ pathname: route.page,
412
+ dynamicSegments,
413
+ fileKey: route.page
414
+ });
415
+ }
416
+ return routes;
417
+ }
329
418
  function findPageFiles(dir, baseDir = dir) {
330
419
  const files = [];
331
420
  try {
@@ -351,7 +440,7 @@ function resolvePagesDirectory(pagesDirectory) {
351
440
  const srcPages = path.join(process.cwd(), "src/pages");
352
441
  return fs.existsSync(srcPages) ? srcPages : path.join(process.cwd(), "pages");
353
442
  }
354
- function extractRoutes(pageKeys, localeSegment) {
443
+ function extractRoutesFromFiles(pageKeys, localeSegment) {
355
444
  const routes = [];
356
445
  for (const key of pageKeys) {
357
446
  if (key.includes("[...")) continue;
@@ -372,33 +461,54 @@ function extractRoutes(pageKeys, localeSegment) {
372
461
  }
373
462
  return routes;
374
463
  }
464
+ function getRoutes(pagesDirectory, localeSegment, debug) {
465
+ const manifest = readRoutesManifest(debug);
466
+ if (manifest) {
467
+ if (debug) {
468
+ console.log(`[next-sitemap] Using routes-manifest.json for route discovery`);
469
+ }
470
+ return extractRoutesFromManifest(manifest);
471
+ }
472
+ if (debug) {
473
+ console.log(`[next-sitemap] Falling back to file scanning for route discovery`);
474
+ }
475
+ const pagesDir = resolvePagesDirectory(pagesDirectory);
476
+ const pageKeys = findPageFiles(pagesDir);
477
+ return extractRoutesFromFiles(pageKeys, localeSegment);
478
+ }
479
+ function getWorkerDirectory(pagesDirectory) {
480
+ const compiledDir = path.join(process.cwd(), ".next", "server", "pages");
481
+ if (fs.existsSync(compiledDir)) {
482
+ return compiledDir;
483
+ }
484
+ return resolvePagesDirectory(pagesDirectory);
485
+ }
375
486
  function createSitemapIndexApiHandler(options) {
376
487
  const { urlsPerSitemap = 5e3, additionalSitemaps, exclude, debug = false } = options;
377
488
  const localeSegment = options.localeSegment ?? "";
378
- const pagesDir = resolvePagesDirectory(options.pagesDirectory);
379
- const pageKeys = findPageFiles(pagesDir);
380
- const routes = extractRoutes(pageKeys, localeSegment);
381
489
  return async function handler(_req, res) {
490
+ const routes = getRoutes(options.pagesDirectory, localeSegment, debug);
491
+ const workerDir = getWorkerDirectory(options.pagesDirectory);
382
492
  if (debug) {
383
493
  console.log(`[next-sitemap] Found ${routes.length} routes`);
494
+ console.log(`[next-sitemap] Worker directory: ${workerDir}`);
384
495
  }
385
- const allPaths = await generateAllPaths(routes, pagesDir, debug);
496
+ const allPaths = await generateAllPaths(routes, workerDir, debug);
386
497
  const filteredPaths = allPaths.filter((p) => !shouldExclude(p, exclude));
387
498
  const sitemapCount = Math.max(1, Math.ceil(filteredPaths.length / urlsPerSitemap));
388
499
  res.setHeader("Content-Type", "application/xml");
389
500
  res.status(200).send(
390
- generateSitemapIndexXml(options.baseUrl, sitemapCount, { additionalSitemaps })
501
+ generateSitemapIndexXml(options.baseUrl, sitemapCount, { additionalSitemaps, poweredBy: options.poweredBy })
391
502
  );
392
503
  };
393
504
  }
394
505
  function createSitemapApiHandler(options) {
395
506
  const { urlsPerSitemap = 5e3, exclude, debug = false } = options;
396
507
  const localeSegment = options.localeSegment ?? "";
397
- const pagesDir = resolvePagesDirectory(options.pagesDirectory);
398
- const pageKeys = findPageFiles(pagesDir);
399
- const routes = extractRoutes(pageKeys, localeSegment);
400
508
  const getFilteredPaths = async () => {
401
- const allPaths = await generateAllPaths(routes, pagesDir, debug);
509
+ const routes = getRoutes(options.pagesDirectory, localeSegment, debug);
510
+ const workerDir = getWorkerDirectory(options.pagesDirectory);
511
+ const allPaths = await generateAllPaths(routes, workerDir, debug);
402
512
  return allPaths.filter((p) => !shouldExclude(p, exclude));
403
513
  };
404
514
  return async function handler(req, res) {
@@ -408,16 +518,15 @@ function createSitemapApiHandler(options) {
408
518
  const paths = filteredPaths.slice(sitemapId * urlsPerSitemap, (sitemapId + 1) * urlsPerSitemap);
409
519
  const entries = pathsToEntries(paths, { ...options, exclude: void 0 });
410
520
  res.setHeader("Content-Type", "application/xml");
411
- res.status(200).send(generateSitemapXml(entries));
521
+ res.status(200).send(generateSitemapXml(entries, { poweredBy: options.poweredBy }));
412
522
  };
413
523
  }
414
524
  async function getSitemapStaticPaths(options) {
415
525
  const { urlsPerSitemap = 5e3, exclude, debug = false } = options;
416
526
  const localeSegment = options.localeSegment ?? "";
417
- const pagesDir = resolvePagesDirectory(options.pagesDirectory);
418
- const pageKeys = findPageFiles(pagesDir);
419
- const routes = extractRoutes(pageKeys, localeSegment);
420
- const allPaths = await generateAllPaths(routes, pagesDir, debug);
527
+ const routes = getRoutes(options.pagesDirectory, localeSegment, debug);
528
+ const workerDir = getWorkerDirectory(options.pagesDirectory);
529
+ const allPaths = await generateAllPaths(routes, workerDir, debug);
421
530
  const filteredPaths = allPaths.filter((pathname) => !shouldExclude(pathname, exclude));
422
531
  const sitemapCount = Math.max(1, Math.ceil(filteredPaths.length / urlsPerSitemap));
423
532
  return {
@@ -60,6 +60,11 @@ interface SitemapConfig {
60
60
  * @default false
61
61
  */
62
62
  debug?: boolean;
63
+ /**
64
+ * Include "Powered by @onruntime/next-sitemap" comment in generated XML
65
+ * @default true
66
+ */
67
+ poweredBy?: boolean;
63
68
  }
64
69
  interface SitemapEntry {
65
70
  url: string;
@@ -71,17 +76,25 @@ interface SitemapEntry {
71
76
  };
72
77
  }
73
78
 
79
+ interface RobotsConfig extends MetadataRoute.Robots {
80
+ /**
81
+ * Include "Powered by @onruntime/next-sitemap" comment
82
+ * @default true
83
+ */
84
+ poweredBy?: boolean;
85
+ }
74
86
  /**
75
87
  * Generate robots.txt content from configuration
76
88
  * Compatible with Next.js MetadataRoute.Robots
77
89
  */
78
- declare function generateRobotsTxt(config: MetadataRoute.Robots): string;
90
+ declare function generateRobotsTxt(config: RobotsConfig): string;
79
91
 
80
92
  interface CreateSitemapApiHandlerOptions extends SitemapConfig {
81
93
  /**
82
94
  * Path to the pages directory to scan for page files.
83
95
  * Can be absolute or relative to process.cwd().
84
96
  * If not provided, auto-detects src/pages or pages.
97
+ * @deprecated Use routes-manifest.json from .next directory instead
85
98
  */
86
99
  pagesDirectory?: string;
87
100
  }
@@ -111,6 +124,6 @@ declare function getSitemapStaticPaths(options: CreateSitemapApiHandlerOptions):
111
124
  * Create API handler for robots.txt
112
125
  * Use in: pages/api/robots.txt.ts
113
126
  */
114
- declare function createRobotsApiHandler(config: MetadataRoute.Robots): (_req: NextApiRequest, res: NextApiResponse) => void;
127
+ declare function createRobotsApiHandler(config: RobotsConfig): (_req: NextApiRequest, res: NextApiResponse) => void;
115
128
 
116
- export { type ChangeFrequency, type CreateSitemapApiHandlerOptions, type SitemapConfig, type SitemapEntry, createRobotsApiHandler, createSitemapApiHandler, createSitemapIndexApiHandler, generateRobotsTxt, getSitemapStaticPaths };
129
+ export { type ChangeFrequency, type CreateSitemapApiHandlerOptions, type RobotsConfig, type SitemapConfig, type SitemapEntry, createRobotsApiHandler, createSitemapApiHandler, createSitemapIndexApiHandler, generateRobotsTxt, getSitemapStaticPaths };
@@ -60,6 +60,11 @@ interface SitemapConfig {
60
60
  * @default false
61
61
  */
62
62
  debug?: boolean;
63
+ /**
64
+ * Include "Powered by @onruntime/next-sitemap" comment in generated XML
65
+ * @default true
66
+ */
67
+ poweredBy?: boolean;
63
68
  }
64
69
  interface SitemapEntry {
65
70
  url: string;
@@ -71,17 +76,25 @@ interface SitemapEntry {
71
76
  };
72
77
  }
73
78
 
79
+ interface RobotsConfig extends MetadataRoute.Robots {
80
+ /**
81
+ * Include "Powered by @onruntime/next-sitemap" comment
82
+ * @default true
83
+ */
84
+ poweredBy?: boolean;
85
+ }
74
86
  /**
75
87
  * Generate robots.txt content from configuration
76
88
  * Compatible with Next.js MetadataRoute.Robots
77
89
  */
78
- declare function generateRobotsTxt(config: MetadataRoute.Robots): string;
90
+ declare function generateRobotsTxt(config: RobotsConfig): string;
79
91
 
80
92
  interface CreateSitemapApiHandlerOptions extends SitemapConfig {
81
93
  /**
82
94
  * Path to the pages directory to scan for page files.
83
95
  * Can be absolute or relative to process.cwd().
84
96
  * If not provided, auto-detects src/pages or pages.
97
+ * @deprecated Use routes-manifest.json from .next directory instead
85
98
  */
86
99
  pagesDirectory?: string;
87
100
  }
@@ -111,6 +124,6 @@ declare function getSitemapStaticPaths(options: CreateSitemapApiHandlerOptions):
111
124
  * Create API handler for robots.txt
112
125
  * Use in: pages/api/robots.txt.ts
113
126
  */
114
- declare function createRobotsApiHandler(config: MetadataRoute.Robots): (_req: NextApiRequest, res: NextApiResponse) => void;
127
+ declare function createRobotsApiHandler(config: RobotsConfig): (_req: NextApiRequest, res: NextApiResponse) => void;
115
128
 
116
- export { type ChangeFrequency, type CreateSitemapApiHandlerOptions, type SitemapConfig, type SitemapEntry, createRobotsApiHandler, createSitemapApiHandler, createSitemapIndexApiHandler, generateRobotsTxt, getSitemapStaticPaths };
129
+ export { type ChangeFrequency, type CreateSitemapApiHandlerOptions, type RobotsConfig, type SitemapConfig, type SitemapEntry, createRobotsApiHandler, createSitemapApiHandler, createSitemapIndexApiHandler, generateRobotsTxt, getSitemapStaticPaths };
@@ -1,17 +1,58 @@
1
- import { existsSync, readdirSync } from 'fs';
1
+ import { existsSync, readFileSync, readdirSync } from 'fs';
2
2
  import { dirname, join, isAbsolute, relative, delimiter } from 'path';
3
3
  import * as childProcess from 'child_process';
4
4
  import { fileURLToPath } from 'url';
5
5
 
6
6
  // src/pages/index.ts
7
+ var JS_EXTENSIONS = /* @__PURE__ */ new Set([
8
+ ".js",
9
+ ".cjs",
10
+ ".mjs",
11
+ ".jsx",
12
+ ".ts",
13
+ ".cts",
14
+ ".mts",
15
+ ".tsx",
16
+ ".json",
17
+ ".node"
18
+ ]);
7
19
  var NO_STATIC_PARAMS = "NO_STATIC_PARAMS";
8
20
  var spawnProcess = childProcess.spawn;
9
21
  var __dirname$1 = dirname(fileURLToPath(import.meta.url));
10
22
  var paramsCache = /* @__PURE__ */ new Map();
11
23
  var isDev = process.env.NODE_ENV === "development";
24
+ function resolveFilePath(directory, fileKey) {
25
+ if (fileKey.startsWith("./")) {
26
+ return join(directory, fileKey.replace("./", ""));
27
+ }
28
+ const basePath = fileKey === "/" ? "/index" : fileKey;
29
+ const jsPath = join(directory, basePath + ".js");
30
+ if (existsSync(jsPath)) {
31
+ return jsPath;
32
+ }
33
+ for (const ext of JS_EXTENSIONS) {
34
+ const sourcePath = join(directory, basePath + ext);
35
+ if (existsSync(sourcePath)) {
36
+ return sourcePath;
37
+ }
38
+ }
39
+ for (const ext of JS_EXTENSIONS) {
40
+ const indexPath = join(directory, basePath, "index" + ext);
41
+ if (existsSync(indexPath)) {
42
+ return indexPath;
43
+ }
44
+ }
45
+ return null;
46
+ }
12
47
  async function executeWorker(directory, fileKey, debug) {
13
- const absolutePath = join(directory, fileKey.replace("./", ""));
48
+ const absolutePath = resolveFilePath(directory, fileKey);
14
49
  const projectRoot = process.cwd();
50
+ if (!absolutePath) {
51
+ if (debug) {
52
+ console.warn(`[next-sitemap] Could not resolve file path for ${fileKey} in ${directory}`);
53
+ }
54
+ return null;
55
+ }
15
56
  const distRoot = join(__dirname$1, "..");
16
57
  const workerPath = join(distRoot, "worker.cjs");
17
58
  const loaderPath = join(distRoot, "loader.js");
@@ -211,7 +252,8 @@ function buildUrl(baseUrl, pathname, locale, defaultLocale) {
211
252
  }
212
253
  return `${baseUrl}/${locale}${normalizedPath}`;
213
254
  }
214
- function generateSitemapXml(entries) {
255
+ function generateSitemapXml(entries, options) {
256
+ const { poweredBy = true } = options || {};
215
257
  const hasAlternates = entries.some((e) => e.alternates?.languages);
216
258
  const xmlns = hasAlternates ? 'xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml"' : 'xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"';
217
259
  const urlEntries = entries.map((entry) => {
@@ -235,13 +277,14 @@ function generateSitemapXml(entries) {
235
277
  ${parts.join("\n")}
236
278
  </url>`;
237
279
  }).join("\n");
238
- return `<?xml version="1.0" encoding="UTF-8"?>
280
+ const comment = poweredBy ? "\n<!-- Powered by @onruntime/next-sitemap -->" : "";
281
+ return `<?xml version="1.0" encoding="UTF-8"?>${comment}
239
282
  <urlset ${xmlns}>
240
283
  ${urlEntries}
241
284
  </urlset>`;
242
285
  }
243
286
  function generateSitemapIndexXml(baseUrl, sitemapCount, options) {
244
- const { sitemapPattern = "/sitemap-{id}.xml", additionalSitemaps = [] } = options || {};
287
+ const { sitemapPattern = "/sitemap-{id}.xml", additionalSitemaps = [], poweredBy = true } = options || {};
245
288
  const now = (/* @__PURE__ */ new Date()).toISOString();
246
289
  const paginatedEntries = Array.from({ length: sitemapCount }, (_, i) => {
247
290
  const loc = `${baseUrl}${sitemapPattern.replace("{id}", String(i))}`;
@@ -258,7 +301,8 @@ function generateSitemapIndexXml(baseUrl, sitemapCount, options) {
258
301
  </sitemap>`;
259
302
  });
260
303
  const allEntries = [...paginatedEntries, ...additionalEntries].join("\n");
261
- return `<?xml version="1.0" encoding="UTF-8"?>
304
+ const comment = poweredBy ? "\n<!-- Powered by @onruntime/next-sitemap -->" : "";
305
+ return `<?xml version="1.0" encoding="UTF-8"?>${comment}
262
306
  <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
263
307
  ${allEntries}
264
308
  </sitemapindex>`;
@@ -266,7 +310,12 @@ ${allEntries}
266
310
 
267
311
  // src/pages/robots.ts
268
312
  function generateRobotsTxt(config) {
313
+ const { poweredBy = true } = config;
269
314
  const lines = [];
315
+ if (poweredBy) {
316
+ lines.push("# Powered by @onruntime/next-sitemap");
317
+ lines.push("");
318
+ }
270
319
  const rules = Array.isArray(config.rules) ? config.rules : [config.rules];
271
320
  for (const rule of rules) {
272
321
  const userAgents = Array.isArray(rule.userAgent) ? rule.userAgent : [rule.userAgent];
@@ -303,6 +352,46 @@ function generateRobotsTxt(config) {
303
352
  }
304
353
 
305
354
  // src/pages/index.ts
355
+ function readRoutesManifest(debug) {
356
+ const manifestPath = join(process.cwd(), ".next", "routes-manifest.json");
357
+ if (!existsSync(manifestPath)) {
358
+ if (debug) {
359
+ console.log(`[next-sitemap] routes-manifest.json not found at ${manifestPath}`);
360
+ }
361
+ return null;
362
+ }
363
+ try {
364
+ const content = readFileSync(manifestPath, "utf-8");
365
+ return JSON.parse(content);
366
+ } catch (err) {
367
+ if (debug) {
368
+ console.warn(`[next-sitemap] Failed to read routes-manifest.json:`, err);
369
+ }
370
+ return null;
371
+ }
372
+ }
373
+ function extractRoutesFromManifest(manifest) {
374
+ const routes = [];
375
+ for (const route of manifest.staticRoutes) {
376
+ if (route.page.startsWith("/api/")) continue;
377
+ routes.push({
378
+ pathname: route.page,
379
+ dynamicSegments: [],
380
+ fileKey: route.page
381
+ });
382
+ }
383
+ for (const route of manifest.dynamicRoutes) {
384
+ if (route.page.startsWith("/api/")) continue;
385
+ if (route.page.includes("[...")) continue;
386
+ const dynamicSegments = route.page.match(/\[([^\]]+)\]/g)?.map((s) => s.slice(1, -1)) ?? [];
387
+ routes.push({
388
+ pathname: route.page,
389
+ dynamicSegments,
390
+ fileKey: route.page
391
+ });
392
+ }
393
+ return routes;
394
+ }
306
395
  function findPageFiles(dir, baseDir = dir) {
307
396
  const files = [];
308
397
  try {
@@ -328,7 +417,7 @@ function resolvePagesDirectory(pagesDirectory) {
328
417
  const srcPages = join(process.cwd(), "src/pages");
329
418
  return existsSync(srcPages) ? srcPages : join(process.cwd(), "pages");
330
419
  }
331
- function extractRoutes(pageKeys, localeSegment) {
420
+ function extractRoutesFromFiles(pageKeys, localeSegment) {
332
421
  const routes = [];
333
422
  for (const key of pageKeys) {
334
423
  if (key.includes("[...")) continue;
@@ -349,33 +438,54 @@ function extractRoutes(pageKeys, localeSegment) {
349
438
  }
350
439
  return routes;
351
440
  }
441
+ function getRoutes(pagesDirectory, localeSegment, debug) {
442
+ const manifest = readRoutesManifest(debug);
443
+ if (manifest) {
444
+ if (debug) {
445
+ console.log(`[next-sitemap] Using routes-manifest.json for route discovery`);
446
+ }
447
+ return extractRoutesFromManifest(manifest);
448
+ }
449
+ if (debug) {
450
+ console.log(`[next-sitemap] Falling back to file scanning for route discovery`);
451
+ }
452
+ const pagesDir = resolvePagesDirectory(pagesDirectory);
453
+ const pageKeys = findPageFiles(pagesDir);
454
+ return extractRoutesFromFiles(pageKeys, localeSegment);
455
+ }
456
+ function getWorkerDirectory(pagesDirectory) {
457
+ const compiledDir = join(process.cwd(), ".next", "server", "pages");
458
+ if (existsSync(compiledDir)) {
459
+ return compiledDir;
460
+ }
461
+ return resolvePagesDirectory(pagesDirectory);
462
+ }
352
463
  function createSitemapIndexApiHandler(options) {
353
464
  const { urlsPerSitemap = 5e3, additionalSitemaps, exclude, debug = false } = options;
354
465
  const localeSegment = options.localeSegment ?? "";
355
- const pagesDir = resolvePagesDirectory(options.pagesDirectory);
356
- const pageKeys = findPageFiles(pagesDir);
357
- const routes = extractRoutes(pageKeys, localeSegment);
358
466
  return async function handler(_req, res) {
467
+ const routes = getRoutes(options.pagesDirectory, localeSegment, debug);
468
+ const workerDir = getWorkerDirectory(options.pagesDirectory);
359
469
  if (debug) {
360
470
  console.log(`[next-sitemap] Found ${routes.length} routes`);
471
+ console.log(`[next-sitemap] Worker directory: ${workerDir}`);
361
472
  }
362
- const allPaths = await generateAllPaths(routes, pagesDir, debug);
473
+ const allPaths = await generateAllPaths(routes, workerDir, debug);
363
474
  const filteredPaths = allPaths.filter((p) => !shouldExclude(p, exclude));
364
475
  const sitemapCount = Math.max(1, Math.ceil(filteredPaths.length / urlsPerSitemap));
365
476
  res.setHeader("Content-Type", "application/xml");
366
477
  res.status(200).send(
367
- generateSitemapIndexXml(options.baseUrl, sitemapCount, { additionalSitemaps })
478
+ generateSitemapIndexXml(options.baseUrl, sitemapCount, { additionalSitemaps, poweredBy: options.poweredBy })
368
479
  );
369
480
  };
370
481
  }
371
482
  function createSitemapApiHandler(options) {
372
483
  const { urlsPerSitemap = 5e3, exclude, debug = false } = options;
373
484
  const localeSegment = options.localeSegment ?? "";
374
- const pagesDir = resolvePagesDirectory(options.pagesDirectory);
375
- const pageKeys = findPageFiles(pagesDir);
376
- const routes = extractRoutes(pageKeys, localeSegment);
377
485
  const getFilteredPaths = async () => {
378
- const allPaths = await generateAllPaths(routes, pagesDir, debug);
486
+ const routes = getRoutes(options.pagesDirectory, localeSegment, debug);
487
+ const workerDir = getWorkerDirectory(options.pagesDirectory);
488
+ const allPaths = await generateAllPaths(routes, workerDir, debug);
379
489
  return allPaths.filter((p) => !shouldExclude(p, exclude));
380
490
  };
381
491
  return async function handler(req, res) {
@@ -385,16 +495,15 @@ function createSitemapApiHandler(options) {
385
495
  const paths = filteredPaths.slice(sitemapId * urlsPerSitemap, (sitemapId + 1) * urlsPerSitemap);
386
496
  const entries = pathsToEntries(paths, { ...options, exclude: void 0 });
387
497
  res.setHeader("Content-Type", "application/xml");
388
- res.status(200).send(generateSitemapXml(entries));
498
+ res.status(200).send(generateSitemapXml(entries, { poweredBy: options.poweredBy }));
389
499
  };
390
500
  }
391
501
  async function getSitemapStaticPaths(options) {
392
502
  const { urlsPerSitemap = 5e3, exclude, debug = false } = options;
393
503
  const localeSegment = options.localeSegment ?? "";
394
- const pagesDir = resolvePagesDirectory(options.pagesDirectory);
395
- const pageKeys = findPageFiles(pagesDir);
396
- const routes = extractRoutes(pageKeys, localeSegment);
397
- const allPaths = await generateAllPaths(routes, pagesDir, debug);
504
+ const routes = getRoutes(options.pagesDirectory, localeSegment, debug);
505
+ const workerDir = getWorkerDirectory(options.pagesDirectory);
506
+ const allPaths = await generateAllPaths(routes, workerDir, debug);
398
507
  const filteredPaths = allPaths.filter((pathname) => !shouldExclude(pathname, exclude));
399
508
  const sitemapCount = Math.max(1, Math.ceil(filteredPaths.length / urlsPerSitemap));
400
509
  return {
package/dist/worker.cjs CHANGED
@@ -3,6 +3,7 @@
3
3
  var fs = require('fs');
4
4
  var module$1 = require('module');
5
5
  var path = require('path');
6
+ var os = require('os');
6
7
  var jiti = require('jiti');
7
8
  require('child_process');
8
9
  var url = require('url');
@@ -116,8 +117,52 @@ process.env.NODE_ENV === "development";
116
117
 
117
118
  // src/worker.ts
118
119
  var joinPath = (...segments) => path.join(...segments);
120
+ var mockedModules = /* @__PURE__ */ new Set();
121
+ var mockTempDir = null;
122
+ function getMockTempDir() {
123
+ if (!mockTempDir) {
124
+ mockTempDir = fs.mkdtempSync(joinPath(os.tmpdir(), "next-sitemap-"));
125
+ }
126
+ return mockTempDir;
127
+ }
128
+ function createMockFile(name) {
129
+ const mockPath = joinPath(getMockTempDir(), `${name.replace(/[^a-zA-Z0-9]/g, "_")}-mock.js`);
130
+ const mockContent = `
131
+ // Auto-generated mock for ${name}
132
+ const handler = {
133
+ get(_, prop) {
134
+ if (prop === Symbol.toPrimitive) return () => "";
135
+ if (prop === "then") return undefined;
136
+ return new Proxy(() => {}, handler);
137
+ },
138
+ apply() { return new Proxy(() => {}, handler); }
139
+ };
140
+ const mock = new Proxy(() => {}, handler);
141
+ module.exports = mock;
142
+ module.exports.default = mock;
143
+ // Common named exports
144
+ module.exports.env = mock;
145
+ module.exports.createEnv = () => mock;
146
+ `;
147
+ fs.writeFileSync(mockPath, mockContent);
148
+ return mockPath;
149
+ }
119
150
  installMockLoader();
120
151
  main();
152
+ function createGenericMock() {
153
+ const handler = {
154
+ get(_, prop) {
155
+ if (prop === Symbol.toPrimitive) return () => "";
156
+ if (prop === "then") return void 0;
157
+ return new Proxy(() => createGenericMock(), handler);
158
+ },
159
+ apply() {
160
+ return createGenericMock();
161
+ }
162
+ };
163
+ return new Proxy(() => {
164
+ }, handler);
165
+ }
121
166
  function installMockLoader() {
122
167
  const ModuleInternal = module$1.Module;
123
168
  const originalLoad = ModuleInternal._load;
@@ -126,13 +171,19 @@ function installMockLoader() {
126
171
  try {
127
172
  resolvedPath = ModuleInternal._resolveFilename(request, parent, isMain);
128
173
  } catch {
129
- return originalLoad(request, parent, isMain);
174
+ mockedModules.add(request);
175
+ return createGenericMock();
130
176
  }
131
177
  const ext = path.extname(resolvedPath).toLowerCase();
132
178
  if (ext && !JS_EXTENSIONS.has(ext)) {
133
179
  return {};
134
180
  }
135
- return originalLoad(request, parent, isMain);
181
+ try {
182
+ return originalLoad(request, parent, isMain);
183
+ } catch {
184
+ mockedModules.add(request);
185
+ return createGenericMock();
186
+ }
136
187
  };
137
188
  }
138
189
  function parseTsconfigAliases(projectRoot) {
@@ -151,7 +202,16 @@ function parseTsconfigAliases(projectRoot) {
151
202
  if (fs.existsSync(srcPath)) {
152
203
  aliases["src/"] = `${srcPath}/`;
153
204
  }
205
+ for (const ext of JS_EXTENSIONS) {
206
+ const envFile = joinPath(projectRoot, `env${ext}`);
207
+ if (fs.existsSync(envFile)) {
208
+ aliases["env"] = createMockFile("env");
209
+ break;
210
+ }
211
+ }
154
212
  }
213
+ aliases["server-only"] = createMockFile("server-only");
214
+ aliases["client-only"] = createMockFile("client-only");
155
215
  for (const [pattern, targets] of Object.entries(paths)) {
156
216
  const target = targets[0];
157
217
  if (!target) continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onruntime/next-sitemap",
3
- "version": "0.7.0",
3
+ "version": "0.9.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": {