@onruntime/next-sitemap 0.4.1 → 0.5.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
@@ -17,6 +17,7 @@ Dynamic sitemap generation for Next.js with automatic route discovery.
17
17
  - Multi-sitemap support with sitemap index (for sites with >50,000 URLs)
18
18
  - hreflang alternates for i18n
19
19
  - Fully static generation (SSG)
20
+ - robots.txt generation with Next.js `MetadataRoute.Robots` compatibility
20
21
 
21
22
  ## Installation
22
23
 
@@ -354,6 +355,47 @@ export async function GET() {
354
355
  </urlset>
355
356
  ```
356
357
 
358
+ ## Robots.txt (Pages Router)
359
+
360
+ For Pages Router, use `createRobotsApiHandler` to generate a `robots.txt` file. The configuration uses the same format as [Next.js `MetadataRoute.Robots`](https://nextjs.org/docs/app/api-reference/file-conventions/metadata/robots).
361
+
362
+ ```typescript
363
+ // pages/api/robots.txt.ts
364
+ import { createRobotsApiHandler } from "@onruntime/next-sitemap/pages";
365
+
366
+ export default createRobotsApiHandler({
367
+ rules: {
368
+ userAgent: "*",
369
+ allow: "/",
370
+ disallow: ["/admin", "/private"],
371
+ },
372
+ sitemap: "https://example.com/sitemap.xml",
373
+ });
374
+ ```
375
+
376
+ Add a rewrite in `next.config.ts`:
377
+
378
+ ```typescript
379
+ {
380
+ source: "/robots.txt",
381
+ destination: "/api/robots.txt",
382
+ }
383
+ ```
384
+
385
+ **Note:** For App Router, use the [native `robots.ts` file convention](https://nextjs.org/docs/app/api-reference/file-conventions/metadata/robots) instead.
386
+
387
+ ## Requirements
388
+
389
+ This package requires a **Node.js server runtime**. It works with:
390
+ - Vercel
391
+ - Netlify (with Next.js runtime)
392
+ - Railway, Render, Fly.io
393
+ - AWS (EC2, ECS, Lambda)
394
+ - Docker containers
395
+ - Any Node.js server
396
+
397
+ It does **not** work with static exports (`output: 'export'`) or static hosting platforms like Cloudflare Pages, Netlify (static mode), or GitHub Pages.
398
+
357
399
  ## Troubleshooting
358
400
 
359
401
  ### Dynamic routes not included in sitemap
@@ -100,6 +100,44 @@ ${allEntries}
100
100
  </sitemapindex>`;
101
101
  }
102
102
 
103
+ // src/pages/robots.ts
104
+ function generateRobotsTxt(config) {
105
+ const lines = [];
106
+ const rules = Array.isArray(config.rules) ? config.rules : [config.rules];
107
+ for (const rule of rules) {
108
+ const userAgents = Array.isArray(rule.userAgent) ? rule.userAgent : [rule.userAgent];
109
+ for (const userAgent of userAgents) {
110
+ lines.push(`User-agent: ${userAgent}`);
111
+ }
112
+ if (rule.allow) {
113
+ const allows = Array.isArray(rule.allow) ? rule.allow : [rule.allow];
114
+ for (const allow of allows) {
115
+ lines.push(`Allow: ${allow}`);
116
+ }
117
+ }
118
+ if (rule.disallow) {
119
+ const disallows = Array.isArray(rule.disallow) ? rule.disallow : [rule.disallow];
120
+ for (const disallow of disallows) {
121
+ lines.push(`Disallow: ${disallow}`);
122
+ }
123
+ }
124
+ if (rule.crawlDelay !== void 0) {
125
+ lines.push(`Crawl-delay: ${rule.crawlDelay}`);
126
+ }
127
+ lines.push("");
128
+ }
129
+ if (config.sitemap) {
130
+ const sitemaps = Array.isArray(config.sitemap) ? config.sitemap : [config.sitemap];
131
+ for (const sitemap of sitemaps) {
132
+ lines.push(`Sitemap: ${sitemap}`);
133
+ }
134
+ }
135
+ if (config.host) {
136
+ lines.push(`Host: ${config.host}`);
137
+ }
138
+ return lines.join("\n").trim() + "\n";
139
+ }
140
+
103
141
  // src/pages/index.ts
104
142
  function extractRoutes(pagesContext, localeSegment) {
105
143
  const routes = [];
@@ -267,7 +305,16 @@ async function getSitemapStaticPaths(options) {
267
305
  fallback: false
268
306
  };
269
307
  }
308
+ function createRobotsApiHandler(config) {
309
+ return function handler(_req, res) {
310
+ const robotsTxt = generateRobotsTxt(config);
311
+ res.setHeader("Content-Type", "text/plain");
312
+ res.status(200).send(robotsTxt);
313
+ };
314
+ }
270
315
 
316
+ exports.createRobotsApiHandler = createRobotsApiHandler;
271
317
  exports.createSitemapApiHandler = createSitemapApiHandler;
272
318
  exports.createSitemapIndexApiHandler = createSitemapIndexApiHandler;
319
+ exports.generateRobotsTxt = generateRobotsTxt;
273
320
  exports.getSitemapStaticPaths = getSitemapStaticPaths;
@@ -1,4 +1,5 @@
1
- import { NextApiRequest, NextApiResponse } from 'next';
1
+ import { MetadataRoute, NextApiRequest, NextApiResponse } from 'next';
2
+ export { MetadataRoute } from 'next';
2
3
 
3
4
  type ChangeFrequency = "always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never";
4
5
  interface SitemapConfig {
@@ -72,6 +73,12 @@ type PageModule = {
72
73
  generateStaticParams?: () => Promise<Record<string, string>[]>;
73
74
  };
74
75
 
76
+ /**
77
+ * Generate robots.txt content from configuration
78
+ * Compatible with Next.js MetadataRoute.Robots
79
+ */
80
+ declare function generateRobotsTxt(config: MetadataRoute.Robots): string;
81
+
75
82
  interface CreateSitemapApiHandlerOptions extends SitemapConfig {
76
83
  /**
77
84
  * The require.context result for page discovery
@@ -110,5 +117,23 @@ declare function getSitemapStaticPaths(options: CreateSitemapApiHandlerOptions):
110
117
  }[];
111
118
  fallback: boolean;
112
119
  }>;
120
+ /**
121
+ * Create API handler for robots.txt
122
+ * Use in: pages/api/robots.txt.ts or pages/robots.txt.ts (with rewrites)
123
+ *
124
+ * @example
125
+ * // pages/api/robots.txt.ts
126
+ * import { createRobotsApiHandler } from "@onruntime/next-sitemap/pages";
127
+ *
128
+ * export default createRobotsApiHandler({
129
+ * rules: {
130
+ * userAgent: "*",
131
+ * allow: "/",
132
+ * disallow: ["/admin", "/private"],
133
+ * },
134
+ * sitemap: "https://example.com/sitemap.xml",
135
+ * });
136
+ */
137
+ declare function createRobotsApiHandler(config: MetadataRoute.Robots): (_req: NextApiRequest, res: NextApiResponse) => void;
113
138
 
114
- export { type ChangeFrequency, type CreateSitemapApiHandlerOptions, type SitemapConfig, type SitemapEntry, createSitemapApiHandler, createSitemapIndexApiHandler, getSitemapStaticPaths };
139
+ export { type ChangeFrequency, type CreateSitemapApiHandlerOptions, type SitemapConfig, type SitemapEntry, createRobotsApiHandler, createSitemapApiHandler, createSitemapIndexApiHandler, generateRobotsTxt, getSitemapStaticPaths };
@@ -1,4 +1,5 @@
1
- import { NextApiRequest, NextApiResponse } from 'next';
1
+ import { MetadataRoute, NextApiRequest, NextApiResponse } from 'next';
2
+ export { MetadataRoute } from 'next';
2
3
 
3
4
  type ChangeFrequency = "always" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" | "never";
4
5
  interface SitemapConfig {
@@ -72,6 +73,12 @@ type PageModule = {
72
73
  generateStaticParams?: () => Promise<Record<string, string>[]>;
73
74
  };
74
75
 
76
+ /**
77
+ * Generate robots.txt content from configuration
78
+ * Compatible with Next.js MetadataRoute.Robots
79
+ */
80
+ declare function generateRobotsTxt(config: MetadataRoute.Robots): string;
81
+
75
82
  interface CreateSitemapApiHandlerOptions extends SitemapConfig {
76
83
  /**
77
84
  * The require.context result for page discovery
@@ -110,5 +117,23 @@ declare function getSitemapStaticPaths(options: CreateSitemapApiHandlerOptions):
110
117
  }[];
111
118
  fallback: boolean;
112
119
  }>;
120
+ /**
121
+ * Create API handler for robots.txt
122
+ * Use in: pages/api/robots.txt.ts or pages/robots.txt.ts (with rewrites)
123
+ *
124
+ * @example
125
+ * // pages/api/robots.txt.ts
126
+ * import { createRobotsApiHandler } from "@onruntime/next-sitemap/pages";
127
+ *
128
+ * export default createRobotsApiHandler({
129
+ * rules: {
130
+ * userAgent: "*",
131
+ * allow: "/",
132
+ * disallow: ["/admin", "/private"],
133
+ * },
134
+ * sitemap: "https://example.com/sitemap.xml",
135
+ * });
136
+ */
137
+ declare function createRobotsApiHandler(config: MetadataRoute.Robots): (_req: NextApiRequest, res: NextApiResponse) => void;
113
138
 
114
- export { type ChangeFrequency, type CreateSitemapApiHandlerOptions, type SitemapConfig, type SitemapEntry, createSitemapApiHandler, createSitemapIndexApiHandler, getSitemapStaticPaths };
139
+ export { type ChangeFrequency, type CreateSitemapApiHandlerOptions, type SitemapConfig, type SitemapEntry, createRobotsApiHandler, createSitemapApiHandler, createSitemapIndexApiHandler, generateRobotsTxt, getSitemapStaticPaths };
@@ -98,6 +98,44 @@ ${allEntries}
98
98
  </sitemapindex>`;
99
99
  }
100
100
 
101
+ // src/pages/robots.ts
102
+ function generateRobotsTxt(config) {
103
+ const lines = [];
104
+ const rules = Array.isArray(config.rules) ? config.rules : [config.rules];
105
+ for (const rule of rules) {
106
+ const userAgents = Array.isArray(rule.userAgent) ? rule.userAgent : [rule.userAgent];
107
+ for (const userAgent of userAgents) {
108
+ lines.push(`User-agent: ${userAgent}`);
109
+ }
110
+ if (rule.allow) {
111
+ const allows = Array.isArray(rule.allow) ? rule.allow : [rule.allow];
112
+ for (const allow of allows) {
113
+ lines.push(`Allow: ${allow}`);
114
+ }
115
+ }
116
+ if (rule.disallow) {
117
+ const disallows = Array.isArray(rule.disallow) ? rule.disallow : [rule.disallow];
118
+ for (const disallow of disallows) {
119
+ lines.push(`Disallow: ${disallow}`);
120
+ }
121
+ }
122
+ if (rule.crawlDelay !== void 0) {
123
+ lines.push(`Crawl-delay: ${rule.crawlDelay}`);
124
+ }
125
+ lines.push("");
126
+ }
127
+ if (config.sitemap) {
128
+ const sitemaps = Array.isArray(config.sitemap) ? config.sitemap : [config.sitemap];
129
+ for (const sitemap of sitemaps) {
130
+ lines.push(`Sitemap: ${sitemap}`);
131
+ }
132
+ }
133
+ if (config.host) {
134
+ lines.push(`Host: ${config.host}`);
135
+ }
136
+ return lines.join("\n").trim() + "\n";
137
+ }
138
+
101
139
  // src/pages/index.ts
102
140
  function extractRoutes(pagesContext, localeSegment) {
103
141
  const routes = [];
@@ -265,5 +303,12 @@ async function getSitemapStaticPaths(options) {
265
303
  fallback: false
266
304
  };
267
305
  }
306
+ function createRobotsApiHandler(config) {
307
+ return function handler(_req, res) {
308
+ const robotsTxt = generateRobotsTxt(config);
309
+ res.setHeader("Content-Type", "text/plain");
310
+ res.status(200).send(robotsTxt);
311
+ };
312
+ }
268
313
 
269
- export { createSitemapApiHandler, createSitemapIndexApiHandler, getSitemapStaticPaths };
314
+ export { createRobotsApiHandler, createSitemapApiHandler, createSitemapIndexApiHandler, generateRobotsTxt, getSitemapStaticPaths };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onruntime/next-sitemap",
3
- "version": "0.4.1",
3
+ "version": "0.5.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": {