@onruntime/next-sitemap 0.5.1 → 0.6.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 +4 -31
- package/dist/app/index.cjs +120 -36
- package/dist/app/index.d.cts +9 -14
- package/dist/app/index.d.ts +9 -14
- package/dist/app/index.js +98 -36
- package/dist/index.d.cts +0 -5
- package/dist/index.d.ts +0 -5
- package/dist/pages/index.cjs +133 -50
- package/dist/pages/index.d.cts +9 -14
- package/dist/pages/index.d.ts +9 -14
- package/dist/pages/index.js +111 -50
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ Dynamic sitemap generation for Next.js with automatic route discovery.
|
|
|
12
12
|
## Features
|
|
13
13
|
|
|
14
14
|
- **App Router** and **Pages Router** support
|
|
15
|
-
- Automatic route discovery using
|
|
15
|
+
- Automatic route discovery using filesystem scan
|
|
16
16
|
- Calls `generateStaticParams` (App Router) or `getStaticPaths` (Pages Router) for dynamic routes
|
|
17
17
|
- Multi-sitemap support with sitemap index (for sites with >50,000 URLs)
|
|
18
18
|
- hreflang alternates for i18n
|
|
@@ -37,12 +37,8 @@ import { createSitemapIndexHandler } from "@onruntime/next-sitemap/app";
|
|
|
37
37
|
|
|
38
38
|
export const dynamic = "force-static";
|
|
39
39
|
|
|
40
|
-
// @ts-expect-error - require.context is a webpack/turbopack feature
|
|
41
|
-
const pagesContext = require.context("../", true, /\/page\.tsx$/);
|
|
42
|
-
|
|
43
40
|
const { GET } = createSitemapIndexHandler({
|
|
44
41
|
baseUrl: "https://example.com",
|
|
45
|
-
pagesContext,
|
|
46
42
|
});
|
|
47
43
|
|
|
48
44
|
export { GET };
|
|
@@ -56,12 +52,8 @@ import { createSitemapHandler } from "@onruntime/next-sitemap/app";
|
|
|
56
52
|
|
|
57
53
|
export const dynamic = "force-static";
|
|
58
54
|
|
|
59
|
-
// @ts-expect-error - require.context is a webpack/turbopack feature
|
|
60
|
-
const pagesContext = require.context("../../", true, /\/page\.tsx$/);
|
|
61
|
-
|
|
62
55
|
const { generateStaticParams, GET } = createSitemapHandler({
|
|
63
56
|
baseUrl: "https://example.com",
|
|
64
|
-
pagesContext,
|
|
65
57
|
});
|
|
66
58
|
|
|
67
59
|
export { generateStaticParams, GET };
|
|
@@ -73,13 +65,10 @@ If your app uses a `[locale]` segment for internationalization, just add `locale
|
|
|
73
65
|
|
|
74
66
|
```typescript
|
|
75
67
|
// app/sitemap.xml/route.ts
|
|
76
|
-
const pagesContext = require.context("../[locale]", true, /\/page\.tsx$/);
|
|
77
|
-
|
|
78
68
|
const { GET } = createSitemapIndexHandler({
|
|
79
69
|
baseUrl: "https://example.com",
|
|
80
70
|
locales: ["en", "fr"],
|
|
81
71
|
defaultLocale: "en",
|
|
82
|
-
pagesContext,
|
|
83
72
|
});
|
|
84
73
|
```
|
|
85
74
|
|
|
@@ -117,12 +106,8 @@ export default nextConfig;
|
|
|
117
106
|
// pages/api/sitemap.xml.ts
|
|
118
107
|
import { createSitemapIndexApiHandler } from "@onruntime/next-sitemap/pages";
|
|
119
108
|
|
|
120
|
-
// @ts-expect-error - require.context is a webpack/turbopack feature
|
|
121
|
-
const pagesContext = require.context("../", true, /^\.\/(?!\[|_|api\/).*\.tsx$/);
|
|
122
|
-
|
|
123
109
|
export default createSitemapIndexApiHandler({
|
|
124
110
|
baseUrl: "https://example.com",
|
|
125
|
-
pagesContext,
|
|
126
111
|
});
|
|
127
112
|
```
|
|
128
113
|
|
|
@@ -132,12 +117,8 @@ export default createSitemapIndexApiHandler({
|
|
|
132
117
|
// pages/api/sitemap/[id].ts
|
|
133
118
|
import { createSitemapApiHandler } from "@onruntime/next-sitemap/pages";
|
|
134
119
|
|
|
135
|
-
// @ts-expect-error - require.context is a webpack/turbopack feature
|
|
136
|
-
const pagesContext = require.context("../../", true, /^\.\/(?!\[|_|api\/).*\.tsx$/);
|
|
137
|
-
|
|
138
120
|
export default createSitemapApiHandler({
|
|
139
121
|
baseUrl: "https://example.com",
|
|
140
|
-
pagesContext,
|
|
141
122
|
});
|
|
142
123
|
```
|
|
143
124
|
|
|
@@ -181,14 +162,10 @@ module.exports = {
|
|
|
181
162
|
// pages/api/sitemap.xml.ts
|
|
182
163
|
import { createSitemapIndexApiHandler } from "@onruntime/next-sitemap/pages";
|
|
183
164
|
|
|
184
|
-
// @ts-expect-error - require.context is a webpack/turbopack feature
|
|
185
|
-
const pagesContext = require.context("../", true, /^\.\/(?!\[|_|api\/).*\.tsx$/);
|
|
186
|
-
|
|
187
165
|
export default createSitemapIndexApiHandler({
|
|
188
166
|
baseUrl: "https://example.com",
|
|
189
167
|
locales: ["en", "fr"],
|
|
190
168
|
defaultLocale: "en",
|
|
191
|
-
pagesContext,
|
|
192
169
|
});
|
|
193
170
|
```
|
|
194
171
|
|
|
@@ -197,11 +174,12 @@ export default createSitemapIndexApiHandler({
|
|
|
197
174
|
| Option | Type | Default | Description |
|
|
198
175
|
|--------|------|---------|-------------|
|
|
199
176
|
| `baseUrl` | `string` | required | Base URL of the site |
|
|
177
|
+
| `appDirectory` | `string` | auto | (App Router) Path to scan for pages. Auto-detects `src/app` or `app` |
|
|
178
|
+
| `pagesDirectory` | `string` | auto | (Pages Router) Path to scan for pages. Auto-detects `src/pages` or `pages` |
|
|
200
179
|
| `locales` | `string[]` | `[]` | List of supported locales |
|
|
201
180
|
| `defaultLocale` | `string` | `undefined` | Default locale (URLs without prefix) |
|
|
202
181
|
| `urlsPerSitemap` | `number` | `5000` | Max URLs per sitemap file |
|
|
203
182
|
| `localeSegment` | `string` | auto | Auto-detected as `"[locale]"` when i18n is configured. Override for custom names like `"[lang]"`. |
|
|
204
|
-
| `pagesContext` | `object` | required | Result of `require.context()` |
|
|
205
183
|
| `exclude` | `string[]` or `function` | `undefined` | Patterns or function to exclude routes |
|
|
206
184
|
| `priority` | `number`, `"auto"`, or `function` | `"auto"` | Priority calculation (auto = depth-based) |
|
|
207
185
|
| `changeFreq` | `ChangeFrequency` or `function` | `"weekly"` | Change frequency for entries |
|
|
@@ -216,14 +194,12 @@ Exclude specific routes from the sitemap using glob patterns or a function:
|
|
|
216
194
|
// Using glob patterns
|
|
217
195
|
const { GET } = createSitemapIndexHandler({
|
|
218
196
|
baseUrl: "https://example.com",
|
|
219
|
-
pagesContext,
|
|
220
197
|
exclude: ["/admin/*", "/api/*", "/private/**"],
|
|
221
198
|
});
|
|
222
199
|
|
|
223
200
|
// Using a function
|
|
224
201
|
const { GET } = createSitemapIndexHandler({
|
|
225
202
|
baseUrl: "https://example.com",
|
|
226
|
-
pagesContext,
|
|
227
203
|
exclude: (path) => path.startsWith("/internal"),
|
|
228
204
|
});
|
|
229
205
|
```
|
|
@@ -273,14 +249,13 @@ Include custom sitemaps in the sitemap index (e.g., for API-fetched data):
|
|
|
273
249
|
```typescript
|
|
274
250
|
const { GET } = createSitemapIndexHandler({
|
|
275
251
|
baseUrl: "https://example.com",
|
|
276
|
-
pagesContext,
|
|
277
252
|
additionalSitemaps: ["/products-sitemap.xml", "/blog-sitemap.xml"],
|
|
278
253
|
});
|
|
279
254
|
```
|
|
280
255
|
|
|
281
256
|
### How It Works
|
|
282
257
|
|
|
283
|
-
1.
|
|
258
|
+
1. The filesystem is scanned to discover all pages in your app/pages directory
|
|
284
259
|
2. For each page found, it extracts the route path
|
|
285
260
|
3. For dynamic routes (e.g., `/projects/[id]`), it calls `generateStaticParams` (App Router) or `getStaticPaths` (Pages Router)
|
|
286
261
|
4. URLs are paginated into multiple sitemaps (default: 5000 URLs each)
|
|
@@ -405,7 +380,6 @@ If your dynamic routes (e.g., `/articles/[slug]`) are not appearing in the sitem
|
|
|
405
380
|
```typescript
|
|
406
381
|
const { GET } = createSitemapIndexHandler({
|
|
407
382
|
baseUrl: "https://example.com",
|
|
408
|
-
pagesContext,
|
|
409
383
|
debug: true, // Enable debug logging
|
|
410
384
|
});
|
|
411
385
|
```
|
|
@@ -441,7 +415,6 @@ For routes that fetch data from external sources (APIs, databases like Payload C
|
|
|
441
415
|
// app/sitemap.xml/route.ts
|
|
442
416
|
const { GET } = createSitemapIndexHandler({
|
|
443
417
|
baseUrl: "https://example.com",
|
|
444
|
-
pagesContext,
|
|
445
418
|
additionalSitemaps: ["/articles-sitemap.xml"],
|
|
446
419
|
});
|
|
447
420
|
|
package/dist/app/index.cjs
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var fs = require('fs');
|
|
4
|
+
var path = require('path');
|
|
5
|
+
var jiti$1 = require('jiti');
|
|
6
|
+
|
|
7
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
8
|
+
function _interopNamespace(e) {
|
|
9
|
+
if (e && e.__esModule) return e;
|
|
10
|
+
var n = Object.create(null);
|
|
11
|
+
if (e) {
|
|
12
|
+
Object.keys(e).forEach(function (k) {
|
|
13
|
+
if (k !== 'default') {
|
|
14
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
15
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
get: function () { return e[k]; }
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
n.default = e;
|
|
23
|
+
return Object.freeze(n);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
|
|
27
|
+
var path__namespace = /*#__PURE__*/_interopNamespace(path);
|
|
28
|
+
|
|
29
|
+
// src/app/index.ts
|
|
30
|
+
|
|
3
31
|
// src/index.ts
|
|
4
32
|
function calculateDepthPriority(pathname) {
|
|
5
33
|
if (pathname === "/") return 1;
|
|
@@ -101,11 +129,49 @@ ${allEntries}
|
|
|
101
129
|
}
|
|
102
130
|
|
|
103
131
|
// src/app/index.ts
|
|
104
|
-
function
|
|
132
|
+
function findPageFiles(dir, baseDir = dir) {
|
|
133
|
+
const files = [];
|
|
134
|
+
try {
|
|
135
|
+
const entries = fs__namespace.readdirSync(dir, { withFileTypes: true });
|
|
136
|
+
for (const entry of entries) {
|
|
137
|
+
const fullPath = path__namespace.join(dir, entry.name);
|
|
138
|
+
if (entry.isDirectory()) {
|
|
139
|
+
files.push(...findPageFiles(fullPath, baseDir));
|
|
140
|
+
} else if (entry.name === "page.tsx" || entry.name === "page.ts") {
|
|
141
|
+
const relativePath = "./" + path__namespace.relative(baseDir, fullPath).replace(/\\/g, "/");
|
|
142
|
+
files.push(relativePath);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
} catch {
|
|
146
|
+
}
|
|
147
|
+
return files;
|
|
148
|
+
}
|
|
149
|
+
function detectAppDirectory() {
|
|
150
|
+
const srcApp = path__namespace.join(process.cwd(), "src/app");
|
|
151
|
+
if (fs__namespace.existsSync(srcApp)) {
|
|
152
|
+
return srcApp;
|
|
153
|
+
}
|
|
154
|
+
return path__namespace.join(process.cwd(), "app");
|
|
155
|
+
}
|
|
156
|
+
function resolveAppDirectory(options) {
|
|
157
|
+
if (options.appDirectory) {
|
|
158
|
+
return path__namespace.isAbsolute(options.appDirectory) ? options.appDirectory : path__namespace.join(process.cwd(), options.appDirectory);
|
|
159
|
+
}
|
|
160
|
+
return detectAppDirectory();
|
|
161
|
+
}
|
|
162
|
+
function getPageKeys(options) {
|
|
163
|
+
return findPageFiles(resolveAppDirectory(options));
|
|
164
|
+
}
|
|
165
|
+
var jiti = jiti$1.createJiti((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)), { jsx: true });
|
|
166
|
+
async function importPage(appDirectory, key) {
|
|
167
|
+
const relativePath = key.replace("./", "");
|
|
168
|
+
const absolutePath = path__namespace.join(appDirectory, relativePath);
|
|
169
|
+
return jiti.import(absolutePath);
|
|
170
|
+
}
|
|
171
|
+
function extractRoutes(pageKeys, localeSegment) {
|
|
105
172
|
const routes = [];
|
|
106
|
-
for (const key of
|
|
173
|
+
for (const key of pageKeys) {
|
|
107
174
|
if (key.includes("[...")) continue;
|
|
108
|
-
const pageModule = pagesContext(key);
|
|
109
175
|
let pathname = key.replace("./", "/").replace("/page.tsx", "");
|
|
110
176
|
if (localeSegment) {
|
|
111
177
|
pathname = pathname.replace(
|
|
@@ -124,12 +190,12 @@ function extractRoutes(pagesContext, localeSegment) {
|
|
|
124
190
|
routes.push({
|
|
125
191
|
pathname,
|
|
126
192
|
dynamicSegments,
|
|
127
|
-
|
|
193
|
+
key
|
|
128
194
|
});
|
|
129
195
|
}
|
|
130
196
|
return routes;
|
|
131
197
|
}
|
|
132
|
-
async function getAllPaths(routes, debug = false) {
|
|
198
|
+
async function getAllPaths(routes, appDirectory, debug = false) {
|
|
133
199
|
const allPaths = ["/"];
|
|
134
200
|
const seenPaths = /* @__PURE__ */ new Set(["/"]);
|
|
135
201
|
for (const route of routes) {
|
|
@@ -138,37 +204,51 @@ async function getAllPaths(routes, debug = false) {
|
|
|
138
204
|
allPaths.push(route.pathname);
|
|
139
205
|
seenPaths.add(route.pathname);
|
|
140
206
|
}
|
|
141
|
-
} else
|
|
207
|
+
} else {
|
|
208
|
+
let getParams = null;
|
|
142
209
|
try {
|
|
143
|
-
const params = await route.getParams();
|
|
144
210
|
if (debug) {
|
|
145
|
-
console.log(`[next-sitemap] ${route.pathname}:
|
|
211
|
+
console.log(`[next-sitemap] ${route.pathname}: importing ${route.key}`);
|
|
146
212
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
213
|
+
const module = await importPage(appDirectory, route.key);
|
|
214
|
+
getParams = module.generateStaticParams || null;
|
|
215
|
+
} catch (error) {
|
|
216
|
+
if (debug) {
|
|
217
|
+
console.warn(`[next-sitemap] ${route.pathname}: import failed:`, error);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (getParams) {
|
|
221
|
+
try {
|
|
222
|
+
const params = await getParams();
|
|
223
|
+
if (debug) {
|
|
224
|
+
console.log(`[next-sitemap] ${route.pathname}: generateStaticParams returned ${params.length} params`);
|
|
225
|
+
}
|
|
226
|
+
for (const param of params) {
|
|
227
|
+
let dynamicPath = route.pathname;
|
|
228
|
+
for (const segment of route.dynamicSegments) {
|
|
229
|
+
const value = param[segment];
|
|
230
|
+
if (value === void 0) {
|
|
231
|
+
if (debug) {
|
|
232
|
+
console.warn(`[next-sitemap] ${route.pathname}: missing param "${segment}" in`, param);
|
|
233
|
+
}
|
|
234
|
+
continue;
|
|
154
235
|
}
|
|
155
|
-
|
|
236
|
+
dynamicPath = dynamicPath.replace(`[${segment}]`, value);
|
|
237
|
+
}
|
|
238
|
+
if (!seenPaths.has(dynamicPath)) {
|
|
239
|
+
allPaths.push(dynamicPath);
|
|
240
|
+
seenPaths.add(dynamicPath);
|
|
156
241
|
}
|
|
157
|
-
dynamicPath = dynamicPath.replace(`[${segment}]`, value);
|
|
158
|
-
}
|
|
159
|
-
if (!seenPaths.has(dynamicPath)) {
|
|
160
|
-
allPaths.push(dynamicPath);
|
|
161
|
-
seenPaths.add(dynamicPath);
|
|
162
242
|
}
|
|
243
|
+
} catch (error) {
|
|
244
|
+
console.error(`[next-sitemap] Error calling generateStaticParams for ${route.pathname}:`, error);
|
|
245
|
+
}
|
|
246
|
+
} else {
|
|
247
|
+
if (debug) {
|
|
248
|
+
console.warn(
|
|
249
|
+
`[next-sitemap] Skipping dynamic route ${route.pathname}: no generateStaticParams exported. Use additionalSitemaps for routes that fetch data at runtime.`
|
|
250
|
+
);
|
|
163
251
|
}
|
|
164
|
-
} catch (error) {
|
|
165
|
-
console.error(`[next-sitemap] Error calling generateStaticParams for ${route.pathname}:`, error);
|
|
166
|
-
}
|
|
167
|
-
} else if (route.dynamicSegments.length > 0) {
|
|
168
|
-
if (debug) {
|
|
169
|
-
console.warn(
|
|
170
|
-
`[next-sitemap] Skipping dynamic route ${route.pathname}: no generateStaticParams exported. Use additionalSitemaps for routes that fetch data at runtime.`
|
|
171
|
-
);
|
|
172
252
|
}
|
|
173
253
|
}
|
|
174
254
|
}
|
|
@@ -200,18 +280,20 @@ function pathsToEntries(paths, config) {
|
|
|
200
280
|
function createSitemapIndexHandler(options) {
|
|
201
281
|
const { urlsPerSitemap = 5e3, locales = [], defaultLocale, additionalSitemaps, exclude, debug = false } = options;
|
|
202
282
|
const localeSegment = options.localeSegment ?? (locales.length > 0 || defaultLocale ? "[locale]" : "");
|
|
203
|
-
const
|
|
283
|
+
const appDir = resolveAppDirectory(options);
|
|
284
|
+
const pageKeys = getPageKeys(options);
|
|
285
|
+
const routes = extractRoutes(pageKeys, localeSegment);
|
|
204
286
|
return {
|
|
205
287
|
GET: async () => {
|
|
206
288
|
if (debug) {
|
|
207
289
|
console.log(`[next-sitemap] Found ${routes.length} routes:`);
|
|
208
290
|
routes.forEach((r) => {
|
|
209
|
-
const
|
|
210
|
-
const segments =
|
|
211
|
-
console.log(` ${r.pathname}${segments}
|
|
291
|
+
const isDynamic = r.dynamicSegments.length > 0;
|
|
292
|
+
const segments = isDynamic ? ` [${r.dynamicSegments.join(", ")}]` : "";
|
|
293
|
+
console.log(` ${r.pathname}${segments}${isDynamic ? " (dynamic)" : ""}`);
|
|
212
294
|
});
|
|
213
295
|
}
|
|
214
|
-
const allPaths = await getAllPaths(routes, debug);
|
|
296
|
+
const allPaths = await getAllPaths(routes, appDir, debug);
|
|
215
297
|
const filteredPaths = allPaths.filter((pathname) => !shouldExclude(pathname, exclude));
|
|
216
298
|
const sitemapCount = Math.max(1, Math.ceil(filteredPaths.length / urlsPerSitemap));
|
|
217
299
|
const xml = generateSitemapIndexXml(options.baseUrl, sitemapCount, {
|
|
@@ -226,9 +308,11 @@ function createSitemapIndexHandler(options) {
|
|
|
226
308
|
function createSitemapHandler(options) {
|
|
227
309
|
const { urlsPerSitemap = 5e3, locales = [], defaultLocale, exclude, debug = false } = options;
|
|
228
310
|
const localeSegment = options.localeSegment ?? (locales.length > 0 || defaultLocale ? "[locale]" : "");
|
|
229
|
-
const
|
|
311
|
+
const appDir = resolveAppDirectory(options);
|
|
312
|
+
const pageKeys = getPageKeys(options);
|
|
313
|
+
const routes = extractRoutes(pageKeys, localeSegment);
|
|
230
314
|
const getFilteredPaths = async () => {
|
|
231
|
-
const allPaths = await getAllPaths(routes, debug);
|
|
315
|
+
const allPaths = await getAllPaths(routes, appDir, debug);
|
|
232
316
|
return allPaths.filter((pathname) => !shouldExclude(pathname, exclude));
|
|
233
317
|
};
|
|
234
318
|
return {
|
package/dist/app/index.d.cts
CHANGED
|
@@ -16,11 +16,6 @@ interface SitemapConfig {
|
|
|
16
16
|
* Number of URLs per sitemap file (default: 5000)
|
|
17
17
|
*/
|
|
18
18
|
urlsPerSitemap?: number;
|
|
19
|
-
/**
|
|
20
|
-
* Path to the app directory relative to the project root
|
|
21
|
-
* Default: "./src/app" or "./app"
|
|
22
|
-
*/
|
|
23
|
-
appDir?: string;
|
|
24
19
|
/**
|
|
25
20
|
* Locale segment in the URL path (e.g., "[locale]")
|
|
26
21
|
* Auto-detected as "[locale]" when locales or defaultLocale is set
|
|
@@ -66,19 +61,19 @@ interface SitemapEntry {
|
|
|
66
61
|
languages?: Record<string, string>;
|
|
67
62
|
};
|
|
68
63
|
}
|
|
69
|
-
type PageModule = {
|
|
70
|
-
generateStaticParams?: () => Promise<Record<string, string>[]>;
|
|
71
|
-
};
|
|
72
64
|
|
|
73
65
|
interface CreateSitemapHandlerOptions extends SitemapConfig {
|
|
74
66
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
67
|
+
* Path to the app directory to scan for page.tsx files.
|
|
68
|
+
* Can be absolute or relative to process.cwd().
|
|
69
|
+
* If not provided, auto-detects src/app or app.
|
|
70
|
+
*
|
|
71
|
+
* Example:
|
|
72
|
+
* ```ts
|
|
73
|
+
* appDirectory: 'src/app/(frontend)'
|
|
74
|
+
* ```
|
|
77
75
|
*/
|
|
78
|
-
|
|
79
|
-
keys: () => string[];
|
|
80
|
-
(key: string): PageModule;
|
|
81
|
-
};
|
|
76
|
+
appDirectory?: string;
|
|
82
77
|
/**
|
|
83
78
|
* Enable debug logging to diagnose issues with route discovery
|
|
84
79
|
* Logs info about generateStaticParams calls and skipped routes
|
package/dist/app/index.d.ts
CHANGED
|
@@ -16,11 +16,6 @@ interface SitemapConfig {
|
|
|
16
16
|
* Number of URLs per sitemap file (default: 5000)
|
|
17
17
|
*/
|
|
18
18
|
urlsPerSitemap?: number;
|
|
19
|
-
/**
|
|
20
|
-
* Path to the app directory relative to the project root
|
|
21
|
-
* Default: "./src/app" or "./app"
|
|
22
|
-
*/
|
|
23
|
-
appDir?: string;
|
|
24
19
|
/**
|
|
25
20
|
* Locale segment in the URL path (e.g., "[locale]")
|
|
26
21
|
* Auto-detected as "[locale]" when locales or defaultLocale is set
|
|
@@ -66,19 +61,19 @@ interface SitemapEntry {
|
|
|
66
61
|
languages?: Record<string, string>;
|
|
67
62
|
};
|
|
68
63
|
}
|
|
69
|
-
type PageModule = {
|
|
70
|
-
generateStaticParams?: () => Promise<Record<string, string>[]>;
|
|
71
|
-
};
|
|
72
64
|
|
|
73
65
|
interface CreateSitemapHandlerOptions extends SitemapConfig {
|
|
74
66
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
67
|
+
* Path to the app directory to scan for page.tsx files.
|
|
68
|
+
* Can be absolute or relative to process.cwd().
|
|
69
|
+
* If not provided, auto-detects src/app or app.
|
|
70
|
+
*
|
|
71
|
+
* Example:
|
|
72
|
+
* ```ts
|
|
73
|
+
* appDirectory: 'src/app/(frontend)'
|
|
74
|
+
* ```
|
|
77
75
|
*/
|
|
78
|
-
|
|
79
|
-
keys: () => string[];
|
|
80
|
-
(key: string): PageModule;
|
|
81
|
-
};
|
|
76
|
+
appDirectory?: string;
|
|
82
77
|
/**
|
|
83
78
|
* Enable debug logging to diagnose issues with route discovery
|
|
84
79
|
* Logs info about generateStaticParams calls and skipped routes
|
package/dist/app/index.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { createJiti } from 'jiti';
|
|
4
|
+
|
|
5
|
+
// src/app/index.ts
|
|
6
|
+
|
|
1
7
|
// src/index.ts
|
|
2
8
|
function calculateDepthPriority(pathname) {
|
|
3
9
|
if (pathname === "/") return 1;
|
|
@@ -99,11 +105,49 @@ ${allEntries}
|
|
|
99
105
|
}
|
|
100
106
|
|
|
101
107
|
// src/app/index.ts
|
|
102
|
-
function
|
|
108
|
+
function findPageFiles(dir, baseDir = dir) {
|
|
109
|
+
const files = [];
|
|
110
|
+
try {
|
|
111
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
112
|
+
for (const entry of entries) {
|
|
113
|
+
const fullPath = path.join(dir, entry.name);
|
|
114
|
+
if (entry.isDirectory()) {
|
|
115
|
+
files.push(...findPageFiles(fullPath, baseDir));
|
|
116
|
+
} else if (entry.name === "page.tsx" || entry.name === "page.ts") {
|
|
117
|
+
const relativePath = "./" + path.relative(baseDir, fullPath).replace(/\\/g, "/");
|
|
118
|
+
files.push(relativePath);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
} catch {
|
|
122
|
+
}
|
|
123
|
+
return files;
|
|
124
|
+
}
|
|
125
|
+
function detectAppDirectory() {
|
|
126
|
+
const srcApp = path.join(process.cwd(), "src/app");
|
|
127
|
+
if (fs.existsSync(srcApp)) {
|
|
128
|
+
return srcApp;
|
|
129
|
+
}
|
|
130
|
+
return path.join(process.cwd(), "app");
|
|
131
|
+
}
|
|
132
|
+
function resolveAppDirectory(options) {
|
|
133
|
+
if (options.appDirectory) {
|
|
134
|
+
return path.isAbsolute(options.appDirectory) ? options.appDirectory : path.join(process.cwd(), options.appDirectory);
|
|
135
|
+
}
|
|
136
|
+
return detectAppDirectory();
|
|
137
|
+
}
|
|
138
|
+
function getPageKeys(options) {
|
|
139
|
+
return findPageFiles(resolveAppDirectory(options));
|
|
140
|
+
}
|
|
141
|
+
var jiti = createJiti(import.meta.url, { jsx: true });
|
|
142
|
+
async function importPage(appDirectory, key) {
|
|
143
|
+
const relativePath = key.replace("./", "");
|
|
144
|
+
const absolutePath = path.join(appDirectory, relativePath);
|
|
145
|
+
return jiti.import(absolutePath);
|
|
146
|
+
}
|
|
147
|
+
function extractRoutes(pageKeys, localeSegment) {
|
|
103
148
|
const routes = [];
|
|
104
|
-
for (const key of
|
|
149
|
+
for (const key of pageKeys) {
|
|
105
150
|
if (key.includes("[...")) continue;
|
|
106
|
-
const pageModule = pagesContext(key);
|
|
107
151
|
let pathname = key.replace("./", "/").replace("/page.tsx", "");
|
|
108
152
|
if (localeSegment) {
|
|
109
153
|
pathname = pathname.replace(
|
|
@@ -122,12 +166,12 @@ function extractRoutes(pagesContext, localeSegment) {
|
|
|
122
166
|
routes.push({
|
|
123
167
|
pathname,
|
|
124
168
|
dynamicSegments,
|
|
125
|
-
|
|
169
|
+
key
|
|
126
170
|
});
|
|
127
171
|
}
|
|
128
172
|
return routes;
|
|
129
173
|
}
|
|
130
|
-
async function getAllPaths(routes, debug = false) {
|
|
174
|
+
async function getAllPaths(routes, appDirectory, debug = false) {
|
|
131
175
|
const allPaths = ["/"];
|
|
132
176
|
const seenPaths = /* @__PURE__ */ new Set(["/"]);
|
|
133
177
|
for (const route of routes) {
|
|
@@ -136,37 +180,51 @@ async function getAllPaths(routes, debug = false) {
|
|
|
136
180
|
allPaths.push(route.pathname);
|
|
137
181
|
seenPaths.add(route.pathname);
|
|
138
182
|
}
|
|
139
|
-
} else
|
|
183
|
+
} else {
|
|
184
|
+
let getParams = null;
|
|
140
185
|
try {
|
|
141
|
-
const params = await route.getParams();
|
|
142
186
|
if (debug) {
|
|
143
|
-
console.log(`[next-sitemap] ${route.pathname}:
|
|
187
|
+
console.log(`[next-sitemap] ${route.pathname}: importing ${route.key}`);
|
|
144
188
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
189
|
+
const module = await importPage(appDirectory, route.key);
|
|
190
|
+
getParams = module.generateStaticParams || null;
|
|
191
|
+
} catch (error) {
|
|
192
|
+
if (debug) {
|
|
193
|
+
console.warn(`[next-sitemap] ${route.pathname}: import failed:`, error);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (getParams) {
|
|
197
|
+
try {
|
|
198
|
+
const params = await getParams();
|
|
199
|
+
if (debug) {
|
|
200
|
+
console.log(`[next-sitemap] ${route.pathname}: generateStaticParams returned ${params.length} params`);
|
|
201
|
+
}
|
|
202
|
+
for (const param of params) {
|
|
203
|
+
let dynamicPath = route.pathname;
|
|
204
|
+
for (const segment of route.dynamicSegments) {
|
|
205
|
+
const value = param[segment];
|
|
206
|
+
if (value === void 0) {
|
|
207
|
+
if (debug) {
|
|
208
|
+
console.warn(`[next-sitemap] ${route.pathname}: missing param "${segment}" in`, param);
|
|
209
|
+
}
|
|
210
|
+
continue;
|
|
152
211
|
}
|
|
153
|
-
|
|
212
|
+
dynamicPath = dynamicPath.replace(`[${segment}]`, value);
|
|
213
|
+
}
|
|
214
|
+
if (!seenPaths.has(dynamicPath)) {
|
|
215
|
+
allPaths.push(dynamicPath);
|
|
216
|
+
seenPaths.add(dynamicPath);
|
|
154
217
|
}
|
|
155
|
-
dynamicPath = dynamicPath.replace(`[${segment}]`, value);
|
|
156
|
-
}
|
|
157
|
-
if (!seenPaths.has(dynamicPath)) {
|
|
158
|
-
allPaths.push(dynamicPath);
|
|
159
|
-
seenPaths.add(dynamicPath);
|
|
160
218
|
}
|
|
219
|
+
} catch (error) {
|
|
220
|
+
console.error(`[next-sitemap] Error calling generateStaticParams for ${route.pathname}:`, error);
|
|
221
|
+
}
|
|
222
|
+
} else {
|
|
223
|
+
if (debug) {
|
|
224
|
+
console.warn(
|
|
225
|
+
`[next-sitemap] Skipping dynamic route ${route.pathname}: no generateStaticParams exported. Use additionalSitemaps for routes that fetch data at runtime.`
|
|
226
|
+
);
|
|
161
227
|
}
|
|
162
|
-
} catch (error) {
|
|
163
|
-
console.error(`[next-sitemap] Error calling generateStaticParams for ${route.pathname}:`, error);
|
|
164
|
-
}
|
|
165
|
-
} else if (route.dynamicSegments.length > 0) {
|
|
166
|
-
if (debug) {
|
|
167
|
-
console.warn(
|
|
168
|
-
`[next-sitemap] Skipping dynamic route ${route.pathname}: no generateStaticParams exported. Use additionalSitemaps for routes that fetch data at runtime.`
|
|
169
|
-
);
|
|
170
228
|
}
|
|
171
229
|
}
|
|
172
230
|
}
|
|
@@ -198,18 +256,20 @@ function pathsToEntries(paths, config) {
|
|
|
198
256
|
function createSitemapIndexHandler(options) {
|
|
199
257
|
const { urlsPerSitemap = 5e3, locales = [], defaultLocale, additionalSitemaps, exclude, debug = false } = options;
|
|
200
258
|
const localeSegment = options.localeSegment ?? (locales.length > 0 || defaultLocale ? "[locale]" : "");
|
|
201
|
-
const
|
|
259
|
+
const appDir = resolveAppDirectory(options);
|
|
260
|
+
const pageKeys = getPageKeys(options);
|
|
261
|
+
const routes = extractRoutes(pageKeys, localeSegment);
|
|
202
262
|
return {
|
|
203
263
|
GET: async () => {
|
|
204
264
|
if (debug) {
|
|
205
265
|
console.log(`[next-sitemap] Found ${routes.length} routes:`);
|
|
206
266
|
routes.forEach((r) => {
|
|
207
|
-
const
|
|
208
|
-
const segments =
|
|
209
|
-
console.log(` ${r.pathname}${segments}
|
|
267
|
+
const isDynamic = r.dynamicSegments.length > 0;
|
|
268
|
+
const segments = isDynamic ? ` [${r.dynamicSegments.join(", ")}]` : "";
|
|
269
|
+
console.log(` ${r.pathname}${segments}${isDynamic ? " (dynamic)" : ""}`);
|
|
210
270
|
});
|
|
211
271
|
}
|
|
212
|
-
const allPaths = await getAllPaths(routes, debug);
|
|
272
|
+
const allPaths = await getAllPaths(routes, appDir, debug);
|
|
213
273
|
const filteredPaths = allPaths.filter((pathname) => !shouldExclude(pathname, exclude));
|
|
214
274
|
const sitemapCount = Math.max(1, Math.ceil(filteredPaths.length / urlsPerSitemap));
|
|
215
275
|
const xml = generateSitemapIndexXml(options.baseUrl, sitemapCount, {
|
|
@@ -224,9 +284,11 @@ function createSitemapIndexHandler(options) {
|
|
|
224
284
|
function createSitemapHandler(options) {
|
|
225
285
|
const { urlsPerSitemap = 5e3, locales = [], defaultLocale, exclude, debug = false } = options;
|
|
226
286
|
const localeSegment = options.localeSegment ?? (locales.length > 0 || defaultLocale ? "[locale]" : "");
|
|
227
|
-
const
|
|
287
|
+
const appDir = resolveAppDirectory(options);
|
|
288
|
+
const pageKeys = getPageKeys(options);
|
|
289
|
+
const routes = extractRoutes(pageKeys, localeSegment);
|
|
228
290
|
const getFilteredPaths = async () => {
|
|
229
|
-
const allPaths = await getAllPaths(routes, debug);
|
|
291
|
+
const allPaths = await getAllPaths(routes, appDir, debug);
|
|
230
292
|
return allPaths.filter((pathname) => !shouldExclude(pathname, exclude));
|
|
231
293
|
};
|
|
232
294
|
return {
|
package/dist/index.d.cts
CHANGED
|
@@ -16,11 +16,6 @@ interface SitemapConfig {
|
|
|
16
16
|
* Number of URLs per sitemap file (default: 5000)
|
|
17
17
|
*/
|
|
18
18
|
urlsPerSitemap?: number;
|
|
19
|
-
/**
|
|
20
|
-
* Path to the app directory relative to the project root
|
|
21
|
-
* Default: "./src/app" or "./app"
|
|
22
|
-
*/
|
|
23
|
-
appDir?: string;
|
|
24
19
|
/**
|
|
25
20
|
* Locale segment in the URL path (e.g., "[locale]")
|
|
26
21
|
* Auto-detected as "[locale]" when locales or defaultLocale is set
|
package/dist/index.d.ts
CHANGED
|
@@ -16,11 +16,6 @@ interface SitemapConfig {
|
|
|
16
16
|
* Number of URLs per sitemap file (default: 5000)
|
|
17
17
|
*/
|
|
18
18
|
urlsPerSitemap?: number;
|
|
19
|
-
/**
|
|
20
|
-
* Path to the app directory relative to the project root
|
|
21
|
-
* Default: "./src/app" or "./app"
|
|
22
|
-
*/
|
|
23
|
-
appDir?: string;
|
|
24
19
|
/**
|
|
25
20
|
* Locale segment in the URL path (e.g., "[locale]")
|
|
26
21
|
* Auto-detected as "[locale]" when locales or defaultLocale is set
|
package/dist/pages/index.cjs
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var fs = require('fs');
|
|
4
|
+
var path = require('path');
|
|
5
|
+
var jiti$1 = require('jiti');
|
|
6
|
+
|
|
7
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
8
|
+
function _interopNamespace(e) {
|
|
9
|
+
if (e && e.__esModule) return e;
|
|
10
|
+
var n = Object.create(null);
|
|
11
|
+
if (e) {
|
|
12
|
+
Object.keys(e).forEach(function (k) {
|
|
13
|
+
if (k !== 'default') {
|
|
14
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
15
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
get: function () { return e[k]; }
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
n.default = e;
|
|
23
|
+
return Object.freeze(n);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
var fs__namespace = /*#__PURE__*/_interopNamespace(fs);
|
|
27
|
+
var path__namespace = /*#__PURE__*/_interopNamespace(path);
|
|
28
|
+
|
|
29
|
+
// src/pages/index.ts
|
|
30
|
+
|
|
3
31
|
// src/index.ts
|
|
4
32
|
function calculateDepthPriority(pathname) {
|
|
5
33
|
if (pathname === "/") return 1;
|
|
@@ -139,13 +167,50 @@ function generateRobotsTxt(config) {
|
|
|
139
167
|
}
|
|
140
168
|
|
|
141
169
|
// src/pages/index.ts
|
|
142
|
-
function
|
|
170
|
+
function findPageFiles(dir, baseDir = dir) {
|
|
171
|
+
const files = [];
|
|
172
|
+
try {
|
|
173
|
+
const entries = fs__namespace.readdirSync(dir, { withFileTypes: true });
|
|
174
|
+
for (const entry of entries) {
|
|
175
|
+
const fullPath = path__namespace.join(dir, entry.name);
|
|
176
|
+
if (entry.isDirectory()) {
|
|
177
|
+
if (entry.name === "api") continue;
|
|
178
|
+
files.push(...findPageFiles(fullPath, baseDir));
|
|
179
|
+
} else if ((entry.name.endsWith(".tsx") || entry.name.endsWith(".ts")) && !entry.name.startsWith("_")) {
|
|
180
|
+
const relativePath = "./" + path__namespace.relative(baseDir, fullPath).replace(/\\/g, "/");
|
|
181
|
+
files.push(relativePath);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
} catch {
|
|
185
|
+
}
|
|
186
|
+
return files;
|
|
187
|
+
}
|
|
188
|
+
function detectPagesDirectory() {
|
|
189
|
+
const srcPages = path__namespace.join(process.cwd(), "src/pages");
|
|
190
|
+
if (fs__namespace.existsSync(srcPages)) {
|
|
191
|
+
return srcPages;
|
|
192
|
+
}
|
|
193
|
+
return path__namespace.join(process.cwd(), "pages");
|
|
194
|
+
}
|
|
195
|
+
function resolvePagesDirectory(options) {
|
|
196
|
+
if (options.pagesDirectory) {
|
|
197
|
+
return path__namespace.isAbsolute(options.pagesDirectory) ? options.pagesDirectory : path__namespace.join(process.cwd(), options.pagesDirectory);
|
|
198
|
+
}
|
|
199
|
+
return detectPagesDirectory();
|
|
200
|
+
}
|
|
201
|
+
function getPageKeys(options) {
|
|
202
|
+
return findPageFiles(resolvePagesDirectory(options));
|
|
203
|
+
}
|
|
204
|
+
var jiti = jiti$1.createJiti((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)), { jsx: true });
|
|
205
|
+
async function importPage(pagesDirectory, key) {
|
|
206
|
+
const relativePath = key.replace("./", "");
|
|
207
|
+
const absolutePath = path__namespace.join(pagesDirectory, relativePath);
|
|
208
|
+
return jiti.import(absolutePath);
|
|
209
|
+
}
|
|
210
|
+
function extractRoutes(pageKeys, localeSegment) {
|
|
143
211
|
const routes = [];
|
|
144
|
-
for (const key of
|
|
212
|
+
for (const key of pageKeys) {
|
|
145
213
|
if (key.includes("[...")) continue;
|
|
146
|
-
if (key.includes("/api/")) continue;
|
|
147
|
-
if (key.includes("/_")) continue;
|
|
148
|
-
const pageModule = pagesContext(key);
|
|
149
214
|
let pathname = key.replace(/^\.\//, "/").replace(/\.tsx?$/, "").replace(/\/index$/, "/");
|
|
150
215
|
if (pathname === "") pathname = "/";
|
|
151
216
|
if (localeSegment) {
|
|
@@ -161,25 +226,15 @@ function extractRoutes(pagesContext, localeSegment) {
|
|
|
161
226
|
pathname = "/" + pathname;
|
|
162
227
|
}
|
|
163
228
|
const dynamicSegments = pathname.match(/\[([^\]]+)\]/g)?.map((s) => s.slice(1, -1)) || [];
|
|
164
|
-
const getStaticPaths = pageModule.getStaticPaths;
|
|
165
|
-
let getParams = null;
|
|
166
|
-
if (getStaticPaths) {
|
|
167
|
-
getParams = async () => {
|
|
168
|
-
const result = await getStaticPaths();
|
|
169
|
-
return result.paths.map((p) => p.params);
|
|
170
|
-
};
|
|
171
|
-
} else if (pageModule.generateStaticParams) {
|
|
172
|
-
getParams = pageModule.generateStaticParams;
|
|
173
|
-
}
|
|
174
229
|
routes.push({
|
|
175
230
|
pathname,
|
|
176
231
|
dynamicSegments,
|
|
177
|
-
|
|
232
|
+
key
|
|
178
233
|
});
|
|
179
234
|
}
|
|
180
235
|
return routes;
|
|
181
236
|
}
|
|
182
|
-
async function getAllPaths(routes, debug = false) {
|
|
237
|
+
async function getAllPaths(routes, pagesDirectory, debug = false) {
|
|
183
238
|
const allPaths = ["/"];
|
|
184
239
|
const seenPaths = /* @__PURE__ */ new Set(["/"]);
|
|
185
240
|
for (const route of routes) {
|
|
@@ -188,37 +243,59 @@ async function getAllPaths(routes, debug = false) {
|
|
|
188
243
|
allPaths.push(route.pathname);
|
|
189
244
|
seenPaths.add(route.pathname);
|
|
190
245
|
}
|
|
191
|
-
} else
|
|
246
|
+
} else {
|
|
247
|
+
let getParams = null;
|
|
192
248
|
try {
|
|
193
|
-
const params = await route.getParams();
|
|
194
249
|
if (debug) {
|
|
195
|
-
console.log(`[next-sitemap] ${route.pathname}:
|
|
250
|
+
console.log(`[next-sitemap] ${route.pathname}: importing ${route.key}`);
|
|
251
|
+
}
|
|
252
|
+
const module = await importPage(pagesDirectory, route.key);
|
|
253
|
+
const getStaticPaths = module.getStaticPaths;
|
|
254
|
+
if (getStaticPaths) {
|
|
255
|
+
getParams = async () => {
|
|
256
|
+
const result = await getStaticPaths();
|
|
257
|
+
return result.paths.map((p) => p.params);
|
|
258
|
+
};
|
|
259
|
+
} else if (module.generateStaticParams) {
|
|
260
|
+
getParams = module.generateStaticParams;
|
|
261
|
+
}
|
|
262
|
+
} catch (error) {
|
|
263
|
+
if (debug) {
|
|
264
|
+
console.warn(`[next-sitemap] ${route.pathname}: import failed:`, error);
|
|
196
265
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
266
|
+
}
|
|
267
|
+
if (getParams) {
|
|
268
|
+
try {
|
|
269
|
+
const params = await getParams();
|
|
270
|
+
if (debug) {
|
|
271
|
+
console.log(`[next-sitemap] ${route.pathname}: getStaticPaths returned ${params.length} params`);
|
|
272
|
+
}
|
|
273
|
+
for (const param of params) {
|
|
274
|
+
let dynamicPath = route.pathname;
|
|
275
|
+
for (const segment of route.dynamicSegments) {
|
|
276
|
+
const value = param[segment];
|
|
277
|
+
if (value === void 0) {
|
|
278
|
+
if (debug) {
|
|
279
|
+
console.warn(`[next-sitemap] ${route.pathname}: missing param "${segment}" in`, param);
|
|
280
|
+
}
|
|
281
|
+
continue;
|
|
204
282
|
}
|
|
205
|
-
|
|
283
|
+
dynamicPath = dynamicPath.replace(`[${segment}]`, value);
|
|
284
|
+
}
|
|
285
|
+
if (!seenPaths.has(dynamicPath)) {
|
|
286
|
+
allPaths.push(dynamicPath);
|
|
287
|
+
seenPaths.add(dynamicPath);
|
|
206
288
|
}
|
|
207
|
-
dynamicPath = dynamicPath.replace(`[${segment}]`, value);
|
|
208
|
-
}
|
|
209
|
-
if (!seenPaths.has(dynamicPath)) {
|
|
210
|
-
allPaths.push(dynamicPath);
|
|
211
|
-
seenPaths.add(dynamicPath);
|
|
212
289
|
}
|
|
290
|
+
} catch (error) {
|
|
291
|
+
console.error(`[next-sitemap] Error calling getStaticPaths for ${route.pathname}:`, error);
|
|
292
|
+
}
|
|
293
|
+
} else {
|
|
294
|
+
if (debug) {
|
|
295
|
+
console.warn(
|
|
296
|
+
`[next-sitemap] Skipping dynamic route ${route.pathname}: no getStaticPaths exported. Use additionalSitemaps for routes that fetch data at runtime.`
|
|
297
|
+
);
|
|
213
298
|
}
|
|
214
|
-
} catch (error) {
|
|
215
|
-
console.error(`[next-sitemap] Error calling getStaticPaths for ${route.pathname}:`, error);
|
|
216
|
-
}
|
|
217
|
-
} else if (route.dynamicSegments.length > 0) {
|
|
218
|
-
if (debug) {
|
|
219
|
-
console.warn(
|
|
220
|
-
`[next-sitemap] Skipping dynamic route ${route.pathname}: no getStaticPaths exported. Use additionalSitemaps for routes that fetch data at runtime.`
|
|
221
|
-
);
|
|
222
299
|
}
|
|
223
300
|
}
|
|
224
301
|
}
|
|
@@ -250,17 +327,19 @@ function pathsToEntries(paths, config) {
|
|
|
250
327
|
function createSitemapIndexApiHandler(options) {
|
|
251
328
|
const { urlsPerSitemap = 5e3, additionalSitemaps, exclude, debug = false } = options;
|
|
252
329
|
const localeSegment = options.localeSegment ?? "";
|
|
253
|
-
const
|
|
330
|
+
const pagesDir = resolvePagesDirectory(options);
|
|
331
|
+
const pageKeys = getPageKeys(options);
|
|
332
|
+
const routes = extractRoutes(pageKeys, localeSegment);
|
|
254
333
|
if (debug) {
|
|
255
334
|
console.log(`[next-sitemap] Found ${routes.length} routes:`);
|
|
256
335
|
routes.forEach((r) => {
|
|
257
|
-
const
|
|
258
|
-
const segments =
|
|
259
|
-
console.log(` ${r.pathname}${segments}
|
|
336
|
+
const isDynamic = r.dynamicSegments.length > 0;
|
|
337
|
+
const segments = isDynamic ? ` [${r.dynamicSegments.join(", ")}]` : "";
|
|
338
|
+
console.log(` ${r.pathname}${segments}${isDynamic ? " (dynamic)" : ""}`);
|
|
260
339
|
});
|
|
261
340
|
}
|
|
262
341
|
return async function handler(_req, res) {
|
|
263
|
-
const allPaths = await getAllPaths(routes, debug);
|
|
342
|
+
const allPaths = await getAllPaths(routes, pagesDir, debug);
|
|
264
343
|
const filteredPaths = allPaths.filter((pathname) => !shouldExclude(pathname, exclude));
|
|
265
344
|
const sitemapCount = Math.max(1, Math.ceil(filteredPaths.length / urlsPerSitemap));
|
|
266
345
|
const xml = generateSitemapIndexXml(options.baseUrl, sitemapCount, {
|
|
@@ -273,9 +352,11 @@ function createSitemapIndexApiHandler(options) {
|
|
|
273
352
|
function createSitemapApiHandler(options) {
|
|
274
353
|
const { urlsPerSitemap = 5e3, exclude, debug = false } = options;
|
|
275
354
|
const localeSegment = options.localeSegment ?? "";
|
|
276
|
-
const
|
|
355
|
+
const pagesDir = resolvePagesDirectory(options);
|
|
356
|
+
const pageKeys = getPageKeys(options);
|
|
357
|
+
const routes = extractRoutes(pageKeys, localeSegment);
|
|
277
358
|
const getFilteredPaths = async () => {
|
|
278
|
-
const allPaths = await getAllPaths(routes, debug);
|
|
359
|
+
const allPaths = await getAllPaths(routes, pagesDir, debug);
|
|
279
360
|
return allPaths.filter((pathname) => !shouldExclude(pathname, exclude));
|
|
280
361
|
};
|
|
281
362
|
return async function handler(req, res) {
|
|
@@ -294,8 +375,10 @@ function createSitemapApiHandler(options) {
|
|
|
294
375
|
async function getSitemapStaticPaths(options) {
|
|
295
376
|
const { urlsPerSitemap = 5e3, exclude, debug = false } = options;
|
|
296
377
|
const localeSegment = options.localeSegment ?? "";
|
|
297
|
-
const
|
|
298
|
-
const
|
|
378
|
+
const pagesDir = resolvePagesDirectory(options);
|
|
379
|
+
const pageKeys = getPageKeys(options);
|
|
380
|
+
const routes = extractRoutes(pageKeys, localeSegment);
|
|
381
|
+
const allPaths = await getAllPaths(routes, pagesDir, debug);
|
|
299
382
|
const filteredPaths = allPaths.filter((pathname) => !shouldExclude(pathname, exclude));
|
|
300
383
|
const sitemapCount = Math.max(1, Math.ceil(filteredPaths.length / urlsPerSitemap));
|
|
301
384
|
return {
|
package/dist/pages/index.d.cts
CHANGED
|
@@ -19,11 +19,6 @@ interface SitemapConfig {
|
|
|
19
19
|
* Number of URLs per sitemap file (default: 5000)
|
|
20
20
|
*/
|
|
21
21
|
urlsPerSitemap?: number;
|
|
22
|
-
/**
|
|
23
|
-
* Path to the app directory relative to the project root
|
|
24
|
-
* Default: "./src/app" or "./app"
|
|
25
|
-
*/
|
|
26
|
-
appDir?: string;
|
|
27
22
|
/**
|
|
28
23
|
* Locale segment in the URL path (e.g., "[locale]")
|
|
29
24
|
* Auto-detected as "[locale]" when locales or defaultLocale is set
|
|
@@ -69,9 +64,6 @@ interface SitemapEntry {
|
|
|
69
64
|
languages?: Record<string, string>;
|
|
70
65
|
};
|
|
71
66
|
}
|
|
72
|
-
type PageModule = {
|
|
73
|
-
generateStaticParams?: () => Promise<Record<string, string>[]>;
|
|
74
|
-
};
|
|
75
67
|
|
|
76
68
|
/**
|
|
77
69
|
* Generate robots.txt content from configuration
|
|
@@ -81,13 +73,16 @@ declare function generateRobotsTxt(config: MetadataRoute.Robots): string;
|
|
|
81
73
|
|
|
82
74
|
interface CreateSitemapApiHandlerOptions extends SitemapConfig {
|
|
83
75
|
/**
|
|
84
|
-
*
|
|
85
|
-
*
|
|
76
|
+
* Path to the pages directory to scan for page files.
|
|
77
|
+
* Can be absolute or relative to process.cwd().
|
|
78
|
+
* If not provided, auto-detects src/pages or pages.
|
|
79
|
+
*
|
|
80
|
+
* Example:
|
|
81
|
+
* ```ts
|
|
82
|
+
* pagesDirectory: 'src/pages'
|
|
83
|
+
* ```
|
|
86
84
|
*/
|
|
87
|
-
|
|
88
|
-
keys: () => string[];
|
|
89
|
-
(key: string): PageModule;
|
|
90
|
-
};
|
|
85
|
+
pagesDirectory?: string;
|
|
91
86
|
/**
|
|
92
87
|
* Enable debug logging to diagnose issues with route discovery
|
|
93
88
|
* Logs info about getStaticPaths calls and skipped routes
|
package/dist/pages/index.d.ts
CHANGED
|
@@ -19,11 +19,6 @@ interface SitemapConfig {
|
|
|
19
19
|
* Number of URLs per sitemap file (default: 5000)
|
|
20
20
|
*/
|
|
21
21
|
urlsPerSitemap?: number;
|
|
22
|
-
/**
|
|
23
|
-
* Path to the app directory relative to the project root
|
|
24
|
-
* Default: "./src/app" or "./app"
|
|
25
|
-
*/
|
|
26
|
-
appDir?: string;
|
|
27
22
|
/**
|
|
28
23
|
* Locale segment in the URL path (e.g., "[locale]")
|
|
29
24
|
* Auto-detected as "[locale]" when locales or defaultLocale is set
|
|
@@ -69,9 +64,6 @@ interface SitemapEntry {
|
|
|
69
64
|
languages?: Record<string, string>;
|
|
70
65
|
};
|
|
71
66
|
}
|
|
72
|
-
type PageModule = {
|
|
73
|
-
generateStaticParams?: () => Promise<Record<string, string>[]>;
|
|
74
|
-
};
|
|
75
67
|
|
|
76
68
|
/**
|
|
77
69
|
* Generate robots.txt content from configuration
|
|
@@ -81,13 +73,16 @@ declare function generateRobotsTxt(config: MetadataRoute.Robots): string;
|
|
|
81
73
|
|
|
82
74
|
interface CreateSitemapApiHandlerOptions extends SitemapConfig {
|
|
83
75
|
/**
|
|
84
|
-
*
|
|
85
|
-
*
|
|
76
|
+
* Path to the pages directory to scan for page files.
|
|
77
|
+
* Can be absolute or relative to process.cwd().
|
|
78
|
+
* If not provided, auto-detects src/pages or pages.
|
|
79
|
+
*
|
|
80
|
+
* Example:
|
|
81
|
+
* ```ts
|
|
82
|
+
* pagesDirectory: 'src/pages'
|
|
83
|
+
* ```
|
|
86
84
|
*/
|
|
87
|
-
|
|
88
|
-
keys: () => string[];
|
|
89
|
-
(key: string): PageModule;
|
|
90
|
-
};
|
|
85
|
+
pagesDirectory?: string;
|
|
91
86
|
/**
|
|
92
87
|
* Enable debug logging to diagnose issues with route discovery
|
|
93
88
|
* Logs info about getStaticPaths calls and skipped routes
|
package/dist/pages/index.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { createJiti } from 'jiti';
|
|
4
|
+
|
|
5
|
+
// src/pages/index.ts
|
|
6
|
+
|
|
1
7
|
// src/index.ts
|
|
2
8
|
function calculateDepthPriority(pathname) {
|
|
3
9
|
if (pathname === "/") return 1;
|
|
@@ -137,13 +143,50 @@ function generateRobotsTxt(config) {
|
|
|
137
143
|
}
|
|
138
144
|
|
|
139
145
|
// src/pages/index.ts
|
|
140
|
-
function
|
|
146
|
+
function findPageFiles(dir, baseDir = dir) {
|
|
147
|
+
const files = [];
|
|
148
|
+
try {
|
|
149
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
150
|
+
for (const entry of entries) {
|
|
151
|
+
const fullPath = path.join(dir, entry.name);
|
|
152
|
+
if (entry.isDirectory()) {
|
|
153
|
+
if (entry.name === "api") continue;
|
|
154
|
+
files.push(...findPageFiles(fullPath, baseDir));
|
|
155
|
+
} else if ((entry.name.endsWith(".tsx") || entry.name.endsWith(".ts")) && !entry.name.startsWith("_")) {
|
|
156
|
+
const relativePath = "./" + path.relative(baseDir, fullPath).replace(/\\/g, "/");
|
|
157
|
+
files.push(relativePath);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
} catch {
|
|
161
|
+
}
|
|
162
|
+
return files;
|
|
163
|
+
}
|
|
164
|
+
function detectPagesDirectory() {
|
|
165
|
+
const srcPages = path.join(process.cwd(), "src/pages");
|
|
166
|
+
if (fs.existsSync(srcPages)) {
|
|
167
|
+
return srcPages;
|
|
168
|
+
}
|
|
169
|
+
return path.join(process.cwd(), "pages");
|
|
170
|
+
}
|
|
171
|
+
function resolvePagesDirectory(options) {
|
|
172
|
+
if (options.pagesDirectory) {
|
|
173
|
+
return path.isAbsolute(options.pagesDirectory) ? options.pagesDirectory : path.join(process.cwd(), options.pagesDirectory);
|
|
174
|
+
}
|
|
175
|
+
return detectPagesDirectory();
|
|
176
|
+
}
|
|
177
|
+
function getPageKeys(options) {
|
|
178
|
+
return findPageFiles(resolvePagesDirectory(options));
|
|
179
|
+
}
|
|
180
|
+
var jiti = createJiti(import.meta.url, { jsx: true });
|
|
181
|
+
async function importPage(pagesDirectory, key) {
|
|
182
|
+
const relativePath = key.replace("./", "");
|
|
183
|
+
const absolutePath = path.join(pagesDirectory, relativePath);
|
|
184
|
+
return jiti.import(absolutePath);
|
|
185
|
+
}
|
|
186
|
+
function extractRoutes(pageKeys, localeSegment) {
|
|
141
187
|
const routes = [];
|
|
142
|
-
for (const key of
|
|
188
|
+
for (const key of pageKeys) {
|
|
143
189
|
if (key.includes("[...")) continue;
|
|
144
|
-
if (key.includes("/api/")) continue;
|
|
145
|
-
if (key.includes("/_")) continue;
|
|
146
|
-
const pageModule = pagesContext(key);
|
|
147
190
|
let pathname = key.replace(/^\.\//, "/").replace(/\.tsx?$/, "").replace(/\/index$/, "/");
|
|
148
191
|
if (pathname === "") pathname = "/";
|
|
149
192
|
if (localeSegment) {
|
|
@@ -159,25 +202,15 @@ function extractRoutes(pagesContext, localeSegment) {
|
|
|
159
202
|
pathname = "/" + pathname;
|
|
160
203
|
}
|
|
161
204
|
const dynamicSegments = pathname.match(/\[([^\]]+)\]/g)?.map((s) => s.slice(1, -1)) || [];
|
|
162
|
-
const getStaticPaths = pageModule.getStaticPaths;
|
|
163
|
-
let getParams = null;
|
|
164
|
-
if (getStaticPaths) {
|
|
165
|
-
getParams = async () => {
|
|
166
|
-
const result = await getStaticPaths();
|
|
167
|
-
return result.paths.map((p) => p.params);
|
|
168
|
-
};
|
|
169
|
-
} else if (pageModule.generateStaticParams) {
|
|
170
|
-
getParams = pageModule.generateStaticParams;
|
|
171
|
-
}
|
|
172
205
|
routes.push({
|
|
173
206
|
pathname,
|
|
174
207
|
dynamicSegments,
|
|
175
|
-
|
|
208
|
+
key
|
|
176
209
|
});
|
|
177
210
|
}
|
|
178
211
|
return routes;
|
|
179
212
|
}
|
|
180
|
-
async function getAllPaths(routes, debug = false) {
|
|
213
|
+
async function getAllPaths(routes, pagesDirectory, debug = false) {
|
|
181
214
|
const allPaths = ["/"];
|
|
182
215
|
const seenPaths = /* @__PURE__ */ new Set(["/"]);
|
|
183
216
|
for (const route of routes) {
|
|
@@ -186,37 +219,59 @@ async function getAllPaths(routes, debug = false) {
|
|
|
186
219
|
allPaths.push(route.pathname);
|
|
187
220
|
seenPaths.add(route.pathname);
|
|
188
221
|
}
|
|
189
|
-
} else
|
|
222
|
+
} else {
|
|
223
|
+
let getParams = null;
|
|
190
224
|
try {
|
|
191
|
-
const params = await route.getParams();
|
|
192
225
|
if (debug) {
|
|
193
|
-
console.log(`[next-sitemap] ${route.pathname}:
|
|
226
|
+
console.log(`[next-sitemap] ${route.pathname}: importing ${route.key}`);
|
|
227
|
+
}
|
|
228
|
+
const module = await importPage(pagesDirectory, route.key);
|
|
229
|
+
const getStaticPaths = module.getStaticPaths;
|
|
230
|
+
if (getStaticPaths) {
|
|
231
|
+
getParams = async () => {
|
|
232
|
+
const result = await getStaticPaths();
|
|
233
|
+
return result.paths.map((p) => p.params);
|
|
234
|
+
};
|
|
235
|
+
} else if (module.generateStaticParams) {
|
|
236
|
+
getParams = module.generateStaticParams;
|
|
194
237
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
238
|
+
} catch (error) {
|
|
239
|
+
if (debug) {
|
|
240
|
+
console.warn(`[next-sitemap] ${route.pathname}: import failed:`, error);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
if (getParams) {
|
|
244
|
+
try {
|
|
245
|
+
const params = await getParams();
|
|
246
|
+
if (debug) {
|
|
247
|
+
console.log(`[next-sitemap] ${route.pathname}: getStaticPaths returned ${params.length} params`);
|
|
248
|
+
}
|
|
249
|
+
for (const param of params) {
|
|
250
|
+
let dynamicPath = route.pathname;
|
|
251
|
+
for (const segment of route.dynamicSegments) {
|
|
252
|
+
const value = param[segment];
|
|
253
|
+
if (value === void 0) {
|
|
254
|
+
if (debug) {
|
|
255
|
+
console.warn(`[next-sitemap] ${route.pathname}: missing param "${segment}" in`, param);
|
|
256
|
+
}
|
|
257
|
+
continue;
|
|
202
258
|
}
|
|
203
|
-
|
|
259
|
+
dynamicPath = dynamicPath.replace(`[${segment}]`, value);
|
|
260
|
+
}
|
|
261
|
+
if (!seenPaths.has(dynamicPath)) {
|
|
262
|
+
allPaths.push(dynamicPath);
|
|
263
|
+
seenPaths.add(dynamicPath);
|
|
204
264
|
}
|
|
205
|
-
dynamicPath = dynamicPath.replace(`[${segment}]`, value);
|
|
206
|
-
}
|
|
207
|
-
if (!seenPaths.has(dynamicPath)) {
|
|
208
|
-
allPaths.push(dynamicPath);
|
|
209
|
-
seenPaths.add(dynamicPath);
|
|
210
265
|
}
|
|
266
|
+
} catch (error) {
|
|
267
|
+
console.error(`[next-sitemap] Error calling getStaticPaths for ${route.pathname}:`, error);
|
|
268
|
+
}
|
|
269
|
+
} else {
|
|
270
|
+
if (debug) {
|
|
271
|
+
console.warn(
|
|
272
|
+
`[next-sitemap] Skipping dynamic route ${route.pathname}: no getStaticPaths exported. Use additionalSitemaps for routes that fetch data at runtime.`
|
|
273
|
+
);
|
|
211
274
|
}
|
|
212
|
-
} catch (error) {
|
|
213
|
-
console.error(`[next-sitemap] Error calling getStaticPaths for ${route.pathname}:`, error);
|
|
214
|
-
}
|
|
215
|
-
} else if (route.dynamicSegments.length > 0) {
|
|
216
|
-
if (debug) {
|
|
217
|
-
console.warn(
|
|
218
|
-
`[next-sitemap] Skipping dynamic route ${route.pathname}: no getStaticPaths exported. Use additionalSitemaps for routes that fetch data at runtime.`
|
|
219
|
-
);
|
|
220
275
|
}
|
|
221
276
|
}
|
|
222
277
|
}
|
|
@@ -248,17 +303,19 @@ function pathsToEntries(paths, config) {
|
|
|
248
303
|
function createSitemapIndexApiHandler(options) {
|
|
249
304
|
const { urlsPerSitemap = 5e3, additionalSitemaps, exclude, debug = false } = options;
|
|
250
305
|
const localeSegment = options.localeSegment ?? "";
|
|
251
|
-
const
|
|
306
|
+
const pagesDir = resolvePagesDirectory(options);
|
|
307
|
+
const pageKeys = getPageKeys(options);
|
|
308
|
+
const routes = extractRoutes(pageKeys, localeSegment);
|
|
252
309
|
if (debug) {
|
|
253
310
|
console.log(`[next-sitemap] Found ${routes.length} routes:`);
|
|
254
311
|
routes.forEach((r) => {
|
|
255
|
-
const
|
|
256
|
-
const segments =
|
|
257
|
-
console.log(` ${r.pathname}${segments}
|
|
312
|
+
const isDynamic = r.dynamicSegments.length > 0;
|
|
313
|
+
const segments = isDynamic ? ` [${r.dynamicSegments.join(", ")}]` : "";
|
|
314
|
+
console.log(` ${r.pathname}${segments}${isDynamic ? " (dynamic)" : ""}`);
|
|
258
315
|
});
|
|
259
316
|
}
|
|
260
317
|
return async function handler(_req, res) {
|
|
261
|
-
const allPaths = await getAllPaths(routes, debug);
|
|
318
|
+
const allPaths = await getAllPaths(routes, pagesDir, debug);
|
|
262
319
|
const filteredPaths = allPaths.filter((pathname) => !shouldExclude(pathname, exclude));
|
|
263
320
|
const sitemapCount = Math.max(1, Math.ceil(filteredPaths.length / urlsPerSitemap));
|
|
264
321
|
const xml = generateSitemapIndexXml(options.baseUrl, sitemapCount, {
|
|
@@ -271,9 +328,11 @@ function createSitemapIndexApiHandler(options) {
|
|
|
271
328
|
function createSitemapApiHandler(options) {
|
|
272
329
|
const { urlsPerSitemap = 5e3, exclude, debug = false } = options;
|
|
273
330
|
const localeSegment = options.localeSegment ?? "";
|
|
274
|
-
const
|
|
331
|
+
const pagesDir = resolvePagesDirectory(options);
|
|
332
|
+
const pageKeys = getPageKeys(options);
|
|
333
|
+
const routes = extractRoutes(pageKeys, localeSegment);
|
|
275
334
|
const getFilteredPaths = async () => {
|
|
276
|
-
const allPaths = await getAllPaths(routes, debug);
|
|
335
|
+
const allPaths = await getAllPaths(routes, pagesDir, debug);
|
|
277
336
|
return allPaths.filter((pathname) => !shouldExclude(pathname, exclude));
|
|
278
337
|
};
|
|
279
338
|
return async function handler(req, res) {
|
|
@@ -292,8 +351,10 @@ function createSitemapApiHandler(options) {
|
|
|
292
351
|
async function getSitemapStaticPaths(options) {
|
|
293
352
|
const { urlsPerSitemap = 5e3, exclude, debug = false } = options;
|
|
294
353
|
const localeSegment = options.localeSegment ?? "";
|
|
295
|
-
const
|
|
296
|
-
const
|
|
354
|
+
const pagesDir = resolvePagesDirectory(options);
|
|
355
|
+
const pageKeys = getPageKeys(options);
|
|
356
|
+
const routes = extractRoutes(pageKeys, localeSegment);
|
|
357
|
+
const allPaths = await getAllPaths(routes, pagesDir, debug);
|
|
297
358
|
const filteredPaths = allPaths.filter((pathname) => !shouldExclude(pathname, exclude));
|
|
298
359
|
const sitemapCount = Math.max(1, Math.ceil(filteredPaths.length / urlsPerSitemap));
|
|
299
360
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onruntime/next-sitemap",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.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": {
|
|
@@ -38,11 +38,15 @@
|
|
|
38
38
|
"files": [
|
|
39
39
|
"dist"
|
|
40
40
|
],
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"jiti": "^2.6.1"
|
|
43
|
+
},
|
|
41
44
|
"peerDependencies": {
|
|
42
45
|
"next": ">=14.0.0"
|
|
43
46
|
},
|
|
44
47
|
"devDependencies": {
|
|
45
48
|
"@types/node": "^22.0.0",
|
|
49
|
+
"@types/react": "^19.2.7",
|
|
46
50
|
"next": "^16.1.1",
|
|
47
51
|
"tsup": "^8",
|
|
48
52
|
"tsx": "^4",
|